Compare commits

..

20 Commits

Author SHA1 Message Date
d50c7ec8d4 bump version: 0.1.18-beta -> 0.1.19-beta 2019-03-13 15:16:48 +01:00
c0fdf377d1 prepare 0.1.18 2019-03-13 15:14:36 +01:00
70c11c8988 fix GitHub Pretend throwing a 500 error 2019-03-13 15:12:13 +01:00
67b19becc1 fix API Call for sentry-client, add missing template 2019-03-13 14:27:34 +01:00
ae64024ef4 bump version: 0.1.17-beta -> 0.1.18-beta 2019-03-13 11:31:05 +01:00
e6571826cb adjust versions for client packages, auto build client-packages 2019-03-12 17:30:57 +01:00
c621e61978 fix included yaml file 2019-03-12 17:19:13 +01:00
3626fa4b98 add sentry client 2019-03-12 17:18:08 +01:00
01b0eb159a fix Server Error when downloading metadata 2019-03-12 17:08:53 +01:00
63aa48d981 bump version: 0.1.16-beta -> 0.1.17-beta 2019-03-12 15:55:18 +01:00
2e0ba05d55 prepare 0.1.17 2019-03-12 15:55:14 +01:00
b2ac57bb67 switch to vertical navigation 2019-03-12 13:35:09 +01:00
4c22e5c2c8 don't use celery heartbeat, use TCP keepalive instead 2019-03-12 13:34:54 +01:00
4a7b0ec8a9 remove Application.user_is_authorized 2019-03-12 10:56:01 +01:00
330118249e bump version: 0.1.15-beta -> 0.1.16-beta 2019-03-11 21:35:11 +01:00
8d4dabde02 finalize RabbitMQ replacement, update debian package, remove redis tgz 2019-03-11 21:35:06 +01:00
cf7323c41b bump version: 0.1.14-beta -> 0.1.15-beta 2019-03-11 21:01:18 +01:00
edd856df7d redis -> rabbitmq 2019-03-11 20:46:19 +01:00
5e35859db6 bump version: 0.1.13-beta -> 0.1.14-beta 2019-03-11 11:44:34 +01:00
acabb2df54 fix unittests 2019-03-11 11:44:12 +01:00
66 changed files with 1038 additions and 239 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.1.13-beta
current_version = 0.1.19-beta
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
@ -15,6 +15,10 @@ values =
beta
stable
[bumpversion:file:client-packages/allauth/setup.py]
[bumpversion:file:client-packages/sentry-auth-passbook/setup.py]
[bumpversion:file:helm/passbook/values.yaml]
[bumpversion:file:helm/passbook/Chart.yaml]

View File

@ -1,98 +1,124 @@
# Global Variables
before_script:
- "python3 -m pip install -U virtualenv"
- "virtualenv env"
- "source env/bin/activate"
- "pip3 install -U -r requirements-dev.txt"
- "python3 -m pip install -U virtualenv"
- "virtualenv env"
- "source env/bin/activate"
- "pip3 install -U -r requirements-dev.txt"
stages:
- test
- build
- docs
- deploy
- test
- build
- docs
- deploy
image: python:3.6
services:
- postgres:latest
- postgres:latest
variables:
POSTGRES_DB: passbook
POSTGRES_USER: passbook
POSTGRES_PASSWORD: 'EK-5jnKfjrGRm<77'
POSTGRES_DB: passbook
POSTGRES_USER: passbook
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
include:
- /allauth/.gitlab-ci.yml
- /client-packages/allauth/.gitlab-ci.yml
isort:
script:
- isort -c -sg env
stage: test
script:
- isort -c -sg env
stage: test
migrations:
script:
- python manage.py migrate
stage: test
script:
- python manage.py migrate
stage: test
prospector:
script:
- prospector
stage: test
script:
- prospector
stage: test
pylint:
script:
- pylint passbook
stage: test
script:
- pylint passbook
stage: test
coverage:
script:
- coverage run manage.py test
- coverage report
stage: test
script:
- coverage run manage.py test
- coverage report
stage: test
bandit:
script:
- bandit -r passbook
stage: test
script:
- bandit -r passbook
stage: test
package-docker:
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
before_script:
- echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json
script:
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.1.13-beta
stage: build
only:
- tags
- /^version/.*$/
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
before_script:
- echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json
script:
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.1.19-beta
stage: build
only:
- tags
- /^version/.*$/
package-helm:
stage: build
script:
- curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash
- helm init --client-only
- helm package helm/passbook
- ./manage.py nexus_upload --method put --url $NEXUS_URL --auth $NEXUS_AUTH --repo helm *.tgz
only:
- tags
- /^version/.*$/
stage: build
script:
- curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash
- helm init --client-only
- helm package helm/passbook
- ./manage.py nexus_upload --method put --url $NEXUS_URL --auth $NEXUS_AUTH --repo helm *.tgz
only:
- tags
- /^version/.*$/
package-debian:
before_script:
- apt update
- apt install -y --no-install-recommends build-essential debhelper devscripts equivs python3 python3-dev python3-pip libsasl2-dev libldap2-dev
- mk-build-deps debian/control
- apt install ./*build-deps*deb -f -y
- python3 -m pip install -U virtualenv pip
- virtualenv env
- source env/bin/activate
- pip3 install -U -r requirements.txt -r requirements-dev.txt
- ./manage.py collectstatic --no-input
image: ubuntu:18.04
script:
- debuild -us -uc
- cp ../passbook*.deb .
- ./manage.py nexus_upload --method post --url $NEXUS_URL --auth $NEXUS_AUTH --repo apt passbook*deb
artifacts:
paths:
- passbook*deb
expire_in: 2 days
stage: build
only:
- tags
- /^version/.*$/
before_script:
- apt update
- apt install -y --no-install-recommends build-essential debhelper devscripts equivs python3 python3-dev python3-pip libsasl2-dev libldap2-dev
- mk-build-deps debian/control
- apt install ./*build-deps*deb -f -y
- python3 -m pip install -U virtualenv pip
- virtualenv env
- source env/bin/activate
- pip3 install -U -r requirements.txt -r requirements-dev.txt
- ./manage.py collectstatic --no-input
image: ubuntu:18.04
script:
- debuild -us -uc
- cp ../passbook*.deb .
- ./manage.py nexus_upload --method post --url $NEXUS_URL --auth $NEXUS_AUTH --repo apt passbook*deb
artifacts:
paths:
- passbook*deb
expire_in: 2 days
stage: build
only:
- tags
- /^version/.*$/
package-client-package-allauth:
script:
- cd client-packages/allauth
- python setup.py sdist
- twine upload --username $TWINE_USERNAME --password $TWINE_PASSWORD dist/*
stage: build
only:
refs:
- tags
- /^version/.*$/
changes:
- client-packages/allauth/**
package-client-package-sentry:
script:
- cd client-packages/sentry-auth-passbook
- python setup.py sdist
- twine upload --username $TWINE_USERNAME --password $TWINE_PASSWORD dist/*
stage: build
only:
refs:
- tags
- /^version/.*$/
changes:
- client-packages/sentry-auth-passbook/**
# docs:
# stage: docs

View File

@ -7,6 +7,7 @@ ignore-paths:
- migrations
- docs
- node_modules
- client-packages
uses:
- django

View File

@ -1,5 +1,6 @@
"""passbook provider"""
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
from allauth_passbook.provider import PassbookProvider
urlpatterns = default_urlpatterns(PassbookProvider)

View File

@ -1,10 +1,10 @@
"""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

View File

@ -3,7 +3,7 @@ from setuptools import setup
setup(
name='django-allauth-passbook',
version='1.0.0',
version='0.1.19-beta',
description='passbook support for django-allauth',
# long_description='\n'.join(read_simple('docs/index.md')[2:]),
long_description_content_type='text/markdown',

View File

@ -0,0 +1,5 @@
*.pyc
*.egg-info/
*.eggs
/dist
/build

View File

@ -0,0 +1,32 @@
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

View File

@ -0,0 +1,201 @@
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.

View File

@ -0,0 +1,3 @@
include setup.py package.json webpack.config.js README.rst MANIFEST.in LICENSE AUTHORS
recursive-include sentry_auth_supervisr/templates *
global-exclude *~

View File

@ -0,0 +1,26 @@
.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

View File

@ -0,0 +1,55 @@
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"

View File

@ -0,0 +1,14 @@
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'
]

View File

@ -0,0 +1,7 @@
from __future__ import absolute_import
from sentry.auth import register
from .provider import PassbookOAuth2Provider
register('passbook', PassbookOAuth2Provider)

View File

@ -0,0 +1,45 @@
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)

View File

@ -0,0 +1,14 @@
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)

View File

@ -0,0 +1,62 @@
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']

View File

@ -0,0 +1,75 @@
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')

View File

@ -0,0 +1,12 @@
[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/*

View File

@ -0,0 +1,45 @@
#!/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.1.19-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'
],
)

View File

@ -0,0 +1,6 @@
from sentry.testutils import TestCase
class GitHubOAuth2ProviderTest(TestCase):
def test_simple(self):
pass

View File

@ -0,0 +1,17 @@
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

33
debian/changelog vendored
View File

@ -1,4 +1,35 @@
passbook (0.1.13) stable; urgency=medium
passbook (0.1.18) stable; urgency=medium
* bump version: 0.1.16-beta -> 0.1.17-beta
* fix Server Error when downloading metadata
* add sentry client
* fix included yaml file
* adjust versions for client packages, auto build client-packages
* bump version: 0.1.17-beta -> 0.1.18-beta
* fix API Call for sentry-client, add missing template
* fix GitHub Pretend throwing a 500 error
-- Jens Langhammer <jens.langhammer@beryju.org> Wed, 13 Mar 2019 14:14:10 +0000
passbook (0.1.17) stable; urgency=medium
* bump version: 0.1.15-beta -> 0.1.16-beta
* remove Application.user_is_authorized
* don't use celery heartbeat, use TCP keepalive instead
* switch to vertical navigation
-- Jens Langhammer <jens.langhammer@beryju.org> Tue, 12 Mar 2019 14:54:27 +0000
passbook (0.1.16) stable; urgency=medium
* Replace redis with RabbitMQ
* updated debian package to suggest RabbitMQ
* update helm chart to require RabbitMQ
* fix invalid default config in debian package
-- Jens Langhammer <jens.langhammer@beryju.org> Mon, 11 Mar 2019 10:28:36 +0000
passbook (0.1.14) stable; urgency=medium
* bump version: 0.1.11-beta -> 0.1.12-beta
* Fix DoesNotExist error when running PolicyEngine against None user

2
debian/control vendored
View File

@ -8,7 +8,7 @@ Standards-Version: 3.9.6
Package: passbook
Architecture: all
Recommends: mysql-server, redis-server
Recommends: mysql-server, rabbitmq-server
Pre-Depends: adduser, libldap2-dev, libsasl2-dev
Depends: python3 (>= 3.5) | python3.6 | python3.7, python3-pip, dbconfig-pgsql | dbconfig-no-thanks, ${misc:Depends}
Description: Authentication Provider/Proxy supporting protocols like SAML, OAuth, LDAP and more.

View File

@ -1,4 +1,3 @@
debug: false
http:
host: 0.0.0.0
port: 8000
@ -8,37 +7,71 @@ log:
console: INFO
file: DEBUG
file: /var/log/passbook/passbook.log
# Error reporting, disabled by default
# error_report_enabled: true
debug: false
secure_proxy_header:
HTTP_X_FORWARDED_PROTO: https
rabbitmq: guest:guest@localhost/passbook
# Error reporting, sends stacktrace to sentry.services.beryju.org
error_report_enabled: true
# Set this to the server's external address.
# This is used to generate external URLs
external_url: http://image.example.com
# This dictates how the Path is generated
# can be either of:
# - view_sha512_short
# - view_md5
# - view_sha256
# - view_sha512
default_return_view: view_sha256
# Set this to true if you only want to use external authentication
external_auth_only: false
# If this is true, images are automatically claimed if the windows user exists
# in django
auto_claim_enabled: true
# LDAP Authentication
# ldap:
# enabled: false
# server:
# uri: 'ldap://dc1.example.com'
# tls: false
# bind:
# dn: ''
# password: ''
# search_base: ''
# filter: '(sAMAccountName=%(user)s)'
# require_group: ''
passbook:
sign_up:
# Enables signup, created users are stored in internal Database and created in LDAP if ldap.create_users is true
enabled: true
password_reset:
# Enable password reset, passwords are reset in internal Database and in LDAP if ldap.reset_password is true
enabled: true
# Verification the user has to provide in order to be able to reset passwords. Can be any combination of `email`, `2fa`, `security_questions`
verification:
- email
# Text used in title, on login page and multiple other places
branding: passbook
login:
# Override URL used for logo
logo_url: null
# Override URL used for Background on Login page
bg_url: null
# Optionally add a subtext, placed below logo on the login page
subtext: null
footer:
links:
# Optionally add links to the footer on the login page
# - name: test
# href: https://test
# Specify which fields can be used to authenticate. Can be any combination of `username` and `email`
uid_fields:
- username
- email
session:
remember_age: 2592000 # 60 * 60 * 24 * 30, one month
# Provider-specific settings
ldap:
# Which field from `uid_fields` maps to which LDAP Attribute
login_field_map:
username: sAMAccountName
email: mail # or userPrincipalName
user_attribute_map:
active_directory:
username: "%(sAMAccountName)s"
email: "%(mail)s"
name: "%(displayName)"
oauth_client:
# List of python packages with sources types to load.
types:
- passbook.oauth_client.source_types.discord
- passbook.oauth_client.source_types.facebook
- passbook.oauth_client.source_types.github
- passbook.oauth_client.source_types.google
- passbook.oauth_client.source_types.reddit
- passbook.oauth_client.source_types.supervisr
- passbook.oauth_client.source_types.twitter
saml_idp:
# List of python packages with provider types to load.
types:
- passbook.saml_idp.processors.generic
- passbook.saml_idp.processors.aws
- passbook.saml_idp.processors.gitlab
- passbook.saml_idp.processors.nextcloud
- passbook.saml_idp.processors.salesforce
- passbook.saml_idp.processors.shibboleth
- passbook.saml_idp.processors.wordpress_orange

View File

@ -1,6 +1,6 @@
apiVersion: v1
appVersion: "0.1.13-beta"
appVersion: "0.1.19-beta"
description: A Helm chart for passbook.
name: passbook
version: "0.1.13-beta"
version: "0.1.19-beta"
icon: https://passbook.beryju.org/images/logo.png

Binary file not shown.

Binary file not shown.

View File

@ -1,9 +1,9 @@
dependencies:
- name: redis
- name: rabbitmq
repository: https://kubernetes-charts.storage.googleapis.com/
version: 5.1.0
version: 4.3.2
- name: postgresql
repository: https://kubernetes-charts.storage.googleapis.com/
version: 3.10.1
digest: sha256:04bd136761f070e94a2ff32ff48ff87f5e07fbd451e5fd7f65551e3bd4680e5e
generated: 2019-02-08T12:08:49.090666+01:00
digest: sha256:c36e054785f7d706d7d3f525eb1b167dbc89b42f84da7fc167a18bbb6542c999
generated: 2019-03-11T20:36:35.125079+01:00

View File

@ -1,6 +1,6 @@
dependencies:
- name: redis
version: 5.1.0
- name: rabbitmq
version: 4.3.2
repository: https://kubernetes-charts.storage.googleapis.com/
- name: postgresql
version: 3.10.1

View File

@ -36,7 +36,7 @@ data:
debug: false
secure_proxy_header:
HTTP_X_FORWARDED_PROTO: https
redis: ":{{ .Values.redis.password }}@{{ .Release.Name }}-redis-master"
rabbitmq: "user:{{ .Values.rabbitmq.rabbitmq.password }}@{{ .Release.Name }}-rabbitmq"
# Error reporting, sends stacktrace to sentry.services.beryju.org
error_report_enabled: {{ .Values.config.error_reporting }}

View File

@ -5,7 +5,7 @@
replicaCount: 1
image:
tag: 0.1.13-beta
tag: 0.1.19-beta
nameOverride: ""
@ -18,8 +18,12 @@ config:
host: localhost
postgresql:
postgresqlDatabase: passbook
postgresqlPassword: foo
postgresqlDatabase: passbook
postgresqlPassword: foo
rabbitmq:
rabbitmq:
password: foo
service:
type: ClusterIP
@ -33,7 +37,6 @@ ingress:
path: /
hosts:
- passbook.k8s.local
- kubernetes-healthcheck-host
defaultHost: passbook.k8s.local
tls: []
# - secretName: chart-example-tls

View File

@ -1,2 +1,2 @@
"""passbook"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -1,2 +1,2 @@
"""passbook admin"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -4,49 +4,4 @@
{% load is_active %}
{% block nav_secondary %}
<ul class="nav navbar-nav navbar-persistent">
<li class="{% is_active 'passbook_admin:overview' %}">
<a href="{% url 'passbook_admin:overview' %}">{% trans 'Overview' %}</a>
</li>
<li
class="{% is_active 'passbook_admin:applications' 'passbook_admin:application-create' 'passbook_admin:application-update' 'passbook_admin:application-delete' %}">
<a href="{% url 'passbook_admin:applications' %}">{% trans 'Applications' %}</a>
</li>
<li
class="{% is_active 'passbook_admin:sources' 'passbook_admin:source-create' 'passbook_admin:source-update' 'passbook_admin:source-delete' %}">
<a href="{% url 'passbook_admin:sources' %}">{% trans 'Sources' %}</a>
</li>
<li
class="{% is_active 'passbook_admin:providers' 'passbook_admin:provider-create' 'passbook_admin:provider-update' 'passbook_admin:provider-delete' %}">
<a href="{% url 'passbook_admin:providers' %}">{% trans 'Providers' %}</a>
</li>
<li
class="{% is_active 'passbook_admin:property-mappings' 'passbook_admin:property-mapping-create' 'passbook_admin:property-mapping-update' 'passbook_admin:property-mapping-delete' %}">
<a href="{% url 'passbook_admin:property-mappings' %}">{% trans 'Property Mappings' %}</a>
</li>
<li
class="{% is_active 'passbook_admin:factors' 'passbook_admin:factor-create' 'passbook_admin:factor-update' 'passbook_admin:factor-delete' %}">
<a href="{% url 'passbook_admin:factors' %}">{% trans 'Factors' %}</a>
</li>
<li
class="{% is_active 'passbook_admin:policies' 'passbook_admin:policy-create' 'passbook_admin:policy-update' 'passbook_admin:policy-delete' 'passbook_admin:policy-test' %}">
<a href="{% url 'passbook_admin:policies' %}">{% trans 'Policies' %}</a>
</li>
<li
class="{% is_active 'passbook_admin:invitations' 'passbook_admin:invitation-create' 'passbook_admin:invitation-update' 'passbook_admin:invitation-delete' 'passbook_admin:invitation-test' %}">
<a href="{% url 'passbook_admin:invitations' %}">{% trans 'Invitations' %}</a>
</li>
<li class="{% is_active 'passbook_admin:users' 'passbook_admin:user-update' 'passbook_admin:user-delete' %}">
<a href="{% url 'passbook_admin:users' %}">{% trans 'Users' %}</a>
</li>
<li class="{% is_active 'passbook_admin:groups' 'passbook_admin:group-update' 'passbook_admin:group-delete' %}">
<a href="{% url 'passbook_admin:groups' %}">{% trans 'Groups' %}</a>
</li>
<li class="{% is_active 'passbook_admin:audit-log' %}">
<a href="{% url 'passbook_admin:audit-log' %}">{% trans 'Audit Log' %}</a>
</li>
<li class="{% is_active_app 'admin' %}">
<a href="{% url 'admin:index' %}">{% trans 'Django' %}</a>
</li>
</ul>
{% endblock %}

View File

@ -1,2 +1,2 @@
"""passbook api"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -1,2 +1,2 @@
"""passbook audit Header"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -1,2 +1,2 @@
"""passbook captcha_factor Header"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -1,2 +1,2 @@
"""passbook core"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -152,11 +152,6 @@ class Application(PolicyModel):
objects = InheritanceManager()
def user_is_authorized(self, user: User) -> bool:
"""Check if user is authorized to use this application"""
from passbook.core.policies import PolicyEngine
return PolicyEngine(self.policies.all()).for_user(user).build().result
def get_provider(self):
"""Get casted provider instance"""
if not self.provider:

View File

@ -7,7 +7,6 @@ raven
markdown
colorlog
celery
redis
psycopg2
idna<2.8,>=2.5
cherrypy

View File

@ -184,8 +184,10 @@ CELERY_TIMEZONE = TIME_ZONE
CELERY_BEAT_SCHEDULE = {}
CELERY_CREATE_MISSING_QUEUES = True
CELERY_TASK_DEFAULT_QUEUE = 'passbook'
CELERY_BROKER_URL = 'redis://%s' % CONFIG.get('redis')
CELERY_RESULT_BACKEND = 'redis://%s' % CONFIG.get('redis')
CELERY_BROKER_URL = 'amqp://%s' % CONFIG.get('rabbitmq')
CELERY_RESULT_BACKEND = 'rpc://'
CELERY_ACKS_LATE = True
CELERY_BROKER_HEARTBEAT = 0
# Raven settings
RAVEN_CONFIG = {

View File

@ -1,2 +1,2 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="270px" height="10px" viewBox="0 0 270 10" enable-background="new 0 0 270 10" xml:space="preserve"><defs><style>.cls-1{isolation:isolate;}.cls-2{fill:#fff;}</style></defs><g class="cls-1"><path class="cls-2" d="M1.65,11V2.45H2.87V3a2.81,2.81,0,0,1,.47-.45A1.13,1.13,0,0,1,4,2.38,1.11,1.11,0,0,1,5.1,3a1.55,1.55,0,0,1,.16.5,5.61,5.61,0,0,1,0,.81V6.58c0,.45,0,.77,0,1a1.17,1.17,0,0,1-.55.9,1.23,1.23,0,0,1-.7.16,1.35,1.35,0,0,1-.64-.16A1.53,1.53,0,0,1,2.89,8h0v3ZM4.08,4.43a1.21,1.21,0,0,0-.14-.6.51.51,0,0,0-.46-.22A.54.54,0,0,0,3,3.82a.8.8,0,0,0-.17.54V6.73A.68.68,0,0,0,3,7.2a.6.6,0,0,0,.44.18A.53.53,0,0,0,4,7.17a1,1,0,0,0,.12-.5Z"/><path class="cls-2" d="M8.63,8.54V7.91h0a2.24,2.24,0,0,1-.48.52,1.13,1.13,0,0,1-.69.18A1.39,1.39,0,0,1,7,8.54a1.09,1.09,0,0,1-.43-.24,1.32,1.32,0,0,1-.33-.49A2.33,2.33,0,0,1,6.11,7a4.89,4.89,0,0,1,.08-.91,1.51,1.51,0,0,1,.31-.65,1.44,1.44,0,0,1,.59-.38A3.19,3.19,0,0,1,8,4.93h.59V4.33a1,1,0,0,0-.13-.52A.52.52,0,0,0,8,3.61a.71.71,0,0,0-.44.15.78.78,0,0,0-.26.46H6.13A2,2,0,0,1,6.69,2.9a1.73,1.73,0,0,1,.57-.38A2,2,0,0,1,8,2.38a2.18,2.18,0,0,1,.72.12,1.71,1.71,0,0,1,.59.36,2,2,0,0,1,.38.6,2.18,2.18,0,0,1,.14.84V8.54Zm0-2.62-.34,0a1.2,1.2,0,0,0-.67.18.76.76,0,0,0-.29.68.89.89,0,0,0,.17.56A.55.55,0,0,0,8,7.53a.63.63,0,0,0,.49-.2.91.91,0,0,0,.17-.58Z"/><path class="cls-2" d="M13,4.16a.59.59,0,0,0-.2-.47.65.65,0,0,0-.42-.16.59.59,0,0,0-.45.19.66.66,0,0,0-.15.43.8.8,0,0,0,.08.33.85.85,0,0,0,.44.29l.71.29a1.73,1.73,0,0,1,.95.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.56,1.56,0,0,1-.58.39,1.88,1.88,0,0,1-2-.32,1.58,1.58,0,0,1-.4-.57,1.81,1.81,0,0,1-.17-.8h1.15a1.11,1.11,0,0,0,.17.47.56.56,0,0,0,.49.22.71.71,0,0,0,.47-.18A.59.59,0,0,0,13,6.8a.69.69,0,0,0-.13-.43,1.08,1.08,0,0,0-.48-.32l-.59-.21a2.08,2.08,0,0,1-.9-.64,1.66,1.66,0,0,1-.33-1,1.89,1.89,0,0,1,.14-.72,1.78,1.78,0,0,1,.4-.57,1.5,1.5,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.6,1.6,0,0,1,.54.38,1.85,1.85,0,0,1,.36.57,1.82,1.82,0,0,1,.13.7Z"/><path class="cls-2" d="M17.2,4.16a.63.63,0,0,0-.2-.47.69.69,0,0,0-.43-.16.55.55,0,0,0-.44.19.62.62,0,0,0-.16.43.68.68,0,0,0,.09.33.81.81,0,0,0,.43.29l.72.29a1.7,1.7,0,0,1,.94.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.61,1.61,0,0,1-.57.39,1.81,1.81,0,0,1-.74.15,1.76,1.76,0,0,1-1.24-.47,1.61,1.61,0,0,1-.41-.57,2,2,0,0,1-.17-.8h1.15a1.12,1.12,0,0,0,.18.47.53.53,0,0,0,.48.22.72.72,0,0,0,.48-.18.59.59,0,0,0,.21-.48.69.69,0,0,0-.14-.43,1,1,0,0,0-.48-.32l-.58-.21a2.06,2.06,0,0,1-.91-.64,1.66,1.66,0,0,1-.33-1A1.89,1.89,0,0,1,15,3.44a1.78,1.78,0,0,1,.4-.57,1.58,1.58,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.75,1.75,0,0,1,.55.38,1.85,1.85,0,0,1,.36.57,2,2,0,0,1,.13.7Z"/><path class="cls-2" d="M19.2,8.54V0h1.22V3h0a1.53,1.53,0,0,1,.48-.47,1.39,1.39,0,0,1,.65-.16,1.26,1.26,0,0,1,.69.16,1.35,1.35,0,0,1,.4.39,1.18,1.18,0,0,1,.15.51,7.72,7.72,0,0,1,0,1V6.73a5.56,5.56,0,0,1-.05.8,1.56,1.56,0,0,1-.15.5,1.12,1.12,0,0,1-1.07.58,1.15,1.15,0,0,1-.7-.18A3.79,3.79,0,0,1,20.42,8v.55Zm2.44-4.21a1,1,0,0,0-.13-.51A.5.5,0,0,0,21,3.61a.57.57,0,0,0-.44.18.66.66,0,0,0-.18.48V6.63a.83.83,0,0,0,.17.54.52.52,0,0,0,.45.21.49.49,0,0,0,.45-.22,1.11,1.11,0,0,0,.15-.6Z"/><path class="cls-2" d="M23.76,4.49a4.83,4.83,0,0,1,0-.68A1.55,1.55,0,0,1,24,3.26a1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24A1.59,1.59,0,0,1,24,7.73a1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1,0-.68ZM25,6.69a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17A.55.55,0,0,0,26,7.21a.72.72,0,0,0,.16-.52V4.3A.74.74,0,0,0,26,3.78a.55.55,0,0,0-.44-.17.53.53,0,0,0-.43.17A.74.74,0,0,0,25,4.3Z"/><path class="cls-2" d="M28.2,4.49a4.83,4.83,0,0,1,.05-.68,1.55,1.55,0,0,1,.18-.55,1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24,1.59,1.59,0,0,1-.62-.64,1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1-.05-.68Zm1.22,2.2a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17.55.55,0,0,0,.44-.17.72.72,0,0,0,.16-.52V4.3a.74.74,0,0,0-.16-.52A.55.55,0,0,0,30,3.61a.53.53,0,0,0-.43.17.74.74,0,0,0-.17.52Z"/><path class="cls-2" d="M32.75,8.54V0H34V5.11h0l1.47-2.66H36.7L35.24,4.93,37,8.54H35.66l-1.1-2.63L34,6.83V8.54Z"/></g></svg>
width="270px" height="20px" viewBox="0 0 270 10" enable-background="new 0 0 270 10" xml:space="preserve"><defs><style>.cls-1{isolation:isolate;}.cls-2{fill:#fff;}</style></defs><g class="cls-1"><path class="cls-2" d="M1.65,11V2.45H2.87V3a2.81,2.81,0,0,1,.47-.45A1.13,1.13,0,0,1,4,2.38,1.11,1.11,0,0,1,5.1,3a1.55,1.55,0,0,1,.16.5,5.61,5.61,0,0,1,0,.81V6.58c0,.45,0,.77,0,1a1.17,1.17,0,0,1-.55.9,1.23,1.23,0,0,1-.7.16,1.35,1.35,0,0,1-.64-.16A1.53,1.53,0,0,1,2.89,8h0v3ZM4.08,4.43a1.21,1.21,0,0,0-.14-.6.51.51,0,0,0-.46-.22A.54.54,0,0,0,3,3.82a.8.8,0,0,0-.17.54V6.73A.68.68,0,0,0,3,7.2a.6.6,0,0,0,.44.18A.53.53,0,0,0,4,7.17a1,1,0,0,0,.12-.5Z"/><path class="cls-2" d="M8.63,8.54V7.91h0a2.24,2.24,0,0,1-.48.52,1.13,1.13,0,0,1-.69.18A1.39,1.39,0,0,1,7,8.54a1.09,1.09,0,0,1-.43-.24,1.32,1.32,0,0,1-.33-.49A2.33,2.33,0,0,1,6.11,7a4.89,4.89,0,0,1,.08-.91,1.51,1.51,0,0,1,.31-.65,1.44,1.44,0,0,1,.59-.38A3.19,3.19,0,0,1,8,4.93h.59V4.33a1,1,0,0,0-.13-.52A.52.52,0,0,0,8,3.61a.71.71,0,0,0-.44.15.78.78,0,0,0-.26.46H6.13A2,2,0,0,1,6.69,2.9a1.73,1.73,0,0,1,.57-.38A2,2,0,0,1,8,2.38a2.18,2.18,0,0,1,.72.12,1.71,1.71,0,0,1,.59.36,2,2,0,0,1,.38.6,2.18,2.18,0,0,1,.14.84V8.54Zm0-2.62-.34,0a1.2,1.2,0,0,0-.67.18.76.76,0,0,0-.29.68.89.89,0,0,0,.17.56A.55.55,0,0,0,8,7.53a.63.63,0,0,0,.49-.2.91.91,0,0,0,.17-.58Z"/><path class="cls-2" d="M13,4.16a.59.59,0,0,0-.2-.47.65.65,0,0,0-.42-.16.59.59,0,0,0-.45.19.66.66,0,0,0-.15.43.8.8,0,0,0,.08.33.85.85,0,0,0,.44.29l.71.29a1.73,1.73,0,0,1,.95.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.56,1.56,0,0,1-.58.39,1.88,1.88,0,0,1-2-.32,1.58,1.58,0,0,1-.4-.57,1.81,1.81,0,0,1-.17-.8h1.15a1.11,1.11,0,0,0,.17.47.56.56,0,0,0,.49.22.71.71,0,0,0,.47-.18A.59.59,0,0,0,13,6.8a.69.69,0,0,0-.13-.43,1.08,1.08,0,0,0-.48-.32l-.59-.21a2.08,2.08,0,0,1-.9-.64,1.66,1.66,0,0,1-.33-1,1.89,1.89,0,0,1,.14-.72,1.78,1.78,0,0,1,.4-.57,1.5,1.5,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.6,1.6,0,0,1,.54.38,1.85,1.85,0,0,1,.36.57,1.82,1.82,0,0,1,.13.7Z"/><path class="cls-2" d="M17.2,4.16a.63.63,0,0,0-.2-.47.69.69,0,0,0-.43-.16.55.55,0,0,0-.44.19.62.62,0,0,0-.16.43.68.68,0,0,0,.09.33.81.81,0,0,0,.43.29l.72.29a1.7,1.7,0,0,1,.94.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.61,1.61,0,0,1-.57.39,1.81,1.81,0,0,1-.74.15,1.76,1.76,0,0,1-1.24-.47,1.61,1.61,0,0,1-.41-.57,2,2,0,0,1-.17-.8h1.15a1.12,1.12,0,0,0,.18.47.53.53,0,0,0,.48.22.72.72,0,0,0,.48-.18.59.59,0,0,0,.21-.48.69.69,0,0,0-.14-.43,1,1,0,0,0-.48-.32l-.58-.21a2.06,2.06,0,0,1-.91-.64,1.66,1.66,0,0,1-.33-1A1.89,1.89,0,0,1,15,3.44a1.78,1.78,0,0,1,.4-.57,1.58,1.58,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.75,1.75,0,0,1,.55.38,1.85,1.85,0,0,1,.36.57,2,2,0,0,1,.13.7Z"/><path class="cls-2" d="M19.2,8.54V0h1.22V3h0a1.53,1.53,0,0,1,.48-.47,1.39,1.39,0,0,1,.65-.16,1.26,1.26,0,0,1,.69.16,1.35,1.35,0,0,1,.4.39,1.18,1.18,0,0,1,.15.51,7.72,7.72,0,0,1,0,1V6.73a5.56,5.56,0,0,1-.05.8,1.56,1.56,0,0,1-.15.5,1.12,1.12,0,0,1-1.07.58,1.15,1.15,0,0,1-.7-.18A3.79,3.79,0,0,1,20.42,8v.55Zm2.44-4.21a1,1,0,0,0-.13-.51A.5.5,0,0,0,21,3.61a.57.57,0,0,0-.44.18.66.66,0,0,0-.18.48V6.63a.83.83,0,0,0,.17.54.52.52,0,0,0,.45.21.49.49,0,0,0,.45-.22,1.11,1.11,0,0,0,.15-.6Z"/><path class="cls-2" d="M23.76,4.49a4.83,4.83,0,0,1,0-.68A1.55,1.55,0,0,1,24,3.26a1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24A1.59,1.59,0,0,1,24,7.73a1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1,0-.68ZM25,6.69a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17A.55.55,0,0,0,26,7.21a.72.72,0,0,0,.16-.52V4.3A.74.74,0,0,0,26,3.78a.55.55,0,0,0-.44-.17.53.53,0,0,0-.43.17A.74.74,0,0,0,25,4.3Z"/><path class="cls-2" d="M28.2,4.49a4.83,4.83,0,0,1,.05-.68,1.55,1.55,0,0,1,.18-.55,1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24,1.59,1.59,0,0,1-.62-.64,1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1-.05-.68Zm1.22,2.2a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17.55.55,0,0,0,.44-.17.72.72,0,0,0,.16-.52V4.3a.74.74,0,0,0-.16-.52A.55.55,0,0,0,30,3.61a.53.53,0,0,0-.43.17.74.74,0,0,0-.17.52Z"/><path class="cls-2" d="M32.75,8.54V0H34V5.11h0l1.47-2.66H36.7L35.24,4.93,37,8.54H35.66l-1.1-2.63L34,6.83V8.54Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -8,38 +8,40 @@
<div class="toast-notifications-list-pf">
{% include 'partials/messages.html' %}
</div>
<nav class="navbar navbar-default navbar-pf" role="navigation">
<nav class="navbar navbar-pf-vertical">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1">
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
<button type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
<img src="{% static 'img/brand.svg' %}" alt="passbook" />
<img class="navbar-brand-icon" src="{% static 'img/logo.png' %}" alt="" />
<img class="navbar-brand-name" src="{% static 'img/brand.svg' %}" alt="passbook" />
</a>
</div>
<div class="collapse navbar-collapse navbar-collapse-1">
<ul class="nav navbar-nav navbar-utility">
<nav class="collapse navbar-collapse">
<ul class="nav navbar-nav navbar-right navbar-iconic navbar-utility">
<li class="dropdown">
<button class="btn btn-link nav-item-iconic" id="horizontalDropdownMenu1" data-toggle="dropdown"
<button class="btn btn-link dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="true">
<span title="Help" class="fa pficon-help dropdown-title"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="horizontalDropdownMenu1">
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
{% comment %} <li><a href="#0">Help</a></li> {% endcomment %}
<li><a data-toggle="modal" data-target="#about-modal" href="#0">{% trans 'About' %}</a></li>
</ul>
</li>
<li class="dropdown">
<button class="btn btn-link dropdown-toggle" data-toggle="dropdown">
<span class="pficon pficon-user"></span>
<button class="btn btn-link dropdown-toggle nav-item-iconic" id="dropdownMenu2" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="true">
<span title="Username" class="fa pficon-user"></span>
<span class="dropdown-title">
{{ user.username }} <b class="caret"></b>
{{ user.username }} <span class="caret"></span>
</span>
</button>
<ul class="dropdown-menu">
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
<li>
<a href="{% url 'passbook_core:user-settings' %}">{% trans 'User Settings' %}</a>
</li>
@ -53,21 +55,129 @@
</ul>
</li>
</ul>
{% is_active_app 'passbook_admin' as is_admin %}
<ul class="nav navbar-nav navbar-primary {% if is_admin == 'active' %}persistent-secondary{% endif %}">
<li class="{% is_active_url 'passbook_core:overview' %}">
<a href="{% url 'passbook_core:overview' %}">{% trans 'Overview' %}</a>
</li>
{% if user.is_superuser %}
<li class="{% is_active_app 'passbook_admin' %}">
<a href="{% url 'passbook_admin:overview' %}">{% trans 'Administration' %}</a>
{% block nav_secondary %}
{% endblock %}
</li>
{% endif %}
</ul>
</div>
</nav>
</nav>
<div class="nav-pf-vertical nav-pf-vertical-with-sub-menus hide-nav-pf">
<ul class="list-group">
<li class="list-group-item {% is_active_url 'passbook_core:overview' %}">
<a href="{% url 'passbook_core:overview' %}">
<span class="fa fa-dashboard" data-toggle="tooltip" title="{% trans 'Overview' %}"></span>
<span class="list-group-item-value">{% trans 'Overview' %}</span>
</a>
</li>
{% is_active_app 'passbook_admin' as is_admin %}
{% if user.is_superuser %}
<li class="list-group-item {% is_active_app 'passbook_admin' %} secondary-nav-item-pf">
<a href="{% url 'passbook_admin:overview' %}">
<span class="pficon pficon-user" data-toggle="tooltip" title=""
data-original-title="{% trans 'Administration' %}"></span>
<span class="list-group-item-value dropdown-title">{% trans 'Administration' %}</span>
</a>
<div id="user-secondary" class="nav-pf-secondary-nav">
<div class="nav-item-pf-header">
<a href="#0" class="secondary-collapse-toggle-pf" data-toggle="collapse-secondary-nav"></a>
<span>{% trans 'Administration' %}</span>
</div>
<ul class="list-group">
<li class="list-group-item {% is_active 'passbook_admin:overview' %}">
<a href="{% url 'passbook_admin:overview' %}">
<span class="list-group-item-value">
{% trans 'Overview' %}
</span>
</a>
</li>
<li
class="list-group-item {% is_active 'passbook_admin:applications' 'passbook_admin:application-create' 'passbook_admin:application-update' 'passbook_admin:application-delete' %}">
<a href="{% url 'passbook_admin:applications' %}">
<span class="list-group-item-value">
{% trans 'Applications' %}
</span>
</a>
</li>
<li
class="list-group-item {% is_active 'passbook_admin:sources' 'passbook_admin:source-create' 'passbook_admin:source-update' 'passbook_admin:source-delete' %}">
<a href="{% url 'passbook_admin:sources' %}">
<span class="list-group-item-value">
{% trans 'Sources' %}
</span>
</a>
</li>
<li
class="list-group-item {% is_active 'passbook_admin:providers' 'passbook_admin:provider-create' 'passbook_admin:provider-update' 'passbook_admin:provider-delete' %}">
<a href="{% url 'passbook_admin:providers' %}">
<span class="list-group-item-value">
{% trans 'Providers' %}
</span>
</a>
</li>
<li
class="list-group-item {% is_active 'passbook_admin:property-mappings' 'passbook_admin:property-mapping-create' 'passbook_admin:property-mapping-update' 'passbook_admin:property-mapping-delete' %}">
<a href="{% url 'passbook_admin:property-mappings' %}">
<span class="list-group-item-value">
{% trans 'Property Mappings' %}
</span>
</a>
</li>
<li
class="list-group-item {% is_active 'passbook_admin:factors' 'passbook_admin:factor-create' 'passbook_admin:factor-update' 'passbook_admin:factor-delete' %}">
<a href="{% url 'passbook_admin:factors' %}">
<span class="list-group-item-value">
{% trans 'Factors' %}
</span>
</a>
</li>
<li
class="list-group-item {% is_active 'passbook_admin:policies' 'passbook_admin:policy-create' 'passbook_admin:policy-update' 'passbook_admin:policy-delete' 'passbook_admin:policy-test' %}">
<a href="{% url 'passbook_admin:policies' %}">
<span class="list-group-item-value">
{% trans 'Policies' %}
</span>
</a>
</li>
<li
class="list-group-item {% is_active 'passbook_admin:invitations' 'passbook_admin:invitation-create' 'passbook_admin:invitation-update' 'passbook_admin:invitation-delete' 'passbook_admin:invitation-test' %}">
<a href="{% url 'passbook_admin:invitations' %}">
<span class="list-group-item-value">
{% trans 'Invitations' %}
</span>
</a>
</li>
<li
class="list-group-item {% is_active 'passbook_admin:users' 'passbook_admin:user-update' 'passbook_admin:user-delete' %}">
<a href="{% url 'passbook_admin:users' %}">
<span class="list-group-item-value">
{% trans 'Users' %}
</span>
</a>
</li>
<li
class="list-group-item {% is_active 'passbook_admin:groups' 'passbook_admin:group-update' 'passbook_admin:group-delete' %}">
<a href="{% url 'passbook_admin:groups' %}">
<span class="list-group-item-value">
{% trans 'Groups' %}
</span>
</a>
</li>
<li class="list-group-item {% is_active 'passbook_admin:audit-log' %}">
<a href="{% url 'passbook_admin:audit-log' %}">
<span class="list-group-item-value">
{% trans 'Audit Log' %}
</span>
</a>
</li>
<li class="list-group-item {% is_active_app 'admin' %}">
<a href="{% url 'admin:index' %}">
<span class="list-group-item-value">
{% trans 'Django' %}
</span>
</a>
</li>
</ul>
</div>
</li>
{% endif %}
</ul>
</div>
<div class="container-fluid container-cards-pf">
{% block content %}
{% endblock %}

View File

@ -1,7 +1,10 @@
"""passbook util view tests"""
import string
from random import SystemRandom
from django.test import RequestFactory, TestCase
from passbook.core.models import User
from passbook.core.views.utils import LoadingView, PermissionDeniedView
@ -9,6 +12,11 @@ class TestUtilViews(TestCase):
"""Test Utility Views"""
def setUp(self):
self.user = User.objects.create_superuser(
username='unittest user',
email='unittest@example.com',
password=''.join(SystemRandom().choice(
string.ascii_uppercase + string.digits) for _ in range(8)))
self.factory = RequestFactory()
def test_loading_view(self):
@ -21,5 +29,6 @@ class TestUtilViews(TestCase):
def test_permission_denied_view(self):
"""Test PermissionDeniedView"""
request = self.factory.get('something')
request.user = self.user
response = PermissionDeniedView.as_view()(request)
self.assertEqual(response.status_code, 200)

View File

@ -5,6 +5,7 @@ from django.contrib import messages
from django.utils.translation import gettext as _
from passbook.core.models import Application
from passbook.core.policies import PolicyEngine
LOGGER = getLogger(__name__)
@ -28,4 +29,6 @@ class AccessMixin:
def user_has_access(self, application, user):
"""Check if user has access to application."""
LOGGER.debug("Checking permissions of %s on application %s...", user, application)
return application.user_is_authorized(user)
policy_engine = PolicyEngine(application.policies.all())
policy_engine.for_user(user).with_request(self.request).build()
return policy_engine.result

View File

@ -1,2 +1,2 @@
"""passbook hibp_policy"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -1,2 +1,2 @@
"""Passbook ldap app Header"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -1,2 +1,2 @@
"""passbook lib"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -29,7 +29,7 @@ web:
debug: false
secure_proxy_header:
HTTP_X_FORWARDED_PROTO: https
redis: localhost
rabbitmq: guest:guest@localhost/passbook
# Error reporting, sends stacktrace to sentry.services.beryju.org
error_report_enabled: true
secret_key: 9$@r!d^1^jrn#fk#1#@ks#9&i$^s#1)_13%$rwjrhd=e8jfi_s
@ -62,11 +62,6 @@ passbook:
uid_fields:
- username
- email
# Factors to load
factors:
- passbook.core.auth.factors.backend
- passbook.core.auth.factors.dummy
- passbook.captcha_factor.factor
session:
remember_age: 2592000 # 60 * 60 * 24 * 30, one month
# Provider-specific settings

View File

@ -1,2 +1,2 @@
"""passbook oauth_client Header"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -1,2 +1,2 @@
"""passbook oauth_provider Header"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -2,6 +2,7 @@
from logging import getLogger
from urllib.parse import urlencode
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404, redirect, reverse
from django.utils.translation import ugettext as _
@ -49,7 +50,10 @@ class PassbookAuthorizationView(AccessMixin, AuthorizationView):
provider.save()
self._application = application
# Check permissions
if not self.user_has_access(self._application, request.user):
passing, policy_meaages = self.user_has_access(self._application, request.user)
if not passing:
for policy_meaage in policy_meaages:
messages.error(request, policy_meaage)
return redirect('passbook_oauth_provider:oauth2-permission-denied')
actual_response = super().dispatch(request, *args, **kwargs)
if actual_response.status_code == 400:

View File

@ -1,2 +1,2 @@
"""passbook otp Header"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -1,2 +1,2 @@
"""passbook password_expiry"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -1,16 +1,24 @@
"""passbook pretend GitHub Views"""
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.views import View
from oauth2_provider.models import AccessToken
class GitHubUserView(View):
"""Emulate GitHub's /user API Endpoint"""
def verify_access_token(self):
"""Verify access token manually since github uses /user?access_token=..."""
token = get_object_or_404(AccessToken, token=self.request.get('access_token', ''))
return token.user
def get(self, request):
"""Emulate GitHub's /user API Endpoint"""
user = self.verify_access_token()
return JsonResponse({
"login": request.user.username,
"id": request.user.pk,
"login": user.username,
"id": user.pk,
"node_id": "",
"avatar_url": "",
"gravatar_id": "",
@ -27,19 +35,19 @@ class GitHubUserView(View):
"received_events_url": "",
"type": "User",
"site_admin": False,
"name": request.user.name,
"name": user.name,
"company": "",
"blog": "",
"location": "",
"email": request.user.email,
"email": user.email,
"hireable": False,
"bio": "",
"public_repos": 0,
"public_gists": 0,
"followers": 0,
"following": 0,
"created_at": request.user.date_joined,
"updated_at": request.user.date_joined,
"created_at": user.date_joined,
"updated_at": user.date_joined,
"private_gists": 0,
"total_private_repos": 0,
"owned_private_repos": 0,

View File

@ -1,2 +1,2 @@
"""passbook saml_idp Header"""
__version__ = '0.1.13-beta'
__version__ = '0.1.19-beta'

View File

@ -7,6 +7,4 @@ class GitLabProcessor(Processor):
"""GitLab Response Handler Processor for testing against django-saml2-sp."""
def _determine_audience(self):
# Nextcloud expects an audience in this format
# https://<host>
self._audience = self._remote.acs_url.replace('/users/auth/saml/callback', '')

View File

@ -206,7 +206,9 @@ class DescriptorDownloadView(AccessRequiredView):
def get(self, request, application):
"""Replies with the XML Metadata IDSSODescriptor."""
entity_id = CONFIG.y('saml_idp.issuer')
slo_url = request.build_absolute_uri(reverse('passbook_saml_idp:saml-logout'))
slo_url = request.build_absolute_uri(reverse('passbook_saml_idp:saml-logout', kwargs={
'application': application
}))
sso_url = request.build_absolute_uri(reverse('passbook_saml_idp:saml-login', kwargs={
'application': application
}))

View File

@ -1,5 +1,5 @@
-r requirements.txt
-r allauth/requirements.txt
-r client-packages/allauth/requirements.txt
coverage
isort
astroid==2.0.4
@ -13,3 +13,4 @@ unittest-xml-reporting
autopep8
bandit
bumpversion
twine