Compare commits
81 Commits
version/0.
...
version/0.
| Author | SHA1 | Date | |
|---|---|---|---|
| c77f4204c0 | |||
| 5f4452470b | |||
| 9a1270c693 | |||
| 7b9d1a1159 | |||
| cdbe1f6161 | |||
| e43db2e065 | |||
| d1c74d2160 | |||
| f2119ce567 | |||
| 2c4dcb9cf0 | |||
| 93b8266821 | |||
| 443797d9b0 | |||
| a4365ca02c | |||
| 3750083667 | |||
| 66ef067ecf | |||
| b489b0e691 | |||
| f2154d9875 | |||
| 80a50f9bdb | |||
| dc8b89a6b9 | |||
| 8df55f22aa | |||
| f6c322be27 | |||
| a144552059 | |||
| 535d529193 | |||
| 6ed2e137a2 | |||
| 45bd63c720 | |||
| 736e13fc35 | |||
| 966fff008c | |||
| 64f15eadbd | |||
| 81b66ecdcd | |||
| 53e5cf7826 | |||
| 82654b3fd9 | |||
| 9b72c604dd | |||
| 5fb1b8044c | |||
| b8daab4377 | |||
| c5b91bdae8 | |||
| 39a208c55f | |||
| a5bfef9b6b | |||
| f1f4cbef9b | |||
| 8388120b06 | |||
| 2bf96828f1 | |||
| 22838e66fe | |||
| 484dd6de09 | |||
| b743736c26 | |||
| af91e2079b | |||
| cad1c17f14 | |||
| 120d32e4dc | |||
| 238b489e07 | |||
| 4daa70c894 | |||
| f8599438df | |||
| 155c9a4c3f | |||
| 8433b5e583 | |||
| dc5ba144f1 | |||
| 521a8b5356 | |||
| 3453077d7b | |||
| 70ede8581a | |||
| 6e9d297f02 | |||
| 6a7545fd43 | |||
| a8926cbd07 | |||
| 64d7b009ab | |||
| 2b5fddb7bf | |||
| b99d23c119 | |||
| 03905b74ff | |||
| 6b8a59cfbd | |||
| d6fdcd3ef9 | |||
| 53ebc551d2 | |||
| 3d4f43d6e3 | |||
| 074cde7cd5 | |||
| 382e563590 | |||
| ca61a7cc21 | |||
| fa2870afe0 | |||
| 0f46207ea4 | |||
| 1e7d912144 | |||
| f4a676e2fb | |||
| b2c10e2387 | |||
| 8c329dca7d | |||
| 83da175749 | |||
| 995c87938f | |||
| 40678b2f84 | |||
| 8dbbe9102b | |||
| 2f51f354de | |||
| 04b815a33e | |||
| 2a4d68911b |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 0.8.6-beta
|
||||
current_version = 0.8.15-beta
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
|
||||
|
||||
118
.github/workflows/ci.yml
vendored
118
.github/workflows/ci.yml
vendored
@ -15,14 +15,8 @@ jobs:
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
run: sudo pip install -U wheel pipenv && pipenv install --dev
|
||||
- name: Lint with pylint
|
||||
run: pipenv run pylint passbook
|
||||
black:
|
||||
@ -32,14 +26,8 @@ jobs:
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
run: sudo pip install -U wheel pipenv && pipenv install --dev
|
||||
- name: Lint with black
|
||||
run: pipenv run black --check passbook
|
||||
prospector:
|
||||
@ -49,14 +37,8 @@ jobs:
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev && pipenv install --dev prospector --skip-lock
|
||||
run: sudo pip install -U wheel pipenv && pipenv install --dev && pipenv install --dev prospector --skip-lock
|
||||
- name: Lint with prospector
|
||||
run: pipenv run prospector
|
||||
bandit:
|
||||
@ -66,14 +48,8 @@ jobs:
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
run: sudo pip install -U wheel pipenv && pipenv install --dev
|
||||
- name: Lint with bandit
|
||||
run: pipenv run bandit -r passbook
|
||||
# Actual CI tests
|
||||
@ -101,14 +77,8 @@ jobs:
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
run: sudo pip install -U wheel pipenv && pipenv install --dev
|
||||
- name: Run migrations
|
||||
run: pipenv run ./manage.py migrate
|
||||
coverage:
|
||||
@ -135,13 +105,77 @@ jobs:
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs/
|
||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install dependencies
|
||||
run: pip install -U pip pipenv && pipenv install --dev
|
||||
run: sudo pip install -U wheel pipenv && pipenv install --dev
|
||||
- name: Run coverage
|
||||
run: pipenv run ./scripts/coverage.sh
|
||||
# Build
|
||||
build-server:
|
||||
needs:
|
||||
- migrations
|
||||
- coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Docker Login Registry
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
|
||||
- name: Building Docker Image
|
||||
run: docker build
|
||||
--no-cache
|
||||
-t beryju/passbook:${GITHUB_REF##*/}
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry
|
||||
run: docker push beryju/passbook:${GITHUB_REF##*/}
|
||||
build-gatekeeper:
|
||||
needs:
|
||||
- migrations
|
||||
- coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Docker Login Registry
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
|
||||
- name: Building Docker Image
|
||||
run: |
|
||||
cd gatekeeper
|
||||
docker build \
|
||||
--no-cache \
|
||||
-t beryju/passbook-gatekeeper:${GITHUB_REF##*/} \
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry
|
||||
run: docker push beryju/passbook-gatekeeper:${GITHUB_REF##*/}
|
||||
build-static:
|
||||
needs:
|
||||
- migrations
|
||||
- coverage
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
env:
|
||||
POSTGRES_DB: passbook
|
||||
POSTGRES_USER: passbook
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
redis:
|
||||
image: redis:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Docker Login Registry
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
|
||||
- name: Building Docker Image
|
||||
run: docker build
|
||||
--no-cache
|
||||
--network=$(docker network ls | grep github | awk '{print $1}')
|
||||
-t beryju/passbook-static:${GITHUB_REF##*/}
|
||||
-f static.Dockerfile .
|
||||
- name: Push Docker Container to Registry
|
||||
run: docker push beryju/passbook-static:${GITHUB_REF##*/}
|
||||
|
||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@ -16,11 +16,11 @@ jobs:
|
||||
- name: Building Docker Image
|
||||
run: docker build
|
||||
--no-cache
|
||||
-t beryju/passbook:0.8.6-beta
|
||||
-t beryju/passbook:0.8.15-beta
|
||||
-t beryju/passbook:latest
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/passbook:0.8.6-beta
|
||||
run: docker push beryju/passbook:0.8.15-beta
|
||||
- name: Push Docker Container to Registry (latest)
|
||||
run: docker push beryju/passbook:latest
|
||||
build-gatekeeper:
|
||||
@ -37,11 +37,11 @@ jobs:
|
||||
cd gatekeeper
|
||||
docker build \
|
||||
--no-cache \
|
||||
-t beryju/passbook-gatekeeper:0.8.6-beta \
|
||||
-t beryju/passbook-gatekeeper:0.8.15-beta \
|
||||
-t beryju/passbook-gatekeeper:latest \
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/passbook-gatekeeper:0.8.6-beta
|
||||
run: docker push beryju/passbook-gatekeeper:0.8.15-beta
|
||||
- name: Push Docker Container to Registry (latest)
|
||||
run: docker push beryju/passbook-gatekeeper:latest
|
||||
build-static:
|
||||
@ -66,11 +66,11 @@ jobs:
|
||||
run: docker build
|
||||
--no-cache
|
||||
--network=$(docker network ls | grep github | awk '{print $1}')
|
||||
-t beryju/passbook-static:0.8.6-beta
|
||||
-t beryju/passbook-static:0.8.15-beta
|
||||
-t beryju/passbook-static:latest
|
||||
-f static.Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/passbook-static:0.8.6-beta
|
||||
run: docker push beryju/passbook-static:0.8.15-beta
|
||||
- name: Push Docker Container to Registry (latest)
|
||||
run: docker push beryju/passbook-static:latest
|
||||
test-release:
|
||||
|
||||
6
.isort.cfg
Normal file
6
.isort.cfg
Normal file
@ -0,0 +1,6 @@
|
||||
[settings]
|
||||
multi_line_output=3
|
||||
include_trailing_comma=True
|
||||
force_grid_wrap=0
|
||||
use_parentheses=True
|
||||
line_length=88
|
||||
@ -7,7 +7,3 @@ const-rgx=[a-zA-Z0-9_]{1,40}$
|
||||
ignored-modules=django-otp
|
||||
jobs=4
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=20
|
||||
|
||||
587
Pipfile.lock
generated
587
Pipfile.lock
generated
@ -25,10 +25,10 @@
|
||||
},
|
||||
"asgiref": {
|
||||
"hashes": [
|
||||
"sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0",
|
||||
"sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5"
|
||||
"sha256:8036f90603c54e93521e5777b2b9a39ba1bad05773fcf2d208f0299d1df58ce5",
|
||||
"sha256:9ca8b952a0a9afa61d30aa6d3d9b570bb3fd6bafcf7ec9e6bed43b936133db1c"
|
||||
],
|
||||
"version": "==3.2.3"
|
||||
"version": "==3.2.7"
|
||||
},
|
||||
"asn1crypto": {
|
||||
"hashes": [
|
||||
@ -46,40 +46,40 @@
|
||||
},
|
||||
"billiard": {
|
||||
"hashes": [
|
||||
"sha256:26fd494dc3251f8ce1f5559744f18aeed427fdaf29a75d7baae26752a5d3816f",
|
||||
"sha256:f4e09366653aa3cb3ae8ed16423f9ba1665ff426f087bcdbbed86bf3664fe02c"
|
||||
"sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede",
|
||||
"sha256:d91725ce6425f33a97dfa72fb6bfef0e47d4652acd98a032bd1a7fbf06d5fa6a"
|
||||
],
|
||||
"version": "==3.6.2.0"
|
||||
"version": "==3.6.3.0"
|
||||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:33462a79d57c9c4a215e075472509537d03545f54566fc4f776fb0f4cfa616f6",
|
||||
"sha256:34f9a04f529dc849f0e427782d6f3c6b62f7fb734d8f4859b17e5dee0855323e"
|
||||
"sha256:970bd7b332e73d7b51077ed36772c634811b38c81b0cc6ed0f910e50d7ebadf8",
|
||||
"sha256:cdd79a3a7bbe1f33a365f0acfcc75c4405b482b3eb9ce3f4e6b16c418e201ac3"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.12.0"
|
||||
"version": "==1.12.39"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:055da4826f6c9158e4a61549d57a2ce449c27d44ce34ab4c96c7bb7b5c993efc",
|
||||
"sha256:1f7cecfcd38c7cac17b5386014eb04626d1c7559ee8d8ec1526058cd23f6d1d4"
|
||||
"sha256:94232b44e1540b7e043e220bd43f855400d0a243e926b26b3fb72994e971d518",
|
||||
"sha256:e20ba56476b1031ce5ac8e22b59dabc75bd0e03231f124ed6b9ff99fe0b0c96b"
|
||||
],
|
||||
"version": "==1.15.0"
|
||||
"version": "==1.15.39"
|
||||
},
|
||||
"celery": {
|
||||
"hashes": [
|
||||
"sha256:7c544f37a84a5eadc44cab1aa8c9580dff94636bb81978cdf9bf8012d9ea7d8f",
|
||||
"sha256:d3363bb5df72d74420986a435449f3c3979285941dff57d5d97ecba352a0e3e2"
|
||||
"sha256:108a0bf9018a871620936c33a3ee9f6336a89f8ef0a0f567a9001f4aa361415f",
|
||||
"sha256:5b4b37e276033fe47575107a2775469f0b721646a08c96ec2c61531e4fe45f2a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.4.0"
|
||||
"version": "==4.4.2"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
|
||||
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
|
||||
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
|
||||
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
|
||||
],
|
||||
"version": "==2019.11.28"
|
||||
"version": "==2020.4.5.1"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
@ -137,29 +137,27 @@
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c",
|
||||
"sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595",
|
||||
"sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad",
|
||||
"sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651",
|
||||
"sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2",
|
||||
"sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff",
|
||||
"sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d",
|
||||
"sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42",
|
||||
"sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d",
|
||||
"sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e",
|
||||
"sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912",
|
||||
"sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793",
|
||||
"sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13",
|
||||
"sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7",
|
||||
"sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0",
|
||||
"sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879",
|
||||
"sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f",
|
||||
"sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9",
|
||||
"sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2",
|
||||
"sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf",
|
||||
"sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"
|
||||
"sha256:0cacd3ef5c604b8e5f59bf2582c076c98a37fe206b31430d0cd08138aff0986e",
|
||||
"sha256:192ca04a36852a994ef21df13cca4d822adbbdc9d5009c0f96f1d2929e375d4f",
|
||||
"sha256:19ae795137682a9778892fb4390c07811828b173741bce91e30f899424b3934d",
|
||||
"sha256:1b9b535d6b55936a79dbe4990b64bb16048f48747c76c29713fea8c50eca2acf",
|
||||
"sha256:2a2ad24d43398d89f92209289f15265107928f22a8d10385f70def7a698d6a02",
|
||||
"sha256:3be7a5722d5bfe69894d3f7bbed15547b17619f3a88a318aab2e37f457524164",
|
||||
"sha256:49870684da168b90110bbaf86140d4681032c5e6a2461adc7afdd93be5634216",
|
||||
"sha256:587f98ce27ac4547177a0c6fe0986b8736058daffe9160dcf5f1bd411b7fbaa1",
|
||||
"sha256:5aca6f00b2f42546b9bdf11a69f248d1881212ce5b9e2618b04935b87f6f82a1",
|
||||
"sha256:6b744039b55988519cc183149cceb573189b3e46e16ccf6f8c46798bb767c9dc",
|
||||
"sha256:6b91cab3841b4c7cb70e4db1697c69f036c8bc0a253edc0baa6783154f1301e4",
|
||||
"sha256:7598974f6879a338c785c513e7c5a4329fbc58b9f6b9a6305035fca5b1076552",
|
||||
"sha256:7a279f33a081d436e90e91d1a7c338553c04e464de1c9302311a5e7e4b746088",
|
||||
"sha256:95e1296e0157361fe2f5f0ed307fd31f94b0ca13372e3673fa95095a627636a1",
|
||||
"sha256:9fc9da390e98cb6975eadf251b6e5fa088820141061bf041cd5c72deba1dc526",
|
||||
"sha256:cc20316e3f5a6b582fc3b029d8dc03aabeb645acfcb7fc1d9848841a33265748",
|
||||
"sha256:d1bf5a1a0d60c7f9a78e448adcb99aa101f3f9588b16708044638881be15d6bc",
|
||||
"sha256:ed1d0760c7e46436ec90834d6f10477ff09475c692ed1695329d324b2c5cd547",
|
||||
"sha256:ef9a55013676907df6c9d7dd943eb1770d014f68beaa7e73250fb43c759f4585"
|
||||
],
|
||||
"version": "==2.8"
|
||||
"version": "==2.9"
|
||||
},
|
||||
"defusedxml": {
|
||||
"hashes": [
|
||||
@ -171,11 +169,11 @@
|
||||
},
|
||||
"django": {
|
||||
"hashes": [
|
||||
"sha256:2f1ba1db8648484dd5c238fb62504777b7ad090c81c5f1fd8d5eb5ec21b5f283",
|
||||
"sha256:c91c91a7ad6ef67a874a4f76f58ba534f9208412692a840e1d125eb5c279cb0a"
|
||||
"sha256:642d8eceab321ca743ae71e0f985ff8fdca59f07aab3a9fb362c617d23e33a76",
|
||||
"sha256:d4666c2edefa38c5ede0ec1655424c56dc47ceb04b6d8d62a7eac09db89545c1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.3"
|
||||
"version": "==3.0.5"
|
||||
},
|
||||
"django-cors-middleware": {
|
||||
"hashes": [
|
||||
@ -218,10 +216,11 @@
|
||||
},
|
||||
"django-oauth-toolkit": {
|
||||
"hashes": [
|
||||
"sha256:ad1b76275950ebbff708222cec57bbdb879f89bac7df6b9dee0f4b9db485c264"
|
||||
"sha256:28508f83385ab4313936ddedfb310eaa8a1dcb737153d2956383ce47e75c2fab",
|
||||
"sha256:d5a1044af9419ddc048390c5974777ea97874e5b78e33c609e17eebb8423afb2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.2.0"
|
||||
"version": "==1.3.2"
|
||||
},
|
||||
"django-oidc-provider": {
|
||||
"hashes": [
|
||||
@ -240,11 +239,11 @@
|
||||
},
|
||||
"django-prometheus": {
|
||||
"hashes": [
|
||||
"sha256:362ea45e5ee26bdba85ce978aeb370659ca6bbc0d6bac69868a055179e053bd1",
|
||||
"sha256:facaa677386899303ea26c45552371cc43f476e42a81c081011a49cb5564af0b"
|
||||
"sha256:1a8cb752ae4181e38df00e7bd7d5f6495cde18b8b3ff697c22f9d8d2fe48bf28",
|
||||
"sha256:9f024af5495447c8e309f07e5289e7bc1100c5a380ac7cd0afe3a1b2a0b3b534"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.1.0.dev5"
|
||||
"version": "==2.1.0.dev14"
|
||||
},
|
||||
"django-recaptcha": {
|
||||
"hashes": [
|
||||
@ -322,16 +321,17 @@
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
||||
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
||||
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
|
||||
],
|
||||
"version": "==2.8"
|
||||
"version": "==2.9"
|
||||
},
|
||||
"inflection": {
|
||||
"hashes": [
|
||||
"sha256:18ea7fb7a7d152853386523def08736aa8c32636b047ade55f7578c4edeb16ca"
|
||||
"sha256:32a5c3341d9583ec319548b9015b7fbdf8c429cbcb575d326c33ae3a0e90d52c",
|
||||
"sha256:9a15d3598f01220e93f2207c432cfede50daff53137ce660fb8be838ef1ca6cc"
|
||||
],
|
||||
"version": "==0.3.1"
|
||||
"version": "==0.4.0"
|
||||
},
|
||||
"itypes": {
|
||||
"hashes": [
|
||||
@ -349,10 +349,10 @@
|
||||
},
|
||||
"jmespath": {
|
||||
"hashes": [
|
||||
"sha256:3720a4b1bd659dd2eecad0666459b9788813e032b83e7ba58578e48254e0a0e6",
|
||||
"sha256:bde2aef6f44302dfb30320115b17d030798de8c4110e28d5cf6cf91a7a31074c"
|
||||
"sha256:695cb76fa78a10663425d5b73ddc5714eb711157e52704d69be03b1a02ba4fec",
|
||||
"sha256:cca55c8d153173e21baa59983015ad0daf603f9cb799904ff057bfb8ff8dc2d9"
|
||||
],
|
||||
"version": "==0.9.4"
|
||||
"version": "==0.9.5"
|
||||
},
|
||||
"jsonschema": {
|
||||
"hashes": [
|
||||
@ -363,19 +363,19 @@
|
||||
},
|
||||
"kombu": {
|
||||
"hashes": [
|
||||
"sha256:2a9e7adff14d046c9996752b2c48b6d9185d0b992106d5160e1a179907a5d4ac",
|
||||
"sha256:67b32ccb6fea030f8799f8fd50dd08e03a4b99464ebc4952d71d8747b1a52ad1"
|
||||
"sha256:2d1cda774126a044d91a7ff5fa6d09edf99f46924ab332a810760fe6740e9b76",
|
||||
"sha256:598e7e749d6ab54f646b74b2d2df67755dee13894f73ab02a2a9feb8870c7cb2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.6.7"
|
||||
"version": "==4.6.8"
|
||||
},
|
||||
"ldap3": {
|
||||
"hashes": [
|
||||
"sha256:1898194d872539670a2f36d4b56fe5a35d4b9ead28103bec78f05a8993e8122f",
|
||||
"sha256:27cb673e7afcb539f6adcae5a3ecac4e74eb37ca0a2d50dc98f29a3829eee529"
|
||||
"sha256:17f04298b70bf7ecaa5db8a7d8622b5a962ef7fc2b245b2eea705ac1c24338c0",
|
||||
"sha256:81df4ac8b6df10fb1f05b17c18d0cb8c4c344d5a03083c382824960ed959cf5b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.6.1"
|
||||
"version": "==2.7"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
@ -458,11 +458,11 @@
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73",
|
||||
"sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334"
|
||||
"sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3",
|
||||
"sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==20.1"
|
||||
"version": "==20.3"
|
||||
},
|
||||
"prometheus-client": {
|
||||
"hashes": [
|
||||
@ -472,41 +472,39 @@
|
||||
},
|
||||
"psycopg2-binary": {
|
||||
"hashes": [
|
||||
"sha256:040234f8a4a8dfd692662a8308d78f63f31a97e1c42d2480e5e6810c48966a29",
|
||||
"sha256:086f7e89ec85a6704db51f68f0dcae432eff9300809723a6e8782c41c2f48e03",
|
||||
"sha256:18ca813fdb17bc1db73fe61b196b05dd1ca2165b884dd5ec5568877cabf9b039",
|
||||
"sha256:19dc39616850342a2a6db70559af55b22955f86667b5f652f40c0e99253d9881",
|
||||
"sha256:2166e770cb98f02ed5ee2b0b569d40db26788e0bf2ec3ae1a0d864ea6f1d8309",
|
||||
"sha256:3a2522b1d9178575acee4adf8fd9f979f9c0449b00b4164bb63c3475ea6528ed",
|
||||
"sha256:3aa773580f85a28ffdf6f862e59cb5a3cc7ef6885121f2de3fca8d6ada4dbf3b",
|
||||
"sha256:3b5deaa3ee7180585a296af33e14c9b18c218d148e735c7accf78130765a47e3",
|
||||
"sha256:407af6d7e46593415f216c7f56ba087a9a42bd6dc2ecb86028760aa45b802bd7",
|
||||
"sha256:4c3c09fb674401f630626310bcaf6cd6285daf0d5e4c26d6e55ca26a2734e39b",
|
||||
"sha256:4c6717962247445b4f9e21c962ea61d2e884fc17df5ddf5e35863b016f8a1f03",
|
||||
"sha256:50446fae5681fc99f87e505d4e77c9407e683ab60c555ec302f9ac9bffa61103",
|
||||
"sha256:5057669b6a66aa9ca118a2a860159f0ee3acf837eda937bdd2a64f3431361a2d",
|
||||
"sha256:5dd90c5438b4f935c9d01fcbad3620253da89d19c1f5fca9158646407ed7df35",
|
||||
"sha256:659c815b5b8e2a55193ede2795c1e2349b8011497310bb936da7d4745652823b",
|
||||
"sha256:69b13fdf12878b10dc6003acc8d0abf3ad93e79813fd5f3812497c1c9fb9be49",
|
||||
"sha256:7a1cb80e35e1ccea3e11a48afe65d38744a0e0bde88795cc56a4d05b6e4f9d70",
|
||||
"sha256:7e6e3c52e6732c219c07bd97fff6c088f8df4dae3b79752ee3a817e6f32e177e",
|
||||
"sha256:7f42a8490c4fe854325504ce7a6e4796b207960dabb2cbafe3c3959cb00d1d7e",
|
||||
"sha256:84156313f258eafff716b2961644a4483a9be44a5d43551d554844d15d4d224e",
|
||||
"sha256:8578d6b8192e4c805e85f187bc530d0f52ba86c39172e61cd51f68fddd648103",
|
||||
"sha256:890167d5091279a27e2505ff0e1fb273f8c48c41d35c5b92adbf4af80e6b2ed6",
|
||||
"sha256:98e10634792ac0e9e7a92a76b4991b44c2325d3e7798270a808407355e7bb0a1",
|
||||
"sha256:9aadff9032e967865f9778485571e93908d27dab21d0fdfdec0ca779bb6f8ad9",
|
||||
"sha256:9f24f383a298a0c0f9b3113b982e21751a8ecde6615494a3f1470eb4a9d70e9e",
|
||||
"sha256:a73021b44813b5c84eda4a3af5826dd72356a900bac9bd9dd1f0f81ee1c22c2f",
|
||||
"sha256:afd96845e12638d2c44d213d4810a08f4dc4a563f9a98204b7428e567014b1cd",
|
||||
"sha256:b73ddf033d8cd4cc9dfed6324b1ad2a89ba52c410ef6877998422fcb9c23e3a8",
|
||||
"sha256:b8f490f5fad1767a1331df1259763b3bad7d7af12a75b950c2843ba319b2415f",
|
||||
"sha256:dbc5cd56fff1a6152ca59445178652756f4e509f672e49ccdf3d79c1043113a4",
|
||||
"sha256:eac8a3499754790187bb00574ab980df13e754777d346f85e0ff6df929bcd964",
|
||||
"sha256:eaed1c65f461a959284649e37b5051224f4db6ebdc84e40b5e65f2986f101a08"
|
||||
"sha256:008da3ab51adc70a5f1cfbbe5db3a22607ab030eb44bcecf517ad11a0c2b3cac",
|
||||
"sha256:07cf82c870ec2d2ce94d18e70c13323c89f2f2a2628cbf1feee700630be2519a",
|
||||
"sha256:08507efbe532029adee21b8d4c999170a83760d38249936038bd0602327029b5",
|
||||
"sha256:107d9be3b614e52a192719c6bf32e8813030020ea1d1215daa86ded9a24d8b04",
|
||||
"sha256:17a0ea0b0eabf07035e5e0d520dabc7950aeb15a17c6d36128ba99b2721b25b1",
|
||||
"sha256:3286541b9d85a340ee4ed42732d15fc1bb441dc500c97243a768154ab8505bb5",
|
||||
"sha256:3939cf75fc89c5e9ed836e228c4a63604dff95ad19aed2bbf71d5d04c15ed5ce",
|
||||
"sha256:40abc319f7f26c042a11658bf3dd3b0b3bceccf883ec1c565d5c909a90204434",
|
||||
"sha256:51f7823f1b087d2020d8e8c9e6687473d3d239ba9afc162d9b2ab6e80b53f9f9",
|
||||
"sha256:6bb2dd006a46a4a4ce95201f836194eb6a1e863f69ee5bab506673e0ca767057",
|
||||
"sha256:702f09d8f77dc4794651f650828791af82f7c2efd8c91ae79e3d9fe4bb7d4c98",
|
||||
"sha256:7036ccf715925251fac969f4da9ad37e4b7e211b1e920860148a10c0de963522",
|
||||
"sha256:7b832d76cc65c092abd9505cc670c4e3421fd136fb6ea5b94efbe4c146572505",
|
||||
"sha256:8f74e631b67482d504d7e9cf364071fc5d54c28e79a093ff402d5f8f81e23bfa",
|
||||
"sha256:930315ac53dc65cbf52ab6b6d27422611f5fb461d763c531db229c7e1af6c0b3",
|
||||
"sha256:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f",
|
||||
"sha256:a20299ee0ea2f9cca494396ac472d6e636745652a64a418b39522c120fd0a0a4",
|
||||
"sha256:a34826d6465c2e2bbe9d0605f944f19d2480589f89863ed5f091943be27c9de4",
|
||||
"sha256:a69970ee896e21db4c57e398646af9edc71c003bc52a3cc77fb150240fefd266",
|
||||
"sha256:b9a8b391c2b0321e0cd7ec6b4cfcc3dd6349347bd1207d48bcb752aa6c553a66",
|
||||
"sha256:ba13346ff6d3eb2dca0b6fa0d8a9d999eff3dcd9b55f3a890f12b0b6362b2b38",
|
||||
"sha256:bb0608694a91db1e230b4a314e8ed00ad07ed0c518f9a69b83af2717e31291a3",
|
||||
"sha256:c8830b7d5f16fd79d39b21e3d94f247219036b29b30c8270314c46bf8b732389",
|
||||
"sha256:cac918cd7c4c498a60f5d2a61d4f0a6091c2c9490d81bc805c963444032d0dab",
|
||||
"sha256:cc30cb900f42c8a246e2cb76539d9726f407330bc244ca7729c41a44e8d807fb",
|
||||
"sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6",
|
||||
"sha256:d1a8b01f6a964fec702d6b6dac1f91f2b9f9fe41b310cbb16c7ef1fac82df06d",
|
||||
"sha256:e004db88e5a75e5fdab1620fb9f90c9598c2a195a594225ac4ed2a6f1c23e162",
|
||||
"sha256:eb2f43ae3037f1ef5e19339c41cf56947021ac892f668765cd65f8ab9814192e",
|
||||
"sha256:fa466306fcf6b39b8a61d003123d442b23707d635a5cb05ac4e1b62cc79105cd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.8.4"
|
||||
"version": "==2.8.5"
|
||||
},
|
||||
"pyasn1": {
|
||||
"hashes": [
|
||||
@ -524,80 +522,81 @@
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
|
||||
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
|
||||
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
|
||||
],
|
||||
"version": "==2.19"
|
||||
"version": "==2.20"
|
||||
},
|
||||
"pycryptodome": {
|
||||
"hashes": [
|
||||
"sha256:012ca77c2105600e3c6aef43188101ac1d95052c633a4ae8fbebffab20c25f8a",
|
||||
"sha256:05b4d865710f9a6378d3ada28195ff78e52642d3ecffe6fa9d379d870b9bf29d",
|
||||
"sha256:07daddb98f98f771ba027f8f835bdb675aeb84effe41ed5221f520b267429354",
|
||||
"sha256:09bf05a489fe10f9280a5e0163f195e7b9630cafb15f7d72fb9c8f5eb2afa84f",
|
||||
"sha256:0a8d5f2dbb4bbe830ace54286b829bfa529f0853bedaab6225fcb2e6d1f7e356",
|
||||
"sha256:1259b8ca49662b8a941177357f08147d858595c0042e63ff81e9628e925b5c9d",
|
||||
"sha256:238d8b6dd27bd1a04816a68aa90a739e6dd23b192fcd83b50f9360958bff192a",
|
||||
"sha256:2a57daef18a2022a5e4b6f7376c9ddd0c2d946e4b1f1e59b837f5bf295be7380",
|
||||
"sha256:39e5ca2f66d1eac7abcba5ce1a03370d123dc6085620f1cd532dfee27e650178",
|
||||
"sha256:3d516df693c195b8da3795e381429bd420e87081b7e6c2871c62c9897c812cda",
|
||||
"sha256:3e486c5b7228e864665fc479e9f596b2547b5fe29c6f5c8ed3807784d06faed7",
|
||||
"sha256:5029c46b0d41dfb763c3981c0af68eab029f06fe2b94f2299112fc18cf9e8d6d",
|
||||
"sha256:5817c0b3c263025d851da96b90cbc7e95348008f88b990e90d10683dba376666",
|
||||
"sha256:79320f1fc5c9ca682869087c565bb29ca6f334692e940d7365771e9a94382e12",
|
||||
"sha256:887d08beca6368d3d70dc75126607ad76317a9fd07fe61323d8c3cb42add12b6",
|
||||
"sha256:9163fec630495c10c767991e3f8dab32f4427bfb2dfeaa59bb28fe3e52ba66f2",
|
||||
"sha256:95d324e603c5cec5d89e8595236bbf59ade5fe3a72d100ce61eebb323d598750",
|
||||
"sha256:9927aa8a8cb4af681279b6f28a1dcb14e0eb556c1aea8413a1e27608a8516e0c",
|
||||
"sha256:9948c2d5c5c0ee45ed44cee0e2eba2ce60a03be006ed3074521f3da3be162e72",
|
||||
"sha256:a719bd708207fa219fcbf4c8ebbcbc52846045f78179d00445b429fdabdbc1c4",
|
||||
"sha256:bc22ced26ebc46546798fa0141f4418f1db116dec517f0aeaecec87cf7b2416c",
|
||||
"sha256:c41b7e10b72cef00cd63410f31fe50e72dc3a40eafbd146e288384fbe4208064",
|
||||
"sha256:cdb0ad83a5d6bac986a37fcb7562bcbef0aabae8ea19505bab5cf83c4d18af12",
|
||||
"sha256:d8e480f65ac7105cbc288eec2417dc61eaac6ed6e75595aa15b8c7c77c53a68b",
|
||||
"sha256:da2d581da279bc7408d38e16ff77754f5448c4352f2acfe530a5d14d8fc6934a",
|
||||
"sha256:de61091dd68326b600422cf731eb4810c4c6363f18a65bccd6061784b7454f5b",
|
||||
"sha256:ec7d39589f9cfc2a8b83b1d2fc673441757c99d43283e97b2dd46e0e23730db8",
|
||||
"sha256:f3204006869ab037604b1d9f045c4e84882ddd365e4ee8caa5eb1ff47a59188e",
|
||||
"sha256:f4d2174e168d0eabd1fffaf88b4f62c2b6f30a67b8816f31024b8e48be3e2d75",
|
||||
"sha256:fcff8c9d88d58880f7eda2139c7c444552a38f98a9e77ba5970b6e78f54ac358"
|
||||
"sha256:07024fc364869eae8d6ac0d316e089956e6aeffe42dbdcf44fe1320d96becf7f",
|
||||
"sha256:09b6d6bcc01a4eb1a2b4deeff5aa602a108ec5aed8ac75ae554f97d1d7f0a5ad",
|
||||
"sha256:0e10f352ccbbcb5bb2dc4ecaf106564e65702a717d72ab260f9ac4c19753cfc2",
|
||||
"sha256:1f4752186298caf2e9ff5354f2e694d607ca7342aa313a62005235d46e28cf04",
|
||||
"sha256:2fbc472e0b567318fe2052281d5a8c0ae70099b446679815f655e9fbc18c3a65",
|
||||
"sha256:3ec3dc2f80f71fd0c955ce48b81bfaf8914c6f63a41a738f28885a1c4892968a",
|
||||
"sha256:426c188c83c10df71f053e04b4003b1437bae5cb37606440e498b00f160d71d0",
|
||||
"sha256:626c0a1d4d83ec6303f970a17158114f75c3ba1736f7f2983f7b40a265861bd8",
|
||||
"sha256:767ad0fb5d23efc36a4d5c2fc608ac603f3de028909bcf59abc943e0d0bc5a36",
|
||||
"sha256:7ac729d9091ed5478af2b4a4f44f5335a98febbc008af619e4569a59fe503e40",
|
||||
"sha256:83295a3fb5cf50c48631eb5b440cb5e9832d8c14d81d1d45f4497b67a9987de8",
|
||||
"sha256:8be56bde3312e022d9d1d6afa124556460ad5c844c2fc63642f6af723c098d35",
|
||||
"sha256:8f06556a8f7ea7b1e42eff39726bb0dca1c251205debae64e6eebea3cd7b438a",
|
||||
"sha256:9230fcb5d948c3fb40049bace4d33c5d254f8232c2c0bba05d2570aea3ba4520",
|
||||
"sha256:9378c309aec1f8cd8bad361ed0816a440151b97a2a3f6ffdaba1d1a1fb76873a",
|
||||
"sha256:9977086e0f93adb326379897437373871b80501e1d176fec63c7f46fb300c862",
|
||||
"sha256:9a94fca11fdc161460bd8659c15b6adef45c1b20da86402256eaf3addfaab324",
|
||||
"sha256:9c739b7795ccf2ef1fdad8d44e539a39ad300ee6786e804ea7f0c6a786eb5343",
|
||||
"sha256:b1e332587b3b195542e77681389c296e1837ca01240399d88803a075447d3557",
|
||||
"sha256:c109a26a21f21f695d369ff9b87f5d43e0d6c768d8384e10bc74142bed2e092e",
|
||||
"sha256:c818dc1f3eace93ee50c2b6b5c2becf7c418fa5dd1ba6fc0ef7db279ea21d5e4",
|
||||
"sha256:cff31f5a8977534f255f729d5d2467526f2b10563a30bbdade92223e0bf264bd",
|
||||
"sha256:d4f94368ce2d65873a87ad867eb3bf63f4ba81eb97a9ee66d38c2b71ce5a7439",
|
||||
"sha256:d61b012baa8c2b659e9890011358455c0019a4108536b811602d2f638c40802a",
|
||||
"sha256:d6e1bc5c94873bec742afe2dfadce0d20445b18e75c47afc0c115b19e5dd38dd",
|
||||
"sha256:ea83bcd9d6c03248ebd46e71ac313858e0afd5aa2fa81478c0e653242f3eb476",
|
||||
"sha256:ed5761b37615a1f222c5345bbf45272ae2cf8c7dff88a4f53a1e9f977cbb6d95",
|
||||
"sha256:f011cd0062e54658b7086a76f8cf0f4222812acc66e219e196ea2d0a8849d0ed",
|
||||
"sha256:f1add21b6d179179b3c177c33d18a2186a09cc0d3af41ff5ed3f377360b869f2",
|
||||
"sha256:f655addaaaa9974108d4808f4150652589cada96074c87115c52e575bfcd87d5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.9.6"
|
||||
"version": "==3.9.7"
|
||||
},
|
||||
"pycryptodomex": {
|
||||
"hashes": [
|
||||
"sha256:04646e40ef5788bad6d415e52862ffcdf2ac2d888ba4a5c82d5cb44607a042f7",
|
||||
"sha256:132f1e5fa84921f25695a313a6d4988847dfaee7fb1fd0d1fbe03ef678836f58",
|
||||
"sha256:17ad1ebaa00806305d34550fe5d3c776e38a27b8a2678dfb7871ef0209d64e46",
|
||||
"sha256:27736fa02a2d3502e1ca4b150457e56ce3b98f132462f540073498884e5f8975",
|
||||
"sha256:38050b3fd86c74c6c79e40bbe824bec6431c3e4e36f6080ed544673ba2dc133a",
|
||||
"sha256:3b9306b360bddbc8e098b16eab7adacf49389d212db9c3739588ab840a1ca868",
|
||||
"sha256:466e36ba74a7e725625e717fad3f36e0b9293c247b7d0439c66528026ef2834f",
|
||||
"sha256:4f77360b23a21db32a4c35dacffac33dc30ac6a5a77162a34e99ab11ab631516",
|
||||
"sha256:5002388178845683c330a02f4faeddfe7cd477b87824987cca4718fa0c4f2085",
|
||||
"sha256:51be76756abfc1ddc97e1e2e3c38f4e62fb940161162368308ea9e5919e86c34",
|
||||
"sha256:544628ae67d61c31c28a60e621dadd738b303c5266492355d5ebdb6e7dd1e78f",
|
||||
"sha256:6ff9d4a06bc40211eee05cd88436740d698a01233f4aaff9eb70d8a90e578966",
|
||||
"sha256:718329c6ca60260f1c27b8392e372dd51e4e691f7dcb88adc53eb3b76af6363c",
|
||||
"sha256:918bc5a0170fe8ed7b72f202245b34f84a1997f5ca1520b9c7db71126e5acd62",
|
||||
"sha256:a8ea72adde0d010f89abece5f024b1be95a5c52472e9a57b3ac7d59aee3c8238",
|
||||
"sha256:a979d2c7bcc67282b7ec2600db384c63d37d74e250edb99168483605a380bf62",
|
||||
"sha256:b350f9ad09b692aed57e669fc3f8cf918557fae9f0229c6ce9286a6fe8c1b60f",
|
||||
"sha256:be838abc8557a21a60d453c5a4e64c738966b8a0b7d7f8f97eb8bb44041ca452",
|
||||
"sha256:bfa99692d3c8f994c5850cc8a894cba001abd76d34069a8bfaad173dd46387d6",
|
||||
"sha256:c021b66f5b3c4ea0c45422ec3241bfea4a16651e1ee5459a136639d0716ccb3c",
|
||||
"sha256:c7babb64484080057a24c74a82dbf7997904b1710b74caf62e261610f989b437",
|
||||
"sha256:c96b7762b601dc8a58d7712235c3c152868116f58a7ffa40dcd1c6f6cd97405e",
|
||||
"sha256:d67b6e0bae0777a2c6c83275fbd7cbf53cd5f23c2028f908b0f7d996466e5b15",
|
||||
"sha256:e15f39fcfb949cfd5536cc9647daba942b1a99b67e4d7211e3bdbcedbc2f823c",
|
||||
"sha256:e380448f1e39736f6230ec284cd6d771956ad802d6ce5bc56947a2481080cac1",
|
||||
"sha256:e5236f2171b21e704d1854fd809a7228eb22e29c894af31459e41986e6a53f87",
|
||||
"sha256:ea7b48ce8dbbc86ebadcfe56ebc10d413bdd12c9a5ff0b9147a41993f12b80b3",
|
||||
"sha256:f39f5b58d8fe348ed604bb44a89ca93b26130c275db2b249f718f1538cb70500",
|
||||
"sha256:f545f776e45f74c41329e4020463fdd4d0cd0a7501bdf9e50251dafe7bd959a9",
|
||||
"sha256:f667ac7ae29c19530f199854635f1a97e73d0bfd24163e0db6bdba7dba04eb9f"
|
||||
"sha256:1537d2d15b604b303aef56e7f440895a1c81adbee786b91f1f06eddc34da5314",
|
||||
"sha256:1d20ab8369b7558168fc014a0745c678613f9f486dae468cca2d68145196b8a4",
|
||||
"sha256:1ecc9db7409db67765eb008e558879d298406642d33ade43a6488224d23e8081",
|
||||
"sha256:37033976f72af829fe15f7fe5fe1dbed308cc43a98d9dd9d2a0a76de8ca5ee78",
|
||||
"sha256:3c3dd9d4c9c1e279d3945ae422895c901f98987333acc132dc094faf52afec35",
|
||||
"sha256:3c9b3fba037ea52c626060c5a87ee6de7e86c99e8a7c6ee07302539985d2bd64",
|
||||
"sha256:45ee555fc5e28c119a46d44ce373f5237e54a35c61b750fb3a94446b09855dbc",
|
||||
"sha256:4c93038ac011b36512cb0bf2ee3e2aec774e8bc81021d015917c89fe02bb0ee5",
|
||||
"sha256:50163324834edd0c9ce3e4512ded3e221c969086e10fdd5d3fdcaadac5e24a78",
|
||||
"sha256:59b0ea9cda5490f924771456912a225d8d9e678891f9f986661af718534719b2",
|
||||
"sha256:5cf306a17cccc327a33cdc3845629fa13f4573a4ec620ed607c79cf6785f2e27",
|
||||
"sha256:5fff8da399af16a1855f58771223acbbdac720b9969cd03fc5013d2e9a7bd9a4",
|
||||
"sha256:68650ce5b9f7152b8283302a4617269f821695a612692640dd247bd12ab21c0b",
|
||||
"sha256:6b3a9a562688996f760b5077714c3ab8b62ca56061b6e9ab7906841e43e19f91",
|
||||
"sha256:7e938ed51a59e29431ea86fab60423ada2757728db0f78952329fa02a789bd31",
|
||||
"sha256:87aa70daad6f039e814790a06422a3189311198b674b62f13933a2bdcb6b1bcc",
|
||||
"sha256:99be3a1df2b2b9f731ebe1c264a2c07c465e71cee68e35e1640b645b5213a755",
|
||||
"sha256:a3f2908666e6f74b8c4893f86dd02e16170f50e4a78ae7f3468b6208d54bc205",
|
||||
"sha256:ae3d44a639fd11dbdeca47e35e94febb1ee8bc15daf26673331add37146e0b85",
|
||||
"sha256:afb4c2fa3c6f492fd9a8b38d76e13f32d429b8e5e1e00238309391b5591cde0d",
|
||||
"sha256:b1515ce3a8a2c3fa537d137c5ca5f8b7a902044d04e07d7c3aa26c3e026120fb",
|
||||
"sha256:bf391b377413a197000b43ef2b74359974d8927d329a897c9f5ba7b63dca7b9c",
|
||||
"sha256:c436919117c23355740c669f89720673578b9aa4569bbfe105f6c10101fc1966",
|
||||
"sha256:d2c3c280975638e2a2c2fd9cb36ab111980219757fa163a2755594b9448e4138",
|
||||
"sha256:e585d530764c459cbd5d460aed0288807bb881f376ca9a20e653645217895961",
|
||||
"sha256:e76e6638ead4a7d93262a24218f0ff3ff74de6b6c823b7e19dccb31b6a481978",
|
||||
"sha256:ebfc2f885cafda076c31ae30fa0dd81e7e919ec34059a88d3018ed66e83fcce3",
|
||||
"sha256:f5797a39933a3d41526da60856735e6684b2b71a8ca99d5f79555ca121be2f4b",
|
||||
"sha256:f7e5fc5e124200b19a14be33fb0099e956e6ebb5e25d287b0829ef0a78ed76c7",
|
||||
"sha256:fb350e31e55211fec8ddc89fc0256f3b9bc3b44b68a8bde1cf44b3b4e80c0e42"
|
||||
],
|
||||
"version": "==3.9.6"
|
||||
"version": "==3.9.7"
|
||||
},
|
||||
"pyjwkest": {
|
||||
"hashes": [
|
||||
@ -614,16 +613,16 @@
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
|
||||
"sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
|
||||
"sha256:67199f0c41a9c702154efb0e7a8cc08accf830eb003b4d9fa42c4059002e2492",
|
||||
"sha256:700d17888d441604b0bd51535908dcb297561b040819cccde647a92439db5a2a"
|
||||
],
|
||||
"version": "==2.4.6"
|
||||
"version": "==3.0.0a1"
|
||||
},
|
||||
"pyrsistent": {
|
||||
"hashes": [
|
||||
"sha256:cdc7b5e3ed77bed61270a47d35434a30617b9becdf2478af76ad2c6ade307280"
|
||||
"sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3"
|
||||
],
|
||||
"version": "==0.15.7"
|
||||
"version": "==0.16.0"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
@ -673,20 +672,20 @@
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
|
||||
"sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf",
|
||||
"sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5",
|
||||
"sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e",
|
||||
"sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811",
|
||||
"sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e",
|
||||
"sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d",
|
||||
"sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20",
|
||||
"sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689",
|
||||
"sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994",
|
||||
"sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"
|
||||
"sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
|
||||
"sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
|
||||
"sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
|
||||
"sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
|
||||
"sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
|
||||
"sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
|
||||
"sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
|
||||
"sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
|
||||
"sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
|
||||
"sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
|
||||
"sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.3"
|
||||
"version": "==5.3.1"
|
||||
},
|
||||
"qrcode": {
|
||||
"hashes": [
|
||||
@ -705,10 +704,10 @@
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
||||
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
||||
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
||||
],
|
||||
"version": "==2.22.0"
|
||||
"version": "==2.23.0"
|
||||
},
|
||||
"requests-oauthlib": {
|
||||
"hashes": [
|
||||
@ -759,11 +758,11 @@
|
||||
},
|
||||
"sentry-sdk": {
|
||||
"hashes": [
|
||||
"sha256:b06dd27391fd11fb32f84fe054e6a64736c469514a718a99fb5ce1dff95d6b28",
|
||||
"sha256:e023da07cfbead3868e1e2ba994160517885a32dfd994fc455b118e37989479b"
|
||||
"sha256:23808d571d2461a4ce3784ec12bbee5bdb8c026c143fe79d36cef8a6d653e71f",
|
||||
"sha256:bb90a4e19c7233a580715fc986cc44be2c48fc10b31e71580a2037e1c94b6950"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.14.1"
|
||||
"version": "==0.14.3"
|
||||
},
|
||||
"service-identity": {
|
||||
"hashes": [
|
||||
@ -790,10 +789,10 @@
|
||||
},
|
||||
"sqlparse": {
|
||||
"hashes": [
|
||||
"sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177",
|
||||
"sha256:7c3dca29c022744e95b547e867cee89f4fce4373f3549ccd8797d8eb52cdb873"
|
||||
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
|
||||
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
|
||||
],
|
||||
"version": "==0.3.0"
|
||||
"version": "==0.3.1"
|
||||
},
|
||||
"structlog": {
|
||||
"hashes": [
|
||||
@ -805,11 +804,11 @@
|
||||
},
|
||||
"swagger-spec-validator": {
|
||||
"hashes": [
|
||||
"sha256:57e29feb3aa921a9fb98bd70af148746b27c77d3207266f5571cebcce211e685",
|
||||
"sha256:62ef22eca3f429d93fddda5d793d2a1a9057d3732e7a14606e641805326ae4a6"
|
||||
"sha256:61f2d2a732b886cf33c2c24886565be9692e5814cacc17fd973e095b72b33e4f",
|
||||
"sha256:8eb82682871f8d63067b455e2e055c8dd953ca260e791635b58dfe0b73ba1f43"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.4.3"
|
||||
"version": "==2.5.0"
|
||||
},
|
||||
"uritemplate": {
|
||||
"hashes": [
|
||||
@ -848,10 +847,10 @@
|
||||
},
|
||||
"asgiref": {
|
||||
"hashes": [
|
||||
"sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0",
|
||||
"sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5"
|
||||
"sha256:8036f90603c54e93521e5777b2b9a39ba1bad05773fcf2d208f0299d1df58ce5",
|
||||
"sha256:9ca8b952a0a9afa61d30aa6d3d9b570bb3fd6bafcf7ec9e6bed43b936133db1c"
|
||||
],
|
||||
"version": "==3.2.3"
|
||||
"version": "==3.2.7"
|
||||
},
|
||||
"astroid": {
|
||||
"hashes": [
|
||||
@ -869,10 +868,10 @@
|
||||
},
|
||||
"autopep8": {
|
||||
"hashes": [
|
||||
"sha256:0f592a0447acea0c2b0a9602be1e4e3d86db52badd2e3c84f0193bfd89fd3a43"
|
||||
"sha256:cc6be1dfd46f2c7fa00e84a357f1a269683985b09eaffb47654ed551194399eb"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.5"
|
||||
"version": "==1.5.1"
|
||||
},
|
||||
"bandit": {
|
||||
"hashes": [
|
||||
@ -900,10 +899,10 @@
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
||||
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
||||
"sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc",
|
||||
"sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"
|
||||
],
|
||||
"version": "==7.0"
|
||||
"version": "==7.1.1"
|
||||
},
|
||||
"colorama": {
|
||||
"hashes": [
|
||||
@ -915,48 +914,48 @@
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3",
|
||||
"sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c",
|
||||
"sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0",
|
||||
"sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477",
|
||||
"sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a",
|
||||
"sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf",
|
||||
"sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691",
|
||||
"sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73",
|
||||
"sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987",
|
||||
"sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894",
|
||||
"sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e",
|
||||
"sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef",
|
||||
"sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf",
|
||||
"sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68",
|
||||
"sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8",
|
||||
"sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954",
|
||||
"sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2",
|
||||
"sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40",
|
||||
"sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc",
|
||||
"sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc",
|
||||
"sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e",
|
||||
"sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d",
|
||||
"sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f",
|
||||
"sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc",
|
||||
"sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301",
|
||||
"sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea",
|
||||
"sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb",
|
||||
"sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af",
|
||||
"sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52",
|
||||
"sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37",
|
||||
"sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0"
|
||||
"sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0",
|
||||
"sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30",
|
||||
"sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b",
|
||||
"sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0",
|
||||
"sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823",
|
||||
"sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe",
|
||||
"sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037",
|
||||
"sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6",
|
||||
"sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31",
|
||||
"sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd",
|
||||
"sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892",
|
||||
"sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1",
|
||||
"sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78",
|
||||
"sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac",
|
||||
"sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006",
|
||||
"sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014",
|
||||
"sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2",
|
||||
"sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7",
|
||||
"sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8",
|
||||
"sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7",
|
||||
"sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9",
|
||||
"sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1",
|
||||
"sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307",
|
||||
"sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a",
|
||||
"sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435",
|
||||
"sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0",
|
||||
"sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5",
|
||||
"sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441",
|
||||
"sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732",
|
||||
"sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de",
|
||||
"sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.0.3"
|
||||
"version": "==5.0.4"
|
||||
},
|
||||
"django": {
|
||||
"hashes": [
|
||||
"sha256:2f1ba1db8648484dd5c238fb62504777b7ad090c81c5f1fd8d5eb5ec21b5f283",
|
||||
"sha256:c91c91a7ad6ef67a874a4f76f58ba534f9208412692a840e1d125eb5c279cb0a"
|
||||
"sha256:642d8eceab321ca743ae71e0f985ff8fdca59f07aab3a9fb362c617d23e33a76",
|
||||
"sha256:d4666c2edefa38c5ede0ec1655424c56dc47ceb04b6d8d62a7eac09db89545c1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.3"
|
||||
"version": "==3.0.5"
|
||||
},
|
||||
"django-debug-toolbar": {
|
||||
"hashes": [
|
||||
@ -966,19 +965,19 @@
|
||||
"index": "pypi",
|
||||
"version": "==2.2"
|
||||
},
|
||||
"gitdb2": {
|
||||
"gitdb": {
|
||||
"hashes": [
|
||||
"sha256:0375d983fd887d03c8942e81b1b0abc6c320cfb500cd3fe0d9c0eac87fbf2b52",
|
||||
"sha256:b2b3a67090c17dc61f8407ca485e79ae811225ab5ebcd98ac5ee01448e8987b5"
|
||||
"sha256:284a6a4554f954d6e737cddcff946404393e030b76a282c6640df8efd6b3da5e",
|
||||
"sha256:598e0096bb3175a0aab3a0b5aedaa18a9a25c6707e0eca0695ba1a0baf1b2150"
|
||||
],
|
||||
"version": "==3.0.2"
|
||||
"version": "==4.0.2"
|
||||
},
|
||||
"gitpython": {
|
||||
"hashes": [
|
||||
"sha256:620b3c729bbc143b498cfea77e302999deedc55faec5b1067086c9ef90e101bc",
|
||||
"sha256:a43a5d88a5bbc3cf32bb5223e4b4e68fd716db5e9996cad6e561bbfee6e5f4af"
|
||||
"sha256:43da89427bdf18bf07f1164c6d415750693b4d50e28fc9b68de706245147b9dd",
|
||||
"sha256:e426c3b587bd58c482f0b7fe6145ff4ac7ae6c82673fc656f489719abca6f4cb"
|
||||
],
|
||||
"version": "==3.0.8"
|
||||
"version": "==3.1.0"
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
@ -1022,17 +1021,17 @@
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424",
|
||||
"sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"
|
||||
"sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
|
||||
"sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
|
||||
],
|
||||
"version": "==0.7.0"
|
||||
"version": "==0.8.0"
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
"sha256:139d2625547dbfa5fb0b81daebb39601c478c21956dc57e2e07b74450a8c506b",
|
||||
"sha256:61aa52a0f18b71c5cc58232d2cf8f8d09cd67fcad60b742a60124cb8d6951488"
|
||||
"sha256:07f558fece33b05caf857474a366dfcc00562bca13dd8b47b2b3e22d9f9bf55c",
|
||||
"sha256:579170e23f8e0c2f24b0de612f71f648eccb79fb1322c814ae6b3c07b5ba23e8"
|
||||
],
|
||||
"version": "==5.4.4"
|
||||
"version": "==5.4.5"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
@ -1051,11 +1050,11 @@
|
||||
},
|
||||
"pylint-django": {
|
||||
"hashes": [
|
||||
"sha256:440beb814464928aedd2e21196bb6e47a83b63e2cbe886a701ba0f4a64206bbb",
|
||||
"sha256:d5d113605a64cf0e638b707d4cb42106e626f8851bc30a44d5b22bd698ad8483"
|
||||
"sha256:3a4cc19dd6301fc2d36c9fb6e15163001a6d12723c1f7f8c2249223c2a8c68f0",
|
||||
"sha256:c9bbcff6b87ee8466fae274fd7aae3d2d3d4c4d1ea20c48cbce673e837e36048"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.13"
|
||||
"version": "==2.0.14"
|
||||
},
|
||||
"pylint-plugin-utils": {
|
||||
"hashes": [
|
||||
@ -1073,46 +1072,46 @@
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
|
||||
"sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf",
|
||||
"sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5",
|
||||
"sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e",
|
||||
"sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811",
|
||||
"sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e",
|
||||
"sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d",
|
||||
"sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20",
|
||||
"sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689",
|
||||
"sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994",
|
||||
"sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"
|
||||
"sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
|
||||
"sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
|
||||
"sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
|
||||
"sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
|
||||
"sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
|
||||
"sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
|
||||
"sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
|
||||
"sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
|
||||
"sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
|
||||
"sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
|
||||
"sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.3"
|
||||
"version": "==5.3.1"
|
||||
},
|
||||
"regex": {
|
||||
"hashes": [
|
||||
"sha256:07b39bf943d3d2fe63d46281d8504f8df0ff3fe4c57e13d1656737950e53e525",
|
||||
"sha256:0932941cdfb3afcbc26cc3bcf7c3f3d73d5a9b9c56955d432dbf8bbc147d4c5b",
|
||||
"sha256:0e182d2f097ea8549a249040922fa2b92ae28be4be4895933e369a525ba36576",
|
||||
"sha256:10671601ee06cf4dc1bc0b4805309040bb34c9af423c12c379c83d7895622bb5",
|
||||
"sha256:23e2c2c0ff50f44877f64780b815b8fd2e003cda9ce817a7fd00dea5600c84a0",
|
||||
"sha256:26ff99c980f53b3191d8931b199b29d6787c059f2e029b2b0c694343b1708c35",
|
||||
"sha256:27429b8d74ba683484a06b260b7bb00f312e7c757792628ea251afdbf1434003",
|
||||
"sha256:3e77409b678b21a056415da3a56abfd7c3ad03da71f3051bbcdb68cf44d3c34d",
|
||||
"sha256:4e8f02d3d72ca94efc8396f8036c0d3bcc812aefc28ec70f35bb888c74a25161",
|
||||
"sha256:4eae742636aec40cf7ab98171ab9400393360b97e8f9da67b1867a9ee0889b26",
|
||||
"sha256:6a6ae17bf8f2d82d1e8858a47757ce389b880083c4ff2498dba17c56e6c103b9",
|
||||
"sha256:6a6ba91b94427cd49cd27764679024b14a96874e0dc638ae6bdd4b1a3ce97be1",
|
||||
"sha256:7bcd322935377abcc79bfe5b63c44abd0b29387f267791d566bbb566edfdd146",
|
||||
"sha256:98b8ed7bb2155e2cbb8b76f627b2fd12cf4b22ab6e14873e8641f266e0fb6d8f",
|
||||
"sha256:bd25bb7980917e4e70ccccd7e3b5740614f1c408a642c245019cff9d7d1b6149",
|
||||
"sha256:d0f424328f9822b0323b3b6f2e4b9c90960b24743d220763c7f07071e0778351",
|
||||
"sha256:d58e4606da2a41659c84baeb3cfa2e4c87a74cec89a1e7c56bee4b956f9d7461",
|
||||
"sha256:e3cd21cc2840ca67de0bbe4071f79f031c81418deb544ceda93ad75ca1ee9f7b",
|
||||
"sha256:e6c02171d62ed6972ca8631f6f34fa3281d51db8b326ee397b9c83093a6b7242",
|
||||
"sha256:e7c7661f7276507bce416eaae22040fd91ca471b5b33c13f8ff21137ed6f248c",
|
||||
"sha256:ecc6de77df3ef68fee966bb8cb4e067e84d4d1f397d0ef6fce46913663540d77"
|
||||
"sha256:08119f707f0ebf2da60d2f24c2f39ca616277bb67ef6c92b72cbf90cbe3a556b",
|
||||
"sha256:0ce9537396d8f556bcfc317c65b6a0705320701e5ce511f05fc04421ba05b8a8",
|
||||
"sha256:1cbe0fa0b7f673400eb29e9ef41d4f53638f65f9a2143854de6b1ce2899185c3",
|
||||
"sha256:2294f8b70e058a2553cd009df003a20802ef75b3c629506be20687df0908177e",
|
||||
"sha256:23069d9c07e115537f37270d1d5faea3e0bdded8279081c4d4d607a2ad393683",
|
||||
"sha256:24f4f4062eb16c5bbfff6a22312e8eab92c2c99c51a02e39b4eae54ce8255cd1",
|
||||
"sha256:295badf61a51add2d428a46b8580309c520d8b26e769868b922750cf3ce67142",
|
||||
"sha256:2a3bf8b48f8e37c3a40bb3f854bf0121c194e69a650b209628d951190b862de3",
|
||||
"sha256:4385f12aa289d79419fede43f979e372f527892ac44a541b5446617e4406c468",
|
||||
"sha256:5635cd1ed0a12b4c42cce18a8d2fb53ff13ff537f09de5fd791e97de27b6400e",
|
||||
"sha256:5bfed051dbff32fd8945eccca70f5e22b55e4148d2a8a45141a3b053d6455ae3",
|
||||
"sha256:7e1037073b1b7053ee74c3c6c0ada80f3501ec29d5f46e42669378eae6d4405a",
|
||||
"sha256:90742c6ff121a9c5b261b9b215cb476eea97df98ea82037ec8ac95d1be7a034f",
|
||||
"sha256:a58dd45cb865be0ce1d5ecc4cfc85cd8c6867bea66733623e54bd95131f473b6",
|
||||
"sha256:c087bff162158536387c53647411db09b6ee3f9603c334c90943e97b1052a156",
|
||||
"sha256:c162a21e0da33eb3d31a3ac17a51db5e634fc347f650d271f0305d96601dc15b",
|
||||
"sha256:c9423a150d3a4fc0f3f2aae897a59919acd293f4cb397429b120a5fcd96ea3db",
|
||||
"sha256:ccccdd84912875e34c5ad2d06e1989d890d43af6c2242c6fcfa51556997af6cd",
|
||||
"sha256:e91ba11da11cf770f389e47c3f5c30473e6d85e06d7fd9dcba0017d2867aab4a",
|
||||
"sha256:ea4adf02d23b437684cd388d557bf76e3afa72f7fed5bbc013482cc00c816948",
|
||||
"sha256:fb95debbd1a824b2c4376932f2216cc186912e389bdb0e27147778cf6acb3f89"
|
||||
],
|
||||
"version": "==2020.1.8"
|
||||
"version": "==2020.4.4"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
@ -1121,19 +1120,19 @@
|
||||
],
|
||||
"version": "==1.14.0"
|
||||
},
|
||||
"smmap2": {
|
||||
"smmap": {
|
||||
"hashes": [
|
||||
"sha256:0555a7bf4df71d1ef4218e4807bbf9b201f910174e6e08af2e138d4e517b4dde",
|
||||
"sha256:29a9ffa0497e7f2be94ca0ed1ca1aa3cd4cf25a1f6b4f5f87f74b46ed91d609a"
|
||||
"sha256:171484fe62793e3626c8b05dd752eb2ca01854b0c55a1efc0dc4210fccb65446",
|
||||
"sha256:5fead614cf2de17ee0707a8c6a5f2aa5a2fc6c698c70993ba42f515485ffda78"
|
||||
],
|
||||
"version": "==2.0.5"
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"sqlparse": {
|
||||
"hashes": [
|
||||
"sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177",
|
||||
"sha256:7c3dca29c022744e95b547e867cee89f4fce4373f3549ccd8797d8eb52cdb873"
|
||||
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
|
||||
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
|
||||
],
|
||||
"version": "==0.3.0"
|
||||
"version": "==0.3.1"
|
||||
},
|
||||
"stevedore": {
|
||||
"hashes": [
|
||||
@ -1177,11 +1176,11 @@
|
||||
},
|
||||
"unittest-xml-reporting": {
|
||||
"hashes": [
|
||||
"sha256:6584562cde8226fc79fa29e38903c669a02799074a563bb0b70fcd3a8e87829c",
|
||||
"sha256:dd8046a64dc62f3d30301523a54992e0be75a945194491e0a3b718130cb429e0"
|
||||
"sha256:74eaf7739a7957a74f52b8187c5616f61157372189bef0a32ba5c30bbc00e58a",
|
||||
"sha256:e09b8ae70cce9904cdd331f53bf929150962869a5324ab7ff3dd6c8b87e01f7d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.1"
|
||||
"version": "==3.0.2"
|
||||
},
|
||||
"wrapt": {
|
||||
"hashes": [
|
||||
|
||||
@ -23,6 +23,8 @@ services:
|
||||
server:
|
||||
image: beryju/passbook:${SERVER_TAG:-latest}
|
||||
command:
|
||||
- ./manage.py
|
||||
- bootstrap
|
||||
- uwsgi
|
||||
- uwsgi.ini
|
||||
environment:
|
||||
@ -42,6 +44,8 @@ services:
|
||||
worker:
|
||||
image: beryju/passbook:${SERVER_TAG:-latest}
|
||||
command:
|
||||
- ./manage.py
|
||||
- bootstrap
|
||||
- celery
|
||||
- worker
|
||||
- --autoscale=10,3
|
||||
|
||||
@ -12,6 +12,8 @@ The following objects are passed into the variable:
|
||||
- `request.obj`: A Django Model instance. This is only set if the Policy is ran against an object.
|
||||
- `pb_is_sso_flow`: Boolean which is true if request was initiated by authenticating through an external Provider.
|
||||
- `pb_is_group_member(user, group_name)`: Function which checks if `user` is member of a Group with Name `gorup_name`.
|
||||
- `pb_logger`: Standard Python Logger Object, which can be used to debug expressions.
|
||||
- `pb_client_ip`: Client's IP Address.
|
||||
|
||||
There are also the following custom filters available:
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
apiVersion: v1
|
||||
appVersion: "0.8.6-beta"
|
||||
appVersion: "0.8.15-beta"
|
||||
description: A Helm chart for passbook.
|
||||
name: passbook
|
||||
version: "0.8.6-beta"
|
||||
version: "0.8.15-beta"
|
||||
icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png
|
||||
|
||||
@ -25,19 +25,19 @@ spec:
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 10
|
||||
timeoutSeconds: 5
|
||||
httpGet:
|
||||
path: /-/ping
|
||||
path: /
|
||||
port: http
|
||||
readinessProbe:
|
||||
initialDelaySeconds: 10
|
||||
timeoutSeconds: 5
|
||||
httpGet:
|
||||
path: /-/ping
|
||||
path: /
|
||||
port: http
|
||||
resources:
|
||||
requests:
|
||||
|
||||
@ -11,7 +11,7 @@ metadata:
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8080
|
||||
- port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
|
||||
@ -18,7 +18,7 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
passbook.io/component: web
|
||||
k8s.passbook.io/component: web
|
||||
spec:
|
||||
volumes:
|
||||
- name: config-volume
|
||||
@ -27,9 +27,12 @@ spec:
|
||||
initContainers:
|
||||
- name: passbook-database-migrations
|
||||
image: "beryju/passbook:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: Always
|
||||
command:
|
||||
- ./manage.py
|
||||
args:
|
||||
- bootstrap
|
||||
- ./manage.py
|
||||
- migrate
|
||||
volumeMounts:
|
||||
- mountPath: /etc/passbook
|
||||
@ -57,10 +60,12 @@ spec:
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "beryju/passbook:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
imagePullPolicy: Always
|
||||
command:
|
||||
- uwsgi
|
||||
- ./manage.py
|
||||
args:
|
||||
- bootstrap
|
||||
- uwsgi
|
||||
- uwsgi.ini
|
||||
volumeMounts:
|
||||
- mountPath: /etc/passbook
|
||||
|
||||
@ -18,4 +18,4 @@ spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
passbook.io/component: web
|
||||
k8s.passbook.io/component: web
|
||||
|
||||
@ -18,7 +18,7 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "passbook.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
passbook.io/component: worker
|
||||
k8s.passbook.io/component: worker
|
||||
spec:
|
||||
volumes:
|
||||
- name: config-volume
|
||||
@ -29,8 +29,10 @@ spec:
|
||||
image: "beryju/passbook:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- celery
|
||||
- ./manage.py
|
||||
args:
|
||||
- bootstrap
|
||||
- celery
|
||||
- worker
|
||||
- --autoscale=10,3
|
||||
- -E
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
image:
|
||||
tag: 0.8.6-beta
|
||||
tag: 0.8.15-beta
|
||||
|
||||
nameOverride: ""
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ nav:
|
||||
- Sentry: integrations/services/sentry/index.md
|
||||
- Ansible Tower/AWX: integrations/services/tower-awx/index.md
|
||||
|
||||
repo_name: "BeryJu.org/passbook"
|
||||
repo_name: "BeryJu/passbook"
|
||||
repo_url: https://github.com/BeryJu/passbook
|
||||
theme:
|
||||
name: "material"
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook"""
|
||||
__version__ = "0.8.6-beta"
|
||||
__version__ = "0.8.15-beta"
|
||||
|
||||
@ -48,7 +48,7 @@ class YAMLField(forms.CharField):
|
||||
def prepare_value(self, value):
|
||||
if isinstance(value, InvalidYAMLInput):
|
||||
return value
|
||||
return yaml.dump(value, explicit_start=True)
|
||||
return yaml.dump(value, explicit_start=True, default_flow_style=False)
|
||||
|
||||
def has_changed(self, initial, data):
|
||||
if super().has_changed(initial, data):
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
"""passbook form helpers"""
|
||||
from django import forms
|
||||
|
||||
from passbook.admin.fields import YAMLField
|
||||
|
||||
|
||||
class TagModelForm(forms.ModelForm):
|
||||
"""Base form for models that have attributes"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Check if we have an instance, load tags otherwise use an empty dict
|
||||
instance = kwargs.get("instance", None)
|
||||
tags = instance.tags if instance else {}
|
||||
# Make sure all predefined tags exist in tags, and set default if they don't
|
||||
predefined_tags = (
|
||||
self._meta.model().get_predefined_tags() # pylint: disable=no-member
|
||||
)
|
||||
for key, value in predefined_tags.items():
|
||||
if key not in tags:
|
||||
tags[key] = value
|
||||
# Format JSON
|
||||
kwargs["initial"]["tags"] = tags
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def clean_tags(self):
|
||||
"""Make sure all required tags are set"""
|
||||
if hasattr(self.instance, "get_required_keys") and hasattr(
|
||||
self.instance, "tags"
|
||||
):
|
||||
for key in self.instance.get_required_keys():
|
||||
if key not in self.cleaned_data.get("tags"):
|
||||
raise forms.ValidationError("Tag %s missing." % key)
|
||||
return self.cleaned_data.get("tags")
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class TagModelFormMeta:
|
||||
"""Base Meta class that uses the YAMLField"""
|
||||
|
||||
field_classes = {"tags": YAMLField}
|
||||
@ -1,7 +1,4 @@
|
||||
"""passbook core source form fields"""
|
||||
# from django import forms
|
||||
|
||||
SOURCE_FORM_FIELDS = ["name", "slug", "enabled", "policies"]
|
||||
SOURCE_SERIALIZER_FIELDS = ["pk", "name", "slug", "enabled", "policies"]
|
||||
|
||||
# class SourceForm(forms.Form)
|
||||
SOURCE_FORM_FIELDS = ["name", "slug", "enabled"]
|
||||
SOURCE_SERIALIZER_FIELDS = ["pk", "name", "slug", "enabled"]
|
||||
|
||||
@ -1,66 +1,68 @@
|
||||
{% extends "administration/base.html" %}
|
||||
{% extends "base/page.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-catalog"></i>
|
||||
{% trans 'Audit Log' %}
|
||||
</h1>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% block page_content %}
|
||||
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-catalog"></i>
|
||||
{% trans 'Audit Log' %}
|
||||
</h1>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Action' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Context' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'User' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Creation Date' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Client IP' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for entry in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ entry.action }}</div>
|
||||
<small>{{ entry.app|default:'-' }}</small>
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<code>{{ entry.context }}</code>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ entry.user }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ entry.created }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ entry.client_ip }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Action' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Context' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'User' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Creation Date' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Client IP' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for entry in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ entry.action }}</div>
|
||||
<small>{{ entry.app|default:'-' }}</small>
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<code>{{ entry.context }}</code>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ entry.user }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ entry.created }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ entry.client_ip }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
{% extends "overview/base.html" %}
|
||||
{% extends "base/page.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% load is_active %}
|
||||
{% load utils %}
|
||||
|
||||
{% block head %}
|
||||
{{ block.super }}
|
||||
@ -12,3 +16,84 @@
|
||||
<script src="{% static 'node_modules/codemirror/mode/yaml/yaml.js' %}"></script>
|
||||
<script src="{% static 'node_modules/codemirror/mode/jinja2/jinja2.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="pf-c-page__sidebar">
|
||||
<div class="pf-c-page__sidebar-body">
|
||||
<nav class="pf-c-nav" id="page-default-nav-example-primary-nav" aria-label="Global">
|
||||
<ul class="pf-c-nav__list">
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:overview' %}"
|
||||
class="pf-c-nav__link {% is_active_url 'passbook_admin:overview' %}">
|
||||
{% trans 'System Status' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:applications' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:applications' 'passbook_admin:application-create' 'passbook_admin:application-update' 'passbook_admin:application-delete' %}">
|
||||
{% trans 'Applications' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:sources' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:sources' 'passbook_admin:source-create' 'passbook_admin:source-update' 'passbook_admin:source-delete' %}">
|
||||
{% trans 'Sources' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:providers' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:providers' 'passbook_admin:provider-create' 'passbook_admin:provider-update' 'passbook_admin:provider-delete' %}">
|
||||
{% trans 'Providers' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:property-mappings' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:property-mappings' 'passbook_admin:property-mapping-create' 'passbook_admin:property-mapping-update' 'passbook_admin:property-mapping-delete' %}">
|
||||
{% trans 'Property Mappings' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:factors' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:factors' 'passbook_admin:factor-create' 'passbook_admin:factor-update' 'passbook_admin:factor-delete' %}">
|
||||
{% trans 'Factors' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:policies' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:policies' 'passbook_admin:policy-create' 'passbook_admin:policy-update' 'passbook_admin:policy-delete' 'passbook_admin:policy-test' %}">
|
||||
{% trans 'Policies' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:certificate_key_pair' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:certificate_key_pair' 'passbook_admin:certificatekeypair-create' 'passbook_admin:certificatekeypair-update' 'passbook_admin:certificatekeypair-delete' %}">
|
||||
{% trans 'Certificates' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:invitations' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:invitations' 'passbook_admin:invitation-create' 'passbook_admin:invitation-update' 'passbook_admin:invitation-delete' 'passbook_admin:invitation-test' %}">
|
||||
{% trans 'Invitations' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:users' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:users' 'passbook_admin:user-update' 'passbook_admin:user-delete' %}">
|
||||
{% trans 'Users' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:groups' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_admin:groups' 'passbook_admin:group-update' 'passbook_admin:group-delete' %}">
|
||||
{% trans 'Groups' %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-key"></i>
|
||||
{% trans 'Certificate-Key Pairs' %}
|
||||
</h1>
|
||||
<p>{% trans "Import certificates of external providers or create certificates to sign requests with." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
|
||||
<div class="pf-c-toolbar__action-group">
|
||||
<a href="{% url 'passbook_admin:certificatekeypair-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Private Key available' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Fingerprint' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Provider Type' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for kp in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ kp.name }}</div>
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{% if kp.key_data is not None %}
|
||||
{% trans 'Yes' %}
|
||||
{% else %}
|
||||
{% trans 'No' %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ kp.fingerprint }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:certificatekeypair-update' pk=kp.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:certificatekeypair-delete' pk=kp.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@ -52,7 +52,7 @@
|
||||
<small>{% trans 'Warning: Policy is not assigned.' %}</small>
|
||||
{% else %}
|
||||
<i class="pf-icon pf-icon-ok"></i>
|
||||
<small>{% blocktrans with objects=policy.policymodel_set.all|join:', ' %}Assigned to objects {{ objects }}{% endblocktrans %}</small>
|
||||
<small>{% blocktrans with object_count=policy.policymodel_set.all|length %}Assigned to {{ object_count }} objects.{% endblocktrans %}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</th>
|
||||
|
||||
@ -11,16 +11,22 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block beneath_form %}
|
||||
<p class="loading" style="display: none;">
|
||||
<span class="spinner spinner-xs spinner-inline"></span> {% trans 'Processing, please wait...' %}
|
||||
</p>
|
||||
<div class="pf-c-form__group pf-m-action" style="display: none;" id="loading">
|
||||
<div class="pf-c-form__horizontal-group">
|
||||
<span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{{ block.super }}
|
||||
<script>
|
||||
$('form').on('submit', function () {
|
||||
$('p.loading').show();
|
||||
})
|
||||
document.querySelector("form").addEventListener("submit", (e) => {
|
||||
document.getElementById("loading").removeAttribute("style");
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -4,6 +4,7 @@ from django.urls import path
|
||||
from passbook.admin.views import (
|
||||
applications,
|
||||
audit,
|
||||
certificate_key_pair,
|
||||
debug,
|
||||
factors,
|
||||
groups,
|
||||
@ -148,6 +149,27 @@ urlpatterns = [
|
||||
path(
|
||||
"group/<uuid:pk>/delete/", groups.GroupDeleteView.as_view(), name="group-delete"
|
||||
),
|
||||
# Certificate-Key Pairs
|
||||
path(
|
||||
"crypto/certificates/",
|
||||
certificate_key_pair.CertificateKeyPairListView.as_view(),
|
||||
name="certificate_key_pair",
|
||||
),
|
||||
path(
|
||||
"crypto/certificates/create/",
|
||||
certificate_key_pair.CertificateKeyPairCreateView.as_view(),
|
||||
name="certificatekeypair-create",
|
||||
),
|
||||
path(
|
||||
"crypto/certificates/<uuid:pk>/update/",
|
||||
certificate_key_pair.CertificateKeyPairUpdateView.as_view(),
|
||||
name="certificatekeypair-update",
|
||||
),
|
||||
path(
|
||||
"crypto/certificates/<uuid:pk>/delete/",
|
||||
certificate_key_pair.CertificateKeyPairDeleteView.as_view(),
|
||||
name="certificatekeypair-delete",
|
||||
),
|
||||
# Audit Log
|
||||
path("audit/", audit.EventListView.as_view(), name="audit-log"),
|
||||
# Groups
|
||||
|
||||
@ -12,4 +12,4 @@ class EventListView(PermissionListMixin, ListView):
|
||||
template_name = "administration/audit/list.html"
|
||||
permission_required = "passbook_audit.view_event"
|
||||
ordering = "-created"
|
||||
paginate_by = 10
|
||||
paginate_by = 20
|
||||
|
||||
77
passbook/admin/views/certificate_key_pair.py
Normal file
77
passbook/admin/views/certificate_key_pair.py
Normal file
@ -0,0 +1,77 @@
|
||||
"""passbook CertificateKeyPair administration"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic import DeleteView, ListView, UpdateView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
|
||||
from passbook.crypto.forms import CertificateKeyPairForm
|
||||
from passbook.crypto.models import CertificateKeyPair
|
||||
from passbook.lib.views import CreateAssignPermView
|
||||
|
||||
|
||||
class CertificateKeyPairListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
"""Show list of all keypairs"""
|
||||
|
||||
model = CertificateKeyPair
|
||||
permission_required = "passbook_crypto.view_certificatekeypair"
|
||||
ordering = "name"
|
||||
paginate_by = 40
|
||||
template_name = "administration/certificatekeypair/list.html"
|
||||
|
||||
|
||||
class CertificateKeyPairCreateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
):
|
||||
"""Create new CertificateKeyPair"""
|
||||
|
||||
model = CertificateKeyPair
|
||||
form_class = CertificateKeyPairForm
|
||||
permission_required = "passbook_crypto.add_certificatekeypair"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("passbook_admin:certificate_key_pair")
|
||||
success_message = _("Successfully created CertificateKeyPair")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs["type"] = "Certificate-Key Pair"
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class CertificateKeyPairUpdateView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
"""Update certificatekeypair"""
|
||||
|
||||
model = CertificateKeyPair
|
||||
form_class = CertificateKeyPairForm
|
||||
permission_required = "passbook_crypto.change_certificatekeypair"
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("passbook_admin:certificate_key_pair")
|
||||
success_message = _("Successfully updated Certificate-Key Pair")
|
||||
|
||||
|
||||
class CertificateKeyPairDeleteView(
|
||||
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
"""Delete certificatekeypair"""
|
||||
|
||||
model = CertificateKeyPair
|
||||
permission_required = "passbook_crypto.delete_certificatekeypair"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("passbook_admin:certificate_key_pair")
|
||||
success_message = _("Successfully deleted Certificate-Key Pair")
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
return super().delete(request, *args, **kwargs)
|
||||
@ -23,6 +23,8 @@ class InvitationListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
model = Invitation
|
||||
permission_required = "passbook_core.view_invitation"
|
||||
template_name = "administration/invitation/list.html"
|
||||
paginate_by = 10
|
||||
ordering = "-expires"
|
||||
|
||||
|
||||
class InvitationCreateView(
|
||||
|
||||
@ -24,7 +24,8 @@ class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
|
||||
model = Policy
|
||||
permission_required = "passbook_core.view_policy"
|
||||
|
||||
paginate_by = 10
|
||||
ordering = "order"
|
||||
template_name = "administration/policy/list.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@ -34,7 +35,7 @@ class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().order_by("order").select_subclasses()
|
||||
return super().get_queryset().select_subclasses()
|
||||
|
||||
|
||||
class PolicyCreateView(
|
||||
|
||||
@ -22,6 +22,8 @@ class ProviderListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
model = Provider
|
||||
permission_required = "passbook_core.add_provider"
|
||||
template_name = "administration/provider/list.html"
|
||||
paginate_by = 10
|
||||
ordering = "id"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs["types"] = {
|
||||
|
||||
@ -9,7 +9,11 @@ from django.shortcuts import redirect
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic import DeleteView, DetailView, ListView, UpdateView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from guardian.mixins import (
|
||||
PermissionListMixin,
|
||||
PermissionRequiredMixin,
|
||||
get_anonymous_user,
|
||||
)
|
||||
|
||||
from passbook.admin.forms.users import UserForm
|
||||
from passbook.core.models import Nonce, User
|
||||
@ -25,6 +29,9 @@ class UserListView(LoginRequiredMixin, PermissionListMixin, ListView):
|
||||
paginate_by = 40
|
||||
template_name = "administration/user/list.html"
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().exclude(pk=get_anonymous_user().pk)
|
||||
|
||||
|
||||
class UserCreateView(
|
||||
SuccessMessageMixin,
|
||||
|
||||
@ -7,7 +7,7 @@ from rest_framework import routers
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.api.permissions import CustomObjectPermissions
|
||||
from passbook.audit.api.events import EventViewSet
|
||||
from passbook.audit.api import EventViewSet
|
||||
from passbook.core.api.applications import ApplicationViewSet
|
||||
from passbook.core.api.factors import FactorViewSet
|
||||
from passbook.core.api.groups import GroupViewSet
|
||||
|
||||
@ -18,7 +18,7 @@ class EventSerializer(ModelSerializer):
|
||||
"date",
|
||||
"app",
|
||||
"context",
|
||||
"request_ip",
|
||||
"client_ip",
|
||||
"created",
|
||||
]
|
||||
|
||||
@ -33,11 +33,15 @@ def sanitize_dict(source: Dict[Any, Any]) -> Dict[Any, Any]:
|
||||
source[key] = sanitize_dict(value)
|
||||
elif isinstance(value, models.Model):
|
||||
model_content_type = ContentType.objects.get_for_model(value)
|
||||
name = str(value)
|
||||
if hasattr(value, "name"):
|
||||
name = value.name
|
||||
source[key] = sanitize_dict(
|
||||
{
|
||||
"app": model_content_type.app_label,
|
||||
"name": model_content_type.model,
|
||||
"model_name": model_content_type.model,
|
||||
"pk": value.pk,
|
||||
"name": name,
|
||||
}
|
||||
)
|
||||
elif isinstance(value, UUID):
|
||||
@ -133,6 +137,7 @@ class Event(UUIDModel):
|
||||
action=self.action,
|
||||
context=self.context,
|
||||
client_ip=self.client_ip,
|
||||
user=self.user,
|
||||
)
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
@ -34,7 +34,6 @@ def on_user_login_failed(
|
||||
sender, credentials: Dict[str, str], request: HttpRequest, **_
|
||||
):
|
||||
"""Failed Login"""
|
||||
credentials.pop("password")
|
||||
Event.new(EventAction.LOGIN_FAILED, **credentials).from_http(request)
|
||||
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ class PropertyMappingSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
|
||||
model = PropertyMapping
|
||||
fields = ["pk", "name", "__type__"]
|
||||
fields = ["pk", "name", "expression", "__type__"]
|
||||
|
||||
|
||||
class PropertyMappingViewSet(ReadOnlyModelViewSet):
|
||||
|
||||
@ -10,7 +10,8 @@ class ApplicationForm(forms.ModelForm):
|
||||
"""Application Form"""
|
||||
|
||||
provider = forms.ModelChoiceField(
|
||||
queryset=Provider.objects.all().select_subclasses(), required=False
|
||||
queryset=Provider.objects.all().order_by("pk").select_subclasses(),
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
33
passbook/core/migrations/0010_auto_20200221_2208.py
Normal file
33
passbook/core/migrations/0010_auto_20200221_2208.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Generated by Django 3.0.3 on 2020-02-21 22:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_core", "0009_auto_20200221_1410"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="application",
|
||||
name="meta_description",
|
||||
field=models.TextField(blank=True, default=""),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="application",
|
||||
name="meta_icon_url",
|
||||
field=models.TextField(blank=True, default=""),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="application",
|
||||
name="meta_launch_url",
|
||||
field=models.URLField(blank=True, default=""),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="application",
|
||||
name="meta_publisher",
|
||||
field=models.TextField(blank=True, default=""),
|
||||
),
|
||||
]
|
||||
27
passbook/core/migrations/0011_auto_20200222_1822.py
Normal file
27
passbook/core/migrations/0011_auto_20200222_1822.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Generated by Django 3.0.3 on 2020-02-22 18:22
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def fix_application_null(apps, schema_editor):
|
||||
"""Fix Application meta_fields being null"""
|
||||
Application = apps.get_model("passbook_core", "Application")
|
||||
for app in Application.objects.all():
|
||||
if app.meta_launch_url is None:
|
||||
app.meta_launch_url = ""
|
||||
if app.meta_icon_url is None:
|
||||
app.meta_icon_url = ""
|
||||
if app.meta_description is None:
|
||||
app.meta_description = ""
|
||||
if app.meta_publisher is None:
|
||||
app.meta_publisher = ""
|
||||
app.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_core", "0010_auto_20200221_2208"),
|
||||
]
|
||||
|
||||
operations = [migrations.RunPython(fix_application_null)]
|
||||
@ -139,10 +139,10 @@ class Application(ExportModelOperationsMixin("application"), PolicyModel):
|
||||
"Provider", null=True, blank=True, default=None, on_delete=models.SET_DEFAULT
|
||||
)
|
||||
|
||||
meta_launch_url = models.URLField(null=True, blank=True)
|
||||
meta_icon_url = models.TextField(null=True, blank=True)
|
||||
meta_description = models.TextField(null=True, blank=True)
|
||||
meta_publisher = models.TextField(null=True, blank=True)
|
||||
meta_launch_url = models.URLField(default="", blank=True)
|
||||
meta_icon_url = models.TextField(default="", blank=True)
|
||||
meta_description = models.TextField(default="", blank=True)
|
||||
meta_publisher = models.TextField(default="", blank=True)
|
||||
|
||||
objects = InheritanceManager()
|
||||
|
||||
|
||||
@ -18,9 +18,11 @@ password_changed = Signal(providing_args=["user", "password"])
|
||||
def invalidate_policy_cache(sender, instance, **_):
|
||||
"""Invalidate Policy cache when policy is updated"""
|
||||
from passbook.core.models import Policy
|
||||
from passbook.policies.process import cache_key
|
||||
|
||||
if isinstance(instance, Policy):
|
||||
LOGGER.debug("Invalidating policy cache", policy=instance)
|
||||
keys = cache.keys("%s#*" % instance.pk)
|
||||
prefix = cache_key(instance) + "*"
|
||||
keys = cache.keys(prefix)
|
||||
cache.delete_many(keys)
|
||||
LOGGER.debug("Deleted %d keys", len(keys))
|
||||
|
||||
27
passbook/core/templates/403_csrf.html
Normal file
27
passbook/core/templates/403_csrf.html
Normal file
@ -0,0 +1,27 @@
|
||||
{% extends 'login/base.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load utils %}
|
||||
|
||||
{% block card_title %}
|
||||
{{ title }} <span>(403)</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<form>
|
||||
<h3>{{ main }}</h3>
|
||||
{% if no_referer %}
|
||||
<p>{{ no_referer1 }}</p>
|
||||
<p>{{ no_referer2 }}</p>
|
||||
<p>{{ no_referer3 }}</p>
|
||||
{% endif %}
|
||||
{% if no_cookie %}
|
||||
<p>{{ no_cookie1 }}</p>
|
||||
<p>{{ no_cookie2 }}</p>
|
||||
{% endif %}
|
||||
{% if 'back' in request.GET %}
|
||||
<a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
58
passbook/core/templates/base/page.html
Normal file
58
passbook/core/templates/base/page.html
Normal file
@ -0,0 +1,58 @@
|
||||
{% extends "base/skeleton.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% load is_active %}
|
||||
{% load utils %}
|
||||
|
||||
{% block body %}
|
||||
{% include 'partials/messages.html' %}
|
||||
<div class="pf-c-page" id="page-default-nav-example">
|
||||
<a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content">{% trans 'Skip to content' %}</a>
|
||||
<header role="banner" class="pf-c-page__header ws-page-header">
|
||||
<div class="pf-c-page__header-brand">
|
||||
<div class="pf-c-page__header-brand-toggle">
|
||||
<button class="pf-c-button pf-m-plain" type="button" id="page-default-nav-example-nav-toggle"
|
||||
aria-label="Global navigation" aria-expanded="true"
|
||||
aria-controls="page-default-nav-example-primary-nav">
|
||||
<i class="fas fa-bars" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<a class="pf-c-page__header-brand-link">
|
||||
<img class="pf-c-brand" src="{% static 'passbook/logo.png' %}" alt="" />
|
||||
<img class="pf-c-brand" src="{% static 'passbook/brand.svg' %}" alt="passbook" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-page__header-nav">
|
||||
<nav class="pf-c-nav" aria-label="Nav">
|
||||
<ul class="pf-c-nav__horizontal-list ws-top-nav">
|
||||
<li class="pf-c-nav__item"><a class="pf-c-nav__link {% is_active_url 'passbook_core:overview' %}"
|
||||
href="{% url 'passbook_core:overview' %}">{% trans 'Access' %}</a></li>
|
||||
{% if user.is_superuser %}
|
||||
<li class="pf-c-nav__item"><a class="pf-c-nav__link {% is_active_url 'passbook_admin:overview' %}"
|
||||
href="{% url 'passbook_admin:overview' %}">{% trans 'Administrate' %}</a></li>
|
||||
<li class="pf-c-nav__item"><a class="pf-c-nav__link {% is_active_url 'passbook_admin:audit-log' %}"
|
||||
href="{% url 'passbook_admin:audit-log' %}">{% trans 'Monitor' %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="pf-c-page__header-tools">
|
||||
<div class="pf-c-page__header-tools-group pf-m-icons">
|
||||
<a href="{% url 'passbook_core:auth-logout' %}" class="pf-c-button pf-m-plain" type="button">
|
||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-page__header-tools-group">
|
||||
<a href="{% url 'passbook_core:user-settings' %}" class="pf-c-button">
|
||||
{{ user.username }}
|
||||
</a>
|
||||
</div>
|
||||
<img class="pf-c-avatar" src="{% gravatar user.email %}" alt="">
|
||||
</div>
|
||||
</header>
|
||||
{% block page_content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -8,7 +8,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>{% block title %}{% title %}{% endblock %}</title>
|
||||
<title>{% block title %}{% trans title|default:"passbook" %}{% endblock %}</title>
|
||||
<link rel="icon" type="image/png" href="{% static 'passbook/logo.png' %}">
|
||||
<link rel="shortcut icon" type="image/png" href="{% static 'passbook/logo.png' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'node_modules/@patternfly/patternfly/patternfly.css' %}">
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% config 'passbook.branding' as branding %}
|
||||
<!-- HERO -->
|
||||
<tr>
|
||||
<td bgcolor="#7c72dc" align="center" style="padding: 0px 10px 0px 10px;">
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{% config passbook.branding %}</title>
|
||||
<title>passbook</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
@ -118,7 +118,7 @@
|
||||
<!-- ADDRESS -->
|
||||
<tr>
|
||||
<td bgcolor="#1b2a32" align="left" style="padding: 0px 30px 30px 30px; color: #E9ECEF; font-family: 'Metropolis', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;">
|
||||
<p style="margin: 0;"><a href="{% config 'passbook.branding' %}">{% config 'passbook.branding' %}</a></p>
|
||||
<p style="margin: 0;"><a href="passbook">passbook</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
<header class="pf-c-login__header">
|
||||
<img class="pf-c-brand" src="{% static 'passbook/logo.svg' %}" style="height: 60px;"
|
||||
alt="passbook icon" />
|
||||
<img class="pf-c-brand" src="{% static 'passbook/brand.svg' %}" style="height: 80px;"
|
||||
<img class="pf-c-brand" src="{% static 'passbook/brand.svg' %}" style="height: 60px;"
|
||||
alt="passbook branding" />
|
||||
</header>
|
||||
<main class="pf-c-login__main">
|
||||
@ -54,9 +54,10 @@
|
||||
<a href="{{ source.url }}" class="pf-c-login__main-footer-links-item-link">
|
||||
{% if source.icon_path %}
|
||||
<img src="{% static source.icon_path %}" alt="{{ source.name }}">
|
||||
{% endif %}
|
||||
{% if source.icon_url %}
|
||||
{% elif source.icon_url %}
|
||||
<img src="icon_url" alt="{{ source.name }}">
|
||||
{% else %}
|
||||
<i class="pf-icon pf-icon-arrow" title="{{ source.name }}"></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@ -4,27 +4,18 @@
|
||||
{% load i18n %}
|
||||
{% load utils %}
|
||||
|
||||
{% block head %}
|
||||
{{ block.super }}
|
||||
<style>
|
||||
.pf-icon {
|
||||
font-size: 48px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<header class="login-pf-header">
|
||||
<h1>{% trans title %}</h1>
|
||||
</header>
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{% include 'partials/form.html' %}
|
||||
<span class="pf-icon pficon-error-circle-o btn-block"></span>
|
||||
Access denied
|
||||
{% if 'back' in request.GET %}
|
||||
<a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a>
|
||||
{% endif %}
|
||||
<form method="POST" class="pf-c-form">
|
||||
{% csrf_token %}
|
||||
{% include 'partials/form.html' %}
|
||||
<div class="pf-c-form__group">
|
||||
<p>
|
||||
<i class="pf-icon pf-icon-error-circle-o"></i>
|
||||
{% trans 'Access denied' %}
|
||||
</p>
|
||||
</div>
|
||||
{% if 'back' in request.GET %}
|
||||
<a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
{% load utils %}
|
||||
|
||||
{% block title %}
|
||||
{% title title %}
|
||||
{% trans title %}
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base/skeleton.html" %}
|
||||
{% extends "base/page.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
@ -6,120 +6,9 @@
|
||||
{% load is_active %}
|
||||
{% load utils %}
|
||||
|
||||
{% block body %}
|
||||
{% include 'partials/messages.html' %}
|
||||
<div class="pf-c-page" id="page-default-nav-example">
|
||||
<a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content">{% trans 'Skip to content' %}</a>
|
||||
<header role="banner" class="pf-c-page__header">
|
||||
<div class="pf-c-page__header-brand">
|
||||
<div class="pf-c-page__header-brand-toggle">
|
||||
<button class="pf-c-button pf-m-plain" type="button" id="page-default-nav-example-nav-toggle"
|
||||
aria-label="Global navigation" aria-expanded="true"
|
||||
aria-controls="page-default-nav-example-primary-nav">
|
||||
<i class="fas fa-bars" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<a class="pf-c-page__header-brand-link">
|
||||
<img class="pf-c-brand" src="{% static 'passbook/logo.png' %}" alt="" />
|
||||
<img class="pf-c-brand" src="{% static 'passbook/brand.svg' %}" alt="passbook" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-page__header-tools">
|
||||
<div class="pf-c-page__header-tools-group pf-m-icons">
|
||||
<a href="{% url 'passbook_core:auth-logout' %}" class="pf-c-button pf-m-plain" type="button">
|
||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-page__header-tools-group">
|
||||
<a href="{% url 'passbook_core:user-settings' %}" class="pf-c-button">
|
||||
{{ user.username }}
|
||||
</a>
|
||||
</div>
|
||||
<img class="pf-c-avatar" src="{% gravatar user.email %}" alt="">
|
||||
</div>
|
||||
</header>
|
||||
<div class="pf-c-page__sidebar pf-m-dark">
|
||||
<div class="pf-c-page__sidebar-body">
|
||||
<nav class="pf-c-nav pf-m-dark" id="page-default-nav-example-primary-nav" aria-label="Global">
|
||||
<ul class="pf-c-nav__list">
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_core:overview' %}" class="pf-c-nav__link {% is_active_url 'passbook_core:overview' %}">
|
||||
{% trans 'Overview' %}
|
||||
</a>
|
||||
</li>
|
||||
{% if user.is_superuser %}
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:overview' %}" class="pf-c-nav__link {% is_active_url 'passbook_admin:overview' %}">
|
||||
{% trans 'System Status' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:applications' %}" class="pf-c-nav__link {% is_active 'passbook_admin:applications' 'passbook_admin:application-create' 'passbook_admin:application-update' 'passbook_admin:application-delete' %}">
|
||||
{% trans 'Applications' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:sources' %}" class="pf-c-nav__link {% is_active 'passbook_admin:sources' 'passbook_admin:source-create' 'passbook_admin:source-update' 'passbook_admin:source-delete' %}">
|
||||
{% trans 'Sources' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:providers' %}" class="pf-c-nav__link {% is_active 'passbook_admin:providers' 'passbook_admin:provider-create' 'passbook_admin:provider-update' 'passbook_admin:provider-delete' %}">
|
||||
{% trans 'Providers' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:property-mappings' %}" class="pf-c-nav__link {% is_active 'passbook_admin:property-mappings' 'passbook_admin:property-mapping-create' 'passbook_admin:property-mapping-update' 'passbook_admin:property-mapping-delete' %}">
|
||||
{% trans 'Property Mappings' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:factors' %}" class="pf-c-nav__link {% is_active 'passbook_admin:factors' 'passbook_admin:factor-create' 'passbook_admin:factor-update' 'passbook_admin:factor-delete' %}">
|
||||
{% trans 'Factors' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:policies' %}" class="pf-c-nav__link {% is_active 'passbook_admin:policies' 'passbook_admin:policy-create' 'passbook_admin:policy-update' 'passbook_admin:policy-delete' 'passbook_admin:policy-test' %}">
|
||||
{% trans 'Policies' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:invitations' %}" class="pf-c-nav__link {% is_active 'passbook_admin:invitations' 'passbook_admin:invitation-create' 'passbook_admin:invitation-update' 'passbook_admin:invitation-delete' 'passbook_admin:invitation-test' %}">
|
||||
{% trans 'Invitations' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:users' %}" class="pf-c-nav__link {% is_active 'passbook_admin:users' 'passbook_admin:user-update' 'passbook_admin:user-delete' %}">
|
||||
{% trans 'Users' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:groups' %}" class="pf-c-nav__link {% is_active 'passbook_admin:groups' 'passbook_admin:group-update' 'passbook_admin:group-delete' %}">
|
||||
{% trans 'Groups' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_admin:audit-log' %}" class="pf-c-nav__link {% is_active 'passbook_admin:audit-log' %}">
|
||||
{% trans 'Audit Log' %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>Main title</h1>
|
||||
<p>This is a demo of the Page component.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section">
|
||||
|
||||
</section>
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
{% block page_content %}
|
||||
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
@ -24,21 +24,21 @@
|
||||
{% if applications %}
|
||||
<div class="pf-l-gallery pf-m-gutter">
|
||||
{% for app in applications %}
|
||||
<a href="{{ app.launch_url }}" class="pf-c-card pf-m-hoverable pf-m-compact" id="card-1">
|
||||
<a href="{{ app.meta_launch_url }}" class="pf-c-card pf-m-hoverable pf-m-compact" id="card-1">
|
||||
<div class="pf-c-card__head">
|
||||
{% if not app.icon_url %}
|
||||
{% if not app.meta_icon_url %}
|
||||
<i class="pf-icon pf-icon-arrow"></i>
|
||||
{% else %}
|
||||
<img class="app-icon" src="{{ app.icon_url }}" alt="{% trans 'Application Icon' %}">
|
||||
<img class="app-icon pf-c-avatar" src="{{ app.meta_icon_url }}" alt="{% trans 'Application Icon' %}">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||
<p id="card-1-check-label">{{ app.name }}</p>
|
||||
<div class="pf-c-content">
|
||||
<small>{{ app.subtitle }}</small>
|
||||
<small>{{ app.meta_publisher }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">{% trans app.description %}</div>
|
||||
<div class="pf-c-card__body">{% trans app.meta_description %}</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@ -1,67 +1,69 @@
|
||||
{% extends "overview/base.html" %}
|
||||
{% extends "base/page.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load is_active %}
|
||||
{% load static %}
|
||||
{% load passbook_user_settings %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section">
|
||||
<div class="pf-l-split pf-m-gutter">
|
||||
<div class="pf-l-split__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<nav class="pf-c-nav" aria-label="Global">
|
||||
<section class="pf-c-nav__section">
|
||||
<h2 class="pf-c-nav__section-title">{% trans 'General Settings' %}</h2>
|
||||
<ul class="pf-c-nav__list">
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_core:user-settings' %}" class="pf-c-nav__link {% is_active 'passbook_core:user-settings' %}">{% trans 'User Details' %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
{% user_factors as user_factors_loc %}
|
||||
{% if user_factors_loc %}
|
||||
<section class="pf-c-nav__section">
|
||||
<h2 class="pf-c-nav__section-title">{% trans 'Factors' %}</h2>
|
||||
<ul class="pf-c-nav__list">
|
||||
{% for factor in user_factors_loc %}
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url factor.view_name %}" class="pf-c-nav__link {% is_active factor.view_name %}">
|
||||
<i class="{{ factor.icon }}"></i>
|
||||
{{ factor.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% user_sources as user_sources_loc %}
|
||||
{% if user_sources_loc %}
|
||||
<section class="pf-c-nav__section">
|
||||
<h2 class="pf-c-nav__section-title">{% trans 'Sources' %}</h2>
|
||||
<ul class="pf-c-nav__list">
|
||||
{% for source in user_sources_loc %}
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{{ source.view_name }}" class="pf-c-nav__link {% if user_settings.view_name == request.get_full_path %} pf-m-current {% endif %}">
|
||||
<i class="{{ source.icon }}"></i>
|
||||
{{ source.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
</nav>
|
||||
{% block page_content %}
|
||||
<div class="pf-c-page__sidebar">
|
||||
<div class="pf-c-page__sidebar-body">
|
||||
<nav class="pf-c-nav" id="page-default-nav-example-primary-nav" aria-label="Global">
|
||||
<section class="pf-c-nav__section">
|
||||
<h2 class="pf-c-nav__section-title">{% trans 'General Settings' %}</h2>
|
||||
<ul class="pf-c-nav__list">
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url 'passbook_core:user-settings' %}"
|
||||
class="pf-c-nav__link {% is_active 'passbook_core:user-settings' %}">{% trans 'User Details' %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
{% user_factors as user_factors_loc %}
|
||||
{% if user_factors_loc %}
|
||||
<section class="pf-c-nav__section">
|
||||
<h2 class="pf-c-nav__section-title">{% trans 'Factors' %}</h2>
|
||||
<ul class="pf-c-nav__list">
|
||||
{% for factor in user_factors_loc %}
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{% url factor.view_name %}" class="pf-c-nav__link {% is_active factor.view_name %}">
|
||||
<i class="{{ factor.icon }}"></i>
|
||||
{{ factor.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% user_sources as user_sources_loc %}
|
||||
{% if user_sources_loc %}
|
||||
<section class="pf-c-nav__section">
|
||||
<h2 class="pf-c-nav__section-title">{% trans 'Sources' %}</h2>
|
||||
<ul class="pf-c-nav__list">
|
||||
{% for source in user_sources_loc %}
|
||||
<li class="pf-c-nav__item">
|
||||
<a href="{{ source.view_name }}"
|
||||
class="pf-c-nav__link {% if user_settings.view_name == request.get_full_path %} pf-m-current {% endif %}">
|
||||
<i class="{{ source.icon }}"></i>
|
||||
{{ source.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
<section class="pf-c-page__main-section">
|
||||
<div class="pf-l-split pf-m-gutter">
|
||||
<div class="pf-l-split__item">
|
||||
<div class="pf-c-card">
|
||||
{% block page %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-split__item">
|
||||
<div class="pf-c-card">
|
||||
{% block page %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
@ -44,13 +44,13 @@ class LoginView(UserPassesTestMixin, FormView):
|
||||
kwargs["primary_action"] = _("Log in")
|
||||
kwargs["show_sign_up_notice"] = CONFIG.y("passbook.sign_up.enabled")
|
||||
kwargs["sources"] = []
|
||||
sources = Source.objects.filter(enabled=True).select_subclasses()
|
||||
sources = (
|
||||
Source.objects.filter(enabled=True).order_by("name").select_subclasses()
|
||||
)
|
||||
for source in sources:
|
||||
ui_login_button = source.ui_login_button
|
||||
if ui_login_button:
|
||||
kwargs["sources"].append(ui_login_button)
|
||||
# if kwargs["sources"]:
|
||||
# self.template_name = "login/with_sources.html"
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_user(self, uid_value) -> Optional[User]:
|
||||
@ -231,7 +231,6 @@ class PasswordResetView(View):
|
||||
login(request, nonce.user)
|
||||
nonce.delete()
|
||||
messages.success(
|
||||
request,
|
||||
_(("Temporarily authenticated with Nonce, " "please change your password")),
|
||||
request, _(("Temporarily authenticated, please change your password")),
|
||||
)
|
||||
return redirect("passbook_core:user-change-password")
|
||||
|
||||
5
passbook/crypto/admin.py
Normal file
5
passbook/crypto/admin.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""passbook crypto model admin"""
|
||||
|
||||
from passbook.lib.admin import admin_autoregister
|
||||
|
||||
admin_autoregister("passbook_crypto")
|
||||
10
passbook/crypto/apps.py
Normal file
10
passbook/crypto/apps.py
Normal file
@ -0,0 +1,10 @@
|
||||
"""passbook crypto app config"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PassbookCryptoConfig(AppConfig):
|
||||
"""passbook crypto app config"""
|
||||
|
||||
name = "passbook.crypto"
|
||||
label = "passbook_crypto"
|
||||
verbose_name = "passbook Crypto"
|
||||
@ -36,8 +36,7 @@ class CertificateBuilder:
|
||||
x509.Name(
|
||||
[
|
||||
x509.NameAttribute(
|
||||
NameOID.COMMON_NAME,
|
||||
u"passbook Self-signed SAML Certificate",
|
||||
NameOID.COMMON_NAME, u"passbook Self-signed Certificate",
|
||||
),
|
||||
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"passbook"),
|
||||
x509.NameAttribute(
|
||||
@ -50,8 +49,7 @@ class CertificateBuilder:
|
||||
x509.Name(
|
||||
[
|
||||
x509.NameAttribute(
|
||||
NameOID.COMMON_NAME,
|
||||
u"passbook Self-signed SAML Certificate",
|
||||
NameOID.COMMON_NAME, u"passbook Self-signed Certificate",
|
||||
),
|
||||
]
|
||||
)
|
||||
27
passbook/crypto/forms.py
Normal file
27
passbook/crypto/forms.py
Normal file
@ -0,0 +1,27 @@
|
||||
"""passbook Crypto forms"""
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from passbook.crypto.models import CertificateKeyPair
|
||||
|
||||
|
||||
class CertificateKeyPairForm(forms.ModelForm):
|
||||
"""CertificateKeyPair Form"""
|
||||
|
||||
class Meta:
|
||||
|
||||
model = CertificateKeyPair
|
||||
fields = [
|
||||
"name",
|
||||
"certificate_data",
|
||||
"key_data",
|
||||
]
|
||||
widgets = {
|
||||
"name": forms.TextInput(),
|
||||
"certificate_data": forms.Textarea(attrs={"class": "monospaced"}),
|
||||
"key_data": forms.Textarea(attrs={"class": "monospaced"}),
|
||||
}
|
||||
labels = {
|
||||
"certificate_data": _("Certificate"),
|
||||
"key_data": _("Private Key"),
|
||||
}
|
||||
67
passbook/crypto/migrations/0001_initial.py
Normal file
67
passbook/crypto/migrations/0001_initial.py
Normal file
@ -0,0 +1,67 @@
|
||||
# Generated by Django 3.0.3 on 2020-03-03 21:45
|
||||
|
||||
import uuid
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def create_self_signed(apps, schema_editor):
|
||||
CertificateKeyPair = apps.get_model("passbook_crypto", "CertificateKeyPair")
|
||||
db_alias = schema_editor.connection.alias
|
||||
from passbook.crypto.builder import CertificateBuilder
|
||||
|
||||
builder = CertificateBuilder()
|
||||
builder.build()
|
||||
CertificateKeyPair.objects.using(db_alias).create(
|
||||
name="passbook Self-signed Certificate",
|
||||
certificate_data=builder.certificate,
|
||||
key_data=builder.private_key,
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="CertificateKeyPair",
|
||||
fields=[
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("last_updated", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("name", models.TextField()),
|
||||
("certificate_data", models.TextField()),
|
||||
("key_data", models.TextField(blank=True, default="")),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Certificate-Key Pair",
|
||||
"verbose_name_plural": "Certificate-Key Pairs",
|
||||
},
|
||||
),
|
||||
migrations.RunPython(create_self_signed),
|
||||
migrations.AlterField(
|
||||
model_name="certificatekeypair",
|
||||
name="certificate_data",
|
||||
field=models.TextField(help_text="PEM-encoded Certificate data"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="certificatekeypair",
|
||||
name="key_data",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
default="",
|
||||
help_text="Optional Private Key. If this is set, you can use this keypair for encryption.",
|
||||
),
|
||||
),
|
||||
]
|
||||
0
passbook/crypto/migrations/__init__.py
Normal file
0
passbook/crypto/migrations/__init__.py
Normal file
64
passbook/crypto/models.py
Normal file
64
passbook/crypto/models.py
Normal file
@ -0,0 +1,64 @@
|
||||
"""passbook crypto models"""
|
||||
from binascii import hexlify
|
||||
from typing import Optional
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
||||
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
||||
from cryptography.x509 import Certificate, load_pem_x509_certificate
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from passbook.lib.models import CreatedUpdatedModel, UUIDModel
|
||||
|
||||
|
||||
class CertificateKeyPair(UUIDModel, CreatedUpdatedModel):
|
||||
"""CertificateKeyPair that can be used for signing or encrypting if `key_data`
|
||||
is set, otherwise it can be used to verify remote data."""
|
||||
|
||||
name = models.TextField()
|
||||
certificate_data = models.TextField(help_text=_("PEM-encoded Certificate data"))
|
||||
key_data = models.TextField(
|
||||
help_text=_(
|
||||
"Optional Private Key. If this is set, you can use this keypair for encryption."
|
||||
),
|
||||
blank=True,
|
||||
default="",
|
||||
)
|
||||
|
||||
_cert: Optional[Certificate] = None
|
||||
_key: Optional[RSAPrivateKey] = None
|
||||
|
||||
@property
|
||||
def certificate(self) -> Certificate:
|
||||
"""Get python cryptography Certificate instance"""
|
||||
if not self._cert:
|
||||
self._cert = load_pem_x509_certificate(
|
||||
self.certificate_data.encode("utf-8"), default_backend()
|
||||
)
|
||||
return self._cert
|
||||
|
||||
@property
|
||||
def private_key(self) -> Optional[RSAPrivateKey]:
|
||||
"""Get python cryptography PrivateKey instance"""
|
||||
if not self._key:
|
||||
self._key = load_pem_private_key(
|
||||
str.encode("\n".join([x.strip() for x in self.key_data.split("\n")])),
|
||||
password=None,
|
||||
backend=default_backend(),
|
||||
)
|
||||
return self._key
|
||||
|
||||
@property
|
||||
def fingerprint(self) -> str:
|
||||
"""Get SHA256 Fingerprint of certificate_data"""
|
||||
return hexlify(self.certificate.fingerprint(hashes.SHA256())).decode("utf-8")
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Certificate-Key Pair {self.name} {self.fingerprint}"
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _("Certificate-Key Pair")
|
||||
verbose_name_plural = _("Certificate-Key Pairs")
|
||||
@ -2,7 +2,7 @@
|
||||
from captcha.fields import ReCaptchaField
|
||||
from django import forms
|
||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from passbook.factors.captcha.models import CaptchaFactor
|
||||
from passbook.factors.forms import GENERAL_FIELDS
|
||||
@ -28,3 +28,8 @@ class CaptchaFactorForm(forms.ModelForm):
|
||||
"public_key": forms.TextInput(),
|
||||
"private_key": forms.TextInput(),
|
||||
}
|
||||
help_texts = {
|
||||
"policies": _(
|
||||
"Policies which determine if this factor applies to the current user."
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"""passbook administration forms"""
|
||||
from django import forms
|
||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from passbook.factors.email.models import EmailFactor
|
||||
from passbook.factors.forms import GENERAL_FIELDS
|
||||
@ -41,3 +41,8 @@ class EmailFactorForm(forms.ModelForm):
|
||||
"ssl_keyfile": _("SSL Keyfile (optional)"),
|
||||
"ssl_certfile": _("SSL Certfile (optional)"),
|
||||
}
|
||||
help_texts = {
|
||||
"policies": _(
|
||||
"Policies which determine if this factor applies to the current user."
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ from django import forms
|
||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
from django.core.validators import RegexValidator
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_otp.models import Device
|
||||
|
||||
from passbook.factors.forms import GENERAL_FIELDS
|
||||
@ -80,3 +80,8 @@ class OTPFactorForm(forms.ModelForm):
|
||||
"order": forms.NumberInput(),
|
||||
"policies": FilteredSelectMultiple(_("policies"), False),
|
||||
}
|
||||
help_texts = {
|
||||
"policies": _(
|
||||
"Policies which determine if this factor applies to the current user."
|
||||
)
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from passbook.factors.forms import GENERAL_FIELDS
|
||||
from passbook.factors.password.models import PasswordFactor
|
||||
@ -49,3 +49,8 @@ class PasswordFactorForm(forms.ModelForm):
|
||||
"password_policies": FilteredSelectMultiple(_("password policies"), False),
|
||||
"reset_factors": FilteredSelectMultiple(_("reset factors"), False),
|
||||
}
|
||||
help_texts = {
|
||||
"policies": _(
|
||||
"Policies which determine if this factor applies to the current user."
|
||||
)
|
||||
}
|
||||
|
||||
@ -25,18 +25,6 @@ passbook:
|
||||
password_reset:
|
||||
# Enable password reset, passwords are reset in internal Database and in LDAP if ldap.reset_password is true
|
||||
enabled: true
|
||||
# Verification the user has to provide in order to be able to reset passwords. Can be any combination of `email`, `2fa`, `security_questions`
|
||||
verification:
|
||||
- email
|
||||
# Text used in title, on login page and multiple other places
|
||||
branding: passbook
|
||||
login:
|
||||
# Override URL used for logo
|
||||
logo_url: null
|
||||
# Override URL used for Background on Login page
|
||||
bg_url: null
|
||||
# Optionally add a subtext, placed below logo on the login page
|
||||
subtext: null
|
||||
footer:
|
||||
links:
|
||||
# Optionally add links to the footer on the login page
|
||||
@ -46,14 +34,3 @@ passbook:
|
||||
uid_fields:
|
||||
- username
|
||||
- email
|
||||
# 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)"
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
"""passbook lib fields"""
|
||||
from itertools import chain
|
||||
|
||||
from django import forms
|
||||
from django.contrib.postgres.utils import prefix_validation_error
|
||||
|
||||
from passbook.lib.widgets import DynamicArrayWidget
|
||||
|
||||
|
||||
class DynamicArrayField(forms.Field):
|
||||
"""Show array field as a dynamic amount of textboxes"""
|
||||
|
||||
default_error_messages = {
|
||||
"item_invalid": "Item %(nth)s in the array did not validate: "
|
||||
}
|
||||
|
||||
def __init__(self, base_field, **kwargs):
|
||||
self.base_field = base_field
|
||||
self.max_length = kwargs.pop("max_length", None)
|
||||
kwargs.setdefault("widget", DynamicArrayWidget)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
cleaned_data = []
|
||||
errors = []
|
||||
value = [x for x in value if x]
|
||||
for index, item in enumerate(value):
|
||||
try:
|
||||
cleaned_data.append(self.base_field.clean(item))
|
||||
except forms.ValidationError as error:
|
||||
errors.append(
|
||||
prefix_validation_error(
|
||||
error,
|
||||
self.error_messages["item_invalid"],
|
||||
code="item_invalid",
|
||||
params={"nth": index},
|
||||
)
|
||||
)
|
||||
if errors:
|
||||
raise forms.ValidationError(list(chain.from_iterable(errors)))
|
||||
if not cleaned_data and self.required:
|
||||
raise forms.ValidationError(self.error_messages["required"])
|
||||
return cleaned_data
|
||||
|
||||
def has_changed(self, initial, data):
|
||||
if not data and not initial:
|
||||
return False
|
||||
return super().has_changed(initial, data)
|
||||
9
passbook/lib/logging.py
Normal file
9
passbook/lib/logging.py
Normal file
@ -0,0 +1,9 @@
|
||||
"""logging helpers"""
|
||||
from os import getpid
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def add_process_id(logger, method_name, event_dict):
|
||||
"""Add the current process ID"""
|
||||
event_dict["pid"] = getpid()
|
||||
return event_dict
|
||||
0
passbook/lib/management/__init__.py
Normal file
0
passbook/lib/management/__init__.py
Normal file
0
passbook/lib/management/commands/__init__.py
Normal file
0
passbook/lib/management/commands/__init__.py
Normal file
65
passbook/lib/management/commands/bootstrap.py
Normal file
65
passbook/lib/management/commands/bootstrap.py
Normal file
@ -0,0 +1,65 @@
|
||||
"""passbook management command to bootstrap"""
|
||||
from argparse import REMAINDER
|
||||
from subprocess import Popen # nosec
|
||||
from sys import exit as _exit
|
||||
from sys import stderr, stdin, stdout
|
||||
from time import sleep
|
||||
from typing import List
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import connection
|
||||
from django.db.utils import OperationalError
|
||||
from django_redis import get_redis_connection
|
||||
from redis.exceptions import ConnectionError as RedisConnectionError
|
||||
from structlog import get_logger
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Bootstrap passbook, ensure Database and Cache are
|
||||
reachable, and directories are writeable"""
|
||||
|
||||
help = """Bootstrap passbook, ensure Database and Cache are
|
||||
reachable, and directories are writeable"""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("command", nargs=REMAINDER)
|
||||
|
||||
def check_database(self) -> bool:
|
||||
"""Return true if database is reachable, false otherwise"""
|
||||
try:
|
||||
connection.cursor()
|
||||
LOGGER.info("Database reachable")
|
||||
return True
|
||||
except OperationalError:
|
||||
LOGGER.info("Database unreachable")
|
||||
return False
|
||||
|
||||
def check_cache(self) -> bool:
|
||||
"""Return true if cache is reachable, false otherwise"""
|
||||
try:
|
||||
con = get_redis_connection("default")
|
||||
con.ping()
|
||||
LOGGER.info("Cache reachable")
|
||||
return True
|
||||
except RedisConnectionError:
|
||||
LOGGER.info("Cache unreachable")
|
||||
return False
|
||||
|
||||
def handle(self, *args, **options):
|
||||
LOGGER.info("passbook bootstrapping...")
|
||||
should_check = True
|
||||
while should_check:
|
||||
should_check = not (self.check_database() and self.check_cache())
|
||||
sleep(1)
|
||||
LOGGER.info("Dependencies are up, starting command...")
|
||||
commands: List[str] = options.get("command", ["exit", "1"])
|
||||
proc = Popen(args=commands, stdout=stdout, stderr=stderr, stdin=stdin) # nosec
|
||||
try:
|
||||
proc.wait()
|
||||
_exit(proc.returncode)
|
||||
except KeyboardInterrupt:
|
||||
LOGGER.info("Killing process")
|
||||
proc.kill()
|
||||
_exit(254)
|
||||
@ -1,29 +1,28 @@
|
||||
"""passbook sentry integration"""
|
||||
from billiard.exceptions import WorkerLostError
|
||||
from botocore.client import ClientError
|
||||
from django.core.exceptions import DisallowedHost, ValidationError
|
||||
from django.db import InternalError, OperationalError, ProgrammingError
|
||||
from django_redis.exceptions import ConnectionInterrupted
|
||||
from redis.exceptions import RedisError
|
||||
from rest_framework.exceptions import APIException
|
||||
from structlog import get_logger
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class SentryIgnoredException(Exception):
|
||||
"""Base Class for all errors that are supressed, and not sent to sentry."""
|
||||
"""Base Class for all errors that are suppressed, and not sent to sentry."""
|
||||
|
||||
|
||||
def before_send(event, hint):
|
||||
"""Check if error is database error, and ignore if so"""
|
||||
from django_redis.exceptions import ConnectionInterrupted
|
||||
from django.db import OperationalError, InternalError
|
||||
from django.core.exceptions import ValidationError
|
||||
from rest_framework.exceptions import APIException
|
||||
from billiard.exceptions import WorkerLostError
|
||||
from django.core.exceptions import DisallowedHost
|
||||
from botocore.client import ClientError
|
||||
from redis.exceptions import RedisError
|
||||
|
||||
ignored_classes = (
|
||||
OperationalError,
|
||||
InternalError,
|
||||
ProgrammingError,
|
||||
ConnectionInterrupted,
|
||||
APIException,
|
||||
InternalError,
|
||||
ConnectionResetError,
|
||||
WorkerLostError,
|
||||
DisallowedHost,
|
||||
|
||||
@ -3,11 +3,9 @@ from hashlib import md5
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from django import template
|
||||
from django.apps import apps
|
||||
from django.db.models import Model
|
||||
from django.template import Context
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from passbook.lib.config import CONFIG
|
||||
from passbook.lib.utils.urls import is_url_absolute
|
||||
@ -40,38 +38,6 @@ def fieldtype(field):
|
||||
return field.__class__.__name__
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def title(context: Context, *title) -> str:
|
||||
"""Return either just branding or title - branding"""
|
||||
branding = CONFIG.y("passbook.branding", "passbook")
|
||||
if not title:
|
||||
return branding
|
||||
if "request" not in context:
|
||||
return ""
|
||||
resolver_match = context.request.resolver_match
|
||||
if not resolver_match:
|
||||
return ""
|
||||
# Include App Title in title
|
||||
app = ""
|
||||
if resolver_match.namespace != "":
|
||||
dj_app = None
|
||||
namespace = context.request.resolver_match.namespace.split(":")[0]
|
||||
# New label (App URL Namespace == App Label)
|
||||
dj_app = apps.get_app_config(namespace)
|
||||
title_modifier = getattr(dj_app, "title_modifier", None)
|
||||
if title_modifier:
|
||||
app_title = dj_app.title_modifier(context.request)
|
||||
app = app_title + " -"
|
||||
return _(
|
||||
"%(title)s - %(app)s %(branding)s"
|
||||
% {
|
||||
"title": " - ".join([str(x) for x in title]),
|
||||
"branding": branding,
|
||||
"app": app,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def config(path, default=""):
|
||||
"""Get a setting from the database. Returns default is setting doesn't exist."""
|
||||
|
||||
@ -9,6 +9,7 @@ from jinja2.nativetypes import NativeEnvironment
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.factors.view import AuthenticationView
|
||||
from passbook.lib.utils.http import get_client_ip
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -18,7 +19,7 @@ LOGGER = get_logger()
|
||||
|
||||
|
||||
class Evaluator:
|
||||
"""Validate and evaulate jinja2-based expressions"""
|
||||
"""Validate and evaluate jinja2-based expressions"""
|
||||
|
||||
_env: NativeEnvironment
|
||||
|
||||
@ -50,11 +51,15 @@ class Evaluator:
|
||||
"""Return dictionary with additional global variables passed to expression"""
|
||||
# update passbook/policies/expression/templates/policy/expression/form.html
|
||||
# update docs/policies/expression/index.md
|
||||
kwargs["pb_is_sso_flow"] = request.http_request.session.get(
|
||||
AuthenticationView.SESSION_IS_SSO_LOGIN, False
|
||||
)
|
||||
kwargs["pb_is_group_member"] = Evaluator.jinja2_func_is_group_member
|
||||
kwargs["pb_logger"] = get_logger()
|
||||
if request.http_request:
|
||||
kwargs["pb_is_sso_flow"] = request.http_request.session.get(
|
||||
AuthenticationView.SESSION_IS_SSO_LOGIN, False
|
||||
)
|
||||
kwargs["pb_client_ip"] = (
|
||||
get_client_ip(request.http_request) or "255.255.255.255"
|
||||
)
|
||||
return kwargs
|
||||
|
||||
def evaluate(self, expression_source: str, request: PolicyRequest) -> PolicyResult:
|
||||
@ -77,7 +82,7 @@ class Evaluator:
|
||||
req=request,
|
||||
)
|
||||
return PolicyResult(False)
|
||||
if isinstance(result, list) and len(result) == 2:
|
||||
if isinstance(result, (list, tuple)) and len(result) == 2:
|
||||
return PolicyResult(*result)
|
||||
if result:
|
||||
return PolicyResult(result)
|
||||
|
||||
@ -9,15 +9,17 @@
|
||||
<p>
|
||||
Expression using <a href="https://jinja.palletsprojects.com/en/2.11.x/templates/">Jinja</a>. Following variables are available:
|
||||
</p>
|
||||
<ul>
|
||||
<ul class="pf-c-list">
|
||||
<li><code>request.user</code>: Passbook User Object (<a href="https://beryju.github.io/passbook/property-mappings/reference/user-object/">Reference</a>)</li>
|
||||
<li><code>request.http_request</code>: Django HTTP Request Object (<a href="https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects">Reference</a>) </li>
|
||||
<li><code>request.obj</code>: Model the Policy is run against. </li>
|
||||
<li><code>pb_is_sso_flow</code>: Boolean which is true if request was initiated by authenticating through an external Provider.</li>
|
||||
<li><code>pb_is_group_member(user, group_name)</code>: Function which checks if <code>user</code> is member of a Group with Name <code>group_name</code>.</li>
|
||||
<li><code>pb_logger</code>: Standard Python Logger Object, which can be used to debug expressions.</li>
|
||||
<li><code>pb_client_ip</code>: Client's IP Address.</li>
|
||||
</ul>
|
||||
<p>Custom Filters:</p>
|
||||
<ul>
|
||||
<ul class="pf-c-list">
|
||||
<li><code>regex_match(regex)</code>: Checks if value matches <code>regex</code></li>
|
||||
<li><code>regex_replace(regex, repl)</code>: Replace string matched by <code>regex</code> with <code>repl</code></li>
|
||||
</ul>
|
||||
|
||||
0
passbook/policies/expression/tests/__init__.py
Normal file
0
passbook/policies/expression/tests/__init__.py
Normal file
58
passbook/policies/expression/tests/test_evaluator.py
Normal file
58
passbook/policies/expression/tests/test_evaluator.py
Normal file
@ -0,0 +1,58 @@
|
||||
"""evaluator tests"""
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import TestCase
|
||||
from guardian.shortcuts import get_anonymous_user
|
||||
|
||||
from passbook.policies.expression.evaluator import Evaluator
|
||||
from passbook.policies.types import PolicyRequest
|
||||
|
||||
|
||||
class TestEvaluator(TestCase):
|
||||
"""Evaluator tests"""
|
||||
|
||||
def setUp(self):
|
||||
self.request = PolicyRequest(user=get_anonymous_user())
|
||||
|
||||
def test_valid(self):
|
||||
"""test simple value expression"""
|
||||
template = "True"
|
||||
evaluator = Evaluator()
|
||||
self.assertEqual(evaluator.evaluate(template, self.request).passing, True)
|
||||
|
||||
def test_messages(self):
|
||||
"""test expression with message return"""
|
||||
template = "False, 'some message'"
|
||||
evaluator = Evaluator()
|
||||
result = evaluator.evaluate(template, self.request)
|
||||
self.assertEqual(result.passing, False)
|
||||
self.assertEqual(result.messages, ("some message",))
|
||||
|
||||
def test_invalid_syntax(self):
|
||||
"""test invalid syntax"""
|
||||
template = "{%"
|
||||
evaluator = Evaluator()
|
||||
result = evaluator.evaluate(template, self.request)
|
||||
self.assertEqual(result.passing, False)
|
||||
self.assertEqual(result.messages, ("tag name expected",))
|
||||
|
||||
def test_undefined(self):
|
||||
"""test undefined result"""
|
||||
template = "{{ foo.bar }}"
|
||||
evaluator = Evaluator()
|
||||
result = evaluator.evaluate(template, self.request)
|
||||
self.assertEqual(result.passing, False)
|
||||
self.assertEqual(result.messages, ("'foo' is undefined",))
|
||||
|
||||
def test_validate(self):
|
||||
"""test validate"""
|
||||
template = "True"
|
||||
evaluator = Evaluator()
|
||||
result = evaluator.validate(template)
|
||||
self.assertEqual(result, True)
|
||||
|
||||
def test_validate_invalid(self):
|
||||
"""test validate"""
|
||||
template = "{%"
|
||||
evaluator = Evaluator()
|
||||
with self.assertRaises(ValidationError):
|
||||
evaluator.validate(template)
|
||||
@ -5,16 +5,19 @@ from multiprocessing.connection import Connection
|
||||
from django.core.cache import cache
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.core.models import Policy
|
||||
from passbook.core.models import Policy, User
|
||||
from passbook.policies.exceptions import PolicyException
|
||||
from passbook.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
def cache_key(policy, user):
|
||||
def cache_key(policy: Policy, user: User = None) -> str:
|
||||
"""Generate Cache key for policy"""
|
||||
return f"policy_{policy.pk}#{user.pk}"
|
||||
prefix = f"policy_{policy.pk}"
|
||||
if user:
|
||||
prefix += f"#{user.pk}"
|
||||
return prefix
|
||||
|
||||
|
||||
class PolicyProcess(Process):
|
||||
@ -33,7 +36,7 @@ class PolicyProcess(Process):
|
||||
def run(self):
|
||||
"""Task wrapper to run policy checking"""
|
||||
LOGGER.debug(
|
||||
"Running policy",
|
||||
"P_ENG(proc): Running policy",
|
||||
policy=self.policy,
|
||||
user=self.request.user,
|
||||
process="PolicyProcess",
|
||||
@ -41,13 +44,13 @@ class PolicyProcess(Process):
|
||||
try:
|
||||
policy_result = self.policy.passes(self.request)
|
||||
except PolicyException as exc:
|
||||
LOGGER.debug(exc)
|
||||
LOGGER.debug("P_ENG(proc): error", exc=exc)
|
||||
policy_result = PolicyResult(False, str(exc))
|
||||
# Invert result if policy.negate is set
|
||||
if self.policy.negate:
|
||||
policy_result.passing = not policy_result.passing
|
||||
LOGGER.debug(
|
||||
"Got result",
|
||||
"P_ENG(proc): Finished",
|
||||
policy=self.policy,
|
||||
result=policy_result,
|
||||
process="PolicyProcess",
|
||||
@ -56,5 +59,5 @@ class PolicyProcess(Process):
|
||||
)
|
||||
key = cache_key(self.policy, self.request.user)
|
||||
cache.set(key, policy_result)
|
||||
LOGGER.debug("Cached policy evaluation", key=key)
|
||||
LOGGER.debug("P_ENG(proc): Cached policy evaluation", key=key)
|
||||
self.connection.send(policy_result)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"""policy structures"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
from typing import TYPE_CHECKING, Optional, Tuple
|
||||
|
||||
from django.db.models import Model
|
||||
from django.http import HttpRequest
|
||||
@ -14,11 +14,13 @@ class PolicyRequest:
|
||||
"""Data-class to hold policy request data"""
|
||||
|
||||
user: User
|
||||
http_request: HttpRequest
|
||||
obj: Model
|
||||
http_request: Optional[HttpRequest]
|
||||
obj: Optional[Model]
|
||||
|
||||
def __init__(self, user: User):
|
||||
self.user = user
|
||||
self.http_request = None
|
||||
self.obj = None
|
||||
|
||||
def __str__(self):
|
||||
return f"<PolicyRequest user={self.user}>"
|
||||
|
||||
@ -34,7 +34,7 @@ class ApplicationGatewayProviderSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
|
||||
model = ApplicationGatewayProvider
|
||||
fields = ["pk", "name", "host", "client"]
|
||||
fields = ["pk", "name", "internal_host", "external_host", "client"]
|
||||
read_only_fields = ["client"]
|
||||
|
||||
|
||||
|
||||
@ -3,15 +3,12 @@
|
||||
{% load utils %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}
|
||||
{% title 'Authorize Application' %}
|
||||
{% block card_title %}
|
||||
{% trans 'Authorize Application' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<header class="login-pf-header">
|
||||
<h1>{% trans 'Authorize Application' %}</h1>
|
||||
</header>
|
||||
<form method="POST">
|
||||
<form method="POST" class="pf-c-form">
|
||||
{% csrf_token %}
|
||||
{% if not error %}
|
||||
{% csrf_token %}
|
||||
@ -20,32 +17,40 @@
|
||||
{{ field }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<div class="form-group">
|
||||
<div class="pf-c-form__group">
|
||||
<p class="subtitle">
|
||||
{% blocktrans with remote=application.name %}
|
||||
You're about to sign into {{ remote }}
|
||||
You're about to sign into {{ remote }}.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p>{% trans "Application requires following permissions" %}</p>
|
||||
<ul>
|
||||
<ul class="pf-c-list">
|
||||
{% for scope in scopes_descriptions %}
|
||||
<li>{{ scope }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{{ form.errors }}
|
||||
{{ form.non_field_errors }}
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<p>
|
||||
{% blocktrans with user=user %}
|
||||
You are logged in as {{ user }}. Not you?
|
||||
{% endblocktrans %}
|
||||
<a href="{% url 'passbook_core:auth-logout' %}">{% trans 'Logout' %}</a>
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-success btn-disabled btn-lg click-spinner" name="allow" value="{% trans 'Continue' %}">
|
||||
<a href="{% back %}" class="btn btn-default btn-lg">{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
<div class="form-group spinner-hidden hidden">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<input type="submit" class="pf-c-button pf-m-primary" name="allow" value="{% trans 'Continue' %}">
|
||||
<a href="{% back %}" class="pf-c-button pf-m-secondary">{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
<div class="pf-c-form__group" style="display: none;" id="loading">
|
||||
<div class="pf-c-form__horizontal-group">
|
||||
<span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
@ -61,9 +66,8 @@
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
$('.click-spinner').on('click', function (e) {
|
||||
$('.spinner-hidden').removeClass('hidden');
|
||||
$(e.target).addClass('disabled');
|
||||
})
|
||||
document.querySelector("form").addEventListener("submit", (e) => {
|
||||
document.getElementById("loading").removeAttribute("style");
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
"""passbook auth oidc provider app config"""
|
||||
from importlib import import_module
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db.utils import InternalError, OperationalError, ProgrammingError
|
||||
from django.urls import include, path
|
||||
@ -34,3 +36,5 @@ class PassbookProviderOIDCConfig(AppConfig):
|
||||
include("oidc_provider.urls", namespace="oidc_provider"),
|
||||
),
|
||||
)
|
||||
|
||||
import_module("passbook.providers.oidc.signals")
|
||||
|
||||
14
passbook/providers/oidc/claims.py
Normal file
14
passbook/providers/oidc/claims.py
Normal file
@ -0,0 +1,14 @@
|
||||
"""passbook oidc claim helpers"""
|
||||
from typing import Any, Dict
|
||||
|
||||
from passbook.core.models import User
|
||||
|
||||
|
||||
def userinfo(claims: Dict[str, Any], user: User) -> Dict[str, Any]:
|
||||
"""Populate claims from userdata"""
|
||||
claims["name"] = user.name
|
||||
claims["given_name"] = user.name
|
||||
claims["family_name"] = user.name
|
||||
claims["email"] = user.email
|
||||
|
||||
return claims
|
||||
@ -19,6 +19,8 @@ class OIDCProviderForm(forms.ModelForm):
|
||||
self.fields["client_secret"].initial = generate_client_secret()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.instance.reuse_consent = False # This is managed by passbook
|
||||
self.instance.require_consent = True # This is managed by passbook
|
||||
response = super().save(*args, **kwargs)
|
||||
# Check if openidprovider class instance exists
|
||||
if not OpenIDProvider.objects.filter(oidc_client=self.instance).exists():
|
||||
|
||||
@ -4,5 +4,6 @@ INSTALLED_APPS = [
|
||||
"oidc_provider",
|
||||
]
|
||||
|
||||
OIDC_AFTER_USERLOGIN_HOOK = "passbook.providers.oidc.lib.check_permissions"
|
||||
OIDC_AFTER_USERLOGIN_HOOK = "passbook.providers.oidc.auth.check_permissions"
|
||||
OIDC_IDTOKEN_INCLUDE_CLAIMS = True
|
||||
OIDC_USERINFO = "passbook.providers.oidc.claims.userinfo"
|
||||
|
||||
16
passbook/providers/oidc/signals.py
Normal file
16
passbook/providers/oidc/signals.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""OIDC Provider signals"""
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from passbook.core.models import Application
|
||||
from passbook.providers.oidc.models import OpenIDProvider
|
||||
|
||||
|
||||
@receiver(post_save, sender=Application)
|
||||
# pylint: disable=unused-argument
|
||||
def on_application_save(sender, instance: Application, **_):
|
||||
"""Synchronize application's skip_authorization with oidc_client's require_consent"""
|
||||
if isinstance(instance.provider, OpenIDProvider):
|
||||
instance.provider.oidc_client.require_consent = not instance.skip_authorization
|
||||
instance.provider.oidc_client.save()
|
||||
print("updating skip_authz")
|
||||
@ -3,15 +3,12 @@
|
||||
{% load utils %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}
|
||||
{% title 'Authorize Application' %}
|
||||
{% block card_title %}
|
||||
{% trans 'Authorize Application' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<header class="login-pf-header">
|
||||
<h1>{% trans 'Authorize Application' %}</h1>
|
||||
</header>
|
||||
<form method="POST">
|
||||
<form method="POST" class="pf-c-form">
|
||||
{% csrf_token %}
|
||||
{% if not error %}
|
||||
{% csrf_token %}
|
||||
@ -20,14 +17,14 @@
|
||||
{{ field }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<div class="form-group">
|
||||
<div class="pf-c-form__group">
|
||||
<p class="subtitle">
|
||||
{% blocktrans with remote=client.name %}
|
||||
You're about to sign into {{ remote }}
|
||||
You're about to sign into {{ remote }}.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p>{% trans "Application requires following permissions" %}</p>
|
||||
<ul>
|
||||
<ul class="pf-c-list">
|
||||
{% for scope in scopes %}
|
||||
<li>{{ scope.name }}</li>
|
||||
{% endfor %}
|
||||
@ -35,18 +32,26 @@
|
||||
{{ hidden_inputs }}
|
||||
{{ form.errors }}
|
||||
{{ form.non_field_errors }}
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<p>
|
||||
{% blocktrans with user=user %}
|
||||
You are logged in as {{ user }}. Not you?
|
||||
{% endblocktrans %}
|
||||
<a href="{% url 'passbook_core:auth-logout' %}">{% trans 'Logout' %}</a>
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-success btn-disabled btn-lg click-spinner" name="allow" value="{% trans 'Continue' %}">
|
||||
<a href="{% back %}" class="btn btn-default btn-lg">{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
<div class="form-group spinner-hidden hidden">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<input type="submit" class="pf-c-button pf-m-primary" name="allow" value="{% trans 'Continue' %}">
|
||||
<a href="{% back %}" class="pf-c-button pf-m-secondary">{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
<div class="pf-c-form__group" style="display: none;" id="loading">
|
||||
<div class="pf-c-form__horizontal-group">
|
||||
<span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
@ -62,9 +67,8 @@
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
$('.click-spinner').on('click', function (e) {
|
||||
$('.spinner-hidden').removeClass('hidden');
|
||||
$(e.target).addClass('disabled');
|
||||
})
|
||||
document.querySelector("form").addEventListener("submit", (e) => {
|
||||
document.getElementById("loading").removeAttribute("style");
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
18
passbook/providers/oidc/templates/oidc_provider/error.html
Normal file
18
passbook/providers/oidc/templates/oidc_provider/error.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends 'login/base.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load utils %}
|
||||
|
||||
{% block card_title %}
|
||||
{% trans error %}
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<form>
|
||||
<h3>{% trans description %}</h3>
|
||||
{% if 'back' in request.GET %}
|
||||
<a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@ -24,9 +24,7 @@ class SAMLProviderSerializer(ModelSerializer):
|
||||
"property_mappings",
|
||||
"digest_algorithm",
|
||||
"signature_algorithm",
|
||||
"signing",
|
||||
"signing_cert",
|
||||
"signing_key",
|
||||
"signing_kp",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@ from passbook.providers.saml.models import (
|
||||
SAMLProvider,
|
||||
get_provider_choices,
|
||||
)
|
||||
from passbook.providers.saml.utils.cert import CertificateBuilder
|
||||
|
||||
|
||||
class SAMLProviderForm(forms.ModelForm):
|
||||
@ -19,13 +18,6 @@ class SAMLProviderForm(forms.ModelForm):
|
||||
choices=get_provider_choices(), label="Processor"
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
builder = CertificateBuilder()
|
||||
builder.build()
|
||||
self.fields["signing_cert"].initial = builder.certificate
|
||||
self.fields["signing_key"].initial = builder.private_key
|
||||
|
||||
class Meta:
|
||||
|
||||
model = SAMLProvider
|
||||
@ -41,9 +33,7 @@ class SAMLProviderForm(forms.ModelForm):
|
||||
"property_mappings",
|
||||
"digest_algorithm",
|
||||
"signature_algorithm",
|
||||
"signing",
|
||||
"signing_cert",
|
||||
"signing_key",
|
||||
"signing_kp",
|
||||
]
|
||||
widgets = {
|
||||
"name": forms.TextInput(),
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
# Generated by Django 3.0.3 on 2020-03-03 21:57
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_crypto", "0001_initial"),
|
||||
("passbook_providers_saml", "0006_auto_20200217_2031"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(model_name="samlprovider", name="signing",),
|
||||
migrations.RemoveField(model_name="samlprovider", name="signing_cert",),
|
||||
migrations.RemoveField(model_name="samlprovider", name="signing_key",),
|
||||
migrations.AddField(
|
||||
model_name="samlprovider",
|
||||
name="singing_kp",
|
||||
field=models.ForeignKey(
|
||||
default=None,
|
||||
help_text="Singing is enabled upon selection of a Key Pair.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="passbook_crypto.CertificateKeyPair",
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,16 @@
|
||||
# Generated by Django 3.0.3 on 2020-03-05 16:06
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_providers_saml", "0007_auto_20200303_2157"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="samlprovider", old_name="singing_kp", new_name="signing_kp",
|
||||
),
|
||||
]
|
||||
@ -8,6 +8,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.core.models import PropertyMapping, Provider
|
||||
from passbook.crypto.models import CertificateKeyPair
|
||||
from passbook.lib.utils.reflection import class_to_path, path_to_class
|
||||
from passbook.lib.utils.template import render_to_string
|
||||
from passbook.providers.saml.processors.base import Processor
|
||||
@ -74,9 +75,13 @@ class SAMLProvider(Provider):
|
||||
default="rsa-sha256",
|
||||
)
|
||||
|
||||
signing = models.BooleanField(default=True)
|
||||
signing_cert = models.TextField(verbose_name=_("Singing Certificate"))
|
||||
signing_key = models.TextField()
|
||||
signing_kp = models.ForeignKey(
|
||||
CertificateKeyPair,
|
||||
default=None,
|
||||
null=True,
|
||||
help_text=_("Singing is enabled upon selection of a Key Pair."),
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
|
||||
form = "passbook.providers.saml.forms.SAMLProviderForm"
|
||||
_processor = None
|
||||
|
||||
@ -184,7 +184,7 @@ class Processor:
|
||||
try:
|
||||
self._extract_saml_request()
|
||||
except KeyError:
|
||||
raise CannotHandleAssertion(f"Couldn't find SAML request in user session:")
|
||||
raise CannotHandleAssertion(f"Couldn't find SAML request in user session")
|
||||
|
||||
try:
|
||||
self._decode_and_parse_request()
|
||||
|
||||
@ -3,23 +3,17 @@
|
||||
{% load utils %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}
|
||||
{% title 'Redirecting...' %}
|
||||
{% block card_title %}
|
||||
{% trans 'Redirecting...' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<header class="login-pf-header">
|
||||
<h1>{% trans 'Redirecting...' %}</h1>
|
||||
</header>
|
||||
<form method="POST" action="{{ url }}">
|
||||
{% csrf_token %}
|
||||
{% for key, value in attrs.items %}
|
||||
<input type="hidden" name="{{ key }}" value="{{ value }}">
|
||||
{% endfor %}
|
||||
<div class="login-group">
|
||||
<h3>
|
||||
{% trans "Redirecting..." %}
|
||||
</h3>
|
||||
<p>
|
||||
{% blocktrans with user=user %}
|
||||
You are logged in as {{ user }}.
|
||||
@ -34,6 +28,6 @@
|
||||
{% block scripts %}
|
||||
{{ block.super }}
|
||||
<script>
|
||||
$('form').submit();
|
||||
document.querySelector("form").submit();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -3,20 +3,10 @@
|
||||
{% load utils %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}
|
||||
{% title 'Authorize Application' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<header class="login-pf-header">
|
||||
<h1>{% trans 'Authorize Application' %}</h1>
|
||||
</header>
|
||||
<form method="POST" action="{{ saml_params.acs_url }}">
|
||||
<form method="POST" class="pf-c-form">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="ACSUrl" value="{{ saml_params.acs_url }}">
|
||||
<input type="hidden" name="RelayState" value="{{ saml_params.relay_state }}" />
|
||||
<input type="hidden" name="SAMLResponse" value="{{ saml_params.saml_response }}" />
|
||||
<div class="login-group">
|
||||
<div class="pf-c-form__group">
|
||||
<h3>
|
||||
{% blocktrans with provider=provider.application.name %}
|
||||
You're about to sign into {{ provider }}
|
||||
@ -28,7 +18,9 @@
|
||||
{% endblocktrans %}
|
||||
<a href="{% url 'passbook_core:auth-logout' %}">{% trans 'Not you?' %}</a>
|
||||
</p>
|
||||
<input class="btn btn-primary btn-block btn-lg" type="submit" value="{% trans 'Continue' %}" />
|
||||
</div>
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<input class="pf-c-button pf-m-primary pf-m-block" type="submit" value="{% trans 'Continue' %}" />
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<div class="c-form__horizontal-group">
|
||||
<p>
|
||||
Expression using <a href="https://jinja.palletsprojects.com/en/2.11.x/templates/">Jinja</a>. Following variables are available:
|
||||
<ul>
|
||||
<ul class="pf-c-list">
|
||||
<li><code>user</code>: Passbook User Object (<a href="https://beryju.github.io/passbook/reference/property-mappings/user-object/">Reference</a>)</li>
|
||||
<li><code>request</code>: Django HTTP Request Object (<a href="https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects">Reference</a>) </li>
|
||||
<li><code>provider</code>: Passbook SAML Provider Object (<a href="https://github.com/BeryJu/passbook/blob/master/passbook/providers/saml/models.py#L16">Reference</a>) </li>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="{{ entity_id }}">
|
||||
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
{% if cert_public_key %}
|
||||
<md:KeyDescriptor use="signing">
|
||||
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:X509Data>
|
||||
@ -8,13 +9,7 @@
|
||||
</ds:X509Data>
|
||||
</ds:KeyInfo>
|
||||
</md:KeyDescriptor>
|
||||
<md:KeyDescriptor use="encryption">
|
||||
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:X509Data>
|
||||
<ds:X509Certificate>{{ cert_public_key }}</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
</ds:KeyInfo>
|
||||
</md:KeyDescriptor>
|
||||
{% endif %}
|
||||
<md:NameIDFormat>{{ subject_format }}</md:NameIDFormat>
|
||||
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="{{ slo_url }}"/>
|
||||
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="{{ sso_post_url }}"/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user