Compare commits

..

64 Commits

Author SHA1 Message Date
23146de2bf new release: 0.7.4-beta 2019-11-20 13:15:46 +01:00
e24f4fe3a8 compose(minor): add error_reporting flag 2019-11-20 13:13:10 +01:00
8e6b69f96f ci(minor): disable gatekeeper build due to quay issue 2019-11-20 13:12:51 +01:00
979bea17ed root(minor): disable error reporting by default 2019-11-20 13:12:37 +01:00
30dba285d9 root(minor): remove build stanza from docker-compose as it causes issues 2019-11-20 13:08:32 +01:00
99fadf2e55 new release: 0.7.3-beta 2019-11-19 18:14:06 +01:00
b606e3d0cb static(minor): remove nginx config from bumpversion 2019-11-19 18:14:03 +01:00
be642bc874 root(major): fix dbbackup not working with prometheus 2019-11-19 18:08:25 +01:00
49a347b32f static(major): switch to pixie for static files 2019-11-19 18:00:29 +01:00
089b48aad1 Merge branch 'agw' 2019-11-11 18:14:03 +01:00
2997cb83b1 providers/appgw(major): rewrite to use oauth2_proxy 2019-11-11 18:13:46 +01:00
08f0aca894 provider/oidc(minor): include claims in id_token 2019-11-11 13:19:54 +01:00
80ea7c40b7 helm(minor): fix monitoring username not being in b64 2019-11-08 15:00:08 +01:00
019a0cb14d new release: 0.7.2-beta 2019-11-08 14:26:52 +01:00
97290755e7 root(major): re-add missing dependencies 2019-11-08 14:26:49 +01:00
7f150c96b4 new release: 0.7.1-beta 2019-11-08 14:04:59 +01:00
73558f30d1 root(minor): revert to django_redis cache 2019-11-08 13:58:10 +01:00
dfcfd87644 root(minor): remove old deps from pipfile 2019-11-08 13:55:58 +01:00
2c0f0a68a8 helm(major): add prometheus rules, add switch to enable/disable monitoring 2019-11-08 13:49:28 +01:00
3d73aac3ab helm(minor): add service monitors 2019-11-08 12:24:42 +01:00
e4fbcd3735 root(major): add prometheus 2019-11-08 12:23:51 +01:00
44c0eb37cf sources/saml(minor): fix lint issue 2019-11-07 18:02:59 +01:00
adc3dcc2c4 sources/saml(minor): disallow login if source is not enabled 2019-11-07 17:35:25 +01:00
bac8227371 sources/saml(minor): fix fields not being shown 2019-11-07 17:28:59 +01:00
73d4d9dfe0 admin(major): fix incorrect permissions being set 2019-11-07 17:25:36 +01:00
afdac5f3f8 Merge branch '10-saml-sp' into 'master'
Resolve "Add SAML SP"

Closes #10

See merge request BeryJu.org/passbook!31
2019-11-07 16:05:28 +00:00
dabce36667 sources/saml(major): add saml SP 2019-11-07 17:02:56 +01:00
3bd56ce522 api(minor): fix invalid fieldls being selected 2019-11-07 10:30:22 +01:00
540419d5c1 helm(minor): update chart to use apps/v1, remove deps from git 2019-11-07 10:24:27 +01:00
ed1fcc3930 new release: 0.7.0-beta 2019-11-02 16:31:23 +00:00
c22ddc5394 root(minor): catch keyboardinput and s3 error from sentry 2019-11-02 16:27:28 +00:00
0544864a3f Merge branch '42-attributeerror-nonetype-object-has-no-attribute-startswith' into 'master'
Resolve "AttributeError: 'NoneType' object has no attribute 'startswith'"

Closes #42

See merge request BeryJu.org/passbook!32
2019-11-01 12:53:43 +00:00
0b9fc9e444 root(minor): fallback to empty string if no Host header ise set 2019-11-01 12:50:38 +00:00
e862b97005 all(major): add API for all objects 2019-10-28 17:55:36 +01:00
cffe09b02e all(major): add most models to API 2019-10-28 17:40:57 +01:00
846a86fb62 fix lint 2019-10-28 14:44:46 +01:00
463c130351 core(major): add api for most simple objects 2019-10-28 14:27:43 +01:00
ffca957838 audit(major): AuditEntry -> Event 2019-10-28 14:26:34 +01:00
543e949a48 api(minor): start with api v2 2019-10-28 14:26:07 +01:00
feb80049aa Merge branch 'master' into guardian 2019-10-25 22:18:13 +02:00
5c59c8ccb6 new release: 0.6.11-beta 2019-10-15 16:56:24 +02:00
1fadd82c65 Merge branch '41-commandconnectorerror-error-running-pg_dump-passbook-host-passbook-postgresql-username-pos' into 'master'
Resolve "CommandConnectorError: Error running:  pg_dump passbook --host=passbook-postgresql --username=pos..."

Closes #41

See merge request BeryJu.org/passbook!30
2019-10-15 14:53:59 +00:00
7e7736126d docker(minor): install pg client for pg_dump 2019-10-15 16:51:42 +02:00
5e0915afce helm(major): update postgresql to 11 2019-10-15 16:51:21 +02:00
bf6c9e8c4a new release: 0.6.10-beta 2019-10-15 16:05:51 +02:00
3353aa0298 root(minor): disable uwsgi request loggin and use custom logging instead 2019-10-15 15:57:37 +02:00
d4cb1a98c7 policy(major): simplify PolicyEngine API, add flag to ignore cache for debug purposes 2019-10-15 15:44:59 +02:00
13f4ea0b8b root(minor): sort keys in log output 2019-10-15 15:40:38 +02:00
261d57ad7b Merge branch 'master' into guardian
# Conflicts:
#	Pipfile
#	Pipfile.lock
#	passbook/admin/views/invitations.py
#	passbook/admin/views/policy.py
#	passbook/admin/views/providers.py
#	passbook/admin/views/sources.py
#	passbook/admin/views/users.py
2019-10-15 15:09:11 +02:00
4086252979 core(major): add integrated database backup 2019-10-15 13:52:33 +02:00
8bdf12cff1 recovery(minor): add unittests 2019-10-14 17:12:56 +02:00
65a065c4ee policy(minor): add unittests for policy engine 2019-10-14 16:08:24 +02:00
a691ee529c new release: 0.6.9-beta 2019-10-14 15:00:30 +02:00
f1c4a62612 policy(major): fix error when policy.negate is enabled 2019-10-14 15:00:20 +02:00
358e39ced0 core(major): remove action field from policy 2019-10-14 13:57:38 +02:00
48c3f68cfc deploy(minor): fix helm syntax for configmap 2019-10-14 13:45:27 +02:00
1849a7c383 ci(minor): use getsentry/sentry-cli for sentry notification, use set-commits 2019-10-14 13:42:43 +02:00
37111fd07b core(minor): merge migrations 2019-10-10 17:41:22 +02:00
143a575369 Merge branch 'master' into guardian
# Conflicts:
#	Pipfile
#	Pipfile.lock
#	passbook/core/models.py
2019-10-10 17:29:34 +02:00
344a8817c3 admin(minor): fix linting 2019-10-10 13:05:03 +02:00
3afb0d4f6d admin(minor): remove partial API 2019-10-10 13:04:20 +02:00
c9714893bb admin(major): rewrite all views to use guardian mixins 2019-10-10 13:01:49 +02:00
3185a86b22 core(minor): add separate permission to reset user's password 2019-10-10 13:01:36 +02:00
a53f7a49ac root(minor): start implementing guardian 2019-10-10 10:45:51 +02:00
153 changed files with 3227 additions and 847 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 0.6.8-beta current_version = 0.7.4-beta
tag = True tag = True
commit = True commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*) parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
@ -23,5 +23,3 @@ values =
[bumpversion:file:passbook/__init__.py] [bumpversion:file:passbook/__init__.py]
[bumpversion:file:docker/nginx.conf]

View File

@ -21,6 +21,7 @@ exclude_lines =
def __str__ def __str__
def __repr__ def __repr__
if self\.debug if self\.debug
if TYPE_CHECKING
# Don't complain if tests don't hit defensive assertion code: # Don't complain if tests don't hit defensive assertion code:
raise AssertionError raise AssertionError

4
.gitignore vendored
View File

@ -192,3 +192,7 @@ pip-selfcheck.json
/static/ /static/
local.env.yml local.env.yml
.vscode/ .vscode/
### Helm ###
# Chart dependencies
**/charts/*.tgz

View File

@ -80,9 +80,9 @@ pylint:
- redis:latest - redis:latest
coverage: coverage:
script: script:
- coverage run manage.py test - coverage run --concurrency=multiprocessing manage.py test
- coverage combine
- coverage report - coverage report
- coverage html
stage: test stage: test
services: services:
- postgres:latest - postgres:latest
@ -96,7 +96,7 @@ build-passbook-server:
before_script: before_script:
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
script: script:
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.beryju.org/passbook/server:latest --destination docker.beryju.org/passbook/server:0.6.8-beta - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.beryju.org/passbook/server:latest --destination docker.beryju.org/passbook/server:0.7.4-beta
only: only:
- tags - tags
- /^version/.*$/ - /^version/.*$/
@ -108,7 +108,7 @@ build-passbook-static:
before_script: before_script:
- echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
script: script:
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/static.Dockerfile --destination docker.beryju.org/passbook/static:latest --destination docker.beryju.org/passbook/static:0.6.8-beta - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/static.Dockerfile --destination docker.beryju.org/passbook/static:latest --destination docker.beryju.org/passbook/static:0.7.4-beta
only: only:
- tags - tags
- /^version/.*$/ - /^version/.*$/
@ -116,6 +116,18 @@ build-passbook-static:
services: services:
- postgres:latest - postgres:latest
- redis:latest - redis:latest
# build-passbook-gatekeeper:
# stage: build
# image:
# name: gcr.io/kaniko-project/executor:debug
# entrypoint: [""]
# before_script:
# - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
# script:
# - /kaniko/executor --context $CI_PROJECT_DIR/gatekeeper --dockerfile $CI_PROJECT_DIR/gatekeeper/Dockerfile --destination docker.beryju.org/passbook/gatekeeper:latest --destination docker.beryju.org/passbook/gatekeeper:0.7.4-beta
# only:
# - tags
# - /^version/.*$/
package-helm: package-helm:
image: debian:stretch-slim image: debian:stretch-slim
@ -136,12 +148,17 @@ package-helm:
- /^version/.*$/ - /^version/.*$/
notify-sentry: notify-sentry:
image: alpine image: getsentry/sentry-cli
stage: post-release stage: post-release
variables:
SENTRY_URL: https://sentry.beryju.org
SENTRY_ORG: beryjuorg
SENTRY_PROJECT: passbook
before_script: before_script:
- apk add curl - apk add curl
script: script:
- "curl $SENTRY_RELEASE -X POST -H 'Content-Type: application/json' -d '{\"version\": \"passbook@0.6.8-beta\"}'" - sentry-cli releases new passbook@0.7.4-beta
- sentry-cli releases set-commits --auto passbook@0.7.4-beta
only: only:
- tags - tags
- /^version/.*$/ - /^version/.*$/

33
Pipfile
View File

@ -4,51 +4,56 @@ url = "https://pypi.org/simple"
verify_ssl = true verify_ssl = true
[packages] [packages]
boto3 = "*"
celery = "*" celery = "*"
cherrypy = "*"
defusedxml = "*" defusedxml = "*"
django = "*" django = "*"
kombu = "==4.5.0"
django-cors-middleware = "*" django-cors-middleware = "*"
django-filters = "*" django-dbbackup = "*"
django-filter = "*"
django-guardian = "*"
django-ipware = "*" django-ipware = "*"
django-model-utils = "*" django-model-utils = "*"
django-oauth-toolkit = "*" django-oauth-toolkit = "*"
django-oidc-provider = "*" django-oidc-provider = "*"
django-otp = "*" django-otp = "*"
django-prometheus = "*"
django-recaptcha = "*" django-recaptcha = "*"
django-redis = "*" django-redis = "*"
django-rest-framework = "*" django-rest-framework = "*"
django-storages = "*"
djangorestframework-guardian = "*"
drf-yasg = "*" drf-yasg = "*"
kombu = "==4.5.0"
ldap3 = "*" ldap3 = "*"
lxml = "*" lxml = "*"
markdown = "*"
oauthlib = "*" oauthlib = "*"
packaging = "*" packaging = "*"
psycopg2-binary = "*" psycopg2-binary = "*"
pycryptodome = "*" pycryptodome = "*"
pyuwsgi = "*"
pyyaml = "*" pyyaml = "*"
qrcode = "*" qrcode = "*"
requests-oauthlib = "*" requests-oauthlib = "*"
sentry-sdk = "*" sentry-sdk = "*"
service_identity = "*" service_identity = "*"
signxml = "*" signxml = "*"
urllib3 = {extras = ["secure"],version = "*"}
structlog = "*" structlog = "*"
pyuwsgi = "*" swagger-spec-validator = "*"
urllib3 = {extras = ["secure"],version = "*"}
[requires] [requires]
python_version = "3.7" python_version = "3.7"
[dev-packages] [dev-packages]
coverage = "*"
isort = "*"
pylint = "==2.3.1"
pylint-django = "*"
prospector = "*"
django-debug-toolbar = "*"
bumpversion = "*"
unittest-xml-reporting = "*"
autopep8 = "*" autopep8 = "*"
bandit = "*" bandit = "*"
bumpversion = "*"
colorama = "*" colorama = "*"
coverage = "*"
django-debug-toolbar = "*"
isort = "*"
prospector = "*"
pylint = "==2.3.1"
pylint-django = "*"
unittest-xml-reporting = "*"

590
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "94b3d5140f0c31dac1fc77af75a0df30ae4fb0571bf6b7fcd722487c63dc1872" "sha256": "716683e8e7794821723dcb671c58b3af32c061c52410148e5b5b6c8fc503c935"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -25,17 +25,17 @@
}, },
"asn1crypto": { "asn1crypto": {
"hashes": [ "hashes": [
"sha256:0b199f211ae690df3db4fd6c1c4ff976497fb1da689193e368eedbadc53d9292", "sha256:7bb1cc02a5620b3d72da4ba070bda2f44f0e61b44dee910a302eddff802b6fb5",
"sha256:bca90060bd995c3f62c4433168eab407e44bdbdb567b3f3a396a676c1a4c4a3f" "sha256:87620880a477123e01177a1f73d0f327210b43a3cdbd714efcd2fa49a8d7b384"
], ],
"version": "==1.0.1" "version": "==1.2.0"
}, },
"attrs": { "attrs": {
"hashes": [ "hashes": [
"sha256:ec20e7a4825331c1b5ebf261d111e16fa9612c1f7a5e1f884f12bd53a664dfd2", "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
"sha256:f913492e1663d3c36f502e5e9ba6cd13cf19d7fab50aa13239e420fef95e1396" "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
], ],
"version": "==19.2.0" "version": "==19.3.0"
}, },
"billiard": { "billiard": {
"hashes": [ "hashes": [
@ -44,6 +44,21 @@
], ],
"version": "==3.6.1.0" "version": "==3.6.1.0"
}, },
"boto3": {
"hashes": [
"sha256:228cea7e2b3be79e5393719641854d4000826d7a7baebede903a616b505b8e17",
"sha256:ad6d50dd5726a12c6442c23aabec0c7e09ef610834d9fbda010bade6888d7677"
],
"index": "pypi",
"version": "==1.10.13"
},
"botocore": {
"hashes": [
"sha256:33ee13a42ee1cc2391a3cd3ce12c84026db20cc76a5700d94fbe07a136d0c354",
"sha256:d1c6f01486566521b59fd5d4f6ba0adf526ed0d1807a0c0ba6604e982d014f3d"
],
"version": "==1.13.13"
},
"celery": { "celery": {
"hashes": [ "hashes": [
"sha256:4c4532aa683f170f40bd76f928b70bc06ff171a959e06e71bf35f2f9d6031ef9", "sha256:4c4532aa683f170f40bd76f928b70bc06ff171a959e06e71bf35f2f9d6031ef9",
@ -61,36 +76,41 @@
}, },
"cffi": { "cffi": {
"hashes": [ "hashes": [
"sha256:041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774", "sha256:0b49274afc941c626b605fb59b59c3485c17dc776dc3cc7cc14aca74cc19cc42",
"sha256:046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d", "sha256:0e3ea92942cb1168e38c05c1d56b0527ce31f1a370f6117f1d490b8dcd6b3a04",
"sha256:066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90", "sha256:135f69aecbf4517d5b3d6429207b2dff49c876be724ac0c8bf8e1ea99df3d7e5",
"sha256:066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b", "sha256:19db0cdd6e516f13329cba4903368bff9bb5a9331d3410b1b448daaadc495e54",
"sha256:2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63", "sha256:2781e9ad0e9d47173c0093321bb5435a9dfae0ed6a762aabafa13108f5f7b2ba",
"sha256:300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45", "sha256:291f7c42e21d72144bb1c1b2e825ec60f46d0a7468f5346841860454c7aa8f57",
"sha256:34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25", "sha256:2c5e309ec482556397cb21ede0350c5e82f0eb2621de04b2633588d118da4396",
"sha256:46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3", "sha256:2e9c80a8c3344a92cb04661115898a9129c074f7ab82011ef4b612f645939f12",
"sha256:4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b", "sha256:32a262e2b90ffcfdd97c7a5e24a6012a43c61f1f5a57789ad80af1d26c6acd97",
"sha256:4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647", "sha256:3c9fff570f13480b201e9ab69453108f6d98244a7f495e91b6c654a47486ba43",
"sha256:4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016", "sha256:415bdc7ca8c1c634a6d7163d43fb0ea885a07e9618a64bda407e04b04333b7db",
"sha256:50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4", "sha256:42194f54c11abc8583417a7cf4eaff544ce0de8187abaf5d29029c91b1725ad3",
"sha256:55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb", "sha256:4424e42199e86b21fc4db83bd76909a6fc2a2aefb352cb5414833c030f6ed71b",
"sha256:5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753", "sha256:4a43c91840bda5f55249413037b7a9b79c90b1184ed504883b72c4df70778579",
"sha256:59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7", "sha256:599a1e8ff057ac530c9ad1778293c665cb81a791421f46922d80a86473c13346",
"sha256:73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9", "sha256:5c4fae4e9cdd18c82ba3a134be256e98dc0596af1e7285a3d2602c97dcfa5159",
"sha256:a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f", "sha256:5ecfa867dea6fabe2a58f03ac9186ea64da1386af2159196da51c4904e11d652",
"sha256:a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8", "sha256:62f2578358d3a92e4ab2d830cd1c2049c9c0d0e6d3c58322993cc341bdeac22e",
"sha256:a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f", "sha256:6471a82d5abea994e38d2c2abc77164b4f7fbaaf80261cb98394d5793f11b12a",
"sha256:a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc", "sha256:6d4f18483d040e18546108eb13b1dfa1000a089bcf8529e30346116ea6240506",
"sha256:ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42", "sha256:71a608532ab3bd26223c8d841dde43f3516aa5d2bf37b50ac410bb5e99053e8f",
"sha256:b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3", "sha256:74a1d8c85fb6ff0b30fbfa8ad0ac23cd601a138f7509dc617ebc65ef305bb98d",
"sha256:d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909", "sha256:7b93a885bb13073afb0aa73ad82059a4c41f4b7d8eb8368980448b52d4c7dc2c",
"sha256:d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45", "sha256:7d4751da932caaec419d514eaa4215eaf14b612cff66398dd51129ac22680b20",
"sha256:dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d", "sha256:7f627141a26b551bdebbc4855c1157feeef18241b4b8366ed22a5c7d672ef858",
"sha256:e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512", "sha256:8169cf44dd8f9071b2b9248c35fc35e8677451c52f795daa2bb4643f32a540bc",
"sha256:e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff", "sha256:aa00d66c0fab27373ae44ae26a66a9e43ff2a678bf63a9c7c1a9a4d61172827a",
"sha256:ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201" "sha256:ccb032fda0873254380aa2bfad2582aedc2959186cce61e3a17abc1a55ff89c3",
"sha256:d754f39e0d1603b5b24a7f8484b22d2904fa551fe865fd0d4c3332f078d20d4e",
"sha256:d75c461e20e29afc0aee7172a0950157c704ff0dd51613506bd7d82b718e7410",
"sha256:dcd65317dd15bc0451f3e01c80da2216a31916bdcffd6221ca1202d96584aa25",
"sha256:e570d3ab32e2c2861c4ebe6ffcad6a8abf9347432a37608fe1fbd157b3f0036b",
"sha256:fd43a88e045cf992ed09fa724b5315b790525f2676883a6ea64e3263bae6549d"
], ],
"version": "==1.12.3" "version": "==1.13.2"
}, },
"chardet": { "chardet": {
"hashes": [ "hashes": [
@ -99,21 +119,6 @@
], ],
"version": "==3.0.4" "version": "==3.0.4"
}, },
"cheroot": {
"hashes": [
"sha256:3ff64073efa35b39d5e107410f5c79664dc8c6c5990651e970740c80ab8878a8",
"sha256:d523a1525258730026aa35b86c8c47c8d0e3892fb89f0f39157d4b32a50edf05"
],
"version": "==8.1.0"
},
"cherrypy": {
"hashes": [
"sha256:033368d25fcc6bca143e7efe9adbfd3a6d91cc0d90c37a649261935f116aafab",
"sha256:683e687e7c7b1ba31ef86a113b1eafd0407269fed175bf488d3c839d37d1cc60"
],
"index": "pypi",
"version": "==18.3.0"
},
"coreapi": { "coreapi": {
"hashes": [ "hashes": [
"sha256:46145fcc1f7017c076a2ef684969b641d18a2991051fddec9458ad3f78ffc1cb", "sha256:46145fcc1f7017c076a2ef684969b641d18a2991051fddec9458ad3f78ffc1cb",
@ -130,24 +135,29 @@
}, },
"cryptography": { "cryptography": {
"hashes": [ "hashes": [
"sha256:24b61e5fcb506424d3ec4e18bca995833839bf13c59fc43e530e488f28d46b8c", "sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c",
"sha256:25dd1581a183e9e7a806fe0543f485103232f940fcfc301db65e630512cce643", "sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595",
"sha256:3452bba7c21c69f2df772762be0066c7ed5dc65df494a1d53a58b683a83e1216", "sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad",
"sha256:41a0be220dd1ed9e998f5891948306eb8c812b512dc398e5a01846d855050799", "sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651",
"sha256:5751d8a11b956fbfa314f6553d186b94aa70fdb03d8a4d4f1c82dcacf0cbe28a", "sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2",
"sha256:5f61c7d749048fa6e3322258b4263463bfccefecb0dd731b6561cb617a1d9bb9", "sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff",
"sha256:72e24c521fa2106f19623a3851e9f89ddfdeb9ac63871c7643790f872a305dfc", "sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d",
"sha256:7b97ae6ef5cba2e3bb14256625423413d5ce8d1abb91d4f29b6d1a081da765f8", "sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42",
"sha256:961e886d8a3590fd2c723cf07be14e2a91cf53c25f02435c04d39e90780e3b53", "sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d",
"sha256:96d8473848e984184b6728e2c9d391482008646276c3ff084a1bd89e15ff53a1", "sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e",
"sha256:ae536da50c7ad1e002c3eee101871d93abdc90d9c5f651818450a0d3af718609", "sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912",
"sha256:b0db0cecf396033abb4a93c95d1602f268b3a68bb0a9cc06a7cff587bb9a7292", "sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793",
"sha256:cfee9164954c186b191b91d4193989ca994703b2fff406f71cf454a2d3c7327e", "sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13",
"sha256:e6347742ac8f35ded4a46ff835c60e68c22a536a8ae5c4422966d06946b6d4c6", "sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7",
"sha256:f27d93f0139a3c056172ebb5d4f9056e770fdf0206c2f422ff2ebbad142e09ed", "sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0",
"sha256:f57b76e46a58b63d1c6375017f4564a28f19a5ca912691fd2e4261b3414b618d" "sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879",
"sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f",
"sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9",
"sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2",
"sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf",
"sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"
], ],
"version": "==2.7" "version": "==2.8"
}, },
"defusedxml": { "defusedxml": {
"hashes": [ "hashes": [
@ -159,11 +169,11 @@
}, },
"django": { "django": {
"hashes": [ "hashes": [
"sha256:4025317ca01f75fc79250ff7262a06d8ba97cd4f82e93394b2a0a6a4a925caeb", "sha256:16040e1288c6c9f68c6da2fe75ebde83c0a158f6f5d54f4c5177b0c1478c5b86",
"sha256:a8ca1033acac9f33995eb2209a6bf18a4681c3e5269a878e9a7e0b7384ed1ca3" "sha256:89c2007ca4fa5b351a51a279eccff298520783b713bf28efb89dfb81c80ea49b"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.2.6" "version": "==2.2.7"
}, },
"django-cors-middleware": { "django-cors-middleware": {
"hashes": [ "hashes": [
@ -173,12 +183,28 @@
"index": "pypi", "index": "pypi",
"version": "==1.4.0" "version": "==1.4.0"
}, },
"django-filters": { "django-dbbackup": {
"hashes": [ "hashes": [
"sha256:1a9799a41106dc53ed894e952a24e8dee9b4fb37f010f22d178c09c90c61d711" "sha256:9470e5d8bdaee4feb878b1b66c59eb9b27a131cccd648bf7cbfe70930acd4fc0"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.2.1" "version": "==3.2.0"
},
"django-filter": {
"hashes": [
"sha256:558c727bce3ffa89c4a7a0b13bc8976745d63e5fd576b3a9a851650ef11c401b",
"sha256:c3deb57f0dd7ff94d7dce52a047516822013e2b441bed472b722a317658cfd14"
],
"index": "pypi",
"version": "==2.2.0"
},
"django-guardian": {
"hashes": [
"sha256:8cf4efd67a863eb32beafd4335a38ffb083630f8ab2045212d27f8f9c3abe5a6",
"sha256:e638c9a23eeac534bb68b133975539ed8782f733ab6f35c0b23b4c39cd06b1bb"
],
"index": "pypi",
"version": "==2.1.0"
}, },
"django-ipware": { "django-ipware": {
"hashes": [ "hashes": [
@ -211,11 +237,19 @@
}, },
"django-otp": { "django-otp": {
"hashes": [ "hashes": [
"sha256:79c8253be97246df86540d551dc705e8fe6ca76af8e8c77f78314cd1b513c2cf", "sha256:0009211222388d8ba4a4840b6de21ff24461fd4aad6c6c194926e3091ac65f06",
"sha256:c5bf3916dca5d53cb377aa6dea40aa785c164013fbf750384137362dfa278cf5" "sha256:a9d39b35f7aa8eee82d6d9769d8004ec538e7d7c2f5a1c5e5525cda90d0e9b69"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.7.2" "version": "==0.7.3"
},
"django-prometheus": {
"hashes": [
"sha256:60f331788f9846891e9ea8d7ccd2928b1042e2e99c8d673f97e2b85f5bc20112",
"sha256:bb2d4f8acd681fa5787df77e7482391017f0090c70473bccd2aa7cad327800ad"
],
"index": "pypi",
"version": "==1.1.0"
}, },
"django-recaptcha": { "django-recaptcha": {
"hashes": [ "hashes": [
@ -240,6 +274,14 @@
"index": "pypi", "index": "pypi",
"version": "==0.1.0" "version": "==0.1.0"
}, },
"django-storages": {
"hashes": [
"sha256:87287b7ad2e789cd603373439994e1ac6f94d9dc2e5f8173d2a87aa3ed458bd9",
"sha256:f3b3def96493d3ccde37b864cea376472baf6e8a596504b209278801c510b807"
],
"index": "pypi",
"version": "==1.7.2"
},
"djangorestframework": { "djangorestframework": {
"hashes": [ "hashes": [
"sha256:5488aed8f8df5ec1d70f04b2114abc52ae6729748a176c453313834a9ee179c8", "sha256:5488aed8f8df5ec1d70f04b2114abc52ae6729748a176c453313834a9ee179c8",
@ -247,6 +289,22 @@
], ],
"version": "==3.10.3" "version": "==3.10.3"
}, },
"djangorestframework-guardian": {
"hashes": [
"sha256:1883756452d9bfcc2a51fb4e039a6837a8f6697c756447aa83af085749b59330",
"sha256:3bd3dd6ea58e1bceca5048faf6f8b1a93bb5dcff30ba5eb91b9a0e190a48a0c7"
],
"index": "pypi",
"version": "==0.3.0"
},
"docutils": {
"hashes": [
"sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0",
"sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827",
"sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"
],
"version": "==0.15.2"
},
"drf-yasg": { "drf-yasg": {
"hashes": [ "hashes": [
"sha256:4cfec631880ae527a91ec7cd3241aea2f82189f59e2f089119aa687761afb227", "sha256:4cfec631880ae527a91ec7cd3241aea2f82189f59e2f089119aa687761afb227",
@ -275,6 +333,13 @@
], ],
"version": "==2.8" "version": "==2.8"
}, },
"importlib-metadata": {
"hashes": [
"sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26",
"sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"
],
"version": "==0.23"
},
"inflection": { "inflection": {
"hashes": [ "hashes": [
"sha256:18ea7fb7a7d152853386523def08736aa8c32636b047ade55f7578c4edeb16ca" "sha256:18ea7fb7a7d152853386523def08736aa8c32636b047ade55f7578c4edeb16ca"
@ -287,13 +352,6 @@
], ],
"version": "==1.1.0" "version": "==1.1.0"
}, },
"jaraco.functools": {
"hashes": [
"sha256:35ba944f52b1a7beee8843a5aa6752d1d5b79893eeb7770ea98be6b637bf9345",
"sha256:e9e377644cee5f6f9128b4dab1631fca74981236e95a255f80e4292bcd2b5284"
],
"version": "==2.0"
},
"jinja2": { "jinja2": {
"hashes": [ "hashes": [
"sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
@ -301,6 +359,20 @@
], ],
"version": "==2.10.3" "version": "==2.10.3"
}, },
"jmespath": {
"hashes": [
"sha256:3720a4b1bd659dd2eecad0666459b9788813e032b83e7ba58578e48254e0a0e6",
"sha256:bde2aef6f44302dfb30320115b17d030798de8c4110e28d5cf6cf91a7a31074c"
],
"version": "==0.9.4"
},
"jsonschema": {
"hashes": [
"sha256:2fa0684276b6333ff3c0b1b27081f4b2305f0a36cf702a23db50edb141893c3f",
"sha256:94c0a13b4a0616458b42529091624e66700a17f847453e52279e35509a5b7631"
],
"version": "==3.1.1"
},
"kombu": { "kombu": {
"hashes": [ "hashes": [
"sha256:389ba09e03b15b55b1a7371a441c894fd8121d174f5583bbbca032b9ea8c9edd", "sha256:389ba09e03b15b55b1a7371a441c894fd8121d174f5583bbbca032b9ea8c9edd",
@ -322,6 +394,7 @@
"sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4", "sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4",
"sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc", "sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc",
"sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1", "sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1",
"sha256:1409b14bf83a7d729f92e2a7fbfe7ec929d4883ca071b06e95c539ceedb6497c",
"sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046", "sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046",
"sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36", "sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36",
"sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5", "sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5",
@ -332,11 +405,14 @@
"sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc", "sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc",
"sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7", "sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7",
"sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38", "sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38",
"sha256:9277562f175d2334744ad297568677056861070399cec56ff06abbe2564d1232",
"sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5", "sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5",
"sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832", "sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832",
"sha256:ae88588d687bd476be588010cbbe551e9c2872b816f2da8f01f6f1fda74e1ef0",
"sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a", "sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a",
"sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f", "sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f",
"sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9", "sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9",
"sha256:c7fccd08b14aa437fe096c71c645c0f9be0655a9b1a4b7cffc77bcb23b3d61d2",
"sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692", "sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692",
"sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84", "sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84",
"sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79", "sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79",
@ -345,14 +421,6 @@
"index": "pypi", "index": "pypi",
"version": "==4.4.1" "version": "==4.4.1"
}, },
"markdown": {
"hashes": [
"sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a",
"sha256:56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"
],
"index": "pypi",
"version": "==3.1.1"
},
"markupsafe": { "markupsafe": {
"hashes": [ "hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
@ -409,46 +477,47 @@
"index": "pypi", "index": "pypi",
"version": "==19.2" "version": "==19.2"
}, },
"portend": { "prometheus-client": {
"hashes": [ "hashes": [
"sha256:19dc27bfb3c72471bd30a235a4d5fbefef8a7e31cab367744b5d87a205e7bfd9", "sha256:71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da"
"sha256:d2dca12e585ce29fc357b31ce424a27c16e2d485029252bbf8ddcc9696207976"
], ],
"version": "==2.5" "version": "==0.7.1"
}, },
"psycopg2-binary": { "psycopg2-binary": {
"hashes": [ "hashes": [
"sha256:080c72714784989474f97be9ab0ddf7b2ad2984527e77f2909fcd04d4df53809", "sha256:040234f8a4a8dfd692662a8308d78f63f31a97e1c42d2480e5e6810c48966a29",
"sha256:110457be80b63ff4915febb06faa7be002b93a76e5ba19bf3f27636a2ef58598", "sha256:086f7e89ec85a6704db51f68f0dcae432eff9300809723a6e8782c41c2f48e03",
"sha256:171352a03b22fc099f15103959b52ee77d9a27e028895d7e5fde127aa8e3bac5", "sha256:18ca813fdb17bc1db73fe61b196b05dd1ca2165b884dd5ec5568877cabf9b039",
"sha256:19d013e7b0817087517a4b3cab39c084d78898369e5c46258aab7be4f233d6a1", "sha256:19dc39616850342a2a6db70559af55b22955f86667b5f652f40c0e99253d9881",
"sha256:249b6b21ae4eb0f7b8423b330aa80fab5f821b9ffc3f7561a5e2fd6bb142cf5d", "sha256:2166e770cb98f02ed5ee2b0b569d40db26788e0bf2ec3ae1a0d864ea6f1d8309",
"sha256:2ac0731d2d84b05c7bb39e85b7e123c3a0acd4cda631d8d542802c88deb9e87e", "sha256:3a2522b1d9178575acee4adf8fd9f979f9c0449b00b4164bb63c3475ea6528ed",
"sha256:2b6d561193f0dc3f50acfb22dd52ea8c8dfbc64bcafe3938b5f209cc17cb6f00", "sha256:3aa773580f85a28ffdf6f862e59cb5a3cc7ef6885121f2de3fca8d6ada4dbf3b",
"sha256:2bd23e242e954214944481124755cbefe7c2cf563b1a54cd8d196d502f2578bf", "sha256:3b5deaa3ee7180585a296af33e14c9b18c218d148e735c7accf78130765a47e3",
"sha256:3e1239242ca60b3725e65ab2f13765fc199b03af9eaf1b5572f0e97bdcee5b43", "sha256:407af6d7e46593415f216c7f56ba087a9a42bd6dc2ecb86028760aa45b802bd7",
"sha256:3eb70bb697abbe86b1d2b1316370c02ba320bfd1e9e35cf3b9566a855ea8e4e5", "sha256:4c3c09fb674401f630626310bcaf6cd6285daf0d5e4c26d6e55ca26a2734e39b",
"sha256:51a2fc7e94b98bd1bb5d4570936f24fc2b0541b63eccadf8fdea266db8ad2f70", "sha256:4c6717962247445b4f9e21c962ea61d2e884fc17df5ddf5e35863b016f8a1f03",
"sha256:52f1bdafdc764b7447e393ed39bb263eccb12bfda25a4ac06d82e3a9056251f6", "sha256:50446fae5681fc99f87e505d4e77c9407e683ab60c555ec302f9ac9bffa61103",
"sha256:5b3581319a3951f1e866f4f6c5e42023db0fae0284273b82e97dfd32c51985cd", "sha256:5057669b6a66aa9ca118a2a860159f0ee3acf837eda937bdd2a64f3431361a2d",
"sha256:63c1b66e3b2a3a336288e4bcec499e0dc310cd1dceaed1c46fa7419764c68877", "sha256:5dd90c5438b4f935c9d01fcbad3620253da89d19c1f5fca9158646407ed7df35",
"sha256:8123a99f24ecee469e5c1339427bcdb2a33920a18bb5c0d58b7c13f3b0298ba3", "sha256:659c815b5b8e2a55193ede2795c1e2349b8011497310bb936da7d4745652823b",
"sha256:85e699fcabe7f817c0f0a412d4e7c6627e00c412b418da7666ff353f38e30f67", "sha256:69b13fdf12878b10dc6003acc8d0abf3ad93e79813fd5f3812497c1c9fb9be49",
"sha256:8dbff4557bbef963697583366400822387cccf794ccb001f1f2307ed21854c68", "sha256:7a1cb80e35e1ccea3e11a48afe65d38744a0e0bde88795cc56a4d05b6e4f9d70",
"sha256:908d21d08d6b81f1b7e056bbf40b2f77f8c499ab29e64ec5113052819ef1c89b", "sha256:7e6e3c52e6732c219c07bd97fff6c088f8df4dae3b79752ee3a817e6f32e177e",
"sha256:af39d0237b17d0a5a5f638e9dffb34013ce2b1d41441fd30283e42b22d16858a", "sha256:7f42a8490c4fe854325504ce7a6e4796b207960dabb2cbafe3c3959cb00d1d7e",
"sha256:af51bb9f055a3f4af0187149a8f60c9d516cf7d5565b3dac53358796a8fb2a5b", "sha256:84156313f258eafff716b2961644a4483a9be44a5d43551d554844d15d4d224e",
"sha256:b2ecac57eb49e461e86c092761e6b8e1fd9654dbaaddf71a076dcc869f7014e2", "sha256:8578d6b8192e4c805e85f187bc530d0f52ba86c39172e61cd51f68fddd648103",
"sha256:cd37cc170678a4609becb26b53a2bc1edea65177be70c48dd7b39a1149cabd6e", "sha256:890167d5091279a27e2505ff0e1fb273f8c48c41d35c5b92adbf4af80e6b2ed6",
"sha256:d17e3054b17e1a6cb8c1140f76310f6ede811e75b7a9d461922d2c72973f583e", "sha256:9aadff9032e967865f9778485571e93908d27dab21d0fdfdec0ca779bb6f8ad9",
"sha256:d305313c5a9695f40c46294d4315ed3a07c7d2b55e48a9010dad7db7a66c8b7f", "sha256:9f24f383a298a0c0f9b3113b982e21751a8ecde6615494a3f1470eb4a9d70e9e",
"sha256:dd0ef0eb1f7dd18a3f4187226e226a7284bda6af5671937a221766e6ef1ee88f", "sha256:a73021b44813b5c84eda4a3af5826dd72356a900bac9bd9dd1f0f81ee1c22c2f",
"sha256:e1adff53b56db9905db48a972fb89370ad5736e0450b96f91bcf99cadd96cfd7", "sha256:afd96845e12638d2c44d213d4810a08f4dc4a563f9a98204b7428e567014b1cd",
"sha256:f0d43828003c82dbc9269de87aa449e9896077a71954fbbb10a614c017e65737", "sha256:b73ddf033d8cd4cc9dfed6324b1ad2a89ba52c410ef6877998422fcb9c23e3a8",
"sha256:f78e8b487de4d92640105c1389e5b90be3496b1d75c90a666edd8737cc2dbab7" "sha256:dbc5cd56fff1a6152ca59445178652756f4e509f672e49ccdf3d79c1043113a4",
"sha256:eac8a3499754790187bb00574ab980df13e754777d346f85e0ff6df929bcd964",
"sha256:eaed1c65f461a959284649e37b5051224f4db6ebdc84e40b5e65f2986f101a08"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.8.3" "version": "==2.8.4"
}, },
"pyasn1": { "pyasn1": {
"hashes": [ "hashes": [
@ -472,70 +541,78 @@
}, },
"pycryptodome": { "pycryptodome": {
"hashes": [ "hashes": [
"sha256:023c294367d7189ae224fb61bc8d49a2347704087c1c78dbd5ab114dd5b97761", "sha256:0aa49f3fa110f8dc090bad1671a768cc17d3d3bd01566641ffc0d10d0fec8d49",
"sha256:0f29e1238ad3b6b6e2acd7ea1d8e8b382978a56503f2c48b67d5dc144d143cb0", "sha256:0fafd3c4fb76c6992f34bf2d074f582f388e3b8062b8ba5d65b020634cc221e6",
"sha256:18f376698e3ddcb1d3b312512ca78c9eed132e68ac6d0bf2e72452dfe213e96f", "sha256:17eb9bd5d30a71b0c8a832e3e9cd2b7723f99907c38dc5dd23e59e8c368a70e2",
"sha256:1de815b847982f909dc2e5e2ca641b85cde80d95cc7e6a359c03d4b42cd21568", "sha256:2776255d5c748782f095ec422d42da2eadd8392ac9de7da23db4aed4231272bd",
"sha256:1ff619b8e4050799ca5ca0ffdf8eb0dbccba6997997866755f37e6aa7dde23fe", "sha256:3500826dc3b9a8fdb762bebe551106081a6bdecd4181a3d1bd0206e48bba8974",
"sha256:233a04bb7bdd4b07e14d61d5166150942d872802daa4f049d49a453fe0659e94", "sha256:3aa0d30326dcdef24c632d5c03b8e4d379c6ae0645082b27dd69ea816bb97ecb",
"sha256:33c07e1e36ec84524b49f99f11804d5e4d2188c643e84d914cb1e0a277ed3c79", "sha256:3c7769bdadcc4809508e71997008912cc6d94fd7b5b1f3ef121683ebcac71d81",
"sha256:3701822a085dbebf678bfbdfbd6ebd92ffa80d5a544c9979984bf16a67c9790b", "sha256:3e8c97a38dac6dafd180b4696a522b1581dd1a8e0ea60763458be547bac97361",
"sha256:3f8e6851c0a45429f9b86c1597d3b831b0cff140b3e170a891fce55ef8dac2bb", "sha256:5aca5125a46e458b308b5571ce8fe36d2229f161aa7db27b3ecacded70c6aa8b",
"sha256:4f6cdddf1fe72e7f173e9734aa19b94cbd046b61a8559d650ff222e36021d5c1", "sha256:62beb75f0688f406946312bfef8923d8ab23f5b8013acded931413625299d317",
"sha256:52d20b22c5b1fc952b4c686b99a6c55c3b0b0a673bec30570f156a72198f66ff", "sha256:7725643de3c884a9945a086670787dce637037f32c5c2df7fd602bd5967f3486",
"sha256:5452b534fecf8bf57cf9106d00877f5f4ab7264e7a5e1f5ea8d15b04517d1255", "sha256:872191a02a0c2a3b98dc75c62b32912b220a8ae5ff6ac9e39868f903f55dd6a4",
"sha256:5a7a9a4a7f8f0990fa97fee71c7f7e0c412925c515cfc6d4996961e92c9be8e5", "sha256:8c501e80960d12328d49e1d409daf426f29364a37c602f257c99509999654650",
"sha256:600bf9dd5fbed0feee83950e2a8baacaa1f38b56c237fff270d31e47f8da9e52", "sha256:9512638bfef8ffc94c62751965a4733c3792104dc84771ba54ce0f80f49134df",
"sha256:6840c9881e528224ebf72b3f73b3d11baf399e265106c9f4d9bae4f09615a93a", "sha256:962043051afa7a5ab071b0d8996dc00e564327a18566d3e574a39cb6e097b462",
"sha256:71b041d43fe13004abc36ca720ac64ea489ee8a3407a25116481d0faf9d62494", "sha256:9db72b18b30902a83fa57b0d7dae4ce24f85186695e3bea0d423f1ec7c5b3fbe",
"sha256:7252498b427c421e306473ed344e58235eedd95c15fec2e1b33d333aefa1ea10", "sha256:9ffd4f0bfb5949dfa0e5cedef836364f18da0deb2fba04671607fb3b59b29112",
"sha256:8d2135c941d38f241e0e62dbdfc1ca5d9240527e61316126797f50b6f3e49825", "sha256:a26819f693cf5fc0a2373a3e4b91c38e359cad9f00020a885b667c77f28738d5",
"sha256:a0962aea03933b99cf391c3e10dfef32f77915d5553464264cfbc6711f31d254", "sha256:a3efc575a53511c48361d933e12e07c2eb940db1afda0995285176c372ab7352",
"sha256:a117047a220b3911d425affcd1cbc97a1af7ea7eb5d985d9964d42b4f0558489", "sha256:ababd6685b9d94729a851a0615482156afdacbeaabeea60f67961db0e975b1af",
"sha256:a35a5c588248ba00eb976a8554211e584a55de286783bc69b12bdd7954052b4a", "sha256:b0e9c8c270cd3f8c73b53139f0708f257189a00bbc898be6d3f03995e5f7edc2",
"sha256:c1a4f3f651471b9bf60b0d98fa8a994b8a73ff8ab4edc691e23243c853aaff9f", "sha256:b74173b13c221ee96b608212b9adc2c459a73d3632f04490df42e4f07e7041e6",
"sha256:c419943306756ddd1a1997120bb073733bc223365909c68185106d5521cbc0ef", "sha256:bed297f75ba19cefe2d10beb4959f4f8cb62c2560a3998ad87479485098ee939",
"sha256:c453ad968b67d66448543420ec39770c30bd16d986058255f058ab87c4f6cc1f", "sha256:c639f09e8ce8ad5af9884233f952ade4b73a11b7d41d3b9bb7d4e64d9e1df164",
"sha256:d2d78644655629c7d1b9bf28e479d29facc0949d9ff095103ca9c2314b329ee0", "sha256:c7bc308be67288af1cd44668d59e36356f0ce518337899079ddb0235bd55db79",
"sha256:d7be60dc2126ee350ac7191549f5ab05c2dd76a5d5a3022249f395a401c6ea37", "sha256:cca152dcebc318833ba70499190ce17ee81b525404e2a7548c77f52b439306a7",
"sha256:dbeb08ad850056747aa7d5f33273b7ce0b9a77910604a1be7b7a6f2ef076213f", "sha256:d5261d22bc3a54db26f11dabcda14bbaab72080977e083d795b4b1d1b510c774",
"sha256:f02382dc1bf91fb7123f2a3851fb1b526c871fa9359f387f2bcc847efc74ae52" "sha256:d81111e3da7fc9eee825ba7d8a68b3c1464f41110ef98a7280e0c7fb82c91e73",
"sha256:d95fafa899abb9f82e55ff43f423e100784312b43932514f2c05d41cbb20323e",
"sha256:de411a64d4105d4424441833bd25943208e58c846abf981bba5bbeeba88a49c3",
"sha256:e02c7b3d05b88ff1a236e49a252b2bf8444d3a1d04a056784af766c0909eba36",
"sha256:fbafe9b01b717e0bfbc83cd740ff5bf5cdd3f208815be470ea203942b899bbdf"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.9.0" "version": "==3.9.1"
}, },
"pycryptodomex": { "pycryptodomex": {
"hashes": [ "hashes": [
"sha256:020928b2831b2047288c9143f41c6690eb669d60761c7ca8c5ca743a2c51517c", "sha256:0713fc29cddb14f977887ccf3199d1a00d0b040e8c35785df20d107ad59efabc",
"sha256:0ce1950ba6544eca4d6fd7386e2502d4bd871fcbd5e5b977604f48ea37b29fc6", "sha256:110651378be063d5e0e653d107a14b511bd45c355968a32270f5b1bf8c093056",
"sha256:0d5b1159a24a56fd3359b7b1aa1e4331c394033eababb2972bb923d6767968db", "sha256:158428c0f337984cb3611484d9f61faea973aec624c8f88c5809ab88adab0884",
"sha256:11453e8628cdccbcb08e04405298d659c0c0458cf9bf23eaaa3c201f8d635389", "sha256:17625d9f9442d3567b2532795c9232ed80cc1d6c91064ad48c802f3bff2b937d",
"sha256:22e050089f60e70b97909fe62612ee9589f0be1c928c2aa637f2534eddf61632", "sha256:179125d0b2bcbf5cf9ddf9fb74fe13e30d19fb1c2691cac43b8b37d74df9ddf6",
"sha256:27317f1e8e496a2f208b1c40da425d5fe760b494f95c847bb7c3074c95a8edcb", "sha256:227e660ee3835284fc6195163c467f8d21a1de51d0aa85d32157a1fc4bf16b9a",
"sha256:32e2fe1d0c5fada45b22b647f88367b210dfea40a5cc849b142b4e9fa497c488", "sha256:2f651173c4bb8de6a96493e5cc03b2838eedf4bb1cbfbe2b354e40a2f2f245fc",
"sha256:3a998b390a80fd0d22c7d9fbaf49a9a11772ef90495a8baecdea2e6d09929937", "sha256:33b0e5c9ca02c099ec537138e8ffee1e4d054e49d69258062d89ddbd9f660000",
"sha256:46dda35fbed5426794ab64d483d6257dc43f52e78ba934563492df7cb89f7de6", "sha256:42bead6e7dbca9328a6601ff41d25554606847d92b0fd198ca3f6c971c662c07",
"sha256:4846ca0f2363bdb934c556667b056331d4aabd48f20924b0c5583a49d764d3fc", "sha256:478cce6245e8ff8cda8f733ef1a1161ee6bf5aaa45312e1ace6c7b80fbc1e01f",
"sha256:550f5e6f07b091f986023f871fa8a2bde9875ccae51d4bd07b31fa9855fe994f", "sha256:4ce38cb16b6f41c4b579e3e9a9d66c36ba24192cc0518ce09313c25ae44d2d74",
"sha256:561905b459de41c3ad19912cdcd88c8e24295d01e97b7b2a63d4188c8e4e0dbc", "sha256:4e0bc594c61bd1db86c0060a5eb351c22a6c4c154315a52af1c8cd24c4e6a8a3",
"sha256:5745ca86a4e88a775b7cace28b947a86661d5cc09ecc1c8d97293a7d20c1bb79", "sha256:4e1e616d12f79f256109de14aebcee1bf7e0a78d00b3de6c9a0cf2eb2a80785c",
"sha256:5c2a3bb28dde992f97d856937e973dda0462bf3acb7d0009308a81159a35323b", "sha256:5e4b459ccd6bfe55cc6b030b8983040bc8956f5757b621ae32dd0a26b0f85a91",
"sha256:73a8acc8ff7f09d482e481757d92a250f803e66e0f248019df90a69e61840180", "sha256:61a586b0cb85bc8c60af4ddcae24928a3476c944cb37eb7b9066965bc1d4b4d8",
"sha256:8601613ebc329b853e466f581ad1156638989926e0dcdf52952542a89883836c", "sha256:643bea8898e875e54177c546f2ac704317937230379a9d295ece844c79e00cdb",
"sha256:8b604f4fa1de456d6d19771b01c2823675a75a2c60e51a6b738f71fdfe865370", "sha256:7403d7addaaa4649777ce487832ef8421222960a10d7a95b0f2c9efd217a93e6",
"sha256:96f8622cb8061f4aca95e52cc835659f024bc2e237ee6a9d01117873b7490b98", "sha256:7f36378a699f201aea3e431a3c217c16e63abbe84ddb8d9bd0af9b28e3f826aa",
"sha256:a01c99532c5f7ab96274b5c9f3e135315b79b55ba5c8233fc4d029e0369e94df", "sha256:9146a6cf9eeb4683cfffabc7093fd1063076185d790680596f7a2dfb40f6b4b9",
"sha256:c63040e0313e27b62b0f4295f41adecf96cde7ff4d49f653b81b1958cb1180bf", "sha256:9829d8aa2fb52646eda9041b785e9c6825fc1f1054f2254046fb7628800acb8e",
"sha256:c812cb9f3af63da8eaa251e7e48f8b38c4e40974d2bdae2f0ca7a7a12549727a", "sha256:aa18ad3da8da74cbd119a6c5460079c7357ba8775b2edbc5a78722fc1e52f881",
"sha256:cb9e8ef672b7a961f90e0a497718e0f052f76324f216840a4ec30248e4d19f20", "sha256:ad39a8d3be6c5aad42b1ef839c49a50185618b26d5f1b555b1edd4d9d700e3b9",
"sha256:ce8edda46374c344de87089f9887ad4dd317bb4a22f91f1844202eaf14b08de0", "sha256:b10bb3c640d7666993d5b0aec0e5334131386eddbd200aabcc123fe07c2b8928",
"sha256:de58de0d5f2fb9253707ee718e1378f2194fdd394cdbed1b6464ab44642f5217", "sha256:b17b2f5f65dffdeddf06bb82eb73a6aa55766322c3c45bc5032f9e3259adfdb0",
"sha256:e0100f9b93d0119d846a33e6cb5001ee208519b81c6acf76da614b71de75885b", "sha256:ba5bce9e1fc21160c27015a705e80f49901f1c42aa8bf96ed1d650ce4b5311bd",
"sha256:e530b77bdff5c2bf3065e6a088e1602ad193b43e285bac196d4b8820308ec6bb", "sha256:c2b867277ef5a996b2198bec149abaeaeddbe57a77a4f6840882be382af72297",
"sha256:f048069aa7b530f1c5e84d55c2b28ca7a7272bb3b8d28829d454a94bec6529a8", "sha256:c43d5d7516b0dc8436aef6bf9ebb9fbeaebcbbc4cb1b6a23be4a5f843c2614e3",
"sha256:f6a9271c842e93c349b6007676a62d03dca712c9f4dff66c3270d50504ca9014" "sha256:ce65a7dc9162a6e676f336e45f6602297981afa82f8e7ccc690667316c6b449b",
"sha256:d9a38c3a85dd3dc6cae43eac94b73485fd7e5a1daf74bb510d7220a8b18482d2",
"sha256:de39d7c456147755e5610177bd50cb7c89f74477d608b5ac055fed4e7c4c35c1",
"sha256:ea368b7b4f36c5524d7b47aa583db604085958b92ff6580075230c8d7c88cdbe",
"sha256:ff75fa26b7f8e1eaeba9edfc50b1d21bca913e743ce993a189b07bf483bedda0"
], ],
"version": "==3.9.0" "version": "==3.9.1"
}, },
"pyjwkest": { "pyjwkest": {
"hashes": [ "hashes": [
@ -552,10 +629,24 @@
}, },
"pyparsing": { "pyparsing": {
"hashes": [ "hashes": [
"sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", "sha256:4acadc9a2b96c19fe00932a38ca63e601180c39a189a696abce1eaab641447e1",
"sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4" "sha256:61b5ed888beab19ddccab3478910e2076a6b5a0295dffc43021890e136edf764"
], ],
"version": "==2.4.2" "version": "==2.4.4"
},
"pyrsistent": {
"hashes": [
"sha256:eb6545dbeb1aa69ab1fb4809bfbf5a8705e44d92ef8fc7c2361682a47c46c778"
],
"version": "==0.15.5"
},
"python-dateutil": {
"hashes": [
"sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
"sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
],
"markers": "python_version >= '2.7'",
"version": "==2.8.0"
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
@ -579,6 +670,7 @@
"sha256:861c94442b28cd64af033e88e0f63c66dbd5609f67952dc18694098b47a43f3a", "sha256:861c94442b28cd64af033e88e0f63c66dbd5609f67952dc18694098b47a43f3a",
"sha256:957bc6316ffc8463795d56d9953d58e7f32aa5aad1c5ac80bc45c69f3299961e", "sha256:957bc6316ffc8463795d56d9953d58e7f32aa5aad1c5ac80bc45c69f3299961e",
"sha256:9760c3f56fb5f15852d163429096600906478e9ed2c189a52f2bb21d8a2a986c", "sha256:9760c3f56fb5f15852d163429096600906478e9ed2c189a52f2bb21d8a2a986c",
"sha256:9fdfb98a2992de01e8efad2aeed22c825e36db628b144b2d6b93d81fb549f811",
"sha256:a4b24703ea818196d0be1dc64b3b57b79c67e8dee0cfa207a4216220912035a7", "sha256:a4b24703ea818196d0be1dc64b3b57b79c67e8dee0cfa207a4216220912035a7",
"sha256:ad7f4968c1ddbf139a306d9b075360d959cc554d994ba5e1f512af9a40e62357", "sha256:ad7f4968c1ddbf139a306d9b075360d959cc554d994ba5e1f512af9a40e62357",
"sha256:b1127d34b90f74faf1707718c57a4193ac028b9f4aec0238638983132297d456", "sha256:b1127d34b90f74faf1707718c57a4193ac028b9f4aec0238638983132297d456",
@ -589,6 +681,7 @@
"sha256:ce777ebdf49ce736fc04abf555b5c41ab3f130127543a689dcf8d4871cd18fe4", "sha256:ce777ebdf49ce736fc04abf555b5c41ab3f130127543a689dcf8d4871cd18fe4",
"sha256:d8b4bf930b6a19bc9ee982b9163d948c87501ad91b71516924e8ed25fe85d2ee", "sha256:d8b4bf930b6a19bc9ee982b9163d948c87501ad91b71516924e8ed25fe85d2ee",
"sha256:e2a420f2c4d35f3ec0b7e752a80d7bd385e2c5a64f67c05f2d2d74230e3114b6", "sha256:e2a420f2c4d35f3ec0b7e752a80d7bd385e2c5a64f67c05f2d2d74230e3114b6",
"sha256:ef5eb630f541af6b69378d58594be90a0922fa6d6a50a9248c25b9502585f6bf",
"sha256:fed899ce96f4f2b4d1b9f338dd145a4040ee1d8a5152213af0dd8d4a4d36e9fe" "sha256:fed899ce96f4f2b4d1b9f338dd145a4040ee1d8a5152213af0dd8d4a4d36e9fe"
], ],
"index": "pypi", "index": "pypi",
@ -623,10 +716,10 @@
}, },
"redis": { "redis": {
"hashes": [ "hashes": [
"sha256:98a22fb750c9b9bb46e75e945dc3f61d0ab30d06117cbb21ff9cd1d315fedd3b", "sha256:3613daad9ce5951e426f460deddd5caf469e08a3af633e9578fc77d362becf62",
"sha256:c504251769031b0dd7dd5cf786050a6050197c6de0d37778c80c08cb04ae8275" "sha256:8d0fc278d3f5e1249967cba2eb4a5632d19e45ce5c09442b8422d15ee2c22cc2"
], ],
"version": "==3.3.8" "version": "==3.3.11"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
@ -637,11 +730,11 @@
}, },
"requests-oauthlib": { "requests-oauthlib": {
"hashes": [ "hashes": [
"sha256:bd6533330e8748e94bf0b214775fed487d309b8b8fe823dc45641ebcd9a32f57", "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
"sha256:d3ed0c8f2e3bbc6b344fa63d6f933745ab394469da38db16bdddb461c7e25140" "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.2.0" "version": "==1.3.0"
}, },
"ruamel.yaml": { "ruamel.yaml": {
"hashes": [ "hashes": [
@ -674,13 +767,20 @@
"markers": "platform_python_implementation == 'CPython' and python_version < '3.8'", "markers": "platform_python_implementation == 'CPython' and python_version < '3.8'",
"version": "==0.2.0" "version": "==0.2.0"
}, },
"s3transfer": {
"hashes": [
"sha256:6efc926738a3cd576c2a79725fed9afde92378aa5c6a957e3af010cb019fac9d",
"sha256:b780f2411b824cb541dbcd2c713d0cb61c7d1bcadae204cdddda2b35cef493ba"
],
"version": "==0.2.1"
},
"sentry-sdk": { "sentry-sdk": {
"hashes": [ "hashes": [
"sha256:15e51e74b924180c98bcd636cb4634945b0a99a124d50b433c3a9dc6a582e8db", "sha256:09e1e8f00f22ea580348f83bbbd880adf40b29f1dec494a8e4b33e22f77184fb",
"sha256:1d6a2ee908ec6d8f96c27d78bc39e203df4d586d287c233140af7d8d1aca108a" "sha256:ff1fa7fb85703ae9414c8b427ee73f8363232767c9cd19158f08f6e4f0b58fc7"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.12.3" "version": "==0.13.2"
}, },
"service-identity": { "service-identity": {
"hashes": [ "hashes": [
@ -700,10 +800,10 @@
}, },
"six": { "six": {
"hashes": [ "hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
], ],
"version": "==1.12.0" "version": "==1.13.0"
}, },
"sqlparse": { "sqlparse": {
"hashes": [ "hashes": [
@ -714,18 +814,19 @@
}, },
"structlog": { "structlog": {
"hashes": [ "hashes": [
"sha256:5feae03167620824d3ae3e8915ea8589fc28d1ad6f3edf3cc90ed7c7cb33fab5", "sha256:4287058cf4ce1a59bc5dea290d6386d37f29a37529c9a51cdf7387e51710152b",
"sha256:db441b81c65b0f104a7ce5d86c5432be099956b98b8a2c8be0b3fb3a7a0b1536" "sha256:6640e6690fc31d5949bc614c1a630464d3aaa625284aeb7c6e486c3010d73e12"
], ],
"index": "pypi", "index": "pypi",
"version": "==19.1.0" "version": "==19.2.0"
}, },
"tempora": { "swagger-spec-validator": {
"hashes": [ "hashes": [
"sha256:cb60b1d2b1664104e307f8e5269d7f4acdb077c82e35cd57246ae14a3427d2d6", "sha256:57e29feb3aa921a9fb98bd70af148746b27c77d3207266f5571cebcce211e685",
"sha256:d28a03d2f64ee81aec6e6bff374127ef306fe00c1b7e27c7ff1618344221a699" "sha256:62ef22eca3f429d93fddda5d793d2a1a9057d3732e7a14606e641805326ae4a6"
], ],
"version": "==1.14.1" "index": "pypi",
"version": "==2.4.3"
}, },
"uritemplate": { "uritemplate": {
"hashes": [ "hashes": [
@ -744,6 +845,7 @@
"sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86"
], ],
"index": "pypi", "index": "pypi",
"markers": null,
"version": "==1.25.6" "version": "==1.25.6"
}, },
"vine": { "vine": {
@ -753,12 +855,12 @@
], ],
"version": "==1.3.0" "version": "==1.3.0"
}, },
"zc.lockfile": { "zipp": {
"hashes": [ "hashes": [
"sha256:307ad78227e48be260e64896ec8886edc7eae22d8ec53e4d528ab5537a83203b", "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
"sha256:cc33599b549f0c8a248cb72f3bf32d77712de1ff7ee8814312eb6456b42c015f" "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
], ],
"version": "==2.0" "version": "==0.6.0"
} }
}, },
"develop": { "develop": {
@ -840,11 +942,11 @@
}, },
"django": { "django": {
"hashes": [ "hashes": [
"sha256:4025317ca01f75fc79250ff7262a06d8ba97cd4f82e93394b2a0a6a4a925caeb", "sha256:16040e1288c6c9f68c6da2fe75ebde83c0a158f6f5d54f4c5177b0c1478c5b86",
"sha256:a8ca1033acac9f33995eb2209a6bf18a4681c3e5269a878e9a7e0b7384ed1ca3" "sha256:89c2007ca4fa5b351a51a279eccff298520783b713bf28efb89dfb81c80ea49b"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.2.6" "version": "==2.2.7"
}, },
"django-debug-toolbar": { "django-debug-toolbar": {
"hashes": [ "hashes": [
@ -869,10 +971,10 @@
}, },
"gitpython": { "gitpython": {
"hashes": [ "hashes": [
"sha256:631263cc670aa56ce3d3c414cf0fe2e840f2e913514b138ea28d88a477bbcd21", "sha256:3237caca1139d0a7aa072f6735f5fd2520de52195e0fa1d8b83a9b212a2498b2",
"sha256:6e97b9f0954807f30c2dd8e3165731ed6c477a1b365f194b69d81d7940a08332" "sha256:a7d6bef0775f66ba47f25911d285bcd692ce9053837ff48a120c2b8cf3a71389"
], ],
"version": "==3.0.3" "version": "==3.0.4"
}, },
"isort": { "isort": {
"hashes": [ "hashes": [
@ -884,26 +986,29 @@
}, },
"lazy-object-proxy": { "lazy-object-proxy": {
"hashes": [ "hashes": [
"sha256:02b260c8deb80db09325b99edf62ae344ce9bc64d68b7a634410b8e9a568edbf", "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d",
"sha256:18f9c401083a4ba6e162355873f906315332ea7035803d0fd8166051e3d402e3", "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449",
"sha256:1f2c6209a8917c525c1e2b55a716135ca4658a3042b5122d4e3413a4030c26ce", "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08",
"sha256:2f06d97f0ca0f414f6b707c974aaf8829c2292c1c497642f63824119d770226f", "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a",
"sha256:616c94f8176808f4018b39f9638080ed86f96b55370b5a9463b2ee5c926f6c5f", "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50",
"sha256:63b91e30ef47ef68a30f0c3c278fbfe9822319c15f34b7538a829515b84ca2a0", "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd",
"sha256:77b454f03860b844f758c5d5c6e5f18d27de899a3db367f4af06bec2e6013a8e", "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239",
"sha256:83fe27ba321e4cfac466178606147d3c0aa18e8087507caec78ed5a966a64905", "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb",
"sha256:84742532d39f72df959d237912344d8a1764c2d03fe58beba96a87bfa11a76d8", "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea",
"sha256:874ebf3caaf55a020aeb08acead813baf5a305927a71ce88c9377970fe7ad3c2", "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e",
"sha256:9f5caf2c7436d44f3cec97c2fa7791f8a675170badbfa86e1992ca1b84c37009", "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156",
"sha256:a0c8758d01fcdfe7ae8e4b4017b13552efa7f1197dd7358dc9da0576f9d0328a", "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142",
"sha256:a4def978d9d28cda2d960c279318d46b327632686d82b4917516c36d4c274512", "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442",
"sha256:ad4f4be843dace866af5fc142509e9b9817ca0c59342fdb176ab6ad552c927f5", "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62",
"sha256:ae33dd198f772f714420c5ab698ff05ff900150486c648d29951e9c70694338e", "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db",
"sha256:b4a2b782b8a8c5522ad35c93e04d60e2ba7f7dcb9271ec8e8c3e08239be6c7b4", "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531",
"sha256:c462eb33f6abca3b34cdedbe84d761f31a60b814e173b98ede3c81bb48967c4f", "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383",
"sha256:fd135b8d35dfdcdb984828c84d695937e58cc5f49e1c854eb311c4d6aa03f4f1" "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a",
"sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357",
"sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4",
"sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"
], ],
"version": "==1.4.2" "version": "==1.4.3"
}, },
"mccabe": { "mccabe": {
"hashes": [ "hashes": [
@ -1029,10 +1134,10 @@
}, },
"six": { "six": {
"hashes": [ "hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
], ],
"version": "==1.12.0" "version": "==1.13.0"
}, },
"smmap2": { "smmap2": {
"hashes": [ "hashes": [
@ -1064,20 +1169,25 @@
}, },
"typed-ast": { "typed-ast": {
"hashes": [ "hashes": [
"sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161",
"sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
"sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
"sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
"sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
"sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47",
"sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
"sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
"sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
"sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
"sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2",
"sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e",
"sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
"sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
"sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
"sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
"sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
"sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
"sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66",
"sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
], ],
"markers": "implementation_name == 'cpython'", "markers": "implementation_name == 'cpython'",
@ -1085,11 +1195,11 @@
}, },
"unittest-xml-reporting": { "unittest-xml-reporting": {
"hashes": [ "hashes": [
"sha256:140982e4b58e4052d9ecb775525b246a96bfc1fc26097806e05ea06e9166dd6c", "sha256:358bbdaf24a26d904cc1c26ef3078bca7fc81541e0a54c8961693cc96a6f35e0",
"sha256:d1fbc7a1b6c6680ccfe75b5e9701e5431c646970de049e687b4bb35ba4325d72" "sha256:9d28ddf6524cf0ff9293f61bd12e792de298f8561a5c945acea63fb437789e0e"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.5.1" "version": "==2.5.2"
}, },
"wrapt": { "wrapt": {
"hashes": [ "hashes": [

View File

@ -4,6 +4,8 @@
``` ```
export PASSBOOK_DOMAIN=domain.tld export PASSBOOK_DOMAIN=domain.tld
# Optionally enable Error-reporting
# export PASSBOOK_ERROR_REPORTING=true
docker-compose pull docker-compose pull
docker-compose up -d docker-compose up -d
docker-compose exec server ./manage.py migrate docker-compose exec server ./manage.py migrate

View File

@ -16,5 +16,8 @@ COPY --from=locker /app/requirements-dev.txt /app/
WORKDIR /app/ WORKDIR /app/
RUN pip install -r requirements.txt --no-cache-dir && \ RUN apt-get update && \
apt-get install -y --no-install-recommends postgresql-client-11 && \
rm -rf /var/lib/apt/ && \
pip install -r requirements.txt --no-cache-dir && \
adduser --system --no-create-home --uid 1000 --group --home /app passbook adduser --system --no-create-home --uid 1000 --group --home /app passbook

View File

@ -21,8 +21,6 @@ services:
labels: labels:
- traefik.enable=false - traefik.enable=false
server: server:
build:
context: .
image: docker.beryju.org/passbook/server:${SERVER_TAG:-latest} image: docker.beryju.org/passbook/server:${SERVER_TAG:-latest}
command: command:
- uwsgi - uwsgi
@ -30,6 +28,7 @@ services:
environment: environment:
- PASSBOOK_DOMAIN=${PASSBOOK_DOMAIN} - PASSBOOK_DOMAIN=${PASSBOOK_DOMAIN}
- PASSBOOK_REDIS__HOST=redis - PASSBOOK_REDIS__HOST=redis
- PASSBOOK_ERROR_REPORTING=${PASSBOOK_ERROR_REPORTING:-false}
- PASSBOOK_POSTGRESQL__HOST=postgresql - PASSBOOK_POSTGRESQL__HOST=postgresql
- PASSBOOK_POSTGRESQL__PASSWORD=${PG_PASS:-thisisnotagoodpassword} - PASSBOOK_POSTGRESQL__PASSWORD=${PG_PASS:-thisisnotagoodpassword}
ports: ports:
@ -57,18 +56,16 @@ services:
environment: environment:
- PASSBOOK_DOMAIN=${PASSBOOK_DOMAIN} - PASSBOOK_DOMAIN=${PASSBOOK_DOMAIN}
- PASSBOOK_REDIS__HOST=redis - PASSBOOK_REDIS__HOST=redis
- PASSBOOK_ERROR_REPORTING=${PASSBOOK_ERROR_REPORTING:-false}
- PASSBOOK_POSTGRESQL__HOST=postgresql - PASSBOOK_POSTGRESQL__HOST=postgresql
- PASSBOOK_POSTGRESQL__PASSWORD=${PG_PASS:-thisisnotagoodpassword} - PASSBOOK_POSTGRESQL__PASSWORD=${PG_PASS:-thisisnotagoodpassword}
static: static:
build:
context: .
dockerfile: static.Dockerfile
image: docker.beryju.org/passbook/static:latest image: docker.beryju.org/passbook/static:latest
networks: networks:
- internal - internal
labels: labels:
- traefik.frontend.rule=PathPrefix:/static, /robots.txt - traefik.frontend.rule=PathPrefix:/static, /robots.txt
- traefik.port=80 - traefik.port=8080
- traefik.docker.network=internal - traefik.docker.network=internal
traefik: traefik:
image: traefik:1.7 image: traefik:1.7

View File

@ -1,66 +0,0 @@
user nginx;
worker_processes 1;
error_log stderr warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format json_combined escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status": "$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}';
access_log /dev/stdout json_combined;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
server {
server_name _;
gzip on;
gzip_types application/javascript image/* text/css;
gunzip on;
add_header X-passbook-Version 0.6.8-beta;
add_header Vary X-passbook-Version;
root /data/;
location /_/healthz {
return 204;
}
location ~* \.(jpg|jpeg|png|gif|ico)$ {
expires 30d;
}
location ~* \.(css|js)$ {
expires 7d;
}
}
server {
listen 8080;
location = /stub_status {
stub_status;
}
}
}

View File

@ -1,6 +1,5 @@
[uwsgi] [uwsgi]
http = 0.0.0.0:8000 http = 0.0.0.0:8000
chdir = /app
wsgi-file = passbook/root/wsgi.py wsgi-file = passbook/root/wsgi.py
processes = 2 processes = 2
master = true master = true
@ -8,3 +7,4 @@ threads = 2
enable-threads = true enable-threads = true
uid = passbook uid = passbook
gid = passbook gid = passbook
disable-logging=True

8
gatekeeper/Dockerfile Normal file
View File

@ -0,0 +1,8 @@
FROM quay.io/pusher/oauth2_proxy
COPY templates /templates
ENV OAUTH2_PROXY_EMAIL_DOMAINS=*
ENV OAUTH2_PROXY_PROVIDER=oidc
ENV OAUTH2_PROXY_CUSTOM_TEMPLATES_DIR=/templates
ENV OAUTH2_PROXY_HTTP_ADDRESS=:4180

View File

@ -0,0 +1,18 @@
{{define "error.html"}}
<!DOCTYPE html>
<html lang="en" charset="utf-8">
<head>
<title>{{.Title}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
</head>
<body>
<h2>{{.Title}}</h2>
<p>{{.Message}}</p>
<hr>
<p><a href="{{.ProxyPrefix}}/sign_in">Sign In</a></p>
</body>
</html>
{{end}}

View File

@ -0,0 +1,119 @@
{{define "sign_in.html"}}
<!DOCTYPE html>
<html lang="en" charset="utf-8">
<head>
<title>Sign In with passbook</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.42857143;
color: #333;
background: #f0f0f0;
}
.signin {
display: block;
margin: 20px auto;
max-width: 400px;
background: #fff;
border: 1px solid #ccc;
border-radius: 10px;
padding: 20px;
}
.center {
text-align: center;
}
.btn {
color: #fff;
background-color: #428bca;
border: 1px solid #357ebd;
-webkit-border-radius: 4;
-moz-border-radius: 4;
border-radius: 4px;
font-size: 14px;
padding: 6px 12px;
text-decoration: none;
cursor: pointer;
}
.btn:hover {
background-color: #3071a9;
border-color: #285e8e;
text-decoration: none;
}
label {
display: inline-block;
max-width: 100%;
margin-bottom: 5px;
font-weight: 700;
}
input {
display: block;
width: 100%;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
-webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
margin: 0;
box-sizing: border-box;
}
footer {
display: block;
font-size: 10px;
color: #aaa;
text-align: center;
margin-bottom: 10px;
}
footer a {
display: inline-block;
height: 25px;
line-height: 25px;
color: #aaa;
text-decoration: underline;
}
footer a:hover {
color: #aaa;
}
</style>
</head>
<body>
<div class="signin center">
<form method="GET" action="{{.ProxyPrefix}}/start">
<input type="hidden" name="rd" value="{{.Redirect}}">
<button type="submit" class="btn">Sign in with passbook</button><br />
</form>
</div>
<script>
if (window.location.hash) {
(function () {
var inputs = document.getElementsByName('rd');
for (var i = 0; i < inputs.length; i++) {
inputs[i].value += window.location.hash;
}
})();
}
</script>
</body>
</html>
{{end}}

View File

@ -0,0 +1,10 @@
ingress:
enabled: true
hosts:
- some.address.tld
grafana.ini:
auth.anonymous:
enabled: true
org_name: Main Org.
org_role: Viewer

View File

@ -0,0 +1,63 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: prometheus
rules:
- apiGroups: [""]
resources:
- nodes
- services
- endpoints
- pods
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources:
- configmaps
verbs: ["get"]
- nonResourceURLs: ["/metrics"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: prod-passbook-ng
---
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: prometheus
spec:
serviceAccountName: prometheus
serviceMonitorSelector:
matchLabels:
app.kubernetes.io/name: passbook
enableAdminAPI: false
ruleSelector:
matchLabels:
app.kubernetes.io/name: passbook
storage:
volumeClaimTemplate:
metadata:
labels:
prometheus: k8s
name: prometheus-storage
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 15Gi

11
hack/up.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash -x
# macos specific setting, for some reason
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
export DEBUG=false
export POSTGRES_USER=postgres
# ./manage.py generate_swagger > storhappy-ui/swagger.json
uwsgi docker/uwsgi.ini

View File

@ -1,9 +1,9 @@
dependencies: dependencies:
- name: postgresql - name: postgresql
repository: https://kubernetes-charts.storage.googleapis.com/ repository: https://kubernetes-charts.storage.googleapis.com/
version: 6.3.10 version: 6.5.8
- name: redis - name: redis
repository: https://kubernetes-charts.storage.googleapis.com/ repository: https://kubernetes-charts.storage.googleapis.com/
version: 9.2.1 version: 9.5.1
digest: sha256:bdde250e1401dccdd5161e39c807f9e88b05e3e8e72e74df767a1bbb5fc39a4a digest: sha256:f18b5dc8d0be13d584407405c60d10b6b84d25f7fa8aaa3dd0e5385c38f5c516
generated: "2019-10-01T10:46:06.542706+02:00" generated: "2019-11-07T10:23:07.259176+01:00"

View File

@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
appVersion: "0.6.8-beta" appVersion: "0.7.4-beta"
description: A Helm chart for passbook. description: A Helm chart for passbook.
name: passbook name: passbook
version: "0.6.8-beta" version: "0.7.4-beta"
icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png

Binary file not shown.

View File

@ -1,9 +1,9 @@
dependencies: dependencies:
- name: postgresql - name: postgresql
repository: https://kubernetes-charts.storage.googleapis.com/ repository: https://kubernetes-charts.storage.googleapis.com/
version: 4.2.2 version: 6.5.8
- name: redis - name: redis
repository: https://kubernetes-charts.storage.googleapis.com/ repository: https://kubernetes-charts.storage.googleapis.com/
version: 9.2.1 version: 9.5.1
digest: sha256:8782e974a1094eaeecf1d68f093ca4fb84977217b2bd38b09790a05ec289aec2 digest: sha256:476834fb82f66bc7242c4a5e7343d0a859d8307cb301256beb0eb749983014e4
generated: "2019-10-02T21:03:25.90491153Z" generated: "2019-11-07T10:21:30.902415+01:00"

View File

@ -1,7 +1,7 @@
dependencies: dependencies:
- name: postgresql - name: postgresql
version: 4.2.2 version: 6.5.8
repository: https://kubernetes-charts.storage.googleapis.com/ repository: https://kubernetes-charts.storage.googleapis.com/
- name: redis - name: redis
version: 9.2.1 version: 9.5.1
repository: https://kubernetes-charts.storage.googleapis.com/ repository: https://kubernetes-charts.storage.googleapis.com/

View File

@ -12,5 +12,5 @@ data:
host: "{{ .Release.Name }}-redis-master" host: "{{ .Release.Name }}-redis-master"
cache_db: 0 cache_db: 0
message_queue_db: 1 message_queue_db: 1
error_report_enabled: {{ .Values.config.error_reporting }} error_reporting: {{ .Values.config.error_reporting }}
domain: ".{{ .Values.ingress.hosts[0] }}" domain: ".{{ index .Values.ingress.hosts 0 }}"

View File

@ -0,0 +1,121 @@
{{- if .Values.monitoring.enabled -}}
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: {{ include "passbook.fullname" . }}-static-rules
labels:
app.kubernetes.io/name: {{ include "passbook.name" . }}
helm.sh/chart: {{ include "passbook.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
groups:
- name: Aggregate request counters
rules:
- record: job:django_http_requests_before_middlewares_total:sum_rate30s
expr: sum(rate(django_http_requests_before_middlewares_total[30s])) by (job)
- record: job:django_http_requests_unknown_latency_total:sum_rate30s
expr: sum(rate(django_http_requests_unknown_latency_total[30s])) by (job)
- record: job:django_http_ajax_requests_total:sum_rate30s
expr: sum(rate(django_http_ajax_requests_total[30s])) by (job)
- record: job:django_http_responses_before_middlewares_total:sum_rate30s
expr: sum(rate(django_http_responses_before_middlewares_total[30s])) by (job)
- record: job:django_http_requests_unknown_latency_including_middlewares_total:sum_rate30s
expr: sum(rate(django_http_requests_unknown_latency_including_middlewares_total[30s])) by (job)
- record: job:django_http_requests_body_total_bytes:sum_rate30s
expr: sum(rate(django_http_requests_body_total_bytes[30s])) by (job)
- record: job:django_http_responses_streaming_total:sum_rate30s
expr: sum(rate(django_http_responses_streaming_total[30s])) by (job)
- record: job:django_http_responses_body_total_bytes:sum_rate30s
expr: sum(rate(django_http_responses_body_total_bytes[30s])) by (job)
- record: job:django_http_requests_total:sum_rate30s
expr: sum(rate(django_http_requests_total_by_method[30s])) by (job)
- record: job:django_http_requests_total_by_method:sum_rate30s
expr: sum(rate(django_http_requests_total_by_method[30s])) by (job,method)
- record: job:django_http_requests_total_by_transport:sum_rate30s
expr: sum(rate(django_http_requests_total_by_transport[30s])) by (job,transport)
- record: job:django_http_requests_total_by_view:sum_rate30s
expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) by (job,view)
- record: job:django_http_requests_total_by_view_transport_method:sum_rate30s
expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) by (job,view,transport,method)
- record: job:django_http_responses_total_by_templatename:sum_rate30s
expr: sum(rate(django_http_responses_total_by_templatename[30s])) by (job,templatename)
- record: job:django_http_responses_total_by_status:sum_rate30s
expr: sum(rate(django_http_responses_total_by_status[30s])) by (job,status)
- record: job:django_http_responses_total_by_status_name_method:sum_rate30s
expr: sum(rate(django_http_responses_total_by_status_name_method[30s])) by (job,status,name,method)
- record: job:django_http_responses_total_by_charset:sum_rate30s
expr: sum(rate(django_http_responses_total_by_charset[30s])) by (job,charset)
- record: job:django_http_exceptions_total_by_type:sum_rate30s
expr: sum(rate(django_http_exceptions_total_by_type[30s])) by (job,type)
- record: job:django_http_exceptions_total_by_view:sum_rate30s
expr: sum(rate(django_http_exceptions_total_by_view[30s])) by (job,view)
- name: Aggregate latency histograms
rules:
- record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s
expr: histogram_quantile(0.50, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le))
labels:
quantile: "50"
- record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s
expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le))
labels:
quantile: "95"
- record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s
expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le))
labels:
quantile: "99"
- record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s
expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le))
labels:
quantile: "99.9"
- record: job:django_http_requests_latency_seconds:quantile_rate30s
expr: histogram_quantile(0.50, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le))
labels:
quantile: "50"
- record: job:django_http_requests_latency_seconds:quantile_rate30s
expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le))
labels:
quantile: "95"
- record: job:django_http_requests_latency_seconds:quantile_rate30s
expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le))
labels:
quantile: "99"
- record: job:django_http_requests_latency_seconds:quantile_rate30s
expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le))
labels:
quantile: "99.9"
- name: Aggregate model operations
rules:
- record: job:django_model_inserts_total:sum_rate1m
expr: sum(rate(django_model_inserts_total[1m])) by (job, model)
- record: job:django_model_updates_total:sum_rate1m
expr: sum(rate(django_model_updates_total[1m])) by (job, model)
- record: job:django_model_deletes_total:sum_rate1m
expr: sum(rate(django_model_deletes_total[1m])) by (job, model)
- name: Aggregate database operations
rules:
- record: job:django_db_new_connections_total:sum_rate30s
expr: sum(rate(django_db_new_connections_total[30s])) by (alias, vendor)
- record: job:django_db_new_connection_errors_total:sum_rate30s
expr: sum(rate(django_db_new_connection_errors_total[30s])) by (alias, vendor)
- record: job:django_db_execute_total:sum_rate30s
expr: sum(rate(django_db_execute_total[30s])) by (alias, vendor)
- record: job:django_db_execute_many_total:sum_rate30s
expr: sum(rate(django_db_execute_many_total[30s])) by (alias, vendor)
- record: job:django_db_errors_total:sum_rate30s
expr: sum(rate(django_db_errors_total[30s])) by (alias, vendor, type)
- name: Aggregate migrations
rules:
- record: job:django_migrations_applied_total:max
expr: max(django_migrations_applied_total) by (job, connection)
- record: job:django_migrations_unapplied_total:max
expr: max(django_migrations_unapplied_total) by (job, connection)
- name: Alerts
rules:
- alert: UnappliedMigrations
expr: job:django_migrations_unapplied_total:max > 0
for: 1m
labels:
severity: testing
{{- end }}

View File

@ -4,6 +4,7 @@ type: Opaque
metadata: metadata:
name: {{ include "passbook.fullname" . }}-secret-key name: {{ include "passbook.fullname" . }}-secret-key
data: data:
monitoring_username: bW9uaXRvcg== # monitor in base64
{{- if .Values.config.secret_key }} {{- if .Values.config.secret_key }}
secret_key: {{ .Values.config.secret_key | b64enc | quote }} secret_key: {{ .Values.config.secret_key | b64enc | quote }}
{{- else }} {{- else }}

View File

@ -1,4 +1,4 @@
apiVersion: apps/v1beta2 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: {{ include "passbook.fullname" . }}-static name: {{ include "passbook.fullname" . }}-static
@ -18,10 +18,6 @@ spec:
app.kubernetes.io/name: {{ include "passbook.name" . }} app.kubernetes.io/name: {{ include "passbook.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
k8s.passbook.io/component: static k8s.passbook.io/component: static
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: '9113'
field.cattle.io/workloadMetrics: '[{"path":"/metrics","port":9113,"schema":"HTTP"}]'
spec: spec:
containers: containers:
- name: {{ .Chart.Name }}-static - name: {{ .Chart.Name }}-static
@ -29,7 +25,7 @@ spec:
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
ports: ports:
- name: http - name: http
containerPort: 80 containerPort: 8080
protocol: TCP protocol: TCP
livenessProbe: livenessProbe:
initialDelaySeconds: 10 initialDelaySeconds: 10
@ -50,6 +46,3 @@ spec:
limits: limits:
cpu: 20m cpu: 20m
memory: 20M memory: 20M
- name: {{ .Chart.Name }}-static-prometheus
image: nginx/nginx-prometheus-exporter:0.4.1
imagePullPolicy: IfNotPresent

View File

@ -11,7 +11,7 @@ metadata:
spec: spec:
type: ClusterIP type: ClusterIP
ports: ports:
- port: 80 - port: 8080
targetPort: http targetPort: http
protocol: TCP protocol: TCP
name: http name: http

View File

@ -0,0 +1,17 @@
{{- if .Values.monitoring.enabled -}}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
app.kubernetes.io/name: {{ include "passbook.name" . }}
helm.sh/chart: {{ include "passbook.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
name: {{ include "passbook.fullname" . }}-static-monitoring
spec:
endpoints:
- port: http
selector:
matchLabels:
k8s.passbook.io/component: static
{{- end }}

View File

@ -1,4 +1,4 @@
apiVersion: apps/v1beta2 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: {{ include "passbook.fullname" . }}-web name: {{ include "passbook.fullname" . }}-web

View File

@ -4,13 +4,14 @@ metadata:
name: {{ include "passbook.fullname" . }}-web name: {{ include "passbook.fullname" . }}-web
labels: labels:
app.kubernetes.io/name: {{ include "passbook.name" . }} app.kubernetes.io/name: {{ include "passbook.name" . }}
helm.sh/chart: {{ include "passbook.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }} app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "passbook.chart" . }}
k8s.passbook.io/component: web
spec: spec:
type: {{ .Values.service.type }} type: ClusterIP
ports: ports:
- port: {{ .Values.service.port }} - port: 80
targetPort: http targetPort: http
protocol: TCP protocol: TCP
name: http name: http

View File

@ -0,0 +1,25 @@
{{- if .Values.monitoring.enabled -}}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
app.kubernetes.io/name: {{ include "passbook.name" . }}
helm.sh/chart: {{ include "passbook.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
name: {{ include "passbook.fullname" . }}-web-monitoring
spec:
endpoints:
- basicAuth:
password:
name: {{ include "passbook.fullname" . }}-secret-key
key: secret_key
username:
name: {{ include "passbook.fullname" . }}-secret-key
key: monitoring_username
port: http
interval: 10s
selector:
matchLabels:
k8s.passbook.io/component: web
{{- end }}

View File

@ -1,4 +1,4 @@
apiVersion: apps/v1beta2 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: {{ include "passbook.fullname" . }}-worker name: {{ include "passbook.fullname" . }}-worker

View File

@ -2,7 +2,7 @@
# This is a YAML-formatted file. # This is a YAML-formatted file.
# Declare variables to be passed into your templates. # Declare variables to be passed into your templates.
image: image:
tag: 0.6.8-beta tag: 0.7.4-beta
nameOverride: "" nameOverride: ""
@ -10,23 +10,14 @@ config:
# Optionally specify fixed secret_key, otherwise generated automatically # Optionally specify fixed secret_key, otherwise generated automatically
# secret_key: _k*@6h2u2@q-dku57hhgzb7tnx*ba9wodcb^s9g0j59@=y(@_o # secret_key: _k*@6h2u2@q-dku57hhgzb7tnx*ba9wodcb^s9g0j59@=y(@_o
# Enable error reporting # Enable error reporting
error_reporting: true error_reporting: false
email: email:
host: localhost host: localhost
postgresql: # This Helm chart ships with built-in Prometheus ServiceMonitors and Rules.
postgresqlDatabase: passbook # This requires the CoreOS Prometheus Operator.
monitoring:
redis: enabled: false
cluster:
enabled: false
master:
persistence:
enabled: false
service:
type: ClusterIP
port: 80
ingress: ingress:
enabled: false enabled: false
@ -40,3 +31,14 @@ ingress:
# - secretName: chart-example-tls # - secretName: chart-example-tls
# hosts: # hosts:
# - passbook.k8s.local # - passbook.k8s.local
# These settings configure the packaged PostgreSQL and Redis chart.
postgresql:
postgresqlDatabase: passbook
redis:
cluster:
enabled: false
master:
persistence:
enabled: false

View File

@ -1,2 +1,2 @@
"""passbook""" """passbook"""
__version__ = '0.6.8-beta' __version__ = '0.7.4-beta'

View File

@ -1,6 +0,0 @@
"""Versioned Admin API Urls"""
from django.conf.urls import include, url
urlpatterns = [
url(r'^v1/', include('passbook.admin.api.v1.urls', namespace='v1')),
]

View File

@ -1,36 +0,0 @@
"""passbook admin gorup API"""
from rest_framework.permissions import IsAdminUser
from rest_framework.serializers import ModelSerializer, Serializer
from rest_framework.viewsets import ModelViewSet
from passbook.core.models import Group
class RecursiveField(Serializer):
"""Recursive field for manytomanyfield"""
def to_representation(self, value):
serializer = self.parent.parent.__class__(value, context=self.context)
return serializer.data
def create(self):
raise NotImplementedError()
def update(self):
raise NotImplementedError()
class GroupSerializer(ModelSerializer):
"""Group Serializer"""
children = RecursiveField(many=True)
class Meta:
model = Group
fields = '__all__'
class GroupViewSet(ModelViewSet):
"""Group Viewset"""
permission_classes = [IsAdminUser]
serializer_class = GroupSerializer
queryset = Group.objects.filter(parent__isnull=True)

View File

@ -1,33 +0,0 @@
"""passbook admin API URLs"""
from django.urls import path
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions
from rest_framework.routers import DefaultRouter
from passbook.admin.api.v1.applications import ApplicationViewSet
from passbook.admin.api.v1.groups import GroupViewSet
from passbook.admin.api.v1.users import UserViewSet
router = DefaultRouter()
router.register('applications', ApplicationViewSet)
router.register('groups', GroupViewSet)
router.register('users', UserViewSet)
SchemaView = get_schema_view(
openapi.Info(
title="passbook Administration API",
default_version='v1',
description="Internal passbook API for Administration Interface",
contact=openapi.Contact(email="contact@snippets.local"),
license=openapi.License(name="MIT License"),
),
public=True,
permission_classes=(permissions.IsAdminUser,),
)
urlpatterns = router.urls + [
path('swagger.yml', SchemaView.without_ui(cache_timeout=0), name='schema-json'),
path('swagger/', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
]
app_name = 'passbook.admin'

View File

@ -2,5 +2,6 @@
# from django import forms # from django import forms
SOURCE_FORM_FIELDS = ['name', 'slug', 'enabled', 'policies'] SOURCE_FORM_FIELDS = ['name', 'slug', 'enabled', 'policies']
SOURCE_SERIALIZER_FIELDS = ['pk', 'name', 'slug', 'enabled', 'policies']
# class SourceForm(forms.Form) # class SourceForm(forms.Form)

View File

@ -1,5 +1,5 @@
"""passbook URL Configuration""" """passbook URL Configuration"""
from django.urls import include, path from django.urls import path
from passbook.admin.views import (applications, audit, debug, factors, groups, from passbook.admin.views import (applications, audit, debug, factors, groups,
invitations, overview, policy, invitations, overview, policy,
@ -74,11 +74,9 @@ urlpatterns = [
path('group/<uuid:pk>/update/', groups.GroupUpdateView.as_view(), name='group-update'), path('group/<uuid:pk>/update/', groups.GroupUpdateView.as_view(), name='group-update'),
path('group/<uuid:pk>/delete/', groups.GroupDeleteView.as_view(), name='group-delete'), path('group/<uuid:pk>/delete/', groups.GroupDeleteView.as_view(), name='group-delete'),
# Audit Log # Audit Log
path('audit/', audit.AuditEntryListView.as_view(), name='audit-log'), path('audit/', audit.EventListView.as_view(), name='audit-log'),
# Groups # Groups
path('groups/', groups.GroupListView.as_view(), name='groups'), path('groups/', groups.GroupListView.as_view(), name='groups'),
# API
path('api/', include('passbook.admin.api.urls')),
# Debug # Debug
path('debug/request/', debug.DebugRequestView.as_view(), name='debug-request'), path('debug/request/', debug.DebugRequestView.as_view(), name='debug-request'),
] ]

View File

@ -1,19 +1,24 @@
"""passbook Application administration""" """passbook Application administration"""
from django.contrib import messages 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.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import CreateView, DeleteView, ListView, UpdateView from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.forms.applications import ApplicationForm from passbook.core.forms.applications import ApplicationForm
from passbook.core.models import Application from passbook.core.models import Application
from passbook.lib.views import CreateAssignPermView
class ApplicationListView(AdminRequiredMixin, ListView): class ApplicationListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all applications""" """Show list of all applications"""
model = Application model = Application
permission_required = 'passbook_core.view_application'
ordering = 'name' ordering = 'name'
paginate_by = 40 paginate_by = 40
template_name = 'administration/application/list.html' template_name = 'administration/application/list.html'
@ -22,10 +27,13 @@ class ApplicationListView(AdminRequiredMixin, ListView):
return super().get_queryset().select_subclasses() return super().get_queryset().select_subclasses()
class ApplicationCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class ApplicationCreateView(SuccessMessageMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, CreateAssignPermView):
"""Create new Application""" """Create new Application"""
model = Application
form_class = ApplicationForm form_class = ApplicationForm
permission_required = 'passbook_core.add_application'
template_name = 'generic/create.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:applications') success_url = reverse_lazy('passbook_admin:applications')
@ -36,21 +44,25 @@ class ApplicationCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class ApplicationUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): class ApplicationUpdateView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, UpdateView):
"""Update application""" """Update application"""
model = Application model = Application
form_class = ApplicationForm form_class = ApplicationForm
permission_required = 'passbook_core.change_application'
template_name = 'generic/update.html' template_name = 'generic/update.html'
success_url = reverse_lazy('passbook_admin:applications') success_url = reverse_lazy('passbook_admin:applications')
success_message = _('Successfully updated Application') success_message = _('Successfully updated Application')
class ApplicationDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView): class ApplicationDeleteView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, DeleteView):
"""Delete application""" """Delete application"""
model = Application model = Application
permission_required = 'passbook_core.delete_application'
template_name = 'generic/delete.html' template_name = 'generic/delete.html'
success_url = reverse_lazy('passbook_admin:applications') success_url = reverse_lazy('passbook_admin:applications')

View File

@ -1,16 +1,18 @@
"""passbook AuditEntry administration""" """passbook Event administration"""
from django.views.generic import ListView from django.views.generic import ListView
from guardian.mixins import PermissionListMixin
from passbook.admin.mixins import AdminRequiredMixin from passbook.audit.models import Event
from passbook.audit.models import AuditEntry
class AuditEntryListView(AdminRequiredMixin, ListView): class EventListView(PermissionListMixin, ListView):
"""Show list of all invitations""" """Show list of all invitations"""
model = AuditEntry model = Event
template_name = 'administration/audit/list.html' template_name = 'administration/audit/list.html'
permission_required = 'passbook_audit.view_event'
ordering = '-created'
paginate_by = 10 paginate_by = 10
def get_queryset(self): def get_queryset(self):
return AuditEntry.objects.all().order_by('-created') return Event.objects.all().order_by('-created')

View File

@ -1,11 +1,9 @@
"""passbook administration debug views""" """passbook administration debug views"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView from django.views.generic import TemplateView
from passbook.admin.mixins import AdminRequiredMixin
class DebugRequestView(LoginRequiredMixin, TemplateView):
class DebugRequestView(AdminRequiredMixin, TemplateView):
"""Show debug info about request""" """Show debug info about request"""
template_name = 'administration/debug/request.html' template_name = 'administration/debug/request.html'

View File

@ -1,14 +1,18 @@
"""passbook Factor administration""" """passbook Factor administration"""
from django.contrib import messages 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.contrib.messages.views import SuccessMessageMixin
from django.http import Http404 from django.http import Http404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import CreateView, DeleteView, ListView, UpdateView from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.models import Factor from passbook.core.models import Factor
from passbook.lib.utils.reflection import path_to_class from passbook.lib.utils.reflection import path_to_class
from passbook.lib.views import CreateAssignPermView
def all_subclasses(cls): def all_subclasses(cls):
@ -16,11 +20,13 @@ def all_subclasses(cls):
return set(cls.__subclasses__()).union( return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)]) [s for c in cls.__subclasses__() for s in all_subclasses(c)])
class FactorListView(AdminRequiredMixin, ListView):
class FactorListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all factors""" """Show list of all factors"""
model = Factor model = Factor
template_name = 'administration/factor/list.html' template_name = 'administration/factor/list.html'
permission_required = 'passbook_core.view_factor'
ordering = 'order' ordering = 'order'
paginate_by = 40 paginate_by = 40
@ -32,10 +38,15 @@ class FactorListView(AdminRequiredMixin, ListView):
def get_queryset(self): def get_queryset(self):
return super().get_queryset().select_subclasses() return super().get_queryset().select_subclasses()
class FactorCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
class FactorCreateView(SuccessMessageMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, CreateAssignPermView):
"""Create new Factor""" """Create new Factor"""
model = Factor
template_name = 'generic/create.html' template_name = 'generic/create.html'
permission_required = 'passbook_core.add_factor'
success_url = reverse_lazy('passbook_admin:factors') success_url = reverse_lazy('passbook_admin:factors')
success_message = _('Successfully created Factor') success_message = _('Successfully created Factor')
@ -53,10 +64,13 @@ class FactorCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
raise Http404 raise Http404
return path_to_class(model.form) return path_to_class(model.form)
class FactorUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView):
class FactorUpdateView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, UpdateView):
"""Update factor""" """Update factor"""
model = Factor model = Factor
permission_required = 'passbook_core.update_application'
template_name = 'generic/update.html' template_name = 'generic/update.html'
success_url = reverse_lazy('passbook_admin:factors') success_url = reverse_lazy('passbook_admin:factors')
success_message = _('Successfully updated Factor') success_message = _('Successfully updated Factor')
@ -69,11 +83,14 @@ class FactorUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView):
def get_object(self, queryset=None): def get_object(self, queryset=None):
return Factor.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first() return Factor.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
class FactorDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView):
class FactorDeleteView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, DeleteView):
"""Delete factor""" """Delete factor"""
model = Factor model = Factor
template_name = 'generic/delete.html' template_name = 'generic/delete.html'
permission_required = 'passbook_core.delete_factor'
success_url = reverse_lazy('passbook_admin:factors') success_url = reverse_lazy('passbook_admin:factors')
success_message = _('Successfully deleted Factor') success_message = _('Successfully deleted Factor')

View File

@ -1,28 +1,36 @@
"""passbook Group administration""" """passbook Group administration"""
from django.contrib import messages 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.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import CreateView, DeleteView, ListView, UpdateView from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.forms.groups import GroupForm from passbook.core.forms.groups import GroupForm
from passbook.core.models import Group from passbook.core.models import Group
from passbook.lib.views import CreateAssignPermView
class GroupListView(AdminRequiredMixin, ListView): class GroupListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all groups""" """Show list of all groups"""
model = Group model = Group
permission_required = 'passbook_core.view_group'
ordering = 'name' ordering = 'name'
paginate_by = 40 paginate_by = 40
template_name = 'administration/group/list.html' template_name = 'administration/group/list.html'
class GroupCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class GroupCreateView(SuccessMessageMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, CreateAssignPermView):
"""Create new Group""" """Create new Group"""
model = Group
form_class = GroupForm form_class = GroupForm
permission_required = 'passbook_core.add_group'
template_name = 'generic/create.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:groups') success_url = reverse_lazy('passbook_admin:groups')
@ -33,18 +41,20 @@ class GroupCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class GroupUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): class GroupUpdateView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, UpdateView):
"""Update group""" """Update group"""
model = Group model = Group
form_class = GroupForm form_class = GroupForm
permission_required = 'passbook_core.change_group'
template_name = 'generic/update.html' template_name = 'generic/update.html'
success_url = reverse_lazy('passbook_admin:groups') success_url = reverse_lazy('passbook_admin:groups')
success_message = _('Successfully updated Group') success_message = _('Successfully updated Group')
class GroupDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView): class GroupDeleteView(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
"""Delete group""" """Delete group"""
model = Group model = Group

View File

@ -1,33 +1,40 @@
"""passbook Invitation administration""" """passbook Invitation administration"""
from django.contrib import messages 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.contrib.messages.views import SuccessMessageMixin
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import CreateView, DeleteView, ListView from django.views.generic import DeleteView, ListView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.forms.invitations import InvitationForm from passbook.core.forms.invitations import InvitationForm
from passbook.core.models import Invitation from passbook.core.models import Invitation
from passbook.core.signals import invitation_created from passbook.core.signals import invitation_created
from passbook.lib.views import CreateAssignPermView
class InvitationListView(AdminRequiredMixin, ListView): class InvitationListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all invitations""" """Show list of all invitations"""
model = Invitation model = Invitation
ordering = 'expires' permission_required = 'passbook_core.view_invitation'
paginate_by = 40
template_name = 'administration/invitation/list.html' template_name = 'administration/invitation/list.html'
class InvitationCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class InvitationCreateView(SuccessMessageMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, CreateAssignPermView):
"""Create new Invitation""" """Create new Invitation"""
model = Invitation
form_class = InvitationForm
permission_required = 'passbook_core.add_invitation'
template_name = 'generic/create.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:invitations') success_url = reverse_lazy('passbook_admin:invitations')
success_message = _('Successfully created Invitation') success_message = _('Successfully created Invitation')
form_class = InvitationForm
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['type'] = 'Invitation' kwargs['type'] = 'Invitation'
@ -43,10 +50,14 @@ class InvitationCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
invitation=obj) invitation=obj)
return HttpResponseRedirect(self.success_url) return HttpResponseRedirect(self.success_url)
class InvitationDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView):
class InvitationDeleteView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, DeleteView):
"""Delete invitation""" """Delete invitation"""
model = Invitation model = Invitation
permission_required = 'passbook_core.delete_invitation'
template_name = 'generic/delete.html' template_name = 'generic/delete.html'
success_url = reverse_lazy('passbook_admin:invitations') success_url = reverse_lazy('passbook_admin:invitations')
success_message = _('Successfully deleted Invitation') success_message = _('Successfully deleted Invitation')

View File

@ -1,26 +1,29 @@
"""passbook Policy administration""" """passbook Policy administration"""
from django.contrib import messages 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.contrib.messages.views import SuccessMessageMixin
from django.http import Http404 from django.http import Http404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import (CreateView, DeleteView, FormView, ListView, from django.views.generic import DeleteView, FormView, ListView, UpdateView
UpdateView)
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.forms.policies import PolicyTestForm from passbook.admin.forms.policies import PolicyTestForm
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.models import Policy from passbook.core.models import Policy
from passbook.lib.utils.reflection import path_to_class from passbook.lib.utils.reflection import path_to_class
from passbook.lib.views import CreateAssignPermView
from passbook.policies.engine import PolicyEngine from passbook.policies.engine import PolicyEngine
class PolicyListView(AdminRequiredMixin, ListView): class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all policies""" """Show list of all policies"""
model = Policy model = Policy
ordering = 'name' permission_required = 'passbook_core.view_policy'
paginate_by = 40
template_name = 'administration/policy/list.html' template_name = 'administration/policy/list.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -32,9 +35,13 @@ class PolicyListView(AdminRequiredMixin, ListView):
return super().get_queryset().order_by('order').select_subclasses() return super().get_queryset().order_by('order').select_subclasses()
class PolicyCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class PolicyCreateView(SuccessMessageMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, CreateAssignPermView):
"""Create new Policy""" """Create new Policy"""
model = Policy
permission_required = 'passbook_core.add_policy'
template_name = 'generic/create.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:policies') success_url = reverse_lazy('passbook_admin:policies')
success_message = _('Successfully created Policy') success_message = _('Successfully created Policy')
@ -48,10 +55,13 @@ class PolicyCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
return path_to_class(model.form) return path_to_class(model.form)
class PolicyUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): class PolicyUpdateView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, UpdateView):
"""Update policy""" """Update policy"""
model = Policy model = Policy
permission_required = 'passbook_core.change_policy'
template_name = 'generic/update.html' template_name = 'generic/update.html'
success_url = reverse_lazy('passbook_admin:policies') success_url = reverse_lazy('passbook_admin:policies')
success_message = _('Successfully updated Policy') success_message = _('Successfully updated Policy')
@ -65,10 +75,13 @@ class PolicyUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView):
return Policy.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first() return Policy.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
class PolicyDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView): class PolicyDeleteView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, DeleteView):
"""Delete policy""" """Delete policy"""
model = Policy model = Policy
permission_required = 'passbook_core.delete_policy'
template_name = 'generic/delete.html' template_name = 'generic/delete.html'
success_url = reverse_lazy('passbook_admin:policies') success_url = reverse_lazy('passbook_admin:policies')
success_message = _('Successfully deleted Policy') success_message = _('Successfully deleted Policy')
@ -81,11 +94,12 @@ class PolicyDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView):
return super().delete(request, *args, **kwargs) return super().delete(request, *args, **kwargs)
class PolicyTestView(AdminRequiredMixin, DetailView, FormView): class PolicyTestView(LoginRequiredMixin, DetailView, PermissionRequiredMixin, FormView):
"""View to test policy(s)""" """View to test policy(s)"""
model = Policy model = Policy
form_class = PolicyTestForm form_class = PolicyTestForm
permission_required = 'passbook_core.view_policy'
template_name = 'administration/policy/test.html' template_name = 'administration/policy/test.html'
object = None object = None
@ -103,8 +117,9 @@ class PolicyTestView(AdminRequiredMixin, DetailView, FormView):
def form_valid(self, form): def form_valid(self, form):
policy = self.get_object() policy = self.get_object()
user = form.cleaned_data.get('user') user = form.cleaned_data.get('user')
policy_engine = PolicyEngine([policy]) policy_engine = PolicyEngine([policy], user, self.request)
policy_engine.for_user(user).with_request(self.request).build() policy_engine.use_cache = False
policy_engine.build()
result = policy_engine.passing result = policy_engine.passing
if result: if result:
messages.success(self.request, _('User successfully passed policy.')) messages.success(self.request, _('User successfully passed policy.'))

View File

@ -1,14 +1,18 @@
"""passbook PropertyMapping administration""" """passbook PropertyMapping administration"""
from django.contrib import messages 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.contrib.messages.views import SuccessMessageMixin
from django.http import Http404 from django.http import Http404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import CreateView, DeleteView, ListView, UpdateView from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.models import PropertyMapping from passbook.core.models import PropertyMapping
from passbook.lib.utils.reflection import path_to_class from passbook.lib.utils.reflection import path_to_class
from passbook.lib.views import CreateAssignPermView
def all_subclasses(cls): def all_subclasses(cls):
@ -17,10 +21,11 @@ def all_subclasses(cls):
[s for c in cls.__subclasses__() for s in all_subclasses(c)]) [s for c in cls.__subclasses__() for s in all_subclasses(c)])
class PropertyMappingListView(AdminRequiredMixin, ListView): class PropertyMappingListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all property_mappings""" """Show list of all property_mappings"""
model = PropertyMapping model = PropertyMapping
permission_required = 'passbook_core.view_propertymapping'
template_name = 'administration/property_mapping/list.html' template_name = 'administration/property_mapping/list.html'
ordering = 'name' ordering = 'name'
paginate_by = 40 paginate_by = 40
@ -34,9 +39,13 @@ class PropertyMappingListView(AdminRequiredMixin, ListView):
return super().get_queryset().select_subclasses() return super().get_queryset().select_subclasses()
class PropertyMappingCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class PropertyMappingCreateView(SuccessMessageMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, CreateAssignPermView):
"""Create new PropertyMapping""" """Create new PropertyMapping"""
model = PropertyMapping
permission_required = 'passbook_core.add_propertymapping'
template_name = 'generic/create.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:property-mappings') success_url = reverse_lazy('passbook_admin:property-mappings')
success_message = _('Successfully created Property Mapping') success_message = _('Successfully created Property Mapping')
@ -58,10 +67,13 @@ class PropertyMappingCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateV
return path_to_class(model.form) return path_to_class(model.form)
class PropertyMappingUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): class PropertyMappingUpdateView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, UpdateView):
"""Update property_mapping""" """Update property_mapping"""
model = PropertyMapping model = PropertyMapping
permission_required = 'passbook_core.change_propertymapping'
template_name = 'generic/update.html' template_name = 'generic/update.html'
success_url = reverse_lazy('passbook_admin:property-mappings') success_url = reverse_lazy('passbook_admin:property-mappings')
success_message = _('Successfully updated Property Mapping') success_message = _('Successfully updated Property Mapping')
@ -75,10 +87,13 @@ class PropertyMappingUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateV
return PropertyMapping.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first() return PropertyMapping.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
class PropertyMappingDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView): class PropertyMappingDeleteView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, DeleteView):
"""Delete property_mapping""" """Delete property_mapping"""
model = PropertyMapping model = PropertyMapping
permission_required = 'passbook_core.delete_propertymapping'
template_name = 'generic/delete.html' template_name = 'generic/delete.html'
success_url = reverse_lazy('passbook_admin:property-mappings') success_url = reverse_lazy('passbook_admin:property-mappings')
success_message = _('Successfully deleted Property Mapping') success_message = _('Successfully deleted Property Mapping')

View File

@ -1,21 +1,25 @@
"""passbook Provider administration""" """passbook Provider administration"""
from django.contrib import messages 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.contrib.messages.views import SuccessMessageMixin
from django.http import Http404 from django.http import Http404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import CreateView, DeleteView, ListView, UpdateView from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.models import Provider from passbook.core.models import Provider
from passbook.lib.utils.reflection import path_to_class from passbook.lib.utils.reflection import path_to_class
from passbook.lib.views import CreateAssignPermView
class ProviderListView(AdminRequiredMixin, ListView): class ProviderListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all providers""" """Show list of all providers"""
model = Provider model = Provider
paginate_by = 40 permission_required = 'passbook_core.add_provider'
template_name = 'administration/provider/list.html' template_name = 'administration/provider/list.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -27,9 +31,13 @@ class ProviderListView(AdminRequiredMixin, ListView):
return super().get_queryset().select_subclasses() return super().get_queryset().select_subclasses()
class ProviderCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class ProviderCreateView(SuccessMessageMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, CreateAssignPermView):
"""Create new Provider""" """Create new Provider"""
model = Provider
permission_required = 'passbook_core.add_provider'
template_name = 'generic/create.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:providers') success_url = reverse_lazy('passbook_admin:providers')
success_message = _('Successfully created Provider') success_message = _('Successfully created Provider')
@ -43,10 +51,13 @@ class ProviderCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
return path_to_class(model.form) return path_to_class(model.form)
class ProviderUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): class ProviderUpdateView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, UpdateView):
"""Update provider""" """Update provider"""
model = Provider model = Provider
permission_required = 'passbook_core.change_provider'
template_name = 'generic/update.html' template_name = 'generic/update.html'
success_url = reverse_lazy('passbook_admin:providers') success_url = reverse_lazy('passbook_admin:providers')
success_message = _('Successfully updated Provider') success_message = _('Successfully updated Provider')
@ -60,10 +71,13 @@ class ProviderUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView):
return Provider.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first() return Provider.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
class ProviderDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView): class ProviderDeleteView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, DeleteView):
"""Delete provider""" """Delete provider"""
model = Provider model = Provider
permission_required = 'passbook_core.delete_provider'
template_name = 'generic/delete.html' template_name = 'generic/delete.html'
success_url = reverse_lazy('passbook_admin:providers') success_url = reverse_lazy('passbook_admin:providers')
success_message = _('Successfully deleted Provider') success_message = _('Successfully deleted Provider')

View File

@ -1,14 +1,18 @@
"""passbook Source administration""" """passbook Source administration"""
from django.contrib import messages 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.contrib.messages.views import SuccessMessageMixin
from django.http import Http404 from django.http import Http404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import CreateView, DeleteView, ListView, UpdateView from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.models import Source from passbook.core.models import Source
from passbook.lib.utils.reflection import path_to_class from passbook.lib.utils.reflection import path_to_class
from passbook.lib.views import CreateAssignPermView
def all_subclasses(cls): def all_subclasses(cls):
@ -16,10 +20,11 @@ def all_subclasses(cls):
return set(cls.__subclasses__()).union( return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)]) [s for c in cls.__subclasses__() for s in all_subclasses(c)])
class SourceListView(AdminRequiredMixin, ListView): class SourceListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all sources""" """Show list of all sources"""
model = Source model = Source
permission_required = 'passbook_core.view_source'
ordering = 'name' ordering = 'name'
paginate_by = 40 paginate_by = 40
template_name = 'administration/source/list.html' template_name = 'administration/source/list.html'
@ -33,9 +38,13 @@ class SourceListView(AdminRequiredMixin, ListView):
return super().get_queryset().select_subclasses() return super().get_queryset().select_subclasses()
class SourceCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class SourceCreateView(SuccessMessageMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, CreateAssignPermView):
"""Create new Source""" """Create new Source"""
model = Source
permission_required = 'passbook_core.add_source'
template_name = 'generic/create.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:sources') success_url = reverse_lazy('passbook_admin:sources')
success_message = _('Successfully created Source') success_message = _('Successfully created Source')
@ -48,10 +57,13 @@ class SourceCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
return path_to_class(model.form) return path_to_class(model.form)
class SourceUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): class SourceUpdateView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, UpdateView):
"""Update source""" """Update source"""
model = Source model = Source
permission_required = 'passbook_core.change_source'
template_name = 'generic/update.html' template_name = 'generic/update.html'
success_url = reverse_lazy('passbook_admin:sources') success_url = reverse_lazy('passbook_admin:sources')
success_message = _('Successfully updated Source') success_message = _('Successfully updated Source')
@ -65,10 +77,13 @@ class SourceUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView):
return Source.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first() return Source.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
class SourceDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView): class SourceDeleteView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, DeleteView):
"""Delete source""" """Delete source"""
model = Source model = Source
permission_required = 'passbook_core.delete_source'
template_name = 'generic/delete.html' template_name = 'generic/delete.html'
success_url = reverse_lazy('passbook_admin:sources') success_url = reverse_lazy('passbook_admin:sources')
success_message = _('Successfully deleted Source') success_message = _('Successfully deleted Source')

View File

@ -1,42 +1,50 @@
"""passbook User administration""" """passbook User administration"""
from django.contrib import messages 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.contrib.messages.views import SuccessMessageMixin
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import redirect
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views import View from django.views.generic import DeleteView, DetailView, ListView, UpdateView
from django.views.generic import CreateView, DeleteView, ListView, UpdateView from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.admin.forms.users import UserForm from passbook.admin.forms.users import UserForm
from passbook.admin.mixins import AdminRequiredMixin
from passbook.core.models import Nonce, User from passbook.core.models import Nonce, User
from passbook.lib.views import CreateAssignPermView
class UserListView(AdminRequiredMixin, ListView): class UserListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all users""" """Show list of all users"""
model = User model = User
permission_required = 'passbook_core.view_user'
ordering = 'username' ordering = 'username'
paginate_by = 40 paginate_by = 40
template_name = 'administration/user/list.html' template_name = 'administration/user/list.html'
class UserCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class UserCreateView(SuccessMessageMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, CreateAssignPermView):
"""Create user""" """Create user"""
model = User model = User
form_class = UserForm form_class = UserForm
permission_required = 'passbook_core.add_user'
template_name = 'generic/create.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:users') success_url = reverse_lazy('passbook_admin:users')
success_message = _('Successfully created User') success_message = _('Successfully created User')
class UserUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): class UserUpdateView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, UpdateView):
"""Update user""" """Update user"""
model = User model = User
form_class = UserForm form_class = UserForm
permission_required = 'passbook_core.change_user'
context_object_name = 'object' # By default the object's name context_object_name = 'object' # By default the object's name
# is user which is used by other checks # is user which is used by other checks
@ -45,10 +53,13 @@ class UserUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView):
success_message = _('Successfully updated User') success_message = _('Successfully updated User')
class UserDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView): class UserDeleteView(SuccessMessageMixin, LoginRequiredMixin,
PermissionRequiredMixin, DeleteView):
"""Delete user""" """Delete user"""
model = User model = User
permission_required = 'passbook_core.delete_user'
template_name = 'generic/delete.html' template_name = 'generic/delete.html'
success_url = reverse_lazy('passbook_admin:users') success_url = reverse_lazy('passbook_admin:users')
success_message = _('Successfully deleted User') success_message = _('Successfully deleted User')
@ -58,14 +69,16 @@ class UserDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView):
return super().delete(request, *args, **kwargs) return super().delete(request, *args, **kwargs)
class UserPasswordResetView(AdminRequiredMixin, View): class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
"""Get Password reset link for user""" """Get Password reset link for user"""
# pylint: disable=invalid-name model = User
def get(self, request, pk): permission_required = 'passbook_core.reset_user_password'
def get(self, request, *args, **kwargs):
"""Create nonce for user and return link""" """Create nonce for user and return link"""
user = get_object_or_404(User, pk=pk) super().get(request, *args, **kwargs)
nonce = Nonce.objects.create(user=user) nonce = Nonce.objects.create(user=self.object)
link = request.build_absolute_uri(reverse( link = request.build_absolute_uri(reverse(
'passbook_core:auth-password-reset', kwargs={'nonce': nonce.uuid})) 'passbook_core:auth-password-reset', kwargs={'nonce': nonce.uuid}))
messages.success(request, _('Password reset link: <pre>%(link)s</pre>' % {'link': link})) messages.success(request, _('Password reset link: <pre>%(link)s</pre>' % {'link': link}))

View File

@ -0,0 +1,31 @@
"""permission classes for django restframework"""
from rest_framework.permissions import BasePermission, DjangoObjectPermissions
from passbook.core.models import PolicyModel
from passbook.policies.engine import PolicyEngine
class CustomObjectPermissions(DjangoObjectPermissions):
"""Similar to `DjangoObjectPermissions`, but adding 'view' permissions."""
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
class PolicyPermissions(BasePermission):
"""Permission checker based on PolicyEngine"""
policy_engine: PolicyEngine
def has_object_permission(self, request, view, obj: PolicyModel) -> bool:
# if not obj.po
self.policy_engine = PolicyEngine(obj.policies, request.user, request)
self.policy_engine.request.obj = obj
return self.policy_engine.build().passing

View File

@ -2,7 +2,9 @@
from django.urls import include, path from django.urls import include, path
from passbook.api.v1.urls import urlpatterns as v1_urls from passbook.api.v1.urls import urlpatterns as v1_urls
from passbook.api.v2.urls import urlpatterns as v2_urls
urlpatterns = [ urlpatterns = [
path('v1/', include(v1_urls)) path('v1/', include(v1_urls)),
path('v2/', include(v2_urls)),
] ]

103
passbook/api/v2/urls.py Normal file
View File

@ -0,0 +1,103 @@
"""api v2 urls"""
from django.conf.urls import url
from django.urls import path
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
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.core.api.applications import ApplicationViewSet
from passbook.core.api.factors import FactorViewSet
from passbook.core.api.groups import GroupViewSet
from passbook.core.api.invitations import InvitationViewSet
from passbook.core.api.policies import PolicyViewSet
from passbook.core.api.propertymappings import PropertyMappingViewSet
from passbook.core.api.providers import ProviderViewSet
from passbook.core.api.sources import SourceViewSet
from passbook.core.api.users import UserViewSet
from passbook.factors.captcha.api import CaptchaFactorViewSet
from passbook.factors.dummy.api import DummyFactorViewSet
from passbook.factors.email.api import EmailFactorViewSet
from passbook.factors.otp.api import OTPFactorViewSet
from passbook.factors.password.api import PasswordFactorViewSet
from passbook.lib.utils.reflection import get_apps
from passbook.policies.expiry.api import PasswordExpiryPolicyViewSet
from passbook.policies.group.api import GroupMembershipPolicyViewSet
from passbook.policies.hibp.api import HaveIBeenPwendPolicyViewSet
from passbook.policies.matcher.api import FieldMatcherPolicyViewSet
from passbook.policies.password.api import PasswordPolicyViewSet
from passbook.policies.reputation.api import ReputationPolicyViewSet
from passbook.policies.sso.api import SSOLoginPolicyViewSet
from passbook.policies.webhook.api import WebhookPolicyViewSet
from passbook.providers.app_gw.api import ApplicationGatewayProviderViewSet
from passbook.providers.oauth.api import OAuth2ProviderViewSet
from passbook.providers.oidc.api import OpenIDProviderViewSet
from passbook.providers.saml.api import (SAMLPropertyMappingViewSet,
SAMLProviderViewSet)
from passbook.sources.ldap.api import (LDAPPropertyMappingViewSet,
LDAPSourceViewSet)
from passbook.sources.oauth.api import OAuthSourceViewSet
LOGGER = get_logger()
router = routers.DefaultRouter()
for _passbook_app in get_apps():
if hasattr(_passbook_app, 'api_mountpoint'):
for prefix, viewset in _passbook_app.api_mountpoint:
router.register(prefix, viewset)
LOGGER.debug("Mounted API URLs", app_name=_passbook_app.name)
router.register('core/applications', ApplicationViewSet)
router.register('core/invitations', InvitationViewSet)
router.register('core/groups', GroupViewSet)
router.register('core/users', UserViewSet)
router.register('audit/events', EventViewSet)
router.register('sources/all', SourceViewSet)
router.register('sources/ldap', LDAPSourceViewSet)
router.register('sources/oauth', OAuthSourceViewSet)
router.register('policies/all', PolicyViewSet)
router.register('policies/passwordexpiry', PasswordExpiryPolicyViewSet)
router.register('policies/groupmembership', GroupMembershipPolicyViewSet)
router.register('policies/haveibeenpwned', HaveIBeenPwendPolicyViewSet)
router.register('policies/fieldmatcher', FieldMatcherPolicyViewSet)
router.register('policies/password', PasswordPolicyViewSet)
router.register('policies/reputation', ReputationPolicyViewSet)
router.register('policies/ssologin', SSOLoginPolicyViewSet)
router.register('policies/webhook', WebhookPolicyViewSet)
router.register('providers/all', ProviderViewSet)
router.register('providers/applicationgateway', ApplicationGatewayProviderViewSet)
router.register('providers/oauth', OAuth2ProviderViewSet)
router.register('providers/openid', OpenIDProviderViewSet)
router.register('providers/saml', SAMLProviderViewSet)
router.register('propertymappings/all', PropertyMappingViewSet)
router.register('propertymappings/ldap', LDAPPropertyMappingViewSet)
router.register('propertymappings/saml', SAMLPropertyMappingViewSet)
router.register('factors/all', FactorViewSet)
router.register('factors/captcha', CaptchaFactorViewSet)
router.register('factors/dummy', DummyFactorViewSet)
router.register('factors/email', EmailFactorViewSet)
router.register('factors/otp', OTPFactorViewSet)
router.register('factors/password', PasswordFactorViewSet)
info = openapi.Info(
title="passbook API",
default_version='v2',
# description="Test description",
# terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="hello@beryju.org"),
license=openapi.License(name="MIT License"),
)
SchemaView = get_schema_view(
info,
public=True,
permission_classes=(CustomObjectPermissions,),
)
urlpatterns = [
url(r'^swagger(?P<format>\.json|\.yaml)$',
SchemaView.without_ui(cache_timeout=0), name='schema-json'),
path('swagger/', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', SchemaView.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
] + router.urls

View File

@ -0,0 +1,21 @@
"""Audit API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.audit.models import Event
class EventSerializer(ModelSerializer):
"""Event Serializer"""
class Meta:
model = Event
fields = ['pk', 'user', 'action', 'date', 'app', 'context', 'request_ip', 'created', ]
class EventViewSet(ReadOnlyModelViewSet):
"""Event Read-Only Viewset"""
queryset = Event.objects.all()
serializer_class = EventSerializer

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.6 on 2019-10-28 08:29
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('passbook_audit', '0001_initial'),
]
operations = [
migrations.RenameModel(
old_name='AuditEntry',
new_name='Event',
),
]

View File

@ -12,8 +12,8 @@ from passbook.lib.models import UUIDModel
LOGGER = get_logger() LOGGER = get_logger()
class AuditEntry(UUIDModel): class Event(UUIDModel):
"""An individual audit log entry""" """An individual audit log event"""
ACTION_LOGIN = 'login' ACTION_LOGIN = 'login'
ACTION_LOGIN_FAILED = 'login_failed' ACTION_LOGIN_FAILED = 'login_failed'
@ -46,7 +46,7 @@ class AuditEntry(UUIDModel):
@staticmethod @staticmethod
def create(action, request, **kwargs): def create(action, request, **kwargs):
"""Create AuditEntry from arguments""" """Create Event from arguments"""
client_ip, _ = get_client_ip(request) client_ip, _ = get_client_ip(request)
if not hasattr(request, 'user'): if not hasattr(request, 'user'):
user = None user = None
@ -54,7 +54,7 @@ class AuditEntry(UUIDModel):
user = request.user user = request.user
if isinstance(user, AnonymousUser): if isinstance(user, AnonymousUser):
user = kwargs.get('user', None) user = kwargs.get('user', None)
entry = AuditEntry.objects.create( entry = Event.objects.create(
action=action, action=action,
user=user, user=user,
# User 255.255.255.255 as fallback if IP cannot be determined # User 255.255.255.255 as fallback if IP cannot be determined

View File

@ -2,7 +2,7 @@
from django.contrib.auth.signals import user_logged_in, user_logged_out from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.dispatch import receiver from django.dispatch import receiver
from passbook.audit.models import AuditEntry from passbook.audit.models import Event
from passbook.core.signals import (invitation_created, invitation_used, from passbook.core.signals import (invitation_created, invitation_used,
user_signed_up) user_signed_up)
@ -10,26 +10,26 @@ from passbook.core.signals import (invitation_created, invitation_used,
@receiver(user_logged_in) @receiver(user_logged_in)
def on_user_logged_in(sender, request, user, **kwargs): def on_user_logged_in(sender, request, user, **kwargs):
"""Log successful login""" """Log successful login"""
AuditEntry.create(AuditEntry.ACTION_LOGIN, request) Event.create(Event.ACTION_LOGIN, request)
@receiver(user_logged_out) @receiver(user_logged_out)
def on_user_logged_out(sender, request, user, **kwargs): def on_user_logged_out(sender, request, user, **kwargs):
"""Log successfully logout""" """Log successfully logout"""
AuditEntry.create(AuditEntry.ACTION_LOGOUT, request) Event.create(Event.ACTION_LOGOUT, request)
@receiver(user_signed_up) @receiver(user_signed_up)
def on_user_signed_up(sender, request, user, **kwargs): def on_user_signed_up(sender, request, user, **kwargs):
"""Log successfully signed up""" """Log successfully signed up"""
AuditEntry.create(AuditEntry.ACTION_SIGN_UP, request) Event.create(Event.ACTION_SIGN_UP, request)
@receiver(invitation_created) @receiver(invitation_created)
def on_invitation_created(sender, request, invitation, **kwargs): def on_invitation_created(sender, request, invitation, **kwargs):
"""Log Invitation creation""" """Log Invitation creation"""
AuditEntry.create(AuditEntry.ACTION_INVITE_CREATED, request, Event.create(Event.ACTION_INVITE_CREATED, request,
invitation_uuid=invitation.uuid.hex) invitation_uuid=invitation.uuid.hex)
@receiver(invitation_used) @receiver(invitation_used)
def on_invitation_used(sender, request, invitation, **kwargs): def on_invitation_used(sender, request, invitation, **kwargs):
"""Log Invitation usage""" """Log Invitation usage"""
AuditEntry.create(AuditEntry.ACTION_INVITE_USED, request, Event.create(Event.ACTION_INVITE_USED, request,
invitation_uuid=invitation.uuid.hex) invitation_uuid=invitation.uuid.hex)

View File

@ -1,5 +1,4 @@
"""passbook admin application API""" """Application API Views"""
from rest_framework.permissions import IsAdminUser
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
@ -10,13 +9,14 @@ class ApplicationSerializer(ModelSerializer):
"""Application Serializer""" """Application Serializer"""
class Meta: class Meta:
model = Application model = Application
fields = '__all__' fields = ['pk', 'name', 'slug', 'launch_url', 'icon_url',
'provider', 'policies', 'skip_authorization']
class ApplicationViewSet(ModelViewSet): class ApplicationViewSet(ModelViewSet):
"""Application Viewset""" """Application Viewset"""
permission_classes = [IsAdminUser]
serializer_class = ApplicationSerializer
queryset = Application.objects.all() queryset = Application.objects.all()
serializer_class = ApplicationSerializer

View File

@ -0,0 +1,30 @@
"""Factor API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.core.models import Factor
class FactorSerializer(ModelSerializer):
"""Factor Serializer"""
__type__ = SerializerMethodField(method_name='get_type')
def get_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace('factor', '')
class Meta:
model = Factor
fields = ['pk', 'name', 'slug', 'order', 'enabled', '__type__']
class FactorViewSet(ReadOnlyModelViewSet):
"""Factor Viewset"""
queryset = Factor.objects.all()
serializer_class = FactorSerializer
def get_queryset(self):
return Factor.objects.select_subclasses()

View File

@ -0,0 +1,21 @@
"""Groups API Viewset"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.core.models import Group
class GroupSerializer(ModelSerializer):
"""Group Serializer"""
class Meta:
model = Group
fields = ['pk', 'name', 'parent', 'user_set', 'attributes']
class GroupViewSet(ModelViewSet):
"""Group Viewset"""
queryset = Group.objects.all()
serializer_class = GroupSerializer

View File

@ -0,0 +1,21 @@
"""Invitation API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.core.models import Invitation
class InvitationSerializer(ModelSerializer):
"""Invitation Serializer"""
class Meta:
model = Invitation
fields = ['pk', 'expires', 'fixed_username', 'fixed_email', 'needs_confirmation']
class InvitationViewSet(ModelViewSet):
"""Invitation Viewset"""
queryset = Invitation.objects.all()
serializer_class = InvitationSerializer

View File

@ -0,0 +1,31 @@
"""Policy API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.core.models import Policy
from passbook.policies.forms import GENERAL_FIELDS
class PolicySerializer(ModelSerializer):
"""Policy Serializer"""
__type__ = SerializerMethodField(method_name='get_type')
def get_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace('policy', '')
class Meta:
model = Policy
fields = ['pk'] + GENERAL_FIELDS + ['__type__']
class PolicyViewSet(ReadOnlyModelViewSet):
"""Policy Viewset"""
queryset = Policy.objects.all()
serializer_class = PolicySerializer
def get_queryset(self):
return Policy.objects.select_subclasses()

View File

@ -0,0 +1,30 @@
"""PropertyMapping API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.core.models import PropertyMapping
class PropertyMappingSerializer(ModelSerializer):
"""PropertyMapping Serializer"""
__type__ = SerializerMethodField(method_name='get_type')
def get_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace('propertymapping', '')
class Meta:
model = PropertyMapping
fields = ['pk', 'name', '__type__']
class PropertyMappingViewSet(ReadOnlyModelViewSet):
"""PropertyMapping Viewset"""
queryset = PropertyMapping.objects.all()
serializer_class = PropertyMappingSerializer
def get_queryset(self):
return PropertyMapping.objects.select_subclasses()

View File

@ -0,0 +1,30 @@
"""Provider API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.core.models import Provider
class ProviderSerializer(ModelSerializer):
"""Provider Serializer"""
__type__ = SerializerMethodField(method_name='get_type')
def get_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace('provider', '')
class Meta:
model = Provider
fields = ['pk', 'property_mappings', '__type__']
class ProviderViewSet(ReadOnlyModelViewSet):
"""Provider Viewset"""
queryset = Provider.objects.all()
serializer_class = ProviderSerializer
def get_queryset(self):
return Provider.objects.select_subclasses()

View File

@ -0,0 +1,31 @@
"""Source API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from passbook.admin.forms.source import SOURCE_SERIALIZER_FIELDS
from passbook.core.models import Source
class SourceSerializer(ModelSerializer):
"""Source Serializer"""
__type__ = SerializerMethodField(method_name='get_type')
def get_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace('source', '')
class Meta:
model = Source
fields = SOURCE_SERIALIZER_FIELDS + ['__type__']
class SourceViewSet(ReadOnlyModelViewSet):
"""Source Viewset"""
queryset = Source.objects.all()
serializer_class = SourceSerializer
def get_queryset(self):
return Source.objects.select_subclasses()

View File

@ -1,5 +1,4 @@
"""passbook admin user API""" """User API Views"""
from rest_framework.permissions import IsAdminUser
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
@ -10,14 +9,13 @@ class UserSerializer(ModelSerializer):
"""User Serializer""" """User Serializer"""
class Meta: class Meta:
model = User model = User
fields = ['is_superuser', 'username', 'name', 'email', 'date_joined', fields = ['pk', 'username', 'name', 'email']
'uuid']
class UserViewSet(ModelViewSet): class UserViewSet(ModelViewSet):
"""User Viewset""" """User Viewset"""
permission_classes = [IsAdminUser]
serializer_class = UserSerializer
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserSerializer

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.6 on 2019-10-10 10:58
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('passbook_core', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='user',
options={'permissions': (('reset_user_password', 'Reset Password'),)},
),
]

View File

@ -0,0 +1,14 @@
# Generated by Django 2.2.6 on 2019-10-10 15:41
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('passbook_core', '0002_auto_20191010_1058'),
('passbook_core', '0002_nonce_description'),
]
operations = [
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.6 on 2019-10-14 11:56
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('passbook_core', '0003_auto_20191011_0914'),
]
operations = [
migrations.RemoveField(
model_name='policy',
name='action',
),
]

View File

@ -0,0 +1,14 @@
# Generated by Django 2.2.6 on 2019-10-25 20:22
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('passbook_core', '0004_remove_policy_action'),
('passbook_core', '0003_merge_20191010_1541'),
]
operations = [
]

View File

@ -11,6 +11,7 @@ from django.db import models
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from guardian.mixins import GuardianUserMixin
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from structlog import get_logger from structlog import get_logger
@ -41,7 +42,7 @@ class Group(UUIDModel):
unique_together = (('name', 'parent',),) unique_together = (('name', 'parent',),)
class User(AbstractUser): class User(GuardianUserMixin, AbstractUser):
"""Custom User model to allow easier adding o f user-based settings""" """Custom User model to allow easier adding o f user-based settings"""
uuid = models.UUIDField(default=uuid4, editable=False) uuid = models.UUIDField(default=uuid4, editable=False)
@ -59,6 +60,11 @@ class User(AbstractUser):
self.password_change_date = now() self.password_change_date = now()
return super().set_password(password) return super().set_password(password)
class Meta:
permissions = (
('reset_user_password', 'Reset Password'),
)
class Provider(models.Model): class Provider(models.Model):
"""Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application""" """Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application"""
@ -186,15 +192,7 @@ class Policy(UUIDModel, CreatedUpdatedModel):
"""Policies which specify if a user is authorized to use an Application. Can be overridden by """Policies which specify if a user is authorized to use an Application. Can be overridden by
other types to add other fields, more logic, etc.""" other types to add other fields, more logic, etc."""
ACTION_ALLOW = 'allow'
ACTION_DENY = 'deny'
ACTIONS = (
(ACTION_ALLOW, ACTION_ALLOW),
(ACTION_DENY, ACTION_DENY),
)
name = models.TextField(blank=True, null=True) name = models.TextField(blank=True, null=True)
action = models.CharField(max_length=20, choices=ACTIONS)
negate = models.BooleanField(default=False) negate = models.BooleanField(default=False)
order = models.IntegerField(default=0) order = models.IntegerField(default=0)
timeout = models.IntegerField(default=30) timeout = models.IntegerField(default=30)
@ -202,9 +200,7 @@ class Policy(UUIDModel, CreatedUpdatedModel):
objects = InheritanceManager() objects = InheritanceManager()
def __str__(self): def __str__(self):
if self.name: return f"Policy {self.name}"
return self.name
return f"{self.name} action {self.action}"
def passes(self, request: PolicyRequest) -> PolicyResult: def passes(self, request: PolicyRequest) -> PolicyResult:
"""Check if user instance passes this policy""" """Check if user instance passes this policy"""

View File

@ -11,4 +11,4 @@ LOGGER = get_logger()
def clean_nonces(): def clean_nonces():
"""Remove expired nonces""" """Remove expired nonces"""
amount, _ = Nonce.objects.filter(expires__lt=now(), expiring=True).delete() amount, _ = Nonce.objects.filter(expires__lt=now(), expiring=True).delete()
LOGGER.debug("Deleted expired nonces", amount=amount) LOGGER.debug('Deleted expired nonces', amount=amount)

View File

@ -17,8 +17,8 @@ def user_factors(context: RequestContext) -> List[UserSettings]:
matching_factors: List[UserSettings] = [] matching_factors: List[UserSettings] = []
for factor in _all_factors: for factor in _all_factors:
user_settings = factor.user_settings() user_settings = factor.user_settings()
policy_engine = PolicyEngine(factor.policies.all()) policy_engine = PolicyEngine(factor.policies.all(), user, context.get('request'))
policy_engine.for_user(user).with_request(context.get('request')).build() policy_engine.build()
if policy_engine.passing and user_settings: if policy_engine.passing and user_settings:
matching_factors.append(user_settings) matching_factors.append(user_settings)
return matching_factors return matching_factors
@ -31,8 +31,8 @@ def user_sources(context: RequestContext) -> List[UserSettings]:
matching_sources: List[UserSettings] = [] matching_sources: List[UserSettings] = []
for factor in _all_sources: for factor in _all_sources:
user_settings = factor.user_settings() user_settings = factor.user_settings()
policy_engine = PolicyEngine(factor.policies.all()) policy_engine = PolicyEngine(factor.policies.all(), user, context.get('request'))
policy_engine.for_user(user).with_request(context.get('request')).build() policy_engine.build()
if policy_engine.passing and user_settings: if policy_engine.passing and user_settings:
matching_sources.append(user_settings) matching_sources.append(user_settings)
return matching_sources return matching_sources

View File

@ -31,6 +31,6 @@ class AccessMixin:
def user_has_access(self, application: Application, user: User) -> Tuple[bool, List[str]]: def user_has_access(self, application: Application, user: User) -> Tuple[bool, List[str]]:
"""Check if user has access to application.""" """Check if user has access to application."""
LOGGER.debug("Checking permissions", user=user, application=application) LOGGER.debug("Checking permissions", user=user, application=application)
policy_engine = PolicyEngine(application.policies.all()) policy_engine = PolicyEngine(application.policies.all(), user, self.request)
policy_engine.for_user(user).with_request(self.request).build() policy_engine.build()
return policy_engine.result return policy_engine.result

View File

@ -16,8 +16,7 @@ class OverviewView(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['applications'] = [] kwargs['applications'] = []
for application in Application.objects.all(): for application in Application.objects.all():
engine = PolicyEngine(application.policies.all()) engine = PolicyEngine(application.policies.all(), self.request.user, self.request)
engine.for_user(self.request.user).with_request(self.request)
engine.build() engine.build()
if engine.passing: if engine.passing:
kwargs['applications'].append(application) kwargs['applications'].append(application)

View File

@ -0,0 +1,21 @@
"""CaptchaFactor API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.factors.captcha.models import CaptchaFactor
class CaptchaFactorSerializer(ModelSerializer):
"""CaptchaFactor Serializer"""
class Meta:
model = CaptchaFactor
fields = ['pk', 'name', 'slug', 'order', 'enabled', 'public_key', 'private_key']
class CaptchaFactorViewSet(ModelViewSet):
"""CaptchaFactor Viewset"""
queryset = CaptchaFactor.objects.all()
serializer_class = CaptchaFactorSerializer

View File

@ -0,0 +1,21 @@
"""DummyFactor API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.factors.dummy.models import DummyFactor
class DummyFactorSerializer(ModelSerializer):
"""DummyFactor Serializer"""
class Meta:
model = DummyFactor
fields = ['pk', 'name', 'slug', 'order', 'enabled']
class DummyFactorViewSet(ModelViewSet):
"""DummyFactor Viewset"""
queryset = DummyFactor.objects.all()
serializer_class = DummyFactorSerializer

View File

@ -0,0 +1,33 @@
"""EmailFactor API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.factors.email.models import EmailFactor
class EmailFactorSerializer(ModelSerializer):
"""EmailFactor Serializer"""
class Meta:
model = EmailFactor
fields = ['pk', 'name', 'slug', 'order', 'enabled', 'host',
'port',
'username',
'password',
'use_tls',
'use_ssl',
'timeout',
'from_address',
'ssl_keyfile',
'ssl_certfile', ]
extra_kwargs = {
'password': {'write_only': True}
}
class EmailFactorViewSet(ModelViewSet):
"""EmailFactor Viewset"""
queryset = EmailFactor.objects.all()
serializer_class = EmailFactorSerializer

View File

@ -0,0 +1,21 @@
"""OTPFactor API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.factors.otp.models import OTPFactor
class OTPFactorSerializer(ModelSerializer):
"""OTPFactor Serializer"""
class Meta:
model = OTPFactor
fields = ['pk', 'name', 'slug', 'order', 'enabled', 'enforced']
class OTPFactorViewSet(ModelViewSet):
"""OTPFactor Viewset"""
queryset = OTPFactor.objects.all()
serializer_class = OTPFactorSerializer

View File

@ -0,0 +1,22 @@
"""PasswordFactor API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.factors.password.models import PasswordFactor
class PasswordFactorSerializer(ModelSerializer):
"""PasswordFactor Serializer"""
class Meta:
model = PasswordFactor
fields = ['pk', 'name', 'slug', 'order', 'enabled',
'backends', 'password_policies', 'reset_factors']
class PasswordFactorViewSet(ModelViewSet):
"""PasswordFactor Viewset"""
queryset = PasswordFactor.objects.all()
serializer_class = PasswordFactorSerializer

View File

@ -13,8 +13,8 @@ def password_policy_checker(sender, password, **_):
setattr(sender, '__password__', password) setattr(sender, '__password__', password)
_all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order') _all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order')
for factor in _all_factors: for factor in _all_factors:
policy_engine = PolicyEngine(factor.password_policies.all().select_subclasses()) policy_engine = PolicyEngine(factor.password_policies.all().select_subclasses(), sender)
policy_engine.for_user(sender).build() policy_engine.build()
passing, messages = policy_engine.result passing, messages = policy_engine.result
if not passing: if not passing:
raise PasswordPolicyInvalid(*messages) raise PasswordPolicyInvalid(*messages)

View File

@ -67,8 +67,8 @@ class AuthenticationView(UserPassesTestMixin, View):
for factor in _all_factors: for factor in _all_factors:
LOGGER.debug("Checking if factor applies to user", LOGGER.debug("Checking if factor applies to user",
factor=factor, user=self.pending_user) factor=factor, user=self.pending_user)
policy_engine = PolicyEngine(factor.policies.all()) policy_engine = PolicyEngine(factor.policies.all(), self.pending_user, self.request)
policy_engine.for_user(self.pending_user).with_request(self.request).build() policy_engine.build()
if policy_engine.passing: if policy_engine.passing:
pending_factors.append((factor.uuid.hex, factor.type)) pending_factors.append((factor.uuid.hex, factor.type))
LOGGER.debug("Factor applies", factor=factor, user=self.pending_user) LOGGER.debug("Factor applies", factor=factor, user=self.pending_user)

View File

@ -4,6 +4,7 @@ from django.apps import apps
from django.contrib import admin from django.contrib import admin
from django.contrib.admin.sites import AlreadyRegistered from django.contrib.admin.sites import AlreadyRegistered
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from guardian.admin import GuardedModelAdmin
from passbook.core.models import User from passbook.core.models import User
@ -13,10 +14,9 @@ def admin_autoregister(app):
app_models = apps.get_app_config(app).get_models() app_models = apps.get_app_config(app).get_models()
for model in app_models: for model in app_models:
try: try:
admin.site.register(model) admin.site.register(model, GuardedModelAdmin)
except AlreadyRegistered: except AlreadyRegistered:
pass pass
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)
admin_autoregister('passbook_core')

View File

@ -13,8 +13,8 @@ redis:
debug: false debug: false
# Error reporting, sends stacktrace to sentry.services.beryju.org # Error reporting, sends stacktrace to sentry.beryju.org
error_report_enabled: true error_reporting: false
domain: localhost domain: localhost

View File

@ -11,6 +11,7 @@ def before_send(event, hint):
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from billiard.exceptions import WorkerLostError from billiard.exceptions import WorkerLostError
from django.core.exceptions import DisallowedHost from django.core.exceptions import DisallowedHost
from botocore.client import ClientError
ignored_classes = ( ignored_classes = (
OperationalError, OperationalError,
ConnectionInterrupted, ConnectionInterrupted,
@ -20,6 +21,8 @@ def before_send(event, hint):
WorkerLostError, WorkerLostError,
DisallowedHost, DisallowedHost,
ConnectionResetError, ConnectionResetError,
KeyboardInterrupt,
ClientError
) )
if 'exc_info' in hint: if 'exc_info' in hint:
_exc_type, exc_value, _ = hint['exc_info'] _exc_type, exc_value, _ = hint['exc_info']

13
passbook/lib/tasks.py Normal file
View File

@ -0,0 +1,13 @@
"""passbook misc tasks"""
from django.core import management
from structlog import get_logger
from passbook.root.celery import CELERY_APP
LOGGER = get_logger()
@CELERY_APP.task()
def backup_database():
"""Backup database"""
management.call_command('dbbackup')
LOGGER.info('Successfully backed up database.')

23
passbook/lib/views.py Normal file
View File

@ -0,0 +1,23 @@
"""passbook helper views"""
from django.views.generic import CreateView
from guardian.shortcuts import assign_perm
class CreateAssignPermView(CreateView):
"""Assign permissions to object after creation"""
permissions = [
'%s.view_%s',
'%s.change_%s',
'%s.delete_%s',
]
def form_valid(self, form):
response = super().form_valid(form)
for permission in self.permissions:
full_permission = permission % (
self.object._meta.app_label, self.object._meta.model_name)
print(full_permission)
assign_perm(full_permission, self.request.user, self.object)
return response

View File

@ -1,7 +1,7 @@
"""passbook policy engine""" """passbook policy engine"""
from multiprocessing import Pipe from multiprocessing import Pipe
from multiprocessing.connection import Connection from multiprocessing.connection import Connection
from typing import List, Tuple from typing import List, Optional, Tuple
from django.core.cache import cache from django.core.cache import cache
from django.http import HttpRequest from django.http import HttpRequest
@ -13,44 +13,37 @@ from passbook.policies.struct import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()
class PolicyProcessInfo: class PolicyProcessInfo:
"""Dataclass to hold all information and communication channels to a process""" """Dataclass to hold all information and communication channels to a process"""
process: PolicyProcess process: PolicyProcess
connection: Connection connection: Connection
result: PolicyResult = None result: Optional[PolicyResult]
policy: Policy policy: Policy
def __init__(self, process: PolicyProcess, connection: Connection, policy: Policy): def __init__(self, process: PolicyProcess, connection: Connection, policy: Policy):
self.process = process self.process = process
self.connection = connection self.connection = connection
self.policy = policy self.policy = policy
self.result = None
class PolicyEngine: class PolicyEngine:
"""Orchestrate policy checking, launch tasks and return result""" """Orchestrate policy checking, launch tasks and return result"""
use_cache: bool = True
policies: List[Policy] = [] policies: List[Policy] = []
__request: HttpRequest request: PolicyRequest
__user: User
__processes: List[PolicyProcessInfo] = [] __processes: List[PolicyProcessInfo] = []
def __init__(self, policies, user: User = None, request: HttpRequest = None): def __init__(self, policies, user: User, request: HttpRequest = None):
self.policies = policies self.policies = policies
self.__request = request self.request = PolicyRequest(user)
self.__user = user if request:
self.request.http_request = request
self.__processes = [] self.__processes = []
def for_user(self, user: User) -> 'PolicyEngine':
"""Check policies for user"""
self.__user = user
return self
def with_request(self, request: HttpRequest) -> 'PolicyEngine':
"""Set request"""
self.__request = request
return self
def _select_subclasses(self) -> List[Policy]: def _select_subclasses(self) -> List[Policy]:
"""Make sure all Policies are their respective classes""" """Make sure all Policies are their respective classes"""
return Policy.objects \ return Policy.objects \
@ -60,21 +53,17 @@ class PolicyEngine:
def build(self) -> 'PolicyEngine': def build(self) -> 'PolicyEngine':
"""Build task group""" """Build task group"""
if not self.__user:
raise ValueError("User not set.")
cached_policies = [] cached_policies = []
request = PolicyRequest(self.__user)
request.http_request = self.__request
for policy in self._select_subclasses(): for policy in self._select_subclasses():
cached_policy = cache.get(cache_key(policy, self.__user), None) cached_policy = cache.get(cache_key(policy, self.request.user), None)
if cached_policy: if cached_policy and self.use_cache:
LOGGER.debug("Taking result from cache", policy=policy) LOGGER.debug("Taking result from cache", policy=policy)
cached_policies.append(cached_policy) cached_policies.append(cached_policy)
else: else:
LOGGER.debug("Evaluating policy", policy=policy) LOGGER.debug("Evaluating policy", policy=policy)
our_end, task_end = Pipe(False) our_end, task_end = Pipe(False)
task = PolicyProcess(policy, request, task_end) task = PolicyProcess(policy, self.request, task_end)
LOGGER.debug("Starting Process", for_policy=policy) LOGGER.debug("Starting Process", policy=policy)
task.start() task.start()
self.__processes.append(PolicyProcessInfo(process=task, self.__processes.append(PolicyProcessInfo(process=task,
connection=our_end, policy=policy)) connection=our_end, policy=policy))
@ -91,9 +80,7 @@ class PolicyEngine:
"""Get policy-checking result""" """Get policy-checking result"""
messages: List[str] = [] messages: List[str] = []
for proc_info in self.__processes: for proc_info in self.__processes:
# passing = (policy_action == Policy.ACTION_ALLOW and policy_result) or \ LOGGER.debug("Result", policy=proc_info.policy, passing=proc_info.result.passing)
# (policy_action == Policy.ACTION_DENY and not policy_result)
LOGGER.debug("Result", passing=proc_info.result.passing)
if proc_info.result.messages: if proc_info.result.messages:
messages += proc_info.result.messages messages += proc_info.result.messages
if not proc_info.result.passing: if not proc_info.result.passing:

View File

@ -0,0 +1,21 @@
"""Source API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.policies.expiry.models import PasswordExpiryPolicy
from passbook.policies.forms import GENERAL_SERIALIZER_FIELDS
class PasswordExpiryPolicySerializer(ModelSerializer):
"""Password Expiry Policy Serializer"""
class Meta:
model = PasswordExpiryPolicy
fields = GENERAL_SERIALIZER_FIELDS + ['days', 'deny_only']
class PasswordExpiryPolicyViewSet(ModelViewSet):
"""Source Viewset"""
queryset = PasswordExpiryPolicy.objects.all()
serializer_class = PasswordExpiryPolicySerializer

View File

@ -1,3 +1,4 @@
"""General fields""" """General fields"""
GENERAL_FIELDS = ['name', 'action', 'negate', 'order', 'timeout'] GENERAL_FIELDS = ['name', 'negate', 'order', 'timeout']
GENERAL_SERIALIZER_FIELDS = ['pk', 'name', 'negate', 'order', 'timeout']

View File

@ -0,0 +1,21 @@
"""Source API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.policies.forms import GENERAL_SERIALIZER_FIELDS
from passbook.policies.group.models import GroupMembershipPolicy
class GroupMembershipPolicySerializer(ModelSerializer):
"""Group Membership Policy Serializer"""
class Meta:
model = GroupMembershipPolicy
fields = GENERAL_SERIALIZER_FIELDS + ['group']
class GroupMembershipPolicyViewSet(ModelViewSet):
"""Source Viewset"""
queryset = GroupMembershipPolicy.objects.all()
serializer_class = GroupMembershipPolicySerializer

View File

@ -0,0 +1,21 @@
"""Source API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.policies.forms import GENERAL_SERIALIZER_FIELDS
from passbook.policies.hibp.models import HaveIBeenPwendPolicy
class HaveIBeenPwendPolicySerializer(ModelSerializer):
"""Have I Been Pwned Policy Serializer"""
class Meta:
model = HaveIBeenPwendPolicy
fields = GENERAL_SERIALIZER_FIELDS + ['allowed_count']
class HaveIBeenPwendPolicyViewSet(ModelViewSet):
"""Source Viewset"""
queryset = HaveIBeenPwendPolicy.objects.all()
serializer_class = HaveIBeenPwendPolicySerializer

View File

@ -0,0 +1,21 @@
"""Source API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.policies.forms import GENERAL_SERIALIZER_FIELDS
from passbook.policies.matcher.models import FieldMatcherPolicy
class FieldMatcherPolicySerializer(ModelSerializer):
"""Field Matcher Policy Serializer"""
class Meta:
model = FieldMatcherPolicy
fields = GENERAL_SERIALIZER_FIELDS + ['user_field', 'match_action', 'value', ]
class FieldMatcherPolicyViewSet(ModelViewSet):
"""Source Viewset"""
queryset = FieldMatcherPolicy.objects.all()
serializer_class = FieldMatcherPolicySerializer

View File

@ -0,0 +1,23 @@
"""Source API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.policies.forms import GENERAL_SERIALIZER_FIELDS
from passbook.policies.password.models import PasswordPolicy
class PasswordPolicySerializer(ModelSerializer):
"""Password Policy Serializer"""
class Meta:
model = PasswordPolicy
fields = GENERAL_SERIALIZER_FIELDS + ['amount_uppercase', 'amount_lowercase',
'amount_symbols', 'length_min', 'symbol_charset',
'error_message']
class PasswordPolicyViewSet(ModelViewSet):
"""Source Viewset"""
queryset = PasswordPolicy.objects.all()
serializer_class = PasswordPolicySerializer

View File

@ -40,9 +40,9 @@ class PolicyProcess(Process):
policy_result = PolicyResult(False, str(exc)) policy_result = PolicyResult(False, str(exc))
# Invert result if policy.negate is set # Invert result if policy.negate is set
if self.policy.negate: if self.policy.negate:
policy_result = not policy_result policy_result.passing = not policy_result.passing
LOGGER.debug("Got result", policy=self.policy, result=policy_result, LOGGER.debug("Got result", policy=self.policy, result=policy_result,
process="PolicyProcess") process="PolicyProcess", passing=policy_result.passing, user=self.request.user)
key = cache_key(self.policy, self.request.user) key = cache_key(self.policy, self.request.user)
cache.set(key, policy_result) cache.set(key, policy_result)
LOGGER.debug("Cached policy evaluation", key=key) LOGGER.debug("Cached policy evaluation", key=key)

Some files were not shown because too many files have changed in this diff Show More