Compare commits
45 Commits
version/0.
...
version/0.
Author | SHA1 | Date | |
---|---|---|---|
6f7b917c38 | |||
1456ee6d3e | |||
5155204283 | |||
5509ec9b0f | |||
d6f9b2e47d | |||
67aa4aef11 | |||
9e46c8bfec | |||
1eaa9b9733 | |||
ee05834b69 | |||
fccc8f4959 | |||
c721620f96 | |||
c9f73d718e | |||
bfa58be721 | |||
4bb602149e | |||
81ab9092fc | |||
29d5962c4c | |||
5c75339946 | |||
4774d9a46c | |||
dbe16ba4fd | |||
6972cf00a0 | |||
0445be9712 | |||
89dbdd9585 | |||
da88ce7150 | |||
5f50fcfcf5 | |||
96be087221 | |||
a53a269a8c | |||
59565a5286 | |||
ae3c092238 | |||
e98e5e4e3e | |||
d50c7ec8d4 | |||
c0fdf377d1 | |||
70c11c8988 | |||
67b19becc1 | |||
ae64024ef4 | |||
e6571826cb | |||
c621e61978 | |||
3626fa4b98 | |||
01b0eb159a | |||
63aa48d981 | |||
2e0ba05d55 | |||
b2ac57bb67 | |||
4c22e5c2c8 | |||
4a7b0ec8a9 | |||
330118249e | |||
8d4dabde02 |
@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.1.15-beta
|
current_version = 0.1.24-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,6 +15,10 @@ 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]
|
||||||
|
176
.gitlab-ci.yml
@ -1,98 +1,124 @@
|
|||||||
# Global Variables
|
# Global Variables
|
||||||
before_script:
|
before_script:
|
||||||
- "python3 -m pip install -U virtualenv"
|
- "python3 -m pip install -U virtualenv"
|
||||||
- "virtualenv env"
|
- "virtualenv env"
|
||||||
- "source env/bin/activate"
|
- "source env/bin/activate"
|
||||||
- "pip3 install -U -r requirements-dev.txt"
|
- "pip3 install -U -r requirements-dev.txt"
|
||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
- build
|
- build
|
||||||
- docs
|
- docs
|
||||||
- deploy
|
- deploy
|
||||||
image: python:3.6
|
image: python:3.6
|
||||||
services:
|
services:
|
||||||
- postgres:latest
|
- postgres: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"
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- /allauth/.gitlab-ci.yml
|
- /client-packages/allauth/.gitlab-ci.yml
|
||||||
|
|
||||||
isort:
|
isort:
|
||||||
script:
|
script:
|
||||||
- isort -c -sg env
|
- isort -c -sg env
|
||||||
stage: test
|
stage: test
|
||||||
migrations:
|
migrations:
|
||||||
script:
|
script:
|
||||||
- python manage.py migrate
|
- python manage.py migrate
|
||||||
stage: test
|
stage: test
|
||||||
prospector:
|
prospector:
|
||||||
script:
|
script:
|
||||||
- prospector
|
- prospector
|
||||||
stage: test
|
stage: test
|
||||||
pylint:
|
pylint:
|
||||||
script:
|
script:
|
||||||
- pylint passbook
|
- pylint passbook
|
||||||
stage: test
|
stage: test
|
||||||
coverage:
|
coverage:
|
||||||
script:
|
script:
|
||||||
- coverage run manage.py test
|
- coverage run manage.py test
|
||||||
- coverage report
|
- coverage report
|
||||||
stage: test
|
stage: test
|
||||||
bandit:
|
bandit:
|
||||||
script:
|
script:
|
||||||
- bandit -r passbook
|
- bandit -r passbook
|
||||||
stage: test
|
stage: test
|
||||||
|
|
||||||
package-docker:
|
package-docker:
|
||||||
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.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json
|
- echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json
|
||||||
script:
|
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.15-beta
|
- /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.24-beta
|
||||||
stage: build
|
stage: build
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
- /^version/.*$/
|
- /^version/.*$/
|
||||||
package-helm:
|
package-helm:
|
||||||
stage: build
|
stage: build
|
||||||
script:
|
script:
|
||||||
- curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash
|
- curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash
|
||||||
- helm init --client-only
|
- helm init --client-only
|
||||||
- helm package helm/passbook
|
- helm package helm/passbook
|
||||||
- ./manage.py nexus_upload --method put --url $NEXUS_URL --auth $NEXUS_AUTH --repo helm *.tgz
|
- ./manage.py nexus_upload --method put --url $NEXUS_URL --auth $NEXUS_AUTH --repo helm *.tgz
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
- /^version/.*$/
|
- /^version/.*$/
|
||||||
package-debian:
|
package-debian:
|
||||||
before_script:
|
before_script:
|
||||||
- apt update
|
- apt update
|
||||||
- apt install -y --no-install-recommends build-essential debhelper devscripts equivs python3 python3-dev python3-pip libsasl2-dev libldap2-dev
|
- 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
|
- mk-build-deps debian/control
|
||||||
- apt install ./*build-deps*deb -f -y
|
- apt install ./*build-deps*deb -f -y
|
||||||
- python3 -m pip install -U virtualenv pip
|
- python3 -m pip install -U virtualenv pip
|
||||||
- virtualenv env
|
- virtualenv env
|
||||||
- source env/bin/activate
|
- source env/bin/activate
|
||||||
- pip3 install -U -r requirements.txt -r requirements-dev.txt
|
- pip3 install -U -r requirements.txt -r requirements-dev.txt
|
||||||
- ./manage.py collectstatic --no-input
|
- ./manage.py collectstatic --no-input
|
||||||
image: ubuntu:18.04
|
image: ubuntu:18.04
|
||||||
script:
|
script:
|
||||||
- debuild -us -uc
|
- debuild -us -uc
|
||||||
- cp ../passbook*.deb .
|
- cp ../passbook*.deb .
|
||||||
- ./manage.py nexus_upload --method post --url $NEXUS_URL --auth $NEXUS_AUTH --repo apt passbook*deb
|
- ./manage.py nexus_upload --method post --url $NEXUS_URL --auth $NEXUS_AUTH --repo apt passbook*deb
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- passbook*deb
|
- passbook*deb
|
||||||
expire_in: 2 days
|
expire_in: 2 days
|
||||||
stage: build
|
stage: build
|
||||||
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/**
|
||||||
|
|
||||||
# docs:
|
# docs:
|
||||||
# stage: docs
|
# stage: docs
|
||||||
|
@ -7,6 +7,7 @@ ignore-paths:
|
|||||||
- migrations
|
- migrations
|
||||||
- docs
|
- docs
|
||||||
- node_modules
|
- node_modules
|
||||||
|
- client-packages
|
||||||
|
|
||||||
uses:
|
uses:
|
||||||
- django
|
- django
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""passbook provider"""
|
"""passbook provider"""
|
||||||
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
|
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
|
||||||
|
|
||||||
from allauth_passbook.provider import PassbookProvider
|
from allauth_passbook.provider import PassbookProvider
|
||||||
|
|
||||||
urlpatterns = default_urlpatterns(PassbookProvider)
|
urlpatterns = default_urlpatterns(PassbookProvider)
|
@ -1,10 +1,10 @@
|
|||||||
"""passbook adapter"""
|
"""passbook adapter"""
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from allauth.socialaccount import app_settings
|
from allauth.socialaccount import app_settings
|
||||||
from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter,
|
from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter,
|
||||||
OAuth2CallbackView,
|
OAuth2CallbackView,
|
||||||
OAuth2LoginView)
|
OAuth2LoginView)
|
||||||
|
|
||||||
from allauth_passbook.provider import PassbookProvider
|
from allauth_passbook.provider import PassbookProvider
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@ from setuptools import setup
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django-allauth-passbook',
|
name='django-allauth-passbook',
|
||||||
version='1.0.0',
|
version='0.1.24-beta',
|
||||||
description='passbook support for django-allauth',
|
description='passbook support for django-allauth',
|
||||||
# long_description='\n'.join(read_simple('docs/index.md')[2:]),
|
# long_description='\n'.join(read_simple('docs/index.md')[2:]),
|
||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
5
client-packages/sentry-auth-passbook/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
*.pyc
|
||||||
|
*.egg-info/
|
||||||
|
*.eggs
|
||||||
|
/dist
|
||||||
|
/build
|
32
client-packages/sentry-auth-passbook/.travis.yml
Normal 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
|
201
client-packages/sentry-auth-passbook/LICENSE
Normal 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.
|
3
client-packages/sentry-auth-passbook/MANIFEST.in
Normal 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 *~
|
26
client-packages/sentry-auth-passbook/Makefile
Normal 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
|
55
client-packages/sentry-auth-passbook/README.rst
Normal 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"
|
14
client-packages/sentry-auth-passbook/conftest.py
Normal 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'
|
||||||
|
]
|
@ -0,0 +1,7 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from sentry.auth import register
|
||||||
|
|
||||||
|
from .provider import PassbookOAuth2Provider
|
||||||
|
|
||||||
|
register('passbook', PassbookOAuth2Provider)
|
@ -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)
|
@ -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)
|
@ -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']
|
@ -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')
|
12
client-packages/sentry-auth-passbook/setup.cfg
Normal 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/*
|
45
client-packages/sentry-auth-passbook/setup.py
Normal 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.24-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'
|
||||||
|
],
|
||||||
|
)
|
@ -0,0 +1,6 @@
|
|||||||
|
from sentry.testutils import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class GitHubOAuth2ProviderTest(TestCase):
|
||||||
|
def test_simple(self):
|
||||||
|
pass
|
17
client-packages/sentry-auth-passbook/tests/test_views.py
Normal 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
|
74
debian/changelog
vendored
@ -1,3 +1,77 @@
|
|||||||
|
passbook (0.1.24) stable; urgency=medium
|
||||||
|
|
||||||
|
* bump version: 0.1.22-beta -> 0.1.23-beta
|
||||||
|
* add modal for OAuth Providers showing the URLs
|
||||||
|
* remove user field from form. Closes #32
|
||||||
|
|
||||||
|
-- Jens Langhammer <jens.langhammer@beryju.org> Wed, 20 Mar 2019 21:59:21 +0000
|
||||||
|
|
||||||
|
passbook (0.1.23) stable; urgency=medium
|
||||||
|
|
||||||
|
* add support for OpenID-Connect Discovery
|
||||||
|
|
||||||
|
-- Jens Langhammer <jens.langhammer@beryju.org> Thu, 18 Mar 2019 20:19:27 +0000
|
||||||
|
|
||||||
|
passbook (0.1.22) stable; urgency=medium
|
||||||
|
|
||||||
|
* bump version: 0.1.20-beta -> 0.1.21-beta
|
||||||
|
* fix missing debug template
|
||||||
|
* move icons to single folder, cleanup
|
||||||
|
* fix layout when on mobile viewport and scrolling
|
||||||
|
* fix delete form not working
|
||||||
|
* point to correct icons
|
||||||
|
* add Azure AD Source
|
||||||
|
* Fix OAuth Client's disconnect view having invalid URL names
|
||||||
|
|
||||||
|
-- Jens Langhammer <jens.langhammer@beryju.org> Thu, 14 Mar 2019 20:19:27 +0000
|
||||||
|
|
||||||
|
passbook (0.1.21) stable; urgency=medium
|
||||||
|
|
||||||
|
* bump version: 0.1.19-beta -> 0.1.20-beta
|
||||||
|
* add request debug view
|
||||||
|
* detect HTTPS from reverse proxy
|
||||||
|
|
||||||
|
-- Jens Langhammer <jens.langhammer@beryju.org> Thu, 14 Mar 2019 17:01:49 +0000
|
||||||
|
|
||||||
|
passbook (0.1.20) stable; urgency=medium
|
||||||
|
|
||||||
|
* bump version: 0.1.18-beta -> 0.1.19-beta
|
||||||
|
* fix GitHub Pretend again
|
||||||
|
* add user settings for Sources
|
||||||
|
|
||||||
|
-- Jens Langhammer <jens.langhammer@beryju.org> Wed, 13 Mar 2019 15:49:44 +0000
|
||||||
|
|
||||||
|
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
|
passbook (0.1.14) stable; urgency=medium
|
||||||
|
|
||||||
* bump version: 0.1.11-beta -> 0.1.12-beta
|
* bump version: 0.1.11-beta -> 0.1.12-beta
|
||||||
|
101
debian/etc/passbook/config.yml
vendored
@ -1,4 +1,3 @@
|
|||||||
debug: false
|
|
||||||
http:
|
http:
|
||||||
host: 0.0.0.0
|
host: 0.0.0.0
|
||||||
port: 8000
|
port: 8000
|
||||||
@ -8,37 +7,71 @@ log:
|
|||||||
console: INFO
|
console: INFO
|
||||||
file: DEBUG
|
file: DEBUG
|
||||||
file: /var/log/passbook/passbook.log
|
file: /var/log/passbook/passbook.log
|
||||||
# Error reporting, disabled by default
|
debug: false
|
||||||
# error_report_enabled: true
|
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.
|
passbook:
|
||||||
# This is used to generate external URLs
|
sign_up:
|
||||||
external_url: http://image.example.com
|
# Enables signup, created users are stored in internal Database and created in LDAP if ldap.create_users is true
|
||||||
|
enabled: true
|
||||||
# This dictates how the Path is generated
|
password_reset:
|
||||||
# can be either of:
|
# Enable password reset, passwords are reset in internal Database and in LDAP if ldap.reset_password is true
|
||||||
# - view_sha512_short
|
enabled: true
|
||||||
# - view_md5
|
# Verification the user has to provide in order to be able to reset passwords. Can be any combination of `email`, `2fa`, `security_questions`
|
||||||
# - view_sha256
|
verification:
|
||||||
# - view_sha512
|
- email
|
||||||
default_return_view: view_sha256
|
# Text used in title, on login page and multiple other places
|
||||||
|
branding: passbook
|
||||||
# Set this to true if you only want to use external authentication
|
login:
|
||||||
external_auth_only: false
|
# Override URL used for logo
|
||||||
|
logo_url: null
|
||||||
# If this is true, images are automatically claimed if the windows user exists
|
# Override URL used for Background on Login page
|
||||||
# in django
|
bg_url: null
|
||||||
auto_claim_enabled: true
|
# Optionally add a subtext, placed below logo on the login page
|
||||||
|
subtext: null
|
||||||
# LDAP Authentication
|
footer:
|
||||||
# ldap:
|
links:
|
||||||
# enabled: false
|
# Optionally add links to the footer on the login page
|
||||||
# server:
|
# - name: test
|
||||||
# uri: 'ldap://dc1.example.com'
|
# href: https://test
|
||||||
# tls: false
|
# Specify which fields can be used to authenticate. Can be any combination of `username` and `email`
|
||||||
# bind:
|
uid_fields:
|
||||||
# dn: ''
|
- username
|
||||||
# password: ''
|
- email
|
||||||
# search_base: ''
|
session:
|
||||||
# filter: '(sAMAccountName=%(user)s)'
|
remember_age: 2592000 # 60 * 60 * 24 * 30, one month
|
||||||
# require_group: ''
|
# 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
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
appVersion: "0.1.15-beta"
|
appVersion: "0.1.24-beta"
|
||||||
description: A Helm chart for passbook.
|
description: A Helm chart for passbook.
|
||||||
name: passbook
|
name: passbook
|
||||||
version: "0.1.15-beta"
|
version: "0.1.24-beta"
|
||||||
icon: https://passbook.beryju.org/images/logo.png
|
icon: https://passbook.beryju.org/images/logo.png
|
||||||
|
@ -36,7 +36,7 @@ data:
|
|||||||
debug: false
|
debug: false
|
||||||
secure_proxy_header:
|
secure_proxy_header:
|
||||||
HTTP_X_FORWARDED_PROTO: https
|
HTTP_X_FORWARDED_PROTO: https
|
||||||
rabbitmq: "user:{{ .Values.rabbitmq.rabbitmq.password }}@{{ .Release.Name }}-rabbitmq-master"
|
rabbitmq: "user:{{ .Values.rabbitmq.rabbitmq.password }}@{{ .Release.Name }}-rabbitmq"
|
||||||
# Error reporting, sends stacktrace to sentry.services.beryju.org
|
# Error reporting, sends stacktrace to sentry.services.beryju.org
|
||||||
error_report_enabled: {{ .Values.config.error_reporting }}
|
error_report_enabled: {{ .Values.config.error_reporting }}
|
||||||
|
|
||||||
@ -123,6 +123,7 @@ data:
|
|||||||
- passbook.oauth_client.source_types.reddit
|
- passbook.oauth_client.source_types.reddit
|
||||||
- passbook.oauth_client.source_types.supervisr
|
- passbook.oauth_client.source_types.supervisr
|
||||||
- passbook.oauth_client.source_types.twitter
|
- passbook.oauth_client.source_types.twitter
|
||||||
|
- passbook.oauth_client.source_types.azure_ad
|
||||||
saml_idp:
|
saml_idp:
|
||||||
signing: true
|
signing: true
|
||||||
autosubmit: false
|
autosubmit: false
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
tag: 0.1.15-beta
|
tag: 0.1.24-beta
|
||||||
|
|
||||||
nameOverride: ""
|
nameOverride: ""
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
"""passbook"""
|
"""passbook"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
"""passbook admin"""
|
"""passbook admin"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -4,49 +4,4 @@
|
|||||||
{% load is_active %}
|
{% load is_active %}
|
||||||
|
|
||||||
{% block nav_secondary %}
|
{% 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 %}
|
{% endblock %}
|
||||||
|
31
passbook/admin/templates/administration/debug/request.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{% extends "administration/base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load utils %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% title %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<h1><span class="pficon-applications"></span> {% trans "Request" %}</h1>
|
||||||
|
<hr>
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans 'Key' %}</th>
|
||||||
|
<th>{% trans 'Value' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for key, value in request_dict.items %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ key }}</td>
|
||||||
|
<td>{{ value }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -57,6 +57,10 @@
|
|||||||
<a class="btn btn-default btn-sm"
|
<a class="btn btn-default btn-sm"
|
||||||
href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
|
href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% get_htmls provider as htmls %}
|
||||||
|
{% for html in htmls %}
|
||||||
|
{{ html|safe }}
|
||||||
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -5,6 +5,8 @@ from logging import getLogger
|
|||||||
from django import template
|
from django import template
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
|
|
||||||
|
from passbook.lib.utils.template import render_to_string
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
LOGGER = getLogger(__name__)
|
LOGGER = getLogger(__name__)
|
||||||
|
|
||||||
@ -29,3 +31,24 @@ def get_links(model_instance):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
return links
|
return links
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag(takes_context=True)
|
||||||
|
def get_htmls(context, model_instance):
|
||||||
|
"""Find all html_ methods on an object instance, run them and return as dict"""
|
||||||
|
prefix = 'html_'
|
||||||
|
htmls = []
|
||||||
|
|
||||||
|
if not isinstance(model_instance, Model):
|
||||||
|
LOGGER.warning("Model %s is not instance of Model", model_instance)
|
||||||
|
return htmls
|
||||||
|
|
||||||
|
try:
|
||||||
|
for name, method in inspect.getmembers(model_instance, predicate=inspect.ismethod):
|
||||||
|
if name.startswith(prefix):
|
||||||
|
template, _context = method(context.get('request'))
|
||||||
|
htmls.append(render_to_string(template, _context))
|
||||||
|
except NotImplementedError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return htmls
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""passbook URL Configuration"""
|
"""passbook URL Configuration"""
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
from passbook.admin.views import (applications, audit, factors, groups,
|
from passbook.admin.views import (applications, audit, debug, factors, groups,
|
||||||
invitations, overview, policy,
|
invitations, overview, policy,
|
||||||
property_mapping, providers, sources, users)
|
property_mapping, providers, sources, users)
|
||||||
|
|
||||||
@ -77,5 +77,7 @@ urlpatterns = [
|
|||||||
# Groups
|
# Groups
|
||||||
path('groups/', groups.GroupListView.as_view(), name='groups'),
|
path('groups/', groups.GroupListView.as_view(), name='groups'),
|
||||||
# API
|
# API
|
||||||
path('api/', include('passbook.admin.api.urls'))
|
path('api/', include('passbook.admin.api.urls')),
|
||||||
|
# Debug
|
||||||
|
path('debug/request/', debug.DebugRequestView.as_view(), name='debug-request'),
|
||||||
]
|
]
|
||||||
|
17
passbook/admin/views/debug.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
"""passbook administration debug views"""
|
||||||
|
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
from passbook.admin.mixins import AdminRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
|
class DebugRequestView(AdminRequiredMixin, TemplateView):
|
||||||
|
"""Show debug info about request"""
|
||||||
|
|
||||||
|
template_name = 'administration/debug/request.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
kwargs['request_dict'] = {}
|
||||||
|
for key in dir(self.request):
|
||||||
|
kwargs['request_dict'][key] = getattr(self.request, key)
|
||||||
|
return super().get_context_data(**kwargs)
|
@ -1,2 +1,2 @@
|
|||||||
"""passbook api"""
|
"""passbook api"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
"""passbook audit Header"""
|
"""passbook audit Header"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
"""passbook captcha_factor Header"""
|
"""passbook captcha_factor Header"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
"""passbook core"""
|
"""passbook core"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -152,11 +152,6 @@ class Application(PolicyModel):
|
|||||||
|
|
||||||
objects = InheritanceManager()
|
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):
|
def get_provider(self):
|
||||||
"""Get casted provider instance"""
|
"""Get casted provider instance"""
|
||||||
if not self.provider:
|
if not self.provider:
|
||||||
@ -191,6 +186,12 @@ class Source(PolicyModel):
|
|||||||
"""Return additional Info, such as a callback URL. Show in the administration interface."""
|
"""Return additional Info, such as a callback URL. Show in the administration interface."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def has_user_settings(self):
|
||||||
|
"""Entrypoint to integrate with User settings. Can either return False if no
|
||||||
|
user settings are available, or a tuple or string, string, string where the first string
|
||||||
|
is the name the item has, the second string is the icon and the third is the view-name."""
|
||||||
|
return False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ SECRET_KEY = CONFIG.get('secret_key')
|
|||||||
DEBUG = CONFIG.get('debug')
|
DEBUG = CONFIG.get('debug')
|
||||||
INTERNAL_IPS = ['127.0.0.1']
|
INTERNAL_IPS = ['127.0.0.1']
|
||||||
ALLOWED_HOSTS = CONFIG.get('domains', [])
|
ALLOWED_HOSTS = CONFIG.get('domains', [])
|
||||||
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||||
|
|
||||||
LOGIN_URL = 'passbook_core:auth-login'
|
LOGIN_URL = 'passbook_core:auth-login'
|
||||||
# CSRF_FAILURE_VIEW = 'passbook.core.views.errors.CSRFErrorView.as_view'
|
# CSRF_FAILURE_VIEW = 'passbook.core.views.errors.CSRFErrorView.as_view'
|
||||||
@ -187,6 +188,7 @@ CELERY_TASK_DEFAULT_QUEUE = 'passbook'
|
|||||||
CELERY_BROKER_URL = 'amqp://%s' % CONFIG.get('rabbitmq')
|
CELERY_BROKER_URL = 'amqp://%s' % CONFIG.get('rabbitmq')
|
||||||
CELERY_RESULT_BACKEND = 'rpc://'
|
CELERY_RESULT_BACKEND = 'rpc://'
|
||||||
CELERY_ACKS_LATE = True
|
CELERY_ACKS_LATE = True
|
||||||
|
CELERY_BROKER_HEARTBEAT = 0
|
||||||
|
|
||||||
# Raven settings
|
# Raven settings
|
||||||
RAVEN_CONFIG = {
|
RAVEN_CONFIG = {
|
||||||
|
@ -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"
|
<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 |
@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<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="20px" height="20px" viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
|
|
||||||
<style type="text/css">
|
|
||||||
.st0{fill:#FBBB00;}
|
|
||||||
.st1{fill:#518EF8;}
|
|
||||||
.st2{fill:#28B446;}
|
|
||||||
.st3{fill:#F14336;}
|
|
||||||
</style>
|
|
||||||
<path class="st0" d="M4.4,12.1l-0.7,2.6l-2.5,0.1C0.4,13.3,0,11.7,0,10c0-1.7,0.4-3.2,1.1-4.6h0l2.3,0.4l1,2.3
|
|
||||||
C4.2,8.7,4.1,9.3,4.1,10C4.1,10.7,4.2,11.4,4.4,12.1z"/>
|
|
||||||
<path class="st1" d="M19.8,8.1C19.9,8.7,20,9.4,20,10c0,0.7-0.1,1.4-0.2,2.1c-0.5,2.3-1.8,4.3-3.5,5.7l0,0l-2.9-0.1L13,15.1
|
|
||||||
c1.2-0.7,2.1-1.8,2.6-3h-5.3v-4h5.4H19.8L19.8,8.1z"/>
|
|
||||||
<path class="st2" d="M16.3,17.8L16.3,17.8C14.5,19.2,12.4,20,10,20c-3.8,0-7.1-2.1-8.8-5.3l3.2-2.7c0.8,2.3,3,3.9,5.6,3.9
|
|
||||||
c1.1,0,2.1-0.3,3-0.8L16.3,17.8z"/>
|
|
||||||
<path class="st3" d="M16.4,2.3L13.1,5c-0.9-0.6-2-0.9-3.1-0.9c-2.6,0-4.8,1.7-5.6,4L1.1,5.4h0C2.8,2.2,6.1,0,10,0
|
|
||||||
C12.4,0,14.7,0.9,16.4,2.3z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
1
passbook/core/static/img/logos/azure ad.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21"><title>MS-SymbolLockup</title><rect x="1" y="1" width="9" height="9" fill="#f25022"/><rect x="1" y="11" width="9" height="9" fill="#00a4ef"/><rect x="11" y="1" width="9" height="9" fill="#7fba00"/><rect x="11" y="11" width="9" height="9" fill="#ffb900"/></svg>
|
After Width: | Height: | Size: 343 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 750 B |
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 814 B |
Before Width: | Height: | Size: 788 B After Width: | Height: | Size: 788 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 783 B After Width: | Height: | Size: 783 B |
Before Width: | Height: | Size: 688 B After Width: | Height: | Size: 688 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 889 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -3,7 +3,7 @@
|
|||||||
{% load utils %}
|
{% load utils %}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" class="layout-pf layout-pf-fixed transitions">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
Are you sure you want to delete {{ object_type }} "{{ object }}"?
|
Are you sure you want to delete {{ object_type }} "{{ object }}"?
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
|
<input type="hidden" name="confirmdelete" value="yes">
|
||||||
<a href="{% back %}" class="btn btn-default">{% trans 'Back' %}</a>
|
<a href="{% back %}" class="btn btn-default">{% trans 'Back' %}</a>
|
||||||
<input type="submit" class="btn btn-danger" value="{% trans 'Delete' %}" />
|
<input type="submit" class="btn btn-danger" value="{% trans 'Delete' %}" />
|
||||||
</form>
|
</form>
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
{% for url, icon, name in sources %}
|
{% for url, icon, name in sources %}
|
||||||
<li class="login-pf-social-link">
|
<li class="login-pf-social-link">
|
||||||
<a href="{{ url }}">
|
<a href="{{ url }}">
|
||||||
<img src="{% static 'img/' %}{{ icon }}.svg" alt="{{ name }}"> {{ name }}
|
<img src="{% static 'img/logos/' %}{{ icon }}.svg" alt="{{ name }}"> {{ name }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -8,38 +8,40 @@
|
|||||||
<div class="toast-notifications-list-pf">
|
<div class="toast-notifications-list-pf">
|
||||||
{% include 'partials/messages.html' %}
|
{% include 'partials/messages.html' %}
|
||||||
</div>
|
</div>
|
||||||
<nav class="navbar navbar-default navbar-pf" role="navigation">
|
<nav class="navbar navbar-pf-vertical">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1">
|
<button type="button" class="navbar-toggle">
|
||||||
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
|
<span class="sr-only">Toggle navigation</span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
</button>
|
||||||
<a class="navbar-brand" href="/">
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse navbar-collapse navbar-collapse-1">
|
<nav class="collapse navbar-collapse">
|
||||||
<ul class="nav navbar-nav navbar-utility">
|
<ul class="nav navbar-nav navbar-right navbar-iconic navbar-utility">
|
||||||
<li class="dropdown">
|
<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">
|
aria-haspopup="true" aria-expanded="true">
|
||||||
<span title="Help" class="fa pficon-help dropdown-title"></span>
|
<span title="Help" class="fa pficon-help"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu" aria-labelledby="horizontalDropdownMenu1">
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||||
{% comment %} <li><a href="#0">Help</a></li> {% endcomment %}
|
{% comment %} <li><a href="#0">Help</a></li> {% endcomment %}
|
||||||
<li><a data-toggle="modal" data-target="#about-modal" href="#0">{% trans 'About' %}</a></li>
|
<li><a data-toggle="modal" data-target="#about-modal" href="#0">{% trans 'About' %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<button class="btn btn-link dropdown-toggle" data-toggle="dropdown">
|
<button class="btn btn-link dropdown-toggle nav-item-iconic" id="dropdownMenu2" data-toggle="dropdown"
|
||||||
<span class="pficon pficon-user"></span>
|
aria-haspopup="true" aria-expanded="true">
|
||||||
|
<span title="Username" class="fa pficon-user"></span>
|
||||||
<span class="dropdown-title">
|
<span class="dropdown-title">
|
||||||
{{ user.username }} <b class="caret"></b>
|
{{ user.username }} <span class="caret"></span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'passbook_core:user-settings' %}">{% trans 'User Settings' %}</a>
|
<a href="{% url 'passbook_core:user-settings' %}">{% trans 'User Settings' %}</a>
|
||||||
</li>
|
</li>
|
||||||
@ -53,28 +55,144 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{% is_active_app 'passbook_admin' as is_admin %}
|
</nav>
|
||||||
<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="container-fluid container-cards-pf">
|
<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>
|
||||||
|
<li class="list-group-item {% is_active 'passbook_admin:debug-request' %}">
|
||||||
|
<a href="{% url 'passbook_admin:debug-request' %}">
|
||||||
|
<span class="list-group-item-value">
|
||||||
|
{% trans 'Debug' %}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="container-fluid container-cards-pf container-pf-nav-pf-vertical hide-nav-pf">
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
|
{{ block.super }}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
// initialize tooltips
|
// initialize tooltips
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load is_active %}
|
{% load is_active %}
|
||||||
|
{% load static %}
|
||||||
{% load passbook_user_settings %}
|
{% load passbook_user_settings %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@ -24,6 +25,15 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<li class="nav-divider"></li>
|
||||||
|
{% user_sources as us %}
|
||||||
|
{% for name, icon, link in us %}
|
||||||
|
<li class="{% if link == request.get_full_path %} active {% endif %}">
|
||||||
|
<a href="{{ link }}">
|
||||||
|
<i class="{{ icon }}"></i> {{ name }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
|
|
||||||
from passbook.core.models import Factor
|
from passbook.core.models import Factor, Source
|
||||||
from passbook.core.policies import PolicyEngine
|
from passbook.core.policies import PolicyEngine
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
@ -20,3 +20,17 @@ def user_factors(context):
|
|||||||
if policy_engine.passing and _link:
|
if policy_engine.passing and _link:
|
||||||
matching_factors.append(_link)
|
matching_factors.append(_link)
|
||||||
return matching_factors
|
return matching_factors
|
||||||
|
|
||||||
|
@register.simple_tag(takes_context=True)
|
||||||
|
def user_sources(context):
|
||||||
|
"""Return a list of all sources which are enabled for the user"""
|
||||||
|
user = context.get('request').user
|
||||||
|
_all_sources = Source.objects.filter(enabled=True).select_subclasses()
|
||||||
|
matching_sources = []
|
||||||
|
for factor in _all_sources:
|
||||||
|
_link = factor.has_user_settings()
|
||||||
|
policy_engine = PolicyEngine(factor.policies.all())
|
||||||
|
policy_engine.for_user(user).with_request(context.get('request')).build()
|
||||||
|
if policy_engine.passing and _link:
|
||||||
|
matching_sources.append(_link)
|
||||||
|
return matching_sources
|
||||||
|
@ -5,6 +5,7 @@ from django.contrib import messages
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from passbook.core.models import Application
|
from passbook.core.models import Application
|
||||||
|
from passbook.core.policies import PolicyEngine
|
||||||
|
|
||||||
LOGGER = getLogger(__name__)
|
LOGGER = getLogger(__name__)
|
||||||
|
|
||||||
@ -28,4 +29,6 @@ class AccessMixin:
|
|||||||
def user_has_access(self, application, user):
|
def user_has_access(self, application, user):
|
||||||
"""Check if user has access to application."""
|
"""Check if user has access to application."""
|
||||||
LOGGER.debug("Checking permissions of %s on application %s...", user, 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
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
"""passbook hibp_policy"""
|
"""passbook hibp_policy"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
"""Passbook ldap app Header"""
|
"""Passbook ldap app Header"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
"""passbook lib"""
|
"""passbook lib"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -62,11 +62,6 @@ passbook:
|
|||||||
uid_fields:
|
uid_fields:
|
||||||
- username
|
- username
|
||||||
- email
|
- email
|
||||||
# Factors to load
|
|
||||||
factors:
|
|
||||||
- passbook.core.auth.factors.backend
|
|
||||||
- passbook.core.auth.factors.dummy
|
|
||||||
- passbook.captcha_factor.factor
|
|
||||||
session:
|
session:
|
||||||
remember_age: 2592000 # 60 * 60 * 24 * 30, one month
|
remember_age: 2592000 # 60 * 60 * 24 * 30, one month
|
||||||
# Provider-specific settings
|
# Provider-specific settings
|
||||||
@ -90,6 +85,7 @@ oauth_client:
|
|||||||
- passbook.oauth_client.source_types.reddit
|
- passbook.oauth_client.source_types.reddit
|
||||||
- passbook.oauth_client.source_types.supervisr
|
- passbook.oauth_client.source_types.supervisr
|
||||||
- passbook.oauth_client.source_types.twitter
|
- passbook.oauth_client.source_types.twitter
|
||||||
|
- passbook.oauth_client.source_types.azure_ad
|
||||||
saml_idp:
|
saml_idp:
|
||||||
# List of python packages with provider types to load.
|
# List of python packages with provider types to load.
|
||||||
types:
|
types:
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
"""passbook oauth_client Header"""
|
"""passbook oauth_client Header"""
|
||||||
__version__ = '0.1.15-beta'
|
__version__ = '0.1.24-beta'
|
||||||
|
@ -108,3 +108,17 @@ class GoogleOAuthSourceForm(OAuthSourceForm):
|
|||||||
'access_token_url': 'https://accounts.google.com/o/oauth2/token',
|
'access_token_url': 'https://accounts.google.com/o/oauth2/token',
|
||||||
'profile_url': ' https://www.googleapis.com/oauth2/v1/userinfo',
|
'profile_url': ' https://www.googleapis.com/oauth2/v1/userinfo',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AzureADOAuthSourceForm(OAuthSourceForm):
|
||||||
|
"""OAuth Source form with pre-determined URL for AzureAD"""
|
||||||
|
|
||||||
|
class Meta(OAuthSourceForm.Meta):
|
||||||
|
|
||||||
|
overrides = {
|
||||||
|
'provider_type': 'azure_ad',
|
||||||
|
'request_token_url': '',
|
||||||
|
'authorization_url': 'https://login.microsoftonline.com/common/oauth2/authorize',
|
||||||
|
'access_token_url': 'https://login.microsoftonline.com/common/oauth2/token',
|
||||||
|
'profile_url': ' https://graph.windows.net/myorganization/me?api-version=1.6',
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""OAuth Client models"""
|
"""OAuth Client models"""
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from passbook.core.models import Source, UserSourceConnection
|
from passbook.core.models import Source, UserSourceConnection
|
||||||
@ -29,15 +29,28 @@ class OAuthSource(Source):
|
|||||||
def get_login_button(self):
|
def get_login_button(self):
|
||||||
url = reverse_lazy('passbook_oauth_client:oauth-client-login',
|
url = reverse_lazy('passbook_oauth_client:oauth-client-login',
|
||||||
kwargs={'source_slug': self.slug})
|
kwargs={'source_slug': self.slug})
|
||||||
if self.provider_type == 'github':
|
# if self.provider_type == 'github':
|
||||||
return url, 'github-logo', _('GitHub')
|
# return url, 'github-logo', _('GitHub')
|
||||||
return url, 'generic', _('Generic')
|
return url, self.provider_type, self.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def additional_info(self):
|
def additional_info(self):
|
||||||
return "Callback URL: '%s'" % reverse_lazy('passbook_oauth_client:oauth-client-callback',
|
return "Callback URL: '%s'" % reverse_lazy('passbook_oauth_client:oauth-client-callback',
|
||||||
kwargs={'source_slug': self.slug})
|
kwargs={'source_slug': self.slug})
|
||||||
|
|
||||||
|
def has_user_settings(self):
|
||||||
|
"""Entrypoint to integrate with User settings. Can either return False if no
|
||||||
|
user settings are available, or a tuple or string, string, string where the first string
|
||||||
|
is the name the item has, the second string is the icon and the third is the view-name."""
|
||||||
|
icon_type = self.provider_type
|
||||||
|
if icon_type == 'azure ad':
|
||||||
|
icon_type = 'windows'
|
||||||
|
icon_class = 'fa fa-%s' % icon_type
|
||||||
|
view_name = 'passbook_oauth_client:oauth-client-user'
|
||||||
|
return self.name, icon_class, reverse((view_name), kwargs={
|
||||||
|
'source_slug': self.slug
|
||||||
|
})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
||||||
verbose_name = _('Generic OAuth Source')
|
verbose_name = _('Generic OAuth Source')
|
||||||
@ -103,6 +116,19 @@ class GoogleOAuthSource(OAuthSource):
|
|||||||
verbose_name = _('Google OAuth Source')
|
verbose_name = _('Google OAuth Source')
|
||||||
verbose_name_plural = _('Google OAuth Sources')
|
verbose_name_plural = _('Google OAuth Sources')
|
||||||
|
|
||||||
|
|
||||||
|
class AzureADOAuthSource(OAuthSource):
|
||||||
|
"""Abstract subclass of OAuthSource to specify AzureAD Form"""
|
||||||
|
|
||||||
|
form = 'passbook.oauth_client.forms.AzureADOAuthSourceForm'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
|
||||||
|
abstract = True
|
||||||
|
verbose_name = _('Azure AD OAuth Source')
|
||||||
|
verbose_name_plural = _('Azure AD OAuth Sources')
|
||||||
|
|
||||||
|
|
||||||
class UserOAuthSourceConnection(UserSourceConnection):
|
class UserOAuthSourceConnection(UserSourceConnection):
|
||||||
"""Authorized remote OAuth provider."""
|
"""Authorized remote OAuth provider."""
|
||||||
|
|
||||||
|
52
passbook/oauth_client/source_types/azure_ad.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"""AzureAD OAuth2 Views"""
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
from requests.exceptions import RequestException
|
||||||
|
|
||||||
|
from passbook.oauth_client.clients import OAuth2Client
|
||||||
|
from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
|
||||||
|
from passbook.oauth_client.utils import user_get_or_create
|
||||||
|
from passbook.oauth_client.views.core import OAuthCallback
|
||||||
|
|
||||||
|
LOGGER = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AzureADOAuth2Client(OAuth2Client):
|
||||||
|
"""AzureAD OAuth2 Client"""
|
||||||
|
|
||||||
|
def get_profile_info(self, raw_token):
|
||||||
|
"Fetch user profile information."
|
||||||
|
try:
|
||||||
|
token = json.loads(raw_token)['access_token']
|
||||||
|
headers = {
|
||||||
|
'Authorization': 'Bearer %s' % token
|
||||||
|
}
|
||||||
|
response = self.request('get', self.source.profile_url,
|
||||||
|
headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
except RequestException as exc:
|
||||||
|
LOGGER.warning('Unable to fetch user profile: %s', exc)
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return response.json() or response.text
|
||||||
|
|
||||||
|
|
||||||
|
@MANAGER.source(kind=RequestKind.callback, name='Azure AD')
|
||||||
|
class AzureADOAuthCallback(OAuthCallback):
|
||||||
|
"""AzureAD OAuth2 Callback"""
|
||||||
|
|
||||||
|
client_class = AzureADOAuth2Client
|
||||||
|
|
||||||
|
def get_user_id(self, source, info):
|
||||||
|
return uuid.UUID(info.get('objectId')).int
|
||||||
|
|
||||||
|
def get_or_create_user(self, source, access, info):
|
||||||
|
user_data = {
|
||||||
|
'username': info.get('displayName'),
|
||||||
|
'email': info.get('mail', None) or info.get('otherMails')[0],
|
||||||
|
'name': info.get('displayName'),
|
||||||
|
'password': None,
|
||||||
|
}
|
||||||
|
return user_get_or_create(**user_data)
|
@ -1,6 +0,0 @@
|
|||||||
{% load passbook_oauth_client %}
|
|
||||||
|
|
||||||
{% any_provider as enabled %}
|
|
||||||
{% if enabled %}
|
|
||||||
<div class="btn-group btn-primary btn-block">
|
|
||||||
{% endif %}
|
|
@ -1,6 +0,0 @@
|
|||||||
{% load passbook_oauth_client %}
|
|
||||||
|
|
||||||
{% provider_exists 'facebook' as facebook_enabled %}
|
|
||||||
{% if facebook_enabled %}
|
|
||||||
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='facebook' %}" class="btn" style="background-color:#4267b2;color:white;margin-top:10px;width:100%;"><i class="fa fa-facebook-official" aria-hidden="true"></i></a>
|
|
||||||
{% endif %}
|
|
@ -1,6 +0,0 @@
|
|||||||
{% load passbook_oauth_client %}
|
|
||||||
|
|
||||||
{% provider_exists 'twitter' as twitter_enabled %}
|
|
||||||
{% if twitter_enabled %}
|
|
||||||
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='twitter' %}" class="btn" style="background-color:#55ACEE;color:white;margin-top:10px;width:100%;"><i class="fa fa-twitter" aria-hidden="true"></i></a>
|
|
||||||
{% endif %}
|
|
@ -1,7 +0,0 @@
|
|||||||
{% load passbook_oauth_client %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% provider_exists 'google' as google_enabled %}
|
|
||||||
{% if google_enabled %}
|
|
||||||
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='google' %}" class="btn" style="background-color:white;color:black;margin-top:10px;width:100%;"><img src="{% static 'img/google.svg' %}" style="height:12px"></a>
|
|
||||||
{% endif %}
|
|
@ -1,6 +0,0 @@
|
|||||||
{% load passbook_oauth_client %}
|
|
||||||
|
|
||||||
{% provider_exists 'github' as github_enabled %}
|
|
||||||
{% if github_enabled %}
|
|
||||||
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='github' %}" class="btn" style="background-color:#444444;color:white;margin-top:10px;width:100%;"><i class="fa fa-github" aria-hidden="true"></i></a>
|
|
||||||
{% endif %}
|
|
@ -1,7 +0,0 @@
|
|||||||
{% load passbook_oauth_client %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% provider_exists 'discord' as discord_enabled %}
|
|
||||||
{% if discord_enabled %}
|
|
||||||
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='discord' %}" class="btn" style="background-color:#2C2F33;color:white;margin-top:10px;width:100%;"><img src="{% static 'img/discord.svg' %}" style="height:12px"></a>
|
|
||||||
{% endif %}
|
|