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] | [bumpversion] | ||||||
| current_version = 0.2.0-beta | current_version = 0.2.8-beta | ||||||
| tag = True | tag = True | ||||||
| commit = True | commit = True | ||||||
| parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*) | parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*) | ||||||
| @ -15,10 +15,6 @@ values = | |||||||
| 	beta | 	beta | ||||||
| 	stable | 	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/values.yaml] | ||||||
|  |  | ||||||
| [bumpversion:file:helm/passbook/Chart.yaml] | [bumpversion:file:helm/passbook/Chart.yaml] | ||||||
| @ -27,33 +23,5 @@ values = | |||||||
|  |  | ||||||
| [bumpversion:file:passbook/__init__.py] | [bumpversion:file:passbook/__init__.py] | ||||||
|  |  | ||||||
| [bumpversion:file:passbook/api/__init__.py] | [bumpversion:file:passbook/core/nginx.conf] | ||||||
|  |  | ||||||
| [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] |  | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										113
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							| @ -1,109 +1,132 @@ | |||||||
| # Global Variables | # Global Variables | ||||||
| stages: | stages: | ||||||
|   - build-buildimage |   - build-base-image | ||||||
|  |   - build-dev-image | ||||||
|   - test |   - test | ||||||
|   - build |   - build | ||||||
|   - docs |   - package | ||||||
|   - deploy | image: docker.beryju.org/passbook/dev:latest | ||||||
| image: docker.beryju.org/passbook/build-base:latest |  | ||||||
| services: |  | ||||||
|   - postgres:latest |  | ||||||
|   - redis:latest |  | ||||||
|  |  | ||||||
| variables: | variables: | ||||||
|   POSTGRES_DB: passbook |   POSTGRES_DB: passbook | ||||||
|   POSTGRES_USER: passbook |   POSTGRES_USER: passbook | ||||||
|   POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77" |   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: |   image: | ||||||
|     name: gcr.io/kaniko-project/executor:debug |     name: gcr.io/kaniko-project/executor:debug | ||||||
|     entrypoint: [""] |     entrypoint: [""] | ||||||
|   before_script: |   before_script: | ||||||
|     - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json |     - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json | ||||||
|   script: |   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 |     - /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-buildimage |   stage: build-base-image | ||||||
|   only: |   only: | ||||||
|     refs: |     refs: | ||||||
|       - tags |       - tags | ||||||
|       - /^version/.*$/ |       - /^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: | isort: | ||||||
|   script: |   script: | ||||||
|     - isort -c -sg env |     - isort -c -sg env | ||||||
|   stage: test |   stage: test | ||||||
|  |   services: | ||||||
|  |   - postgres:latest | ||||||
|  |   - redis:latest | ||||||
| migrations: | migrations: | ||||||
|   script: |   script: | ||||||
|     - python manage.py migrate |     - python manage.py migrate | ||||||
|   stage: test |   stage: test | ||||||
|  |   services: | ||||||
|  |   - postgres:latest | ||||||
|  |   - redis:latest | ||||||
| prospector: | prospector: | ||||||
|   script: |   script: | ||||||
|     - prospector |     - prospector | ||||||
|   stage: test |   stage: test | ||||||
|  |   services: | ||||||
|  |   - postgres:latest | ||||||
|  |   - redis:latest | ||||||
| pylint: | pylint: | ||||||
|   script: |   script: | ||||||
|     - pylint passbook |     - pylint passbook | ||||||
|   stage: test |   stage: test | ||||||
|  |   services: | ||||||
|  |   - postgres:latest | ||||||
|  |   - redis:latest | ||||||
| coverage: | coverage: | ||||||
|   script: |   script: | ||||||
|     - python manage.py collectstatic --no-input |  | ||||||
|     - coverage run manage.py test |     - coverage run manage.py test | ||||||
|     - coverage report |     - coverage report | ||||||
|  |     - coverage html | ||||||
|   stage: test |   stage: test | ||||||
| bandit: |   services: | ||||||
|   script: |   - postgres:latest | ||||||
|     - bandit -r passbook |   - redis:latest | ||||||
|   stage: test |  | ||||||
|  |  | ||||||
| package-docker: | package-passbook-server: | ||||||
|   image: |   image: | ||||||
|     name: gcr.io/kaniko-project/executor:debug |     name: gcr.io/kaniko-project/executor:debug | ||||||
|     entrypoint: [""] |     entrypoint: [""] | ||||||
|   before_script: |   before_script: | ||||||
|     - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json |     - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json | ||||||
|   script: |   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 |   stage: build | ||||||
|   only: |   only: | ||||||
|     - tags |     - tags | ||||||
|     - /^version/.*$/ |     - /^version/.*$/ | ||||||
| package-helm: | build-passbook-static: | ||||||
|   stage: build |   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: |   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 |     - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash | ||||||
|  |   script: | ||||||
|     - helm init --client-only |     - helm init --client-only | ||||||
|  |     - helm dependency build helm/passbook | ||||||
|     - helm package helm/passbook |     - helm package helm/passbook | ||||||
|   artifacts: |   artifacts: | ||||||
|     paths: |     paths: | ||||||
|       - passbook-*.tgz |       - passbook-*.tgz | ||||||
|     expire_in: 2 days |     expire_in: 1 week | ||||||
|   only: |   only: | ||||||
|     - tags |     - tags | ||||||
|     - /^version/.*$/ |     - /^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 ./passbook/ /app/passbook | ||||||
| COPY ./manage.py /app/ | 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 | 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 | apiVersion: v1 | ||||||
| appVersion: "0.2.0-beta" | appVersion: "0.2.8-beta" | ||||||
| description: A Helm chart for passbook. | description: A Helm chart for passbook. | ||||||
| name: 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 | icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| apiVersion: apps/v1beta2 | apiVersion: apps/v1beta2 | ||||||
| kind: Deployment | kind: Deployment | ||||||
| metadata: | metadata: | ||||||
|   name: {{ include "passbook.fullname" . }}-web |   name: {{ include "passbook.fullname" . }}-appgw | ||||||
|   labels: |   labels: | ||||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} |     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||||
|     helm.sh/chart: {{ include "passbook.chart" . }} |     helm.sh/chart: {{ include "passbook.chart" . }} | ||||||
| @ -18,7 +18,7 @@ spec: | |||||||
|       labels: |       labels: | ||||||
|         app.kubernetes.io/name: {{ include "passbook.name" . }} |         app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||||
|         app.kubernetes.io/instance: {{ .Release.Name }} |         app.kubernetes.io/instance: {{ .Release.Name }} | ||||||
|         passbook.io/component: web |         passbook.io/component: appgw | ||||||
|     spec: |     spec: | ||||||
|       volumes: |       volumes: | ||||||
|         - name: config-volume |         - name: config-volume | ||||||
| @ -28,8 +28,10 @@ spec: | |||||||
|         - name: {{ .Chart.Name }} |         - name: {{ .Chart.Name }} | ||||||
|           image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}" |           image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}" | ||||||
|           imagePullPolicy: IfNotPresent |           imagePullPolicy: IfNotPresent | ||||||
|           command: ["/bin/sh","-c"] |           command: | ||||||
|           args: ["./manage.py migrate && ./manage.py web"] |             - ./manage.py | ||||||
|  |           args: | ||||||
|  |             - app_gw_web | ||||||
|           ports: |           ports: | ||||||
|             - name: http |             - name: http | ||||||
|               containerPort: 8000 |               containerPort: 8000 | ||||||
| @ -52,16 +54,9 @@ spec: | |||||||
|                 - name: Host |                 - name: Host | ||||||
|                   value: kubernetes-healthcheck-host |                   value: kubernetes-healthcheck-host | ||||||
|           resources: |           resources: | ||||||
| {{ toYaml .Values.resources | indent 12 }} |             requests: | ||||||
|     {{- with .Values.nodeSelector }} |               cpu: 150m | ||||||
|       nodeSelector: |               memory: 300M | ||||||
| {{ toYaml . | indent 8 }} |             limits: | ||||||
|     {{- end }} |               cpu: 500m | ||||||
|     {{- with .Values.affinity }} |               memory: 500M | ||||||
|       affinity: |  | ||||||
| {{ toYaml . | indent 8 }} |  | ||||||
|     {{- end }} |  | ||||||
|     {{- with .Values.tolerations }} |  | ||||||
|       tolerations: |  | ||||||
| {{ toYaml . | indent 8 }} |  | ||||||
|     {{- end }} |  | ||||||
							
								
								
									
										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 -}} | {{- if .Values.ingress.enabled -}} | ||||||
| {{- $fullName := include "passbook.fullname" . -}} | {{- $fullName := include "passbook.fullname" . -}} | ||||||
| {{- $ingressPath := .Values.ingress.path -}} |  | ||||||
| apiVersion: extensions/v1beta1 | apiVersion: extensions/v1beta1 | ||||||
| kind: Ingress | kind: Ingress | ||||||
| metadata: | metadata: | ||||||
| @ -30,9 +29,22 @@ spec: | |||||||
|     - host: {{ . | quote }} |     - host: {{ . | quote }} | ||||||
|       http: |       http: | ||||||
|         paths: |         paths: | ||||||
|           - path: {{ $ingressPath }} |           - path: / | ||||||
|             backend: |             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 |               servicePort: http | ||||||
|   {{- end }} |   {{- end }} | ||||||
| {{- 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 | apiVersion: v1 | ||||||
| kind: Service | kind: Service | ||||||
| metadata: | metadata: | ||||||
|   name: {{ include "passbook.fullname" . }} |   name: {{ include "passbook.fullname" . }}-web | ||||||
|   labels: |   labels: | ||||||
|     app.kubernetes.io/name: {{ include "passbook.name" . }} |     app.kubernetes.io/name: {{ include "passbook.name" . }} | ||||||
|     helm.sh/chart: {{ include "passbook.chart" . }} |     helm.sh/chart: {{ include "passbook.chart" . }} | ||||||
| @ -28,7 +28,10 @@ spec: | |||||||
|         - name: {{ .Chart.Name }} |         - name: {{ .Chart.Name }} | ||||||
|           image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}" |           image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}" | ||||||
|           imagePullPolicy: IfNotPresent |           imagePullPolicy: IfNotPresent | ||||||
|           command: ["./manage.py", "worker"] |           command: | ||||||
|  |             - ./manage.py | ||||||
|  |           args: | ||||||
|  |             - worker | ||||||
|           ports: |           ports: | ||||||
|             - name: http |             - name: http | ||||||
|               containerPort: 8000 |               containerPort: 8000 | ||||||
| @ -37,16 +40,9 @@ spec: | |||||||
|             - mountPath: /etc/passbook |             - mountPath: /etc/passbook | ||||||
|               name: config-volume |               name: config-volume | ||||||
|           resources: |           resources: | ||||||
| {{ toYaml .Values.resources | indent 12 }} |             requests: | ||||||
|     {{- with .Values.nodeSelector }} |               cpu: 150m | ||||||
|       nodeSelector: |               memory: 300M | ||||||
| {{ toYaml . | indent 8 }} |             limits: | ||||||
|     {{- end }} |               cpu: 300m | ||||||
|     {{- with .Values.affinity }} |               memory: 500M | ||||||
|       affinity: |  | ||||||
| {{ toYaml . | indent 8 }} |  | ||||||
|     {{- end }} |  | ||||||
|     {{- with .Values.tolerations }} |  | ||||||
|       tolerations: |  | ||||||
| {{ toYaml . | indent 8 }} |  | ||||||
|     {{- end }} |  | ||||||
| @ -5,7 +5,7 @@ | |||||||
| replicaCount: 1 | replicaCount: 1 | ||||||
|  |  | ||||||
| image: | image: | ||||||
|   tag: 0.2.0-beta |   tag: 0.2.8-beta | ||||||
|  |  | ||||||
| nameOverride: "" | nameOverride: "" | ||||||
|  |  | ||||||
| @ -37,6 +37,8 @@ ingress: | |||||||
|   path: / |   path: / | ||||||
|   hosts: |   hosts: | ||||||
|     - passbook.k8s.local |     - passbook.k8s.local | ||||||
|  |   app_gw_hosts: | ||||||
|  |     - '*.passbook.k8s.local' | ||||||
|   defaultHost: passbook.k8s.local |   defaultHost: passbook.k8s.local | ||||||
|   tls: [] |   tls: [] | ||||||
|   #  - secretName: chart-example-tls |   #  - secretName: chart-example-tls | ||||||
|  | |||||||
| @ -1,2 +1,2 @@ | |||||||
| """passbook""" | """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) |         self._set_content_type(proxy_response) | ||||||
|         response = get_django_response(proxy_response, strict_cookies=False) |         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) |         # LOGGER.debug("RESPONSE RETURNED: %s", response) | ||||||
|         return 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 number of bytes that are going to be read in a file lecture | ||||||
| DEFAULT_AMT = 2 ** 16 | DEFAULT_AMT = 2 ** 16 | ||||||
|  |  | ||||||
| logger = logging.getLogger('revproxy.response') | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_django_response(proxy_response, strict_cookies=False): | 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_AUTHORIZE_APPLICATION = 'authorize_application' | ||||||
|     ACTION_SUSPICIOUS_REQUEST = 'suspicious_request' |     ACTION_SUSPICIOUS_REQUEST = 'suspicious_request' | ||||||
|     ACTION_SIGN_UP = 'sign_up' |     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_CREATED = 'invitation_created' | ||||||
|     ACTION_INVITE_USED = 'invitation_used' |     ACTION_INVITE_USED = 'invitation_used' | ||||||
|     ACTIONS = ( |     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""" | """passbook core""" | ||||||
| __version__ = '0.2.0-beta' | __version__ = '0.2.6-beta' | ||||||
|  | |||||||
| @ -2,11 +2,12 @@ | |||||||
|  |  | ||||||
| from logging import getLogger | 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.core.management.base import BaseCommand | ||||||
| from django.utils import autoreload |  | ||||||
|  |  | ||||||
| from passbook.lib.config import CONFIG | from passbook.lib.config import CONFIG | ||||||
|  | from passbook.root.wsgi import application | ||||||
|  |  | ||||||
| LOGGER = getLogger(__name__) | LOGGER = getLogger(__name__) | ||||||
|  |  | ||||||
| @ -15,16 +16,21 @@ class Command(BaseCommand): | |||||||
|     """Run CherryPy webserver""" |     """Run CherryPy webserver""" | ||||||
|  |  | ||||||
|     def handle(self, *args, **options): |     def handle(self, *args, **options): | ||||||
|         """passbook daphne server""" |         """passbook cherrypy server""" | ||||||
|         autoreload.run_with_reloader(self.daphne_server) |         cherrypy.config.update(CONFIG.get('web')) | ||||||
|  |         cherrypy.tree.graft(application, '/') | ||||||
|     def daphne_server(self): |         # Mount NullObject to serve static files | ||||||
|         """Run daphne server within autoreload""" |         cherrypy.tree.mount(None, settings.STATIC_URL, config={ | ||||||
|         autoreload.raise_last_exception() |             '/': { | ||||||
|         CommandLineInterface().run([ |                 'tools.staticdir.on': True, | ||||||
|             '-p', str(CONFIG.y('web.port', 8000)), |                 'tools.staticdir.dir': settings.STATIC_ROOT, | ||||||
|             '-b', CONFIG.y('web.listen', '0.0.0.0'),  # nosec |                 'tools.expires.on': True, | ||||||
|             '--access-log', '/dev/null', |                 'tools.expires.secs': 86400, | ||||||
|             '--application-close-timeout', '500', |                 'tools.gzip.on': True, | ||||||
|             'passbook.root.asgi:application' |             } | ||||||
|         ]) |         }) | ||||||
|  |         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 static %} | ||||||
| {% load i18n %} | {% 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""" |     """Add all loaded config files to autoreload watcher""" | ||||||
|     for path in CONFIG.loaded_file: |     for path in CONFIG.loaded_file: | ||||||
|         sender.watch_file(path) |         sender.watch_file(path) | ||||||
|  |  | ||||||
|  |  | ||||||
| autoreload_started.connect(signal_handler) | autoreload_started.connect(signal_handler) | ||||||
|  | |||||||
| @ -23,9 +23,13 @@ email: | |||||||
|   use_ssl: false |   use_ssl: false | ||||||
|   from: passbook <passbook@domain.tld> |   from: passbook <passbook@domain.tld> | ||||||
| web: | web: | ||||||
|   listen: 0.0.0.0 |   server.socket_host: 0.0.0.0 | ||||||
|   port: 8000 |   server.socket_port: 8000 | ||||||
|   threads: 30 |   server.thread_pool: 20 | ||||||
|  |   log.screen: false | ||||||
|  |   log.access_file: '' | ||||||
|  |   log.error_file: '' | ||||||
|  |  | ||||||
| debug: false | debug: false | ||||||
| secure_proxy_header: | secure_proxy_header: | ||||||
|   HTTP_X_FORWARDED_PROTO: https |   HTTP_X_FORWARDED_PROTO: https | ||||||
| @ -96,3 +100,6 @@ saml_idp: | |||||||
|   types: |   types: | ||||||
|     - passbook.saml_idp.processors.generic |     - passbook.saml_idp.processors.generic | ||||||
|     - passbook.saml_idp.processors.salesforce |     - 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 | from django import forms | ||||||
|  |  | ||||||
|  | |||||||
| @ -25,8 +25,6 @@ class OAuth2Provider(Provider, AbstractApplication): | |||||||
|                 reverse('passbook_oauth_provider:token')), |                 reverse('passbook_oauth_provider:token')), | ||||||
|             'userinfo_url': request.build_absolute_uri( |             'userinfo_url': request.build_absolute_uri( | ||||||
|                 reverse('passbook_api:openid')), |                 reverse('passbook_api:openid')), | ||||||
|             'openid_url': request.build_absolute_uri( |  | ||||||
|                 reverse('passbook_oauth_provider:openid-discovery')) |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  | |||||||
| @ -1,2 +0,0 @@ | |||||||
| django-oauth-toolkit |  | ||||||
| django-cors-middleware |  | ||||||
| @ -31,15 +31,6 @@ | |||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </form> |         </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> | ||||||
|       <div class="modal-footer"> |       <div class="modal-footer"> | ||||||
|         <button type="button" class="btn btn-primary" data-dismiss="modal">{% trans 'Close' %}</button> |         <button type="button" class="btn btn-primary" data-dismiss="modal">{% trans 'Close' %}</button> | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| from django.urls import path | from django.urls import path | ||||||
| from oauth2_provider import views | from oauth2_provider import views | ||||||
|  |  | ||||||
| from passbook.oauth_provider.views import oauth2, openid | from passbook.oauth_provider.views import oauth2 | ||||||
|  |  | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|     # Custom OAuth 2 Authorize View |     # Custom OAuth 2 Authorize View | ||||||
| @ -17,9 +17,4 @@ urlpatterns = [ | |||||||
|     path("token/", views.TokenView.as_view(), name="token"), |     path("token/", views.TokenView.as_view(), name="token"), | ||||||
|     path("revoke_token/", views.RevokeTokenView.as_view(), name="revoke-token"), |     path("revoke_token/", views.RevokeTokenView.as_view(), name="revoke-token"), | ||||||
|     path("introspect/", views.IntrospectTokenView.as_view(), name="introspect"), |     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() |         provider.save() | ||||||
|         self._application = application |         self._application = application | ||||||
|         # Check permissions |         # 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: |         if not passing: | ||||||
|             for policy_meaage in policy_meaages: |             for policy_message in policy_messages: | ||||||
|                 messages.error(request, policy_meaage) |                 messages.error(request, policy_message) | ||||||
|             return redirect('passbook_oauth_provider:oauth2-permission-denied') |             return redirect('passbook_oauth_provider:oauth2-permission-denied') | ||||||
|         # Some clients don't pass response_type, so we default to code |         # Some clients don't pass response_type, so we default to code | ||||||
|         if 'response_type' not in request.GET: |         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.ldap.apps.PassbookLdapConfig', | ||||||
|     'passbook.oauth_client.apps.PassbookOAuthClientConfig', |     'passbook.oauth_client.apps.PassbookOAuthClientConfig', | ||||||
|     'passbook.oauth_provider.apps.PassbookOAuthProviderConfig', |     'passbook.oauth_provider.apps.PassbookOAuthProviderConfig', | ||||||
|  |     'passbook.oidc_provider.apps.PassbookOIDCProviderConfig', | ||||||
|     'passbook.saml_idp.apps.PassbookSAMLIDPConfig', |     'passbook.saml_idp.apps.PassbookSAMLIDPConfig', | ||||||
|     'passbook.otp.apps.PassbookOTPConfig', |     'passbook.otp.apps.PassbookOTPConfig', | ||||||
|     'passbook.captcha_factor.apps.PassbookCaptchaFactorConfig', |     'passbook.captcha_factor.apps.PassbookCaptchaFactorConfig', | ||||||
| @ -122,7 +123,6 @@ CACHES = { | |||||||
|  |  | ||||||
| MIDDLEWARE = [ | MIDDLEWARE = [ | ||||||
|     'django.contrib.sessions.middleware.SessionMiddleware', |     'django.contrib.sessions.middleware.SessionMiddleware', | ||||||
|     'whitenoise.middleware.WhiteNoiseMiddleware', |  | ||||||
|     'django.contrib.auth.middleware.AuthenticationMiddleware', |     'django.contrib.auth.middleware.AuthenticationMiddleware', | ||||||
|     'passbook.app_gw.middleware.ApplicationGatewayMiddleware', |     'passbook.app_gw.middleware.ApplicationGatewayMiddleware', | ||||||
|     'django.middleware.security.SecurityMiddleware', |     'django.middleware.security.SecurityMiddleware', | ||||||
| @ -221,7 +221,7 @@ CELERY_BEAT_SCHEDULE = { | |||||||
|  |  | ||||||
| if not DEBUG: | if not DEBUG: | ||||||
|     sentry_init( |     sentry_init( | ||||||
|         dsn="https://55b5dd780bc14f4c96bba69b7a9abbcc@sentry.services.beryju.org/8", |         dsn="https://33cdbcb23f8b436dbe0ee06847410b67@sentry.beryju.org/3", | ||||||
|         integrations=[ |         integrations=[ | ||||||
|             DjangoIntegration(), |             DjangoIntegration(), | ||||||
|             CeleryIntegration(), |             CeleryIntegration(), | ||||||
| @ -232,14 +232,13 @@ if not DEBUG: | |||||||
|         ], |         ], | ||||||
|         send_default_pii=True, |         send_default_pii=True, | ||||||
|         before_send=before_send, |         before_send=before_send, | ||||||
|         release='p2@%s' % __version__ |         release='passbook@%s' % __version__ | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
| # Static files (CSS, JavaScript, Images) | # Static files (CSS, JavaScript, Images) | ||||||
| # https://docs.djangoproject.com/en/2.1/howto/static-files/ | # https://docs.djangoproject.com/en/2.1/howto/static-files/ | ||||||
|  |  | ||||||
| STATIC_URL = '/static/' | STATIC_URL = '/static/' | ||||||
| STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' |  | ||||||
|  |  | ||||||
| with CONFIG.cd('log'): | with CONFIG.cd('log'): | ||||||
|     LOGGING = { |     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__) | 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): | def update_score(request, username, amount): | ||||||
|     """Update score for IP and User""" |     """Update score for IP and User""" | ||||||
|     remote_ip, _ = get_client_ip(request) |     remote_ip = get_remote_ip(request) | ||||||
|     if not remote_ip: |  | ||||||
|         remote_ip = '255.255.255.255' |  | ||||||
|     ip_score, _ = IPScore.objects.update_or_create(ip=remote_ip) |     ip_score, _ = IPScore.objects.update_or_create(ip=remote_ip) | ||||||
|     ip_score.score += amount |     ip_score.score += amount | ||||||
|     ip_score.save() |     ip_score.save() | ||||||
|  | |||||||
| @ -1,11 +1,10 @@ | |||||||
| -r requirements.txt | -r requirements.txt | ||||||
| -r client-packages/allauth/requirements.txt |  | ||||||
| coverage | coverage | ||||||
| isort | isort | ||||||
| astroid==2.0.4 | astroid==2.0.4 | ||||||
| pylint==2.1.1 | pylint==2.1.1 | ||||||
| pylint-django==2.0.2 | pylint-django==2.0.2 | ||||||
| prospector | prospector==1.1.5 | ||||||
| django-debug-toolbar | django-debug-toolbar | ||||||
| pycodestyle<2.4.0,>=2.0.0 | pycodestyle<2.4.0,>=2.0.0 | ||||||
| bumpversion | bumpversion | ||||||
| @ -14,3 +13,4 @@ autopep8 | |||||||
| bandit | bandit | ||||||
| bumpversion | bumpversion | ||||||
| twine | twine | ||||||
|  | grpcio-tools | ||||||
|  | |||||||
| @ -1,10 +1,52 @@ | |||||||
| -r passbook/core/requirements.txt | # Root requirements | ||||||
| -r passbook/oauth_client/requirements.txt | celery | ||||||
| -r passbook/ldap/requirements.txt | colorlog | ||||||
| -r passbook/saml_idp/requirements.txt | cherrypy | ||||||
| -r passbook/otp/requirements.txt | django-ipware | ||||||
| -r passbook/oauth_provider/requirements.txt | django-model-utils | ||||||
| -r passbook/captcha_factor/requirements.txt | django-redis | ||||||
| -r passbook/admin/requirements.txt | django>=2.0 | ||||||
| -r passbook/api/requirements.txt | idna<2.8,>=2.5 | ||||||
| -r passbook/app_gw/requirements.txt | 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
	