Compare commits
	
		
			32 Commits
		
	
	
		
			version/0.
			...
			version/0.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 06d15d8a27 | |||
| b5c711854b | |||
| 4cf6c36f34 | |||
| 75a6f6c875 | |||
| 62abe3f256 | |||
| 9296c41650 | |||
| 7fb48fde6d | |||
| 174472bb45 | |||
| 17575ed921 | |||
| b1b1a27444 | |||
| f97a5eeefb | |||
| 10fd96981e | |||
| 67e3eb549c | |||
| 30a6d1f0b1 | |||
| 3d1fa9f048 | |||
| 1d2be6e68b | |||
| c21e343986 | |||
| ff37ed095c | |||
| 8623a2c3fc | |||
| 23d277eaf1 | |||
| 75ced59451 | |||
| bccf424c5e | |||
| 2f9ae40d20 | |||
| 11e1eec3fb | |||
| 765c5633df | |||
| 6344b1aafb | |||
| ed25801e6e | |||
| 4d0148193f | |||
| 804ae15c2e | |||
| b35a9fad86 | |||
| a4f83bd28a | |||
| 796f83c3d0 | 
| @ -1,5 +1,5 @@ | ||||
| [bumpversion] | ||||
| current_version = 0.2.0-beta | ||||
| current_version = 0.2.8-beta | ||||
| tag = True | ||||
| commit = True | ||||
| parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*) | ||||
| @ -15,10 +15,6 @@ values = | ||||
| 	beta | ||||
| 	stable | ||||
|  | ||||
| [bumpversion:file:client-packages/allauth/setup.py] | ||||
|  | ||||
| [bumpversion:file:client-packages/sentry-auth-passbook/setup.py] | ||||
|  | ||||
| [bumpversion:file:helm/passbook/values.yaml] | ||||
|  | ||||
| [bumpversion:file:helm/passbook/Chart.yaml] | ||||
| @ -27,33 +23,5 @@ values = | ||||
|  | ||||
| [bumpversion:file:passbook/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/api/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/core/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/admin/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/captcha_factor/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/oauth_client/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/ldap/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/lib/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/hibp_policy/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/password_expiry_policy/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/saml_idp/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/audit/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/oauth_provider/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/otp/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/app_gw/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/suspicious_policy/__init__.py] | ||||
| [bumpversion:file:passbook/core/nginx.conf] | ||||
|  | ||||
|  | ||||
							
								
								
									
										113
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							| @ -1,109 +1,132 @@ | ||||
| # Global Variables | ||||
| stages: | ||||
|   - build-buildimage | ||||
|   - build-base-image | ||||
|   - build-dev-image | ||||
|   - test | ||||
|   - build | ||||
|   - docs | ||||
|   - deploy | ||||
| image: docker.beryju.org/passbook/build-base:latest | ||||
| services: | ||||
|   - postgres:latest | ||||
|   - redis:latest | ||||
|   - package | ||||
| image: docker.beryju.org/passbook/dev:latest | ||||
|  | ||||
| variables: | ||||
|   POSTGRES_DB: passbook | ||||
|   POSTGRES_USER: passbook | ||||
|   POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77" | ||||
|  | ||||
| create-build-image: | ||||
| before_script: | ||||
|   # Ensure all dependencies are installed, even those not included in passbook/dev | ||||
|   - pip install -r requirements.txt | ||||
|   - pip install -r requirements-dev.txt | ||||
|  | ||||
| create-base-image: | ||||
|   image: | ||||
|     name: gcr.io/kaniko-project/executor:debug | ||||
|     entrypoint: [""] | ||||
|   before_script: | ||||
|     - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json | ||||
|   script: | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.build-base --destination docker.beryju.org/passbook/build-base:latest --destination docker.beryju.org/passbook/build-base:0.2.0-beta | ||||
|   stage: build-buildimage | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.base --destination docker.beryju.org/passbook/base:latest --destination docker.beryju.org/passbook/base:0.2.8-beta | ||||
|   stage: build-base-image | ||||
|   only: | ||||
|     refs: | ||||
|       - tags | ||||
|       - /^version/.*$/ | ||||
|  | ||||
| build-dev-image: | ||||
|   image: | ||||
|     name: gcr.io/kaniko-project/executor:debug | ||||
|     entrypoint: [""] | ||||
|   before_script: | ||||
|     - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json | ||||
|   script: | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.dev --destination docker.beryju.org/passbook/dev:latest --destination docker.beryju.org/passbook/dev:0.2.8-beta | ||||
|   stage: build-dev-image | ||||
|   only: | ||||
|     refs: | ||||
|       - tags | ||||
|       - /^version/.*$/ | ||||
|  | ||||
|  | ||||
| isort: | ||||
|   script: | ||||
|     - isort -c -sg env | ||||
|   stage: test | ||||
|   services: | ||||
|   - postgres:latest | ||||
|   - redis:latest | ||||
| migrations: | ||||
|   script: | ||||
|     - python manage.py migrate | ||||
|   stage: test | ||||
|   services: | ||||
|   - postgres:latest | ||||
|   - redis:latest | ||||
| prospector: | ||||
|   script: | ||||
|     - prospector | ||||
|   stage: test | ||||
|   services: | ||||
|   - postgres:latest | ||||
|   - redis:latest | ||||
| pylint: | ||||
|   script: | ||||
|     - pylint passbook | ||||
|   stage: test | ||||
|   services: | ||||
|   - postgres:latest | ||||
|   - redis:latest | ||||
| coverage: | ||||
|   script: | ||||
|     - python manage.py collectstatic --no-input | ||||
|     - coverage run manage.py test | ||||
|     - coverage report | ||||
|     - coverage html | ||||
|   stage: test | ||||
| bandit: | ||||
|   script: | ||||
|     - bandit -r passbook | ||||
|   stage: test | ||||
|   services: | ||||
|   - postgres:latest | ||||
|   - redis:latest | ||||
|  | ||||
| package-docker: | ||||
| package-passbook-server: | ||||
|   image: | ||||
|     name: gcr.io/kaniko-project/executor:debug | ||||
|     entrypoint: [""] | ||||
|   before_script: | ||||
|     - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json | ||||
|   script: | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.beryju.org/passbook/server:latest --destination docker.beryju.org/passbook/server:0.2.0-beta | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.beryju.org/passbook/server:latest --destination docker.beryju.org/passbook/server:0.2.8-beta | ||||
|   stage: build | ||||
|   only: | ||||
|     - tags | ||||
|     - /^version/.*$/ | ||||
| package-helm: | ||||
| build-passbook-static: | ||||
|   stage: build | ||||
|   image: | ||||
|     name: gcr.io/kaniko-project/executor:debug | ||||
|     entrypoint: [""] | ||||
|   before_script: | ||||
|     - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json | ||||
|   script: | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.static --destination docker.beryju.org/passbook/static:latest --destination docker.beryju.org/passbook/static:0.2.8-beta | ||||
|   only: | ||||
|     - tags | ||||
|     - /^version/.*$/ | ||||
|   # running collectstatic fully initialises django, hence we need that databases | ||||
|   services: | ||||
|     - postgres:latest | ||||
|     - redis:latest | ||||
|  | ||||
| package-helm: | ||||
|   image: debian:stretch-slim | ||||
|   stage: package | ||||
|   before_script: | ||||
|     - apt update && apt install -y curl | ||||
|     - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash | ||||
|   script: | ||||
|     - helm init --client-only | ||||
|     - helm dependency build helm/passbook | ||||
|     - helm package helm/passbook | ||||
|   artifacts: | ||||
|     paths: | ||||
|       - passbook-*.tgz | ||||
|     expire_in: 2 days | ||||
|     expire_in: 1 week | ||||
|   only: | ||||
|     - tags | ||||
|     - /^version/.*$/ | ||||
|  | ||||
| package-client-package-allauth: | ||||
|   script: | ||||
|     - cd client-packages/allauth | ||||
|     - python setup.py sdist | ||||
|     - twine upload --username $TWINE_USERNAME --password $TWINE_PASSWORD dist/* | ||||
|   stage: build | ||||
|   only: | ||||
|     refs: | ||||
|       - tags | ||||
|       - /^version/.*$/ | ||||
|     changes: | ||||
|       - client-packages/allauth/** | ||||
|  | ||||
| package-client-package-sentry: | ||||
|   script: | ||||
|     - cd client-packages/sentry-auth-passbook | ||||
|     - python setup.py sdist | ||||
|     - twine upload --username $TWINE_USERNAME --password $TWINE_PASSWORD dist/* | ||||
|   stage: build | ||||
|   only: | ||||
|     refs: | ||||
|       - tags | ||||
|       - /^version/.*$/ | ||||
|     changes: | ||||
|       - client-packages/sentry-auth-passbook/** | ||||
|  | ||||
							
								
								
									
										32
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								Dockerfile
									
									
									
									
									
								
							| @ -1,34 +1,8 @@ | ||||
| FROM python:3.6-slim-stretch as build | ||||
| FROM docker.beryju.org/passbook/base:latest | ||||
|  | ||||
| COPY ./passbook/ /app/passbook | ||||
| COPY ./manage.py /app/ | ||||
| COPY ./requirements.txt /app/ | ||||
|  | ||||
| WORKDIR /app/ | ||||
|  | ||||
| RUN apt-get update && apt-get install build-essential libssl-dev libffi-dev libpq-dev -y && \ | ||||
|     mkdir /app/static/ && \ | ||||
|     pip install -r requirements.txt && \ | ||||
|     pip install psycopg2 && \ | ||||
|     ./manage.py collectstatic --no-input && \ | ||||
|     apt-get remove --purge -y build-essential && \ | ||||
|     apt-get autoremove --purge -y | ||||
|  | ||||
| FROM python:3.6-slim-stretch | ||||
|  | ||||
| COPY ./passbook/ /app/passbook | ||||
| COPY ./manage.py /app/ | ||||
| COPY ./requirements.txt /app/ | ||||
| COPY --from=build /app/static /app/static/ | ||||
|  | ||||
| WORKDIR /app/ | ||||
|  | ||||
| RUN apt-get update && apt-get install build-essential libssl-dev libffi-dev libpq-dev -y && \ | ||||
|     pip install -r requirements.txt && \ | ||||
|     pip install psycopg2 && \ | ||||
|     adduser --system --home /app/ passbook && \ | ||||
|     chown -R passbook /app/ && \ | ||||
|     apt-get remove --purge -y build-essential && \ | ||||
|     apt-get autoremove --purge -y | ||||
|  | ||||
| USER passbook | ||||
|  | ||||
| WORKDIR /app/ | ||||
|  | ||||
							
								
								
									
										11
									
								
								Dockerfile.base
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Dockerfile.base
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| FROM python:3.7-alpine | ||||
|  | ||||
| COPY ./requirements.txt /app/ | ||||
|  | ||||
| WORKDIR /app/ | ||||
|  | ||||
| RUN apk update && \ | ||||
|     apk add --no-cache openssl-dev build-base libxml2-dev libxslt-dev libffi-dev gcc musl-dev libgcc zlib-dev postgresql-dev && \ | ||||
|     pip install -r /app/requirements.txt  --no-cache-dir && \ | ||||
|     adduser -S passbook && \ | ||||
|     chown -R passbook /app | ||||
| @ -1,12 +0,0 @@ | ||||
| FROM python:3.6 | ||||
|  | ||||
| COPY ./passbook/ /app/passbook | ||||
| COPY ./client-packages/ /app/client-packages | ||||
| COPY ./requirements.txt /app/ | ||||
| COPY ./requirements-dev.txt /app/ | ||||
|  | ||||
| WORKDIR /app/ | ||||
|  | ||||
| RUN apt-get update && apt-get install libssl-dev libffi-dev libpq-dev -y && \ | ||||
|      pip install -U -r requirements-dev.txt && \ | ||||
|      rm -rf /app/* | ||||
							
								
								
									
										5
									
								
								Dockerfile.dev
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Dockerfile.dev
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| FROM docker.beryju.org/passbook/base:latest | ||||
|  | ||||
| COPY ./requirements-dev.txt /app/ | ||||
|  | ||||
| RUN pip install -r /app/requirements-dev.txt  --no-cache-dir | ||||
							
								
								
									
										14
									
								
								Dockerfile.static
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Dockerfile.static
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| FROM docker.beryju.org/passbook/dev:latest as static-build | ||||
|  | ||||
| COPY ./passbook/ /app/passbook | ||||
| COPY ./manage.py /app/ | ||||
| COPY ./requirements.txt /app/ | ||||
|  | ||||
| WORKDIR /app/ | ||||
|  | ||||
| RUN ./manage.py collectstatic --no-input | ||||
|  | ||||
| FROM nginx:latest | ||||
|  | ||||
| COPY --from=static-build /app/static /static/static/ | ||||
| COPY ./passbook/core/nginx.conf /etc/nginx/nginx.conf | ||||
| @ -1,35 +0,0 @@ | ||||
| """passbook provider""" | ||||
| from allauth.socialaccount.providers.base import ProviderAccount | ||||
| from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider | ||||
|  | ||||
|  | ||||
| class PassbookAccount(ProviderAccount): | ||||
|     """passbook account""" | ||||
|  | ||||
|     def to_str(self): | ||||
|         dflt = super().to_str() | ||||
|         return self.account.extra_data.get('username', dflt) | ||||
|  | ||||
|  | ||||
| class PassbookProvider(OAuth2Provider): | ||||
|     """passbook provider""" | ||||
|  | ||||
|     id = 'passbook' | ||||
|     name = 'passbook' | ||||
|     account_class = PassbookAccount | ||||
|  | ||||
|     def extract_uid(self, data): | ||||
|         return str(data['sub']) | ||||
|  | ||||
|     def extract_common_fields(self, data): | ||||
|         return { | ||||
|             'email': data.get('email'), | ||||
|             'username': data.get('preferred_username'), | ||||
|             'name': data.get('name'), | ||||
|         } | ||||
|  | ||||
|     def get_default_scope(self): | ||||
|         return ['openid:userinfo'] | ||||
|  | ||||
|  | ||||
| provider_classes = [PassbookProvider] # noqa | ||||
| @ -1,6 +0,0 @@ | ||||
| """passbook provider""" | ||||
| from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns | ||||
|  | ||||
| from allauth_passbook.provider import PassbookProvider | ||||
|  | ||||
| urlpatterns = default_urlpatterns(PassbookProvider) | ||||
| @ -1,37 +0,0 @@ | ||||
| """passbook adapter""" | ||||
| import requests | ||||
| from allauth.socialaccount import app_settings | ||||
| from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter, | ||||
|                                                           OAuth2CallbackView, | ||||
|                                                           OAuth2LoginView) | ||||
|  | ||||
| from allauth_passbook.provider import PassbookProvider | ||||
|  | ||||
|  | ||||
| class PassbookOAuth2Adapter(OAuth2Adapter): | ||||
|     """passbook OAuth2 Adapter""" | ||||
|     provider_id = PassbookProvider.id | ||||
|     # pylint: disable=no-member | ||||
|     settings = app_settings.PROVIDERS.get(provider_id, {}) # noqa | ||||
|     provider_base_url = settings.get("PASSBOOK_URL", 'https://id.beryju.org') | ||||
|  | ||||
|     access_token_url = '{0}/application/oauth/token/'.format(provider_base_url) | ||||
|     authorize_url = '{0}/application/oauth/authorize/'.format(provider_base_url) | ||||
|     profile_url = '{0}/api/v1/openid/'.format( | ||||
|         provider_base_url) | ||||
|  | ||||
|     def complete_login(self, request, app, access_token, **kwargs): | ||||
|         headers = { | ||||
|             'Authorization': 'Bearer {0}'.format(access_token.token), | ||||
|             'Content-Type': 'application/json', | ||||
|         } | ||||
|         extra_data = requests.get(self.profile_url, headers=headers) | ||||
|  | ||||
|         return self.get_provider().sociallogin_from_response( | ||||
|             request, | ||||
|             extra_data.json() | ||||
|         ) | ||||
|  | ||||
|  | ||||
| oauth2_login = OAuth2LoginView.adapter_view(PassbookOAuth2Adapter) # noqa | ||||
| oauth2_callback = OAuth2CallbackView.adapter_view(PassbookOAuth2Adapter) # noqa | ||||
| @ -1 +0,0 @@ | ||||
| django-allauth | ||||
| @ -1,33 +0,0 @@ | ||||
| """passbook allauth setup.py""" | ||||
| from setuptools import setup | ||||
|  | ||||
| setup( | ||||
|     name='django-allauth-passbook', | ||||
|     version='0.2.0-beta', | ||||
|     description='passbook support for django-allauth', | ||||
|     # long_description='\n'.join(read_simple('docs/index.md')[2:]), | ||||
|     long_description_content_type='text/markdown', | ||||
|     author='BeryJu.org', | ||||
|     author_email='hello@beryju.org', | ||||
|     packages=['allauth_passbook'], | ||||
|     include_package_data=True, | ||||
|     install_requires=['django-allauth'], | ||||
|     keywords='django allauth passbook', | ||||
|     license='MIT', | ||||
|     classifiers=[ | ||||
|         'Intended Audience :: Developers', | ||||
|         'Topic :: Software Development :: Libraries :: Python Modules', | ||||
|         'Environment :: Web Environment', | ||||
|         'Topic :: Internet', | ||||
|         'License :: OSI Approved :: MIT License', | ||||
|         'Operating System :: OS Independent', | ||||
|         'Programming Language :: Python', | ||||
|         'Programming Language :: Python :: 3.4', | ||||
|         'Programming Language :: Python :: 3.5', | ||||
|         'Programming Language :: Python :: 3.6', | ||||
|         'Framework :: Django', | ||||
|         'Framework :: Django :: 1.11', | ||||
|         'Framework :: Django :: 2.0', | ||||
|         'Framework :: Django :: 2.1', | ||||
|     ], | ||||
| ) | ||||
| @ -1,5 +0,0 @@ | ||||
| *.pyc | ||||
| *.egg-info/ | ||||
| *.eggs | ||||
| /dist | ||||
| /build | ||||
| @ -1,32 +0,0 @@ | ||||
| sudo: false | ||||
| language: python | ||||
| services: | ||||
|   - memcached | ||||
|   - postgresql | ||||
|   - redis-server | ||||
| python: | ||||
|   - '2.7' | ||||
| cache: | ||||
|   directories: | ||||
|     - node_modules | ||||
|     - "$HOME/.cache/pip" | ||||
| deploy: | ||||
|   provider: pypi | ||||
|   user: getsentry | ||||
|   password: | ||||
|     secure: kVmxKHkBWRLYyZme05p+WZSJmb8GjHV9uyuaSCVMRlqWCW+GXRB7P1xXR2jb9URTlNdcs56Ab/UrwzCbMFGC8LmwCeFVgIR/ltytVZG2FgXZPWaeA4dH25qK2oGWgzJ/xeiMpmuJqN9hRl25MX6jG7FZKvrrOkG7+8tpPd1yO+uYWZQbnebZMjcPBqEpn7CC0hR39GSoyVAbydpMe5hwENGQM26CepcicdrelfawItoUrXrkJzBHkIQQTO/xRSbCtRJOtzI5lwtv3GP0hcbOy5tI5dhG/93pLwZRc5+dZaCaP7oaVeOcBjN0zfINRQobt8d6h2Qgvd/YyFkGi0/xKn1zMmKIVLOG6VsYwEAUq8wNOsP4A/jdm4Y0J/1oEZStCkpaGpx85TYi4kq1hWQdyqaVJSPhh4Tk4roIaS2zOYQl+nIpbHqmJ4FJrg1il+TCdjBXobATQ1mKRBUrjD+RDzH/r4ogbd8+UwvvvevpqS2K+/wgT6UD0MzDInv9S29CUQvuFhPoqyJb5XRddHMRE9EEK/2Z8tFN91sDATnqfXHgwnvu00q/nKP5JnijBPzGmx7ydgUViIukklDrlPvo9BbRJz0Vr2vbAvMTrLMLCXqi5CwTm+v+iaOf/YaCziaG2vx0eVASYjpOLCedSgRZBubPM8z4E/HMXhChN7sVDWk= | ||||
|   on: | ||||
|     tags: true | ||||
|   distributions: sdist bdist_wheel | ||||
| env: | ||||
|   global: | ||||
|     - PIP_DOWNLOAD_CACHE=".pip_download_cache" | ||||
| before_install: | ||||
|   - pip install codecov | ||||
| install: | ||||
|   - make develop | ||||
| script: | ||||
|   - PYFLAKES_NODOCTEST=1 flake8 | ||||
|   - coverage run --source=. -m py.test tests | ||||
| after_success: | ||||
|   - codecov | ||||
| @ -1,201 +0,0 @@ | ||||
|                               Apache License | ||||
|                         Version 2.0, January 2004 | ||||
|                      http://www.apache.org/licenses/ | ||||
|  | ||||
| TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
|  | ||||
| 1. Definitions. | ||||
|  | ||||
|    "License" shall mean the terms and conditions for use, reproduction, | ||||
|    and distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
|    "Licensor" shall mean the copyright owner or entity authorized by | ||||
|    the copyright owner that is granting the License. | ||||
|  | ||||
|    "Legal Entity" shall mean the union of the acting entity and all | ||||
|    other entities that control, are controlled by, or are under common | ||||
|    control with that entity. For the purposes of this definition, | ||||
|    "control" means (i) the power, direct or indirect, to cause the | ||||
|    direction or management of such entity, whether by contract or | ||||
|    otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|    outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
|    "You" (or "Your") shall mean an individual or Legal Entity | ||||
|    exercising permissions granted by this License. | ||||
|  | ||||
|    "Source" form shall mean the preferred form for making modifications, | ||||
|    including but not limited to software source code, documentation | ||||
|    source, and configuration files. | ||||
|  | ||||
|    "Object" form shall mean any form resulting from mechanical | ||||
|    transformation or translation of a Source form, including but | ||||
|    not limited to compiled object code, generated documentation, | ||||
|    and conversions to other media types. | ||||
|  | ||||
|    "Work" shall mean the work of authorship, whether in Source or | ||||
|    Object form, made available under the License, as indicated by a | ||||
|    copyright notice that is included in or attached to the work | ||||
|    (an example is provided in the Appendix below). | ||||
|  | ||||
|    "Derivative Works" shall mean any work, whether in Source or Object | ||||
|    form, that is based on (or derived from) the Work and for which the | ||||
|    editorial revisions, annotations, elaborations, or other modifications | ||||
|    represent, as a whole, an original work of authorship. For the purposes | ||||
|    of this License, Derivative Works shall not include works that remain | ||||
|    separable from, or merely link (or bind by name) to the interfaces of, | ||||
|    the Work and Derivative Works thereof. | ||||
|  | ||||
|    "Contribution" shall mean any work of authorship, including | ||||
|    the original version of the Work and any modifications or additions | ||||
|    to that Work or Derivative Works thereof, that is intentionally | ||||
|    submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|    or by an individual or Legal Entity authorized to submit on behalf of | ||||
|    the copyright owner. For the purposes of this definition, "submitted" | ||||
|    means any form of electronic, verbal, or written communication sent | ||||
|    to the Licensor or its representatives, including but not limited to | ||||
|    communication on electronic mailing lists, source code control systems, | ||||
|    and issue tracking systems that are managed by, or on behalf of, the | ||||
|    Licensor for the purpose of discussing and improving the Work, but | ||||
|    excluding communication that is conspicuously marked or otherwise | ||||
|    designated in writing by the copyright owner as "Not a Contribution." | ||||
|  | ||||
|    "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|    on behalf of whom a Contribution has been received by Licensor and | ||||
|    subsequently incorporated within the Work. | ||||
|  | ||||
| 2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|    this License, each Contributor hereby grants to You a perpetual, | ||||
|    worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|    copyright license to reproduce, prepare Derivative Works of, | ||||
|    publicly display, publicly perform, sublicense, and distribute the | ||||
|    Work and such Derivative Works in Source or Object form. | ||||
|  | ||||
| 3. Grant of Patent License. Subject to the terms and conditions of | ||||
|    this License, each Contributor hereby grants to You a perpetual, | ||||
|    worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|    (except as stated in this section) patent license to make, have made, | ||||
|    use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|    where such license applies only to those patent claims licensable | ||||
|    by such Contributor that are necessarily infringed by their | ||||
|    Contribution(s) alone or by combination of their Contribution(s) | ||||
|    with the Work to which such Contribution(s) was submitted. If You | ||||
|    institute patent litigation against any entity (including a | ||||
|    cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|    or a Contribution incorporated within the Work constitutes direct | ||||
|    or contributory patent infringement, then any patent licenses | ||||
|    granted to You under this License for that Work shall terminate | ||||
|    as of the date such litigation is filed. | ||||
|  | ||||
| 4. Redistribution. You may reproduce and distribute copies of the | ||||
|    Work or Derivative Works thereof in any medium, with or without | ||||
|    modifications, and in Source or Object form, provided that You | ||||
|    meet the following conditions: | ||||
|  | ||||
|    (a) You must give any other recipients of the Work or | ||||
|        Derivative Works a copy of this License; and | ||||
|  | ||||
|    (b) You must cause any modified files to carry prominent notices | ||||
|        stating that You changed the files; and | ||||
|  | ||||
|    (c) You must retain, in the Source form of any Derivative Works | ||||
|        that You distribute, all copyright, patent, trademark, and | ||||
|        attribution notices from the Source form of the Work, | ||||
|        excluding those notices that do not pertain to any part of | ||||
|        the Derivative Works; and | ||||
|  | ||||
|    (d) If the Work includes a "NOTICE" text file as part of its | ||||
|        distribution, then any Derivative Works that You distribute must | ||||
|        include a readable copy of the attribution notices contained | ||||
|        within such NOTICE file, excluding those notices that do not | ||||
|        pertain to any part of the Derivative Works, in at least one | ||||
|        of the following places: within a NOTICE text file distributed | ||||
|        as part of the Derivative Works; within the Source form or | ||||
|        documentation, if provided along with the Derivative Works; or, | ||||
|        within a display generated by the Derivative Works, if and | ||||
|        wherever such third-party notices normally appear. The contents | ||||
|        of the NOTICE file are for informational purposes only and | ||||
|        do not modify the License. You may add Your own attribution | ||||
|        notices within Derivative Works that You distribute, alongside | ||||
|        or as an addendum to the NOTICE text from the Work, provided | ||||
|        that such additional attribution notices cannot be construed | ||||
|        as modifying the License. | ||||
|  | ||||
|    You may add Your own copyright statement to Your modifications and | ||||
|    may provide additional or different license terms and conditions | ||||
|    for use, reproduction, or distribution of Your modifications, or | ||||
|    for any such Derivative Works as a whole, provided Your use, | ||||
|    reproduction, and distribution of the Work otherwise complies with | ||||
|    the conditions stated in this License. | ||||
|  | ||||
| 5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|    any Contribution intentionally submitted for inclusion in the Work | ||||
|    by You to the Licensor shall be under the terms and conditions of | ||||
|    this License, without any additional terms or conditions. | ||||
|    Notwithstanding the above, nothing herein shall supersede or modify | ||||
|    the terms of any separate license agreement you may have executed | ||||
|    with Licensor regarding such Contributions. | ||||
|  | ||||
| 6. Trademarks. This License does not grant permission to use the trade | ||||
|    names, trademarks, service marks, or product names of the Licensor, | ||||
|    except as required for reasonable and customary use in describing the | ||||
|    origin of the Work and reproducing the content of the NOTICE file. | ||||
|  | ||||
| 7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|    agreed to in writing, Licensor provides the Work (and each | ||||
|    Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|    implied, including, without limitation, any warranties or conditions | ||||
|    of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|    PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|    appropriateness of using or redistributing the Work and assume any | ||||
|    risks associated with Your exercise of permissions under this License. | ||||
|  | ||||
| 8. Limitation of Liability. In no event and under no legal theory, | ||||
|    whether in tort (including negligence), contract, or otherwise, | ||||
|    unless required by applicable law (such as deliberate and grossly | ||||
|    negligent acts) or agreed to in writing, shall any Contributor be | ||||
|    liable to You for damages, including any direct, indirect, special, | ||||
|    incidental, or consequential damages of any character arising as a | ||||
|    result of this License or out of the use or inability to use the | ||||
|    Work (including but not limited to damages for loss of goodwill, | ||||
|    work stoppage, computer failure or malfunction, or any and all | ||||
|    other commercial damages or losses), even if such Contributor | ||||
|    has been advised of the possibility of such damages. | ||||
|  | ||||
| 9. Accepting Warranty or Additional Liability. While redistributing | ||||
|    the Work or Derivative Works thereof, You may choose to offer, | ||||
|    and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|    or other liability obligations and/or rights consistent with this | ||||
|    License. However, in accepting such obligations, You may act only | ||||
|    on Your own behalf and on Your sole responsibility, not on behalf | ||||
|    of any other Contributor, and only if You agree to indemnify, | ||||
|    defend, and hold each Contributor harmless for any liability | ||||
|    incurred by, or claims asserted against, such Contributor by reason | ||||
|    of your accepting any such warranty or additional liability. | ||||
|  | ||||
| END OF TERMS AND CONDITIONS | ||||
|  | ||||
| APPENDIX: How to apply the Apache License to your work. | ||||
|  | ||||
|    To apply the Apache License to your work, attach the following | ||||
|    boilerplate notice, with the fields enclosed by brackets "[]" | ||||
|    replaced with your own identifying information. (Don't include | ||||
|    the brackets!)  The text should be enclosed in the appropriate | ||||
|    comment syntax for the file format. We also recommend that a | ||||
|    file or class name and description of purpose be included on the | ||||
|    same "printed page" as the copyright notice for easier | ||||
|    identification within third-party archives. | ||||
|  | ||||
| Copyright 2016 Functional Software, Inc. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| @ -1,3 +0,0 @@ | ||||
| include setup.py package.json webpack.config.js README.rst MANIFEST.in LICENSE AUTHORS | ||||
| recursive-include sentry_auth_supervisr/templates * | ||||
| global-exclude *~ | ||||
| @ -1,26 +0,0 @@ | ||||
| .PHONY: clean develop install-tests lint publish test | ||||
|  | ||||
| develop: | ||||
| 	pip install "pip>=7" | ||||
| 	pip install -e . | ||||
| 	make install-tests | ||||
|  | ||||
| install-tests: | ||||
| 	pip install .[tests] | ||||
|  | ||||
| lint: | ||||
| 	@echo "--> Linting python" | ||||
| 	flake8 | ||||
| 	@echo "" | ||||
|  | ||||
| test: | ||||
| 	@echo "--> Running Python tests" | ||||
| 	py.test tests || exit 1 | ||||
| 	@echo "" | ||||
|  | ||||
| publish: | ||||
| 	python setup.py sdist bdist_wheel upload | ||||
|  | ||||
| clean: | ||||
| 	rm -rf *.egg-info src/*.egg-info | ||||
| 	rm -rf dist build | ||||
| @ -1,55 +0,0 @@ | ||||
| GitHub Auth for Sentry | ||||
| ====================== | ||||
|  | ||||
| An SSO provider for Sentry which enables GitHub organization-restricted authentication. | ||||
|  | ||||
| Install | ||||
| ------- | ||||
|  | ||||
| :: | ||||
|  | ||||
|     $ pip install https://github.com/getsentry/sentry-auth-github/archive/master.zip | ||||
|  | ||||
| Setup | ||||
| ----- | ||||
|  | ||||
| Create a new application under your organization in GitHub. Enter the **Authorization | ||||
| callback URL** as the prefix to your Sentry installation: | ||||
|  | ||||
| :: | ||||
|  | ||||
|     https://example.sentry.com | ||||
|  | ||||
|  | ||||
| Once done, grab your API keys and drop them in your ``sentry.conf.py``: | ||||
|  | ||||
| .. code-block:: python | ||||
|  | ||||
|     GITHUB_APP_ID = "" | ||||
|  | ||||
|     GITHUB_API_SECRET = "" | ||||
|  | ||||
|  | ||||
| Verified email addresses can optionally be required: | ||||
|  | ||||
| .. code-block:: python | ||||
|  | ||||
|     GITHUB_REQUIRE_VERIFIED_EMAIL = True | ||||
|  | ||||
|  | ||||
| Optionally you may also specify the domain (for GHE users): | ||||
|  | ||||
| .. code-block:: python | ||||
|  | ||||
|     GITHUB_BASE_DOMAIN = "git.example.com" | ||||
|  | ||||
|     GITHUB_API_DOMAIN = "api.git.example.com" | ||||
|  | ||||
|  | ||||
| If Subdomain isolation is disabled in GHE: | ||||
|  | ||||
| .. code-block:: python | ||||
|  | ||||
|     GITHUB_BASE_DOMAIN = "git.example.com" | ||||
|  | ||||
|     GITHUB_API_DOMAIN = "git.example.com/api/v3" | ||||
| @ -1,14 +0,0 @@ | ||||
| from __future__ import absolute_import | ||||
|  | ||||
| # Run tests against sqlite for simplicity | ||||
| import os | ||||
| import os.path | ||||
| import sys | ||||
|  | ||||
| sys.path.insert(0, os.path.join(os.path.dirname(__file__))) | ||||
|  | ||||
| os.environ.setdefault('DB', 'sqlite') | ||||
|  | ||||
| pytest_plugins = [ | ||||
|     'sentry.utils.pytest' | ||||
| ] | ||||
| @ -1,7 +0,0 @@ | ||||
| from __future__ import absolute_import | ||||
|  | ||||
| from sentry.auth import register | ||||
|  | ||||
| from .provider import PassbookOAuth2Provider | ||||
|  | ||||
| register('passbook', PassbookOAuth2Provider) | ||||
| @ -1,45 +0,0 @@ | ||||
| from __future__ import absolute_import, print_function | ||||
|  | ||||
| from requests.exceptions import RequestException | ||||
|  | ||||
| from sentry import http | ||||
| from sentry.utils import json | ||||
|  | ||||
| from .constants import BASE_DOMAIN | ||||
|  | ||||
|  | ||||
| class PassbookApiError(Exception): | ||||
|     def __init__(self, message='', status=0): | ||||
|         super(PassbookApiError, self).__init__(message) | ||||
|         self.status = status | ||||
|  | ||||
|  | ||||
| class PassbookClient(object): | ||||
|     def __init__(self, client_id, client_secret): | ||||
|         self.client_id = client_id | ||||
|         self.client_secret = client_secret | ||||
|         self.http = http.build_session() | ||||
|  | ||||
|     def _request(self, path, access_token): | ||||
|         params = { | ||||
|             'client_id': self.client_id, | ||||
|             'client_secret': self.client_secret, | ||||
|         } | ||||
|  | ||||
|         headers = { | ||||
|             'Authorization': 'Bearer {0}'.format(access_token), | ||||
|         } | ||||
|  | ||||
|         try: | ||||
|             req = self.http.get('https://{0}/{1}'.format(BASE_DOMAIN, path.lstrip('/')), | ||||
|                 params=params, | ||||
|                 headers=headers, | ||||
|             ) | ||||
|         except RequestException as e: | ||||
|             raise PassbookApiError(unicode(e), status=getattr(e, 'status_code', 0)) | ||||
|         if req.status_code < 200 or req.status_code >= 300: | ||||
|             raise PassbookApiError(req.content, status=req.status_code) | ||||
|         return json.loads(req.content) | ||||
|  | ||||
|     def get_user(self, access_token): | ||||
|         return self._request('/api/v1/openid/', access_token) | ||||
| @ -1,14 +0,0 @@ | ||||
| from __future__ import absolute_import, print_function | ||||
|  | ||||
| from django.conf import settings | ||||
|  | ||||
| CLIENT_ID = getattr(settings, 'PASSBOOK_APP_ID', None) | ||||
|  | ||||
| CLIENT_SECRET = getattr(settings, 'PASSBOOK_API_SECRET', None) | ||||
|  | ||||
| SCOPE = 'openid:userinfo' | ||||
|  | ||||
| BASE_DOMAIN = getattr(settings, 'PASSBOOK_BASE_DOMAIN', 'id.beryju.org') | ||||
|  | ||||
| ACCESS_TOKEN_URL = 'https://{0}/application/oauth/token/'.format(BASE_DOMAIN) | ||||
| AUTHORIZE_URL = 'https://{0}/application/oauth/authorize/'.format(BASE_DOMAIN) | ||||
| @ -1,62 +0,0 @@ | ||||
| from __future__ import absolute_import, print_function | ||||
|  | ||||
| from sentry.auth.exceptions import IdentityNotValid | ||||
| from sentry.auth.providers.oauth2 import (OAuth2Callback, OAuth2Login, | ||||
|                                           OAuth2Provider) | ||||
|  | ||||
| from .client import PassbookApiError, PassbookClient | ||||
| from .constants import (ACCESS_TOKEN_URL, AUTHORIZE_URL, CLIENT_ID, | ||||
|                         CLIENT_SECRET, SCOPE) | ||||
| from .views import FetchUser, PassbookConfigureView | ||||
|  | ||||
|  | ||||
| class PassbookOAuth2Provider(OAuth2Provider): | ||||
|     access_token_url = ACCESS_TOKEN_URL | ||||
|     authorize_url = AUTHORIZE_URL | ||||
|     name = 'Passbook' | ||||
|     client_id = CLIENT_ID | ||||
|     client_secret = CLIENT_SECRET | ||||
|  | ||||
|     def __init__(self, **config): | ||||
|         super(PassbookOAuth2Provider, self).__init__(**config) | ||||
|  | ||||
|     def get_configure_view(self): | ||||
|         return PassbookConfigureView.as_view() | ||||
|  | ||||
|     def get_auth_pipeline(self): | ||||
|         return [ | ||||
|             OAuth2Login( | ||||
|                 authorize_url=self.authorize_url, | ||||
|                 client_id=self.client_id, | ||||
|                 scope=SCOPE, | ||||
|             ), | ||||
|             OAuth2Callback( | ||||
|                 access_token_url=self.access_token_url, | ||||
|                 client_id=self.client_id, | ||||
|                 client_secret=self.client_secret, | ||||
|             ), | ||||
|             FetchUser( | ||||
|                 client_id=self.client_id, | ||||
|                 client_secret=self.client_secret, | ||||
|             ), | ||||
|         ] | ||||
|  | ||||
|     def get_refresh_token_url(self): | ||||
|         return ACCESS_TOKEN_URL | ||||
|  | ||||
|     def build_identity(self, state): | ||||
|         data = state['data'] | ||||
|         user_data = state['user'] | ||||
|         return { | ||||
|             'id': user_data['email'], | ||||
|             'email': user_data['email'], | ||||
|             'name': user_data['name'], | ||||
|             'data': self.get_oauth_data(data), | ||||
|         } | ||||
|  | ||||
|     def build_config(self, state): | ||||
|         return {} | ||||
|  | ||||
|     def refresh_identity(self, auth_identity): | ||||
|         client = PassbookClient(self.client_id, self.client_secret) | ||||
|         access_token = auth_identity.data['access_token'] | ||||
| @ -1,75 +0,0 @@ | ||||
| from __future__ import absolute_import, print_function | ||||
|  | ||||
| from django import forms | ||||
|  | ||||
| from sentry.auth.view import AuthView, ConfigureView | ||||
| from sentry.models import AuthIdentity | ||||
|  | ||||
| from .client import PassbookClient | ||||
|  | ||||
|  | ||||
| def _get_name_from_email(email): | ||||
|     """ | ||||
|     Given an email return a capitalized name. Ex. john.smith@example.com would return John Smith. | ||||
|     """ | ||||
|     name = email.rsplit('@', 1)[0] | ||||
|     name = ' '.join([n_part.capitalize() for n_part in name.split('.')]) | ||||
|     return name | ||||
|  | ||||
|  | ||||
| class FetchUser(AuthView): | ||||
|     def __init__(self, client_id, client_secret, *args, **kwargs): | ||||
|         self.client = PassbookClient(client_id, client_secret) | ||||
|         super(FetchUser, self).__init__(*args, **kwargs) | ||||
|  | ||||
|     def handle(self, request, helper): | ||||
|         access_token = helper.fetch_state('data')['access_token'] | ||||
|  | ||||
|         user = self.client.get_user(access_token) | ||||
|  | ||||
|         # A user hasn't set their name in their Passbook profile so it isn't | ||||
|         # populated in the response | ||||
|         if not user.get('name'): | ||||
|             user['name'] = _get_name_from_email(user['email']) | ||||
|  | ||||
|         helper.bind_state('user', user) | ||||
|  | ||||
|         return helper.next_step() | ||||
|  | ||||
|  | ||||
| class ConfirmEmailForm(forms.Form): | ||||
|     email = forms.EmailField(label='Email') | ||||
|  | ||||
|  | ||||
| class ConfirmEmail(AuthView): | ||||
|     def handle(self, request, helper): | ||||
|         user = helper.fetch_state('user') | ||||
|  | ||||
|         # TODO(dcramer): this isnt ideal, but our current flow doesnt really | ||||
|         # support this behavior; | ||||
|         try: | ||||
|             auth_identity = AuthIdentity.objects.select_related('user').get( | ||||
|                 auth_provider=helper.auth_provider, | ||||
|                 ident=user['id'], | ||||
|             ) | ||||
|         except AuthIdentity.DoesNotExist: | ||||
|             pass | ||||
|         else: | ||||
|             user['email'] = auth_identity.user.email | ||||
|  | ||||
|         if user.get('email'): | ||||
|             return helper.next_step() | ||||
|  | ||||
|         form = ConfirmEmailForm(request.POST or None) | ||||
|         if form.is_valid(): | ||||
|             user['email'] = form.cleaned_data['email'] | ||||
|             helper.bind_state('user', user) | ||||
|             return helper.next_step() | ||||
|  | ||||
|         return self.respond('sentry_auth_passbook/enter-email.html', { | ||||
|             'form': form, | ||||
|         }) | ||||
|  | ||||
| class PassbookConfigureView(ConfigureView): | ||||
|     def dispatch(self, request, organization, auth_provider): | ||||
|         return self.render('sentry_auth_passbook/configure.html') | ||||
| @ -1,12 +0,0 @@ | ||||
| [wheel] | ||||
| universal = 1 | ||||
|  | ||||
| [pytest] | ||||
| python_files = test*.py | ||||
| addopts = --tb=native -p no:doctest | ||||
| norecursedirs = bin dist docs htmlcov script hooks node_modules .* {args} | ||||
|  | ||||
| [flake8] | ||||
| ignore = F999,E501,E128,E124,E402,W503,E731,C901 | ||||
| max-line-length = 100 | ||||
| exclude = .tox,.git,*/migrations/*,node_modules/*,docs/* | ||||
| @ -1,45 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| """ | ||||
| sentry-auth-passbook | ||||
| ================== | ||||
|  | ||||
| :copyright: (c) 2016 Functional Software, Inc | ||||
| """ | ||||
| from setuptools import find_packages, setup | ||||
|  | ||||
| install_requires = [ | ||||
|     'sentry>=7.0.0', | ||||
| ] | ||||
|  | ||||
| tests_require = [ | ||||
|     'mock', | ||||
|     'flake8>=2.0,<2.1', | ||||
| ] | ||||
|  | ||||
| setup( | ||||
|     name='sentry-auth-passbook', | ||||
|     version='0.2.0-beta', | ||||
|     author='BeryJu.org', | ||||
|     author_email='support@beryju.org', | ||||
|     url='https://passbook.beryju.org', | ||||
|     description='passbook authentication provider for Sentry', | ||||
|     long_description=__doc__, | ||||
|     license='MIT', | ||||
|     packages=find_packages(exclude=['tests']), | ||||
|     zip_safe=False, | ||||
|     install_requires=install_requires, | ||||
|     tests_require=tests_require, | ||||
|     extras_require={'tests': tests_require}, | ||||
|     include_package_data=True, | ||||
|     entry_points={ | ||||
|         'sentry.apps': [ | ||||
|             'auth_passbook = sentry_auth_passbook', | ||||
|         ], | ||||
|     }, | ||||
|     classifiers=[ | ||||
|         'Intended Audience :: Developers', | ||||
|         'Intended Audience :: System Administrators', | ||||
|         'Operating System :: OS Independent', | ||||
|         'Topic :: Software Development' | ||||
|     ], | ||||
| ) | ||||
| @ -1,6 +0,0 @@ | ||||
| from sentry.testutils import TestCase | ||||
|  | ||||
|  | ||||
| class GitHubOAuth2ProviderTest(TestCase): | ||||
|     def test_simple(self): | ||||
|         pass | ||||
| @ -1,17 +0,0 @@ | ||||
| from __future__ import absolute_import, print_function | ||||
|  | ||||
| import pytest | ||||
| from sentry_auth_sentry.views import _get_name_from_email | ||||
|  | ||||
| expected_data = [ | ||||
|     ('john.smith@example.com', 'John Smith'), | ||||
|     ('john@example.com', 'John'), | ||||
|     ('XYZ-234=3523@example.com', 'Xyz-234=3523'), | ||||
|     ('XYZ.1111@example.com', 'Xyz 1111'), | ||||
|     ('JOHN@example.com', 'John'), | ||||
| ] | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("email,expected_name", expected_data) | ||||
| def test_get_name_from_email(email, expected_name): | ||||
|     assert _get_name_from_email(email) == expected_name | ||||
| @ -1,6 +1,6 @@ | ||||
| apiVersion: v1 | ||||
| appVersion: "0.2.0-beta" | ||||
| appVersion: "0.2.8-beta" | ||||
| description: A Helm chart for passbook. | ||||
| name: passbook | ||||
| version: "0.2.0-beta" | ||||
| version: "0.2.8-beta" | ||||
| icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| apiVersion: apps/v1beta2 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: {{ include "passbook.fullname" . }}-web | ||||
|   name: {{ include "passbook.fullname" . }}-appgw | ||||
|   labels: | ||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|     helm.sh/chart: {{ include "passbook.chart" . }} | ||||
| @ -18,7 +18,7 @@ spec: | ||||
|       labels: | ||||
|         app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|         app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|         passbook.io/component: web | ||||
|         passbook.io/component: appgw | ||||
|     spec: | ||||
|       volumes: | ||||
|         - name: config-volume | ||||
| @ -28,8 +28,10 @@ spec: | ||||
|         - name: {{ .Chart.Name }} | ||||
|           image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}" | ||||
|           imagePullPolicy: IfNotPresent | ||||
|           command: ["/bin/sh","-c"] | ||||
|           args: ["./manage.py migrate && ./manage.py web"] | ||||
|           command: | ||||
|             - ./manage.py | ||||
|           args: | ||||
|             - app_gw_web | ||||
|           ports: | ||||
|             - name: http | ||||
|               containerPort: 8000 | ||||
| @ -52,16 +54,9 @@ spec: | ||||
|                 - name: Host | ||||
|                   value: kubernetes-healthcheck-host | ||||
|           resources: | ||||
| {{ toYaml .Values.resources | indent 12 }} | ||||
|     {{- with .Values.nodeSelector }} | ||||
|       nodeSelector: | ||||
| {{ toYaml . | indent 8 }} | ||||
|     {{- end }} | ||||
|     {{- with .Values.affinity }} | ||||
|       affinity: | ||||
| {{ toYaml . | indent 8 }} | ||||
|     {{- end }} | ||||
|     {{- with .Values.tolerations }} | ||||
|       tolerations: | ||||
| {{ toYaml . | indent 8 }} | ||||
|     {{- end }} | ||||
|             requests: | ||||
|               cpu: 150m | ||||
|               memory: 300M | ||||
|             limits: | ||||
|               cpu: 500m | ||||
|               memory: 500M | ||||
							
								
								
									
										20
									
								
								helm/passbook/templates/appgw-service.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								helm/passbook/templates/appgw-service.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: {{ include "passbook.fullname" . }}-appgw | ||||
|   labels: | ||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|     helm.sh/chart: {{ include "passbook.chart" . }} | ||||
|     app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|     app.kubernetes.io/managed-by: {{ .Release.Service }} | ||||
| spec: | ||||
|   type: {{ .Values.service.type }} | ||||
|   ports: | ||||
|     - port: {{ .Values.service.port }} | ||||
|       targetPort: http | ||||
|       protocol: TCP | ||||
|       name: http | ||||
|   selector: | ||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|     app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|     passbook.io/component: appgw | ||||
| @ -1,6 +1,5 @@ | ||||
| {{- if .Values.ingress.enabled -}} | ||||
| {{- $fullName := include "passbook.fullname" . -}} | ||||
| {{- $ingressPath := .Values.ingress.path -}} | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
| @ -30,9 +29,22 @@ spec: | ||||
|     - host: {{ . | quote }} | ||||
|       http: | ||||
|         paths: | ||||
|           - path: {{ $ingressPath }} | ||||
|           - path: / | ||||
|             backend: | ||||
|               serviceName: {{ $fullName }} | ||||
|               serviceName: {{ $fullName }}-web | ||||
|               servicePort: http | ||||
|           - path: /static/ | ||||
|             backend: | ||||
|               serviceName: {{ $fullName }}-static | ||||
|               servicePort: http | ||||
|   {{- end }} | ||||
|   {{- range .Values.ingress.app_gw_hosts }} | ||||
|     - host: {{ . | quote }} | ||||
|       http: | ||||
|         paths: | ||||
|           - path: / | ||||
|             backend: | ||||
|               serviceName: {{ $fullName }}-appgw | ||||
|               servicePort: http | ||||
|   {{- end }} | ||||
| {{- end }} | ||||
							
								
								
									
										55
									
								
								helm/passbook/templates/static-deployment.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								helm/passbook/templates/static-deployment.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| apiVersion: apps/v1beta2 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: {{ include "passbook.fullname" . }}-static | ||||
|   labels: | ||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|     helm.sh/chart: {{ include "passbook.chart" . }} | ||||
|     app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|     app.kubernetes.io/managed-by: {{ .Release.Service }} | ||||
| spec: | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|       app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|         app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|         k8s.passbook.io/component: static | ||||
|       annotations: | ||||
|         prometheus.io/scrape: "true" | ||||
|         prometheus.io/port: '9113' | ||||
|         field.cattle.io/workloadMetrics: '[{"path":"/metrics","port":9113,"schema":"HTTP"}]' | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: {{ .Chart.Name }}-static | ||||
|           image: "docker.beryju.org/passbook/static:{{ .Values.image.tag }}" | ||||
|           imagePullPolicy: IfNotPresent | ||||
|           ports: | ||||
|             - name: http | ||||
|               containerPort: 80 | ||||
|               protocol: TCP | ||||
|           livenessProbe: | ||||
|             initialDelaySeconds: 10 | ||||
|             timeoutSeconds: 5 | ||||
|             httpGet: | ||||
|               path: /_/healthz | ||||
|               port: http | ||||
|           readinessProbe: | ||||
|             initialDelaySeconds: 10 | ||||
|             timeoutSeconds: 5 | ||||
|             httpGet: | ||||
|               path: /_/healthz | ||||
|               port: http | ||||
|           resources: | ||||
|             requests: | ||||
|               cpu: 10m | ||||
|               memory: 10M | ||||
|             limits: | ||||
|               cpu: 20m | ||||
|               memory: 20M | ||||
|         - name: {{ .Chart.Name }}-static-prometheus | ||||
|           image: nginx/nginx-prometheus-exporter:0.4.1 | ||||
|           imagePullPolicy: IfNotPresent | ||||
							
								
								
									
										21
									
								
								helm/passbook/templates/static-service.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								helm/passbook/templates/static-service.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: {{ include "passbook.fullname" . }}-static | ||||
|   labels: | ||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|     helm.sh/chart: {{ include "passbook.chart" . }} | ||||
|     app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|     app.kubernetes.io/managed-by: {{ .Release.Service }} | ||||
|     k8s.passbook.io/component: static | ||||
| spec: | ||||
|   type: ClusterIP | ||||
|   ports: | ||||
|     - port: 80 | ||||
|       targetPort: http | ||||
|       protocol: TCP | ||||
|       name: http | ||||
|   selector: | ||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|     app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|     k8s.passbook.io/component: static | ||||
							
								
								
									
										72
									
								
								helm/passbook/templates/web-deployment.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								helm/passbook/templates/web-deployment.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| apiVersion: apps/v1beta2 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: {{ include "passbook.fullname" . }}-web | ||||
|   labels: | ||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|     helm.sh/chart: {{ include "passbook.chart" . }} | ||||
|     app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|     app.kubernetes.io/managed-by: {{ .Release.Service }} | ||||
| spec: | ||||
|   replicas: {{ .Values.replicaCount }} | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|       app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|         app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|         passbook.io/component: web | ||||
|     spec: | ||||
|       volumes: | ||||
|         - name: config-volume | ||||
|           configMap: | ||||
|             name: {{ include "passbook.fullname" . }}-config | ||||
|       initContainers: | ||||
|         - name: passbook-database-migrations | ||||
|           image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}" | ||||
|           command: | ||||
|             - ./manage.py | ||||
|           args: | ||||
|             - migrate | ||||
|           volumeMounts: | ||||
|             - mountPath: /etc/passbook | ||||
|               name: config-volume | ||||
|       containers: | ||||
|         - name: {{ .Chart.Name }} | ||||
|           image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}" | ||||
|           imagePullPolicy: IfNotPresent | ||||
|           command: | ||||
|             - ./manage.py | ||||
|           args: | ||||
|             - web | ||||
|           ports: | ||||
|             - name: http | ||||
|               containerPort: 8000 | ||||
|               protocol: TCP | ||||
|           volumeMounts: | ||||
|             - mountPath: /etc/passbook | ||||
|               name: config-volume | ||||
|           livenessProbe: | ||||
|             httpGet: | ||||
|               path: / | ||||
|               port: http | ||||
|               httpHeaders: | ||||
|                 - name: Host | ||||
|                   value: kubernetes-healthcheck-host | ||||
|           readinessProbe: | ||||
|             httpGet: | ||||
|               path: / | ||||
|               port: http | ||||
|               httpHeaders: | ||||
|                 - name: Host | ||||
|                   value: kubernetes-healthcheck-host | ||||
|           resources: | ||||
|             requests: | ||||
|               cpu: 50m | ||||
|               memory: 150M | ||||
|             limits: | ||||
|               cpu: 200m | ||||
|               memory: 300M | ||||
| @ -1,7 +1,7 @@ | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: {{ include "passbook.fullname" . }} | ||||
|   name: {{ include "passbook.fullname" . }}-web | ||||
|   labels: | ||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||
|     helm.sh/chart: {{ include "passbook.chart" . }} | ||||
| @ -28,7 +28,10 @@ spec: | ||||
|         - name: {{ .Chart.Name }} | ||||
|           image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}" | ||||
|           imagePullPolicy: IfNotPresent | ||||
|           command: ["./manage.py", "worker"] | ||||
|           command: | ||||
|             - ./manage.py | ||||
|           args: | ||||
|             - worker | ||||
|           ports: | ||||
|             - name: http | ||||
|               containerPort: 8000 | ||||
| @ -37,16 +40,9 @@ spec: | ||||
|             - mountPath: /etc/passbook | ||||
|               name: config-volume | ||||
|           resources: | ||||
| {{ toYaml .Values.resources | indent 12 }} | ||||
|     {{- with .Values.nodeSelector }} | ||||
|       nodeSelector: | ||||
| {{ toYaml . | indent 8 }} | ||||
|     {{- end }} | ||||
|     {{- with .Values.affinity }} | ||||
|       affinity: | ||||
| {{ toYaml . | indent 8 }} | ||||
|     {{- end }} | ||||
|     {{- with .Values.tolerations }} | ||||
|       tolerations: | ||||
| {{ toYaml . | indent 8 }} | ||||
|     {{- end }} | ||||
|             requests: | ||||
|               cpu: 150m | ||||
|               memory: 300M | ||||
|             limits: | ||||
|               cpu: 300m | ||||
|               memory: 500M | ||||
| @ -5,7 +5,7 @@ | ||||
| replicaCount: 1 | ||||
|  | ||||
| image: | ||||
|   tag: 0.2.0-beta | ||||
|   tag: 0.2.8-beta | ||||
|  | ||||
| nameOverride: "" | ||||
|  | ||||
| @ -37,6 +37,8 @@ ingress: | ||||
|   path: / | ||||
|   hosts: | ||||
|     - passbook.k8s.local | ||||
|   app_gw_hosts: | ||||
|     - '*.passbook.k8s.local' | ||||
|   defaultHost: passbook.k8s.local | ||||
|   tls: [] | ||||
|   #  - secretName: chart-example-tls | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook""" | ||||
| __version__ = '0.2.0-beta' | ||||
| __version__ = '0.2.8-beta' | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook admin""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| django-rest-framework | ||||
| drf_yasg | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook api""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1,3 +0,0 @@ | ||||
| django-rest-framework | ||||
| drf_yasg | ||||
| django-filters | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook Application Security Gateway Header""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
							
								
								
									
										30
									
								
								passbook/app_gw/management/commands/app_gw_web.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								passbook/app_gw/management/commands/app_gw_web.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| """passbook app_gw webserver management command""" | ||||
|  | ||||
| from logging import getLogger | ||||
|  | ||||
| from daphne.cli import CommandLineInterface | ||||
| from django.core.management.base import BaseCommand | ||||
| from django.utils import autoreload | ||||
|  | ||||
| from passbook.lib.config import CONFIG | ||||
|  | ||||
| LOGGER = getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class Command(BaseCommand): | ||||
|     """Run Daphne Webserver for app_gw""" | ||||
|  | ||||
|     def handle(self, *args, **options): | ||||
|         """passbook daphne server""" | ||||
|         autoreload.run_with_reloader(self.daphne_server) | ||||
|  | ||||
|     def daphne_server(self): | ||||
|         """Run daphne server within autoreload""" | ||||
|         autoreload.raise_last_exception() | ||||
|         CommandLineInterface().run([ | ||||
|             '-p', str(CONFIG.y('app_gw.port', 8000)), | ||||
|             '-b', CONFIG.y('app_gw.listen', '0.0.0.0'),  # nosec | ||||
|             '--access-log', '/dev/null', | ||||
|             '--application-close-timeout', '500', | ||||
|             'passbook.app_gw.asgi:application' | ||||
|         ]) | ||||
| @ -221,5 +221,13 @@ class RequestHandler: | ||||
|         self._set_content_type(proxy_response) | ||||
|         response = get_django_response(proxy_response, strict_cookies=False) | ||||
|  | ||||
|         # If response has a 'Location' header, we rewrite that location as well | ||||
|         if 'Location' in response: | ||||
|             LOGGER.debug("Rewriting Location header") | ||||
|             for server_name in self.app_gw.server_name: | ||||
|                 response['Location'] = response['Location'].replace( | ||||
|                     self._parsed_url.hostname, server_name) | ||||
|             LOGGER.debug(response['Location']) | ||||
|  | ||||
|         # LOGGER.debug("RESPONSE RETURNED: %s", response) | ||||
|         return response | ||||
|  | ||||
| @ -9,7 +9,7 @@ from passbook.app_gw.proxy.utils import (cookie_from_string, | ||||
| #: Default number of bytes that are going to be read in a file lecture | ||||
| DEFAULT_AMT = 2 ** 16 | ||||
|  | ||||
| logger = logging.getLogger('revproxy.response') | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def get_django_response(proxy_response, strict_cookies=False): | ||||
|  | ||||
| @ -1,7 +0,0 @@ | ||||
| django-revproxy | ||||
| urllib3[secure] | ||||
| channels | ||||
| service_identity | ||||
| websocket-client | ||||
| daphne<2.3.0 | ||||
| asgiref~=2.3 | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook audit Header""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -22,7 +22,7 @@ class AuditEntry(UUIDModel): | ||||
|     ACTION_AUTHORIZE_APPLICATION = 'authorize_application' | ||||
|     ACTION_SUSPICIOUS_REQUEST = 'suspicious_request' | ||||
|     ACTION_SIGN_UP = 'sign_up' | ||||
|     ACTION_PASSWORD_RESET = 'password_reset' # noqa | ||||
|     ACTION_PASSWORD_RESET = 'password_reset' # noqa # nosec | ||||
|     ACTION_INVITE_CREATED = 'invitation_created' | ||||
|     ACTION_INVITE_USED = 'invitation_used' | ||||
|     ACTIONS = ( | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook captcha_factor Header""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1 +0,0 @@ | ||||
| django-recaptcha | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook core""" | ||||
| __version__ = '0.2.0-beta' | ||||
| __version__ = '0.2.6-beta' | ||||
|  | ||||
| @ -2,11 +2,12 @@ | ||||
|  | ||||
| from logging import getLogger | ||||
|  | ||||
| from daphne.cli import CommandLineInterface | ||||
| import cherrypy | ||||
| from django.conf import settings | ||||
| from django.core.management.base import BaseCommand | ||||
| from django.utils import autoreload | ||||
|  | ||||
| from passbook.lib.config import CONFIG | ||||
| from passbook.root.wsgi import application | ||||
|  | ||||
| LOGGER = getLogger(__name__) | ||||
|  | ||||
| @ -15,16 +16,21 @@ class Command(BaseCommand): | ||||
|     """Run CherryPy webserver""" | ||||
|  | ||||
|     def handle(self, *args, **options): | ||||
|         """passbook daphne server""" | ||||
|         autoreload.run_with_reloader(self.daphne_server) | ||||
|  | ||||
|     def daphne_server(self): | ||||
|         """Run daphne server within autoreload""" | ||||
|         autoreload.raise_last_exception() | ||||
|         CommandLineInterface().run([ | ||||
|             '-p', str(CONFIG.y('web.port', 8000)), | ||||
|             '-b', CONFIG.y('web.listen', '0.0.0.0'),  # nosec | ||||
|             '--access-log', '/dev/null', | ||||
|             '--application-close-timeout', '500', | ||||
|             'passbook.root.asgi:application' | ||||
|         ]) | ||||
|         """passbook cherrypy server""" | ||||
|         cherrypy.config.update(CONFIG.get('web')) | ||||
|         cherrypy.tree.graft(application, '/') | ||||
|         # Mount NullObject to serve static files | ||||
|         cherrypy.tree.mount(None, settings.STATIC_URL, config={ | ||||
|             '/': { | ||||
|                 'tools.staticdir.on': True, | ||||
|                 'tools.staticdir.dir': settings.STATIC_ROOT, | ||||
|                 'tools.expires.on': True, | ||||
|                 'tools.expires.secs': 86400, | ||||
|                 'tools.gzip.on': True, | ||||
|             } | ||||
|         }) | ||||
|         cherrypy.engine.start() | ||||
|         for file in CONFIG.loaded_file: | ||||
|             cherrypy.engine.autoreload.files.add(file) | ||||
|             LOGGER.info("Added '%s' to autoreload triggers", file) | ||||
|         cherrypy.engine.block() | ||||
|  | ||||
							
								
								
									
										66
									
								
								passbook/core/nginx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								passbook/core/nginx.conf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| user  nginx; | ||||
| worker_processes  1; | ||||
|  | ||||
| error_log  stderr warn; | ||||
| pid        /var/run/nginx.pid; | ||||
|  | ||||
| events { | ||||
|     worker_connections  1024; | ||||
| } | ||||
|  | ||||
| http { | ||||
|     include       /etc/nginx/mime.types; | ||||
|     default_type  application/octet-stream; | ||||
|  | ||||
|     log_format json_combined escape=json | ||||
|         '{' | ||||
|             '"time_local":"$time_local",' | ||||
|             '"remote_addr":"$remote_addr",' | ||||
|             '"remote_user":"$remote_user",' | ||||
|             '"request":"$request",' | ||||
|             '"status": "$status",' | ||||
|             '"body_bytes_sent":"$body_bytes_sent",' | ||||
|             '"request_time":"$request_time",' | ||||
|             '"http_referrer":"$http_referer",' | ||||
|             '"http_user_agent":"$http_user_agent"' | ||||
|         '}'; | ||||
|  | ||||
|     access_log /dev/stdout json_combined; | ||||
|  | ||||
|     sendfile        on; | ||||
|     tcp_nopush     on; | ||||
|  | ||||
|     keepalive_timeout  65; | ||||
|  | ||||
|     server { | ||||
|  | ||||
|         server_name _; | ||||
|  | ||||
|         gzip on; | ||||
|         gzip_types application/javascript image/* text/css; | ||||
|         gunzip on; | ||||
|         add_header X-passbook-Version 0.2.8-beta; | ||||
|         add_header Vary X-passbook-Version; | ||||
|         root /static/; | ||||
|  | ||||
|         location /_/healthz { | ||||
|             return 204; | ||||
|         } | ||||
|         location ~* \.(jpg|jpeg|png|gif|ico)$ { | ||||
|             expires 30d; | ||||
|         } | ||||
|         location ~* \.(css|js)$ { | ||||
|             expires 7d; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     server { | ||||
|  | ||||
|         listen 8080; | ||||
|  | ||||
|         location = /stub_status { | ||||
|             stub_status; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										54
									
								
								passbook/core/templates/base/skeleton_login.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								passbook/core/templates/base/skeleton_login.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| {% load static %} | ||||
| {% load i18n %} | ||||
| {% load utils %} | ||||
|  | ||||
| <!DOCTYPE html> | ||||
| <html lang="en" class="layout-pf layout-pf-fixed transitions"> | ||||
|  | ||||
| <head> | ||||
|   <meta charset="UTF-8"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|   <title> | ||||
|     {% block title %} | ||||
|     {% title %} | ||||
|     {% endblock %} | ||||
|   </title> | ||||
|   <link rel="icon" type="image/png" href="{% static 'img/logo.png' %}"> | ||||
|   <link rel="shortcut icon" type="image/png" href="{% static 'img/logo.png' %}"> | ||||
|   <link rel="stylesheet" type="text/css" href="{% static 'css/patternfly.min.css' %}"> | ||||
|   <link rel="stylesheet" type="text/css" href="{% static 'css/patternfly-additions.min.css' %}"> | ||||
|   <link rel="stylesheet" type="text/css" href="{% static 'css/passbook.css' %}"> | ||||
|   <style> | ||||
|     .login-pf { | ||||
|       background-attachment: fixed; | ||||
|       scroll-behavior: smooth; | ||||
|       background-size: cover; | ||||
|     } | ||||
|   </style> | ||||
|   {% block head %} | ||||
|   {% endblock %} | ||||
| </head> | ||||
|  | ||||
| <body class="login-pf"> | ||||
|   {% if 'impersonate_id' in request.session %} | ||||
|   <div class="experimental-pf-bar"> | ||||
|     <span id="experimentalBar" class="experimental-pf-text"> | ||||
|       {% blocktrans with user=user %}You're currently impersonating {{ user }}.{% endblocktrans %} | ||||
|       <a href="?__unimpersonate=True" id="acceptMessage">{% trans 'Stop impersonation' %}</a> | ||||
|     </span> | ||||
|   </div> | ||||
|   {% endif %} | ||||
|   {% block body %} | ||||
|   {% endblock %} | ||||
|   <script src="{% static 'js/jquery.min.js' %}"></script> | ||||
|   <script src="{% static 'js/bootstrap.min.js' %}"></script> | ||||
|   <script src="{% static 'js/patternfly.min.js' %}"></script> | ||||
|   <script src="{% static 'js/passbook.js' %}"></script> | ||||
|   {% block scripts %} | ||||
|   {% endblock %} | ||||
|   <div class="modals"> | ||||
|     {% include 'partials/about_modal.html' %} | ||||
|   </div> | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
| @ -1,4 +1,4 @@ | ||||
| {% extends 'base/skeleton.html' %} | ||||
| {% extends 'base/skeleton_login.html' %} | ||||
|  | ||||
| {% load static %} | ||||
| {% load i18n %} | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook hibp_policy""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| """Passbook ldap app Header""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1 +0,0 @@ | ||||
| ldap3 | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook lib""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -137,4 +137,6 @@ def signal_handler(sender, **kwargs): | ||||
|     """Add all loaded config files to autoreload watcher""" | ||||
|     for path in CONFIG.loaded_file: | ||||
|         sender.watch_file(path) | ||||
|  | ||||
|  | ||||
| autoreload_started.connect(signal_handler) | ||||
|  | ||||
| @ -23,9 +23,13 @@ email: | ||||
|   use_ssl: false | ||||
|   from: passbook <passbook@domain.tld> | ||||
| web: | ||||
|   listen: 0.0.0.0 | ||||
|   port: 8000 | ||||
|   threads: 30 | ||||
|   server.socket_host: 0.0.0.0 | ||||
|   server.socket_port: 8000 | ||||
|   server.thread_pool: 20 | ||||
|   log.screen: false | ||||
|   log.access_file: '' | ||||
|   log.error_file: '' | ||||
|  | ||||
| debug: false | ||||
| secure_proxy_header: | ||||
|   HTTP_X_FORWARDED_PROTO: https | ||||
| @ -96,3 +100,6 @@ saml_idp: | ||||
|   types: | ||||
|     - passbook.saml_idp.processors.generic | ||||
|     - passbook.saml_idp.processors.salesforce | ||||
| app_gw: | ||||
|   listen: 0.0.0.0 | ||||
|   port: 8000 | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook oauth_client Header""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| requests_oauthlib>=0.4.2 | ||||
| oauthlib>=2.0.6 | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook oauth_provider Header""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| """passbook OAuth2 IDP Forms""" | ||||
| """passbook OAuth2 Provider Forms""" | ||||
|  | ||||
| from django import forms | ||||
|  | ||||
|  | ||||
| @ -25,8 +25,6 @@ class OAuth2Provider(Provider, AbstractApplication): | ||||
|                 reverse('passbook_oauth_provider:token')), | ||||
|             'userinfo_url': request.build_absolute_uri( | ||||
|                 reverse('passbook_api:openid')), | ||||
|             'openid_url': request.build_absolute_uri( | ||||
|                 reverse('passbook_oauth_provider:openid-discovery')) | ||||
|         } | ||||
|  | ||||
|     class Meta: | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| django-oauth-toolkit | ||||
| django-cors-middleware | ||||
| @ -31,19 +31,10 @@ | ||||
|             </div> | ||||
|           </div> | ||||
|         </form> | ||||
|         <hr> | ||||
|         <form class="form-horizontal"> | ||||
|           <div class="form-group"> | ||||
|             <label class="col-sm-3 control-label">{% trans 'OpenID Configuration URL' %}</label> | ||||
|             <div class="col-sm-9"> | ||||
|               <input type="text"class="form-control" readonly value="{{ openid_url }}"> | ||||
|             </div> | ||||
|           </div> | ||||
|         </form> | ||||
|       </div> | ||||
|       <div class="modal-footer"> | ||||
|         <button type="button" class="btn btn-primary" data-dismiss="modal">{% trans 'Close' %}</button> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| </div> | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| from django.urls import path | ||||
| from oauth2_provider import views | ||||
|  | ||||
| from passbook.oauth_provider.views import oauth2, openid | ||||
| from passbook.oauth_provider.views import oauth2 | ||||
|  | ||||
| urlpatterns = [ | ||||
|     # Custom OAuth 2 Authorize View | ||||
| @ -17,9 +17,4 @@ urlpatterns = [ | ||||
|     path("token/", views.TokenView.as_view(), name="token"), | ||||
|     path("revoke_token/", views.RevokeTokenView.as_view(), name="revoke-token"), | ||||
|     path("introspect/", views.IntrospectTokenView.as_view(), name="introspect"), | ||||
|     # OpenID-Connect Discovery | ||||
|     path('.well-known/openid-configuration', openid.OpenIDConfigurationView.as_view(), | ||||
|          name='openid-discovery'), | ||||
|     path('.well-known/jwks.json', openid.JSONWebKeyView.as_view(), | ||||
|          name='openid-jwks'), | ||||
| ] | ||||
|  | ||||
| @ -57,10 +57,10 @@ class PassbookAuthorizationView(AccessMixin, AuthorizationView): | ||||
|         provider.save() | ||||
|         self._application = application | ||||
|         # Check permissions | ||||
|         passing, policy_meaages = self.user_has_access(self._application, request.user) | ||||
|         passing, policy_messages = self.user_has_access(self._application, request.user) | ||||
|         if not passing: | ||||
|             for policy_meaage in policy_meaages: | ||||
|                 messages.error(request, policy_meaage) | ||||
|             for policy_message in policy_messages: | ||||
|                 messages.error(request, policy_message) | ||||
|             return redirect('passbook_oauth_provider:oauth2-permission-denied') | ||||
|         # Some clients don't pass response_type, so we default to code | ||||
|         if 'response_type' not in request.GET: | ||||
|  | ||||
| @ -1,35 +0,0 @@ | ||||
| """passbook oauth provider OpenID Views""" | ||||
|  | ||||
| from django.http import HttpRequest, JsonResponse | ||||
| from django.shortcuts import reverse | ||||
| from django.views.generic import View | ||||
|  | ||||
|  | ||||
| class OpenIDConfigurationView(View): | ||||
|     """Return OpenID Configuration""" | ||||
|  | ||||
|     def get_issuer_url(self, request): | ||||
|         """Get correct issuer URL""" | ||||
|         full_url = request.build_absolute_uri(reverse('passbook_oauth_provider:openid-discovery')) | ||||
|         return full_url.replace(".well-known/openid-configuration", "") | ||||
|  | ||||
|     def get(self, request: HttpRequest): | ||||
|         """Get Response conform to https://openid.net/specs/openid-connect-discovery-1_0.html""" | ||||
|         return JsonResponse({ | ||||
|             'issuer': self.get_issuer_url(request), | ||||
|             'authorization_endpoint': request.build_absolute_uri( | ||||
|                 reverse('passbook_oauth_provider:oauth2-authorize')), | ||||
|             'token_endpoint': request.build_absolute_uri(reverse('passbook_oauth_provider:token')), | ||||
|             "jwks_uri": request.build_absolute_uri(reverse('passbook_oauth_provider:openid-jwks')), | ||||
|             "scopes_supported": [ | ||||
|                 "openid", | ||||
|             ], | ||||
|         }) | ||||
|  | ||||
|  | ||||
| class JSONWebKeyView(View): | ||||
|     """JSON Web Key View""" | ||||
|  | ||||
|     def get(self, request: HttpRequest): | ||||
|         """JSON Webkeys are not implemented yet, hence return an empty object""" | ||||
|         return JsonResponse({}) | ||||
							
								
								
									
										31
									
								
								passbook/oidc_provider/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								passbook/oidc_provider/apps.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| """passbook auth oidc provider app config""" | ||||
| from logging import getLogger | ||||
|  | ||||
| from django.apps import AppConfig | ||||
| from django.db.utils import InternalError, OperationalError, ProgrammingError | ||||
| from django.urls import include, path | ||||
|  | ||||
| LOGGER = getLogger(__name__) | ||||
|  | ||||
| class PassbookOIDCProviderConfig(AppConfig): | ||||
|     """passbook auth oidc provider app config""" | ||||
|  | ||||
|     name = 'passbook.oidc_provider' | ||||
|     label = 'passbook_oidc_provider' | ||||
|     verbose_name = 'passbook OIDC Provider' | ||||
|  | ||||
|     def ready(self): | ||||
|         try: | ||||
|             from Cryptodome.PublicKey import RSA | ||||
|             from oidc_provider.models import RSAKey | ||||
|             if not RSAKey.objects.exists(): | ||||
|                 key = RSA.generate(2048) | ||||
|                 rsakey = RSAKey(key=key.exportKey('PEM').decode('utf8')) | ||||
|                 rsakey.save() | ||||
|                 LOGGER.info("Created key") | ||||
|         except (OperationalError, ProgrammingError, InternalError): | ||||
|             pass | ||||
|         from passbook.root import urls | ||||
|         urls.urlpatterns.append( | ||||
|             path('application/oidc/', include('oidc_provider.urls', namespace='oidc_provider')), | ||||
|         ) | ||||
							
								
								
									
										38
									
								
								passbook/oidc_provider/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								passbook/oidc_provider/forms.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| """passbook OIDC IDP Forms""" | ||||
|  | ||||
| from django import forms | ||||
| from oauth2_provider.generators import (generate_client_id, | ||||
|                                         generate_client_secret) | ||||
| from oidc_provider.models import Client | ||||
|  | ||||
| from passbook.oidc_provider.models import OpenIDProvider | ||||
|  | ||||
|  | ||||
| class OIDCProviderForm(forms.ModelForm): | ||||
|     """OpenID Client form""" | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         # Correctly load data from 1:1 rel | ||||
|         if 'instance' in kwargs and kwargs['instance']: | ||||
|             kwargs['instance'] = kwargs['instance'].oidc_client | ||||
|         super().__init__(*args, **kwargs) | ||||
|         self.fields['client_id'].initial = generate_client_id() | ||||
|         self.fields['client_secret'].initial = generate_client_secret() | ||||
|  | ||||
|     def save(self, *args, **kwargs): | ||||
|         response = super().save(*args, **kwargs) | ||||
|         # Check if openidprovider class instance exists | ||||
|         if not OpenIDProvider.objects.filter(oidc_client=self.instance).exists(): | ||||
|             OpenIDProvider.objects.create(oidc_client=self.instance) | ||||
|         return response | ||||
|  | ||||
|     class Meta: | ||||
|         model = Client | ||||
|         fields = [ | ||||
|             'name', 'client_type', 'client_id', 'client_secret', 'response_types', | ||||
|             'jwt_alg', 'reuse_consent', 'require_consent', '_redirect_uris', '_scope' | ||||
|         ] | ||||
|         # exclude = ['owner', 'website_url', 'terms_url', 'contact_email', 'logo', ] | ||||
|         labels = { | ||||
|             'client_secret': "Client Secret" | ||||
|         } | ||||
							
								
								
									
										30
									
								
								passbook/oidc_provider/lib.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								passbook/oidc_provider/lib.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| """OIDC Permission checking""" | ||||
| from logging import getLogger | ||||
|  | ||||
| from django.contrib import messages | ||||
| from django.shortcuts import redirect | ||||
|  | ||||
| from passbook.core.models import Application | ||||
| from passbook.core.policies import PolicyEngine | ||||
|  | ||||
| LOGGER = getLogger(__name__) | ||||
|  | ||||
| def check_permissions(request, user, client): | ||||
|     """Check permissions, used for | ||||
|     https://django-oidc-provider.readthedocs.io/en/latest/ | ||||
|     sections/settings.html#oidc-after-userlogin-hook""" | ||||
|     try: | ||||
|         application = client.openidprovider.application | ||||
|     except Application.DoesNotExist: | ||||
|         return redirect('passbook_oauth_provider:oauth2-permission-denied') | ||||
|     LOGGER.debug("Checking permissions of %s on application %s...", user, application) | ||||
|     policy_engine = PolicyEngine(application.policies.all()) | ||||
|     policy_engine.for_user(user).with_request(request).build() | ||||
|  | ||||
|     # Check permissions | ||||
|     passing, policy_messages = policy_engine.result | ||||
|     if not passing: | ||||
|         for policy_message in policy_messages: | ||||
|             messages.error(request, policy_message) | ||||
|         return redirect('passbook_oauth_provider:oauth2-permission-denied') | ||||
|     return None | ||||
							
								
								
									
										25
									
								
								passbook/oidc_provider/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								passbook/oidc_provider/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| # Generated by Django 2.2.3 on 2019-07-05 12:16 | ||||
|  | ||||
| import django.db.models.deletion | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     initial = True | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('oidc_provider', '0026_client_multiple_response_types'), | ||||
|         ('passbook_core', '0024_ssologinpolicy'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name='OpenIDProvider', | ||||
|             fields=[ | ||||
|                 ('provider_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Provider')), | ||||
|                 ('oidc_client', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='oidc_provider.Client')), | ||||
|             ], | ||||
|             bases=('passbook_core.provider',), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										17
									
								
								passbook/oidc_provider/migrations/0002_auto_20190709_1416.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								passbook/oidc_provider/migrations/0002_auto_20190709_1416.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| # Generated by Django 2.2.3 on 2019-07-09 14:16 | ||||
|  | ||||
| from django.db import migrations | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('passbook_oidc_provider', '0001_initial'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterModelOptions( | ||||
|             name='openidprovider', | ||||
|             options={'verbose_name': 'OpenID Provider', 'verbose_name_plural': 'OpenID Providers'}, | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										0
									
								
								passbook/oidc_provider/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/oidc_provider/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										45
									
								
								passbook/oidc_provider/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								passbook/oidc_provider/models.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| """oidc models""" | ||||
| from django.db import models | ||||
| from django.shortcuts import reverse | ||||
| from django.utils.translation import gettext as _ | ||||
| from oidc_provider.models import Client | ||||
|  | ||||
| from passbook.core.models import Provider | ||||
|  | ||||
|  | ||||
| class OpenIDProvider(Provider): | ||||
|     """Proxy model for OIDC Client""" | ||||
|     # Since oidc_provider doesn't currently support swappable models | ||||
|     # (https://github.com/juanifioren/django-oidc-provider/pull/305) | ||||
|     # we have a 1:1 relationship, and update oidc_client when the form is saved. | ||||
|  | ||||
|     oidc_client = models.OneToOneField(Client, on_delete=models.CASCADE) | ||||
|  | ||||
|     form = 'passbook.oidc_provider.forms.OIDCProviderForm' | ||||
|  | ||||
|     @property | ||||
|     def name(self): | ||||
|         """Name property for UI""" | ||||
|         return self.oidc_client.name | ||||
|  | ||||
|     def __str__(self): | ||||
|         return "OpenID Connect Provider %s" % self.oidc_client.__str__() | ||||
|  | ||||
|     def html_setup_urls(self, request): | ||||
|         """return template and context modal with URLs for authorize, token, openid-config, etc""" | ||||
|         return "oidc_provider/setup_url_modal.html", { | ||||
|             'provider': self, | ||||
|             'authorize': request.build_absolute_uri( | ||||
|                 reverse('oidc_provider:authorize')), | ||||
|             'token': request.build_absolute_uri( | ||||
|                 reverse('oidc_provider:token')), | ||||
|             'userinfo': request.build_absolute_uri( | ||||
|                 reverse('oidc_provider:userinfo')), | ||||
|             'provider_info': request.build_absolute_uri( | ||||
|                 reverse('oidc_provider:provider-info')), | ||||
|         } | ||||
|  | ||||
|     class Meta: | ||||
|  | ||||
|         verbose_name = _('OpenID Provider') | ||||
|         verbose_name_plural = _('OpenID Providers') | ||||
							
								
								
									
										7
									
								
								passbook/oidc_provider/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								passbook/oidc_provider/settings.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| """passbook OIDC Provider""" | ||||
|  | ||||
| INSTALLED_APPS = [ | ||||
|     'oidc_provider', | ||||
| ] | ||||
|  | ||||
| OIDC_AFTER_USERLOGIN_HOOK = "passbook.oidc_provider.lib.check_permissions" | ||||
| @ -0,0 +1,70 @@ | ||||
| {% extends "login/base.html" %} | ||||
|  | ||||
| {% load utils %} | ||||
| {% load i18n %} | ||||
|  | ||||
| {% block title %} | ||||
| {% title 'Authorize Application' %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block card %} | ||||
| <header class="login-pf-header"> | ||||
|   <h1>{% trans 'Authorize Application' %}</h1> | ||||
| </header> | ||||
| <form method="POST"> | ||||
|     {% csrf_token %} | ||||
|     {% if not error %} | ||||
|         {% csrf_token %} | ||||
|         {% for field in form %} | ||||
|             {% if field.is_hidden %} | ||||
|                 {{ field }} | ||||
|             {% endif %} | ||||
|         {% endfor %} | ||||
|         <div class="form-group"> | ||||
|             <p class="subtitle"> | ||||
|                 {% blocktrans with remote=client.name %} | ||||
|                 You're about to sign into {{ remote }} | ||||
|                 {% endblocktrans %} | ||||
|             </p> | ||||
|             <p>{% trans "Application requires following permissions" %}</p> | ||||
|             <ul> | ||||
|                 {% for scope in scopes %} | ||||
|                 <li>{{ scope.name }}</li> | ||||
|                 {% endfor %} | ||||
|             </ul> | ||||
|             {{ hidden_inputs }} | ||||
|             {{ form.errors }} | ||||
|             {{ form.non_field_errors }} | ||||
|             <p> | ||||
|                 {% blocktrans with user=user %} | ||||
|                 You are logged in as {{ user }}. Not you? | ||||
|                 {% endblocktrans %} | ||||
|                 <a href="{% url 'passbook_core:auth-logout' %}">{% trans 'Logout' %}</a> | ||||
|             </p> | ||||
|             <div class="form-group"> | ||||
|                 <input type="submit" class="btn btn-success btn-disabled btn-lg click-spinner" name="allow" value="{% trans 'Continue' %}"> | ||||
|                 <a href="{% back %}" class="btn btn-default btn-lg">{% trans "Cancel" %}</a> | ||||
|             </div> | ||||
|             <div class="form-group spinner-hidden hidden"> | ||||
|                 <div class="spinner"></div> | ||||
|             </div> | ||||
|         </div> | ||||
|     {% else %} | ||||
|     <div class="login-group"> | ||||
|         <p class="subtitle"> | ||||
|             {% blocktrans with err=error.error %}Error: {{ err }}{% endblocktrans %} | ||||
|         </p> | ||||
|         <p>{{ error.description }}</p> | ||||
|     </div> | ||||
|     {% endif %} | ||||
| </form> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block scripts %} | ||||
| <script> | ||||
|     $('.click-spinner').on('click', function (e) { | ||||
|         $('.spinner-hidden').removeClass('hidden'); | ||||
|         $(e.target).addClass('disabled'); | ||||
|     }) | ||||
| </script> | ||||
| {% endblock %} | ||||
| @ -0,0 +1,49 @@ | ||||
| {% load i18n %} | ||||
|  | ||||
| <button class="btn btn-default btn-sm" data-toggle="modal" data-target="#{{ provider.pk }}">{% trans 'View Setup URLs' %}</button> | ||||
| <div class="modal fade" id="{{ provider.pk }}" tabindex="-1" role="dialog" aria-labelledby="{{ provider.pk }}Label" aria-hidden="true"> | ||||
|   <div class="modal-dialog"> | ||||
|     <div class="modal-content"> | ||||
|       <div class="modal-header"> | ||||
|         <button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close"> | ||||
|           <span class="pficon pficon-close"></span> | ||||
|         </button> | ||||
|         <h4 class="modal-title" id="{{ provider.pk }}Label">{% trans 'Setup URLs' %}</h4> | ||||
|       </div> | ||||
|       <div class="modal-body"> | ||||
|         <form class="form-horizontal"> | ||||
|           <div class="form-group"> | ||||
|             <label class="col-sm-3 control-label">{% trans 'Authorize URL' %}</label> | ||||
|             <div class="col-sm-9"> | ||||
|               <input type="text"class="form-control" readonly value="{{ authorize }}"> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="form-group"> | ||||
|             <label class="col-sm-3 control-label">{% trans 'Token URL' %}</label> | ||||
|             <div class="col-sm-9"> | ||||
|               <input type="text" class="form-control" readonly value="{{ token }}"> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="form-group"> | ||||
|             <label class="col-sm-3 control-label">{% trans 'Userinfo Endpoint' %}</label> | ||||
|             <div class="col-sm-9"> | ||||
|               <input type="text" class="form-control" readonly value="{{ userinfo }}"> | ||||
|             </div> | ||||
|           </div> | ||||
|         </form> | ||||
|         <hr> | ||||
|         <form class="form-horizontal"> | ||||
|           <div class="form-group"> | ||||
|             <label class="col-sm-3 control-label">{% trans 'OpenID Configuration URL' %}</label> | ||||
|             <div class="col-sm-9"> | ||||
|               <input type="text"class="form-control" readonly value="{{ provider_info }}"> | ||||
|             </div> | ||||
|           </div> | ||||
|         </form> | ||||
|       </div> | ||||
|       <div class="modal-footer"> | ||||
|         <button type="button" class="btn btn-primary" data-dismiss="modal">{% trans 'Close' %}</button> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook otp Header""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| django_otp | ||||
| qrcode | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook password_expiry""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1,15 +0,0 @@ | ||||
| celery | ||||
| colorlog | ||||
| django-ipware | ||||
| django-model-utils | ||||
| django-redis | ||||
| django>=2.0 | ||||
| djangorestframework | ||||
| idna<2.8,>=2.5 | ||||
| markdown | ||||
| psycopg2 | ||||
| PyYAML | ||||
| sentry-sdk | ||||
| pip | ||||
| whitenoise | ||||
| urllib3<1.25,>=1.21.1 | ||||
| @ -82,6 +82,7 @@ INSTALLED_APPS = [ | ||||
|     'passbook.ldap.apps.PassbookLdapConfig', | ||||
|     'passbook.oauth_client.apps.PassbookOAuthClientConfig', | ||||
|     'passbook.oauth_provider.apps.PassbookOAuthProviderConfig', | ||||
|     'passbook.oidc_provider.apps.PassbookOIDCProviderConfig', | ||||
|     'passbook.saml_idp.apps.PassbookSAMLIDPConfig', | ||||
|     'passbook.otp.apps.PassbookOTPConfig', | ||||
|     'passbook.captcha_factor.apps.PassbookCaptchaFactorConfig', | ||||
| @ -122,7 +123,6 @@ CACHES = { | ||||
|  | ||||
| MIDDLEWARE = [ | ||||
|     'django.contrib.sessions.middleware.SessionMiddleware', | ||||
|     'whitenoise.middleware.WhiteNoiseMiddleware', | ||||
|     'django.contrib.auth.middleware.AuthenticationMiddleware', | ||||
|     'passbook.app_gw.middleware.ApplicationGatewayMiddleware', | ||||
|     'django.middleware.security.SecurityMiddleware', | ||||
| @ -221,7 +221,7 @@ CELERY_BEAT_SCHEDULE = { | ||||
|  | ||||
| if not DEBUG: | ||||
|     sentry_init( | ||||
|         dsn="https://55b5dd780bc14f4c96bba69b7a9abbcc@sentry.services.beryju.org/8", | ||||
|         dsn="https://33cdbcb23f8b436dbe0ee06847410b67@sentry.beryju.org/3", | ||||
|         integrations=[ | ||||
|             DjangoIntegration(), | ||||
|             CeleryIntegration(), | ||||
| @ -232,14 +232,13 @@ if not DEBUG: | ||||
|         ], | ||||
|         send_default_pii=True, | ||||
|         before_send=before_send, | ||||
|         release='p2@%s' % __version__ | ||||
|         release='passbook@%s' % __version__ | ||||
|     ) | ||||
|  | ||||
| # Static files (CSS, JavaScript, Images) | ||||
| # https://docs.djangoproject.com/en/2.1/howto/static-files/ | ||||
|  | ||||
| STATIC_URL = '/static/' | ||||
| STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' | ||||
|  | ||||
| with CONFIG.cd('log'): | ||||
|     LOGGING = { | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook saml_idp Header""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -1,5 +0,0 @@ | ||||
| beautifulsoup4>=4.6.0 | ||||
| lxml>=3.8.0 | ||||
| signxml | ||||
| defusedxml | ||||
| PyCryptodome | ||||
| @ -1,2 +0,0 @@ | ||||
| """passbook suspicious_policy""" | ||||
| __version__ = '0.2.0-beta' | ||||
|  | ||||
| @ -11,11 +11,21 @@ from passbook.suspicious_policy.models import IPScore, UserScore | ||||
| LOGGER = getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def get_remote_ip(request): | ||||
|     """Small wrapper of get_client_ip to catch errors""" | ||||
|     try: | ||||
|         remote_ip, _ = get_client_ip(request) | ||||
|         if remote_ip: | ||||
|             return remote_ip | ||||
|         if 'ip' in request: | ||||
|             return request['ip'] | ||||
|     except (AttributeError, ValueError): | ||||
|         pass | ||||
|     return '255.255.255.255' | ||||
|  | ||||
| def update_score(request, username, amount): | ||||
|     """Update score for IP and User""" | ||||
|     remote_ip, _ = get_client_ip(request) | ||||
|     if not remote_ip: | ||||
|         remote_ip = '255.255.255.255' | ||||
|     remote_ip = get_remote_ip(request) | ||||
|     ip_score, _ = IPScore.objects.update_or_create(ip=remote_ip) | ||||
|     ip_score.score += amount | ||||
|     ip_score.save() | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| -r requirements.txt | ||||
| -r client-packages/allauth/requirements.txt | ||||
| coverage | ||||
| isort | ||||
| astroid==2.0.4 | ||||
| pylint==2.1.1 | ||||
| pylint-django==2.0.2 | ||||
| prospector | ||||
| prospector==1.1.5 | ||||
| django-debug-toolbar | ||||
| pycodestyle<2.4.0,>=2.0.0 | ||||
| bumpversion | ||||
| @ -14,3 +13,4 @@ autopep8 | ||||
| bandit | ||||
| bumpversion | ||||
| twine | ||||
| grpcio-tools | ||||
|  | ||||
| @ -1,10 +1,52 @@ | ||||
| -r passbook/core/requirements.txt | ||||
| -r passbook/oauth_client/requirements.txt | ||||
| -r passbook/ldap/requirements.txt | ||||
| -r passbook/saml_idp/requirements.txt | ||||
| -r passbook/otp/requirements.txt | ||||
| -r passbook/oauth_provider/requirements.txt | ||||
| -r passbook/captcha_factor/requirements.txt | ||||
| -r passbook/admin/requirements.txt | ||||
| -r passbook/api/requirements.txt | ||||
| -r passbook/app_gw/requirements.txt | ||||
| # Root requirements | ||||
| celery | ||||
| colorlog | ||||
| cherrypy | ||||
| django-ipware | ||||
| django-model-utils | ||||
| django-redis | ||||
| django>=2.0 | ||||
| idna<2.8,>=2.5 | ||||
| markdown | ||||
| psycopg2 | ||||
| PyYAML | ||||
| sentry-sdk | ||||
| pip | ||||
| urllib3<1.25,>=1.21.1 | ||||
| grpcio | ||||
| grpcio-reflection | ||||
| protobuf | ||||
| packaging | ||||
| # OAuth Client | ||||
| requests_oauthlib>=0.4.2 | ||||
| oauthlib>=2.0.6 | ||||
| # LDAP Client | ||||
| ldap3 | ||||
| # SAML IDP | ||||
| beautifulsoup4>=4.6.0 | ||||
| lxml>=3.8.0 | ||||
| signxml | ||||
| defusedxml | ||||
| PyCryptodome | ||||
| # OTP | ||||
| django_otp | ||||
| qrcode | ||||
| # OAuth Provider | ||||
| django-oauth-toolkit | ||||
| django-cors-middleware | ||||
| # ReCaptcha | ||||
| django-recaptcha | ||||
| # API | ||||
| drf_yasg | ||||
| djangorestframework==3.9.4 | ||||
| django-filters | ||||
| # AppGW | ||||
| django-revproxy | ||||
| urllib3[secure] | ||||
| channels | ||||
| service_identity | ||||
| websocket-client | ||||
| daphne<2.3.0 | ||||
| asgiref~=2.3 | ||||
| # OIDC Provider | ||||
| django-oidc-provider | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	