Compare commits

...

280 Commits

Author SHA1 Message Date
5265064b10 new release: 0.9.0-pre1 2020-06-08 15:47:22 +02:00
ee8313142f Merge branch 'docs-flows'
# Conflicts:
#	passbook/core/templates/partials/form_horizontal.html
2020-06-08 15:43:46 +02:00
45dd004d00 build(deps): bump boto3 from 1.13.23 to 1.13.24 (#55)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.23 to 1.13.24.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.23...1.13.24)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-08 10:16:49 +02:00
c4ebc5c085 build(deps): bump swagger-spec-validator from 2.6.0 to 2.7.0 (#56)
Bumps [swagger-spec-validator](https://github.com/Yelp/swagger_spec_validator) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/Yelp/swagger_spec_validator/releases)
- [Changelog](https://github.com/Yelp/swagger_spec_validator/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/Yelp/swagger_spec_validator/compare/v2.6.0...v2.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-08 10:04:42 +02:00
31c42f1104 build(deps): bump django-filter from 2.2.0 to 2.3.0 (#54)
Bumps [django-filter](https://github.com/carltongibson/django-filter) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/carltongibson/django-filter/releases)
- [Changelog](https://github.com/carltongibson/django-filter/blob/master/CHANGES.rst)
- [Commits](https://github.com/carltongibson/django-filter/compare/2.2.0...2.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-08 09:02:34 +02:00
8b7f665c82 build(deps): bump django-guardian from 2.2.0 to 2.3.0 (#57)
Bumps [django-guardian](https://github.com/django-guardian/django-guardian) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/django-guardian/django-guardian/releases)
- [Changelog](https://github.com/django-guardian/django-guardian/blob/devel/CHANGES)
- [Commits](https://github.com/django-guardian/django-guardian/compare/v2.2.0...v2.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-08 09:01:33 +02:00
4915205678 WIP Use Flows for Sources and Providers (#32)
* core: start migrating to flows for authorisation

* sources/oauth: start type-hinting

* core: create default user

* core: only show user delete button if an unenrollment flow exists

* flows: Correctly check initial policies on flow with context

* policies: add more verbosity to engine

* sources/oauth: migrate to flows

* sources/oauth: fix typing errors

* flows: add more tests

* sources/oauth: start implementing unittests

* sources/ldap: add option to disable user sync, move connection init to model

* sources/ldap: re-add default PropertyMappings

* providers/saml: re-add default PropertyMappings

* admin: fix missing stage count

* stages/identification: fix sources not being shown

* crypto: fix being unable to save with private key

* crypto: re-add default self-signed keypair

* policies: rewrite cache_key to prevent wrong cache

* sources/saml: migrate to flows for auth and enrollment

* stages/consent: add new stage

* admin: fix PropertyMapping widget not rendering properly

* core: provider.authorization_flow is mandatory

* flows: add support for "autosubmit" attribute on form

* flows: add InMemoryStage for dynamic stages

* flows: optionally allow empty flows from FlowPlanner

* providers/saml: update to authorization_flow

* sources/*: fix flow executor URL

* flows: fix pylint error

* flows: wrap responses in JSON object to easily handle redirects

* flow: dont cache plan's context

* providers/oauth: rewrite OAuth2 Provider to use flows

* providers/*: update docstrings of models

* core: fix forms not passing help_text through safe

* flows: fix HttpResponses not being converted to JSON

* providers/oidc: rewrite to use flows

* flows: fix linting
2020-06-07 16:35:08 +02:00
07da5f7122 Merge branch 'master' into docs-flows 2020-06-05 20:30:59 +02:00
f91e02a0ec flows: allow username for default flow 2020-06-05 20:30:47 +02:00
c62794c738 admin: fix PropertyMapping widget not rendering properly 2020-06-05 20:27:18 +02:00
7664b428e7 sources/ldap: fix expression field not being CodeMirror 2020-06-05 20:18:45 +02:00
9f3f34389e root: update docs URL 2020-06-05 20:18:26 +02:00
30ca926b38 docs: remove last occurrences to jinja2 2020-06-05 20:18:11 +02:00
eeb62f543f docs: fix wrong paths in nav section 2020-06-05 17:31:41 +02:00
6a7ffa855e docs: add example for login flow 2020-06-05 17:29:08 +02:00
0c5aff21bc stages/identification: minor UI fixes 2020-06-05 17:02:50 +02:00
b0f426e51a core: fix help_text not always being shown on horizontal form 2020-06-05 17:01:46 +02:00
ed0094eba2 docs: add runtime 2020-06-05 15:58:10 +02:00
4c1b6d5f48 docs: add requirements for docs 2020-06-05 15:54:59 +02:00
41a7e1ef07 docs: simplify index, update, add screenshots 2020-06-05 12:51:28 +02:00
4ff41be74a Merge branch 'master' into docs-flows
# Conflicts:
#	mkdocs.yml
2020-06-05 12:51:10 +02:00
7067d1f236 docs: fix typo'd URL 2020-06-05 12:10:28 +02:00
73116b9d1a policies/expression: migrate to raw python instead of jinja2 (#49)
* policies/expression: migrate to raw python instead of jinja2

* lib/expression: create base evaluator, custom subclass for policies

* core: rewrite propertymappings to use python

* providers/saml: update to new PropertyMappings

* sources/ldap: update to new PropertyMappings

* docs: update docs for new propertymappings

* root: remove jinja2

* root: re-add jinja to lock file as its implicitly required
2020-06-05 12:00:27 +02:00
147212a5f9 build(deps): bump boto3 from 1.13.22 to 1.13.23 (#53)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.22 to 1.13.23.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.22...1.13.23)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-05 10:27:07 +02:00
5502c319d4 build(deps): bump celery from 4.4.2 to 4.4.4 (#50)
Bumps [celery](https://github.com/celery/celery) from 4.4.2 to 4.4.4.
- [Release notes](https://github.com/celery/celery/releases)
- [Changelog](https://github.com/celery/celery/blob/master/Changelog.rst)
- [Commits](https://github.com/celery/celery/compare/4.4.2...v4.4.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 09:15:42 +02:00
d18b76a47d build(deps): bump boto3 from 1.13.21 to 1.13.22 (#51)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.21 to 1.13.22.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.21...1.13.22)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 09:15:31 +02:00
90ce704def build(deps): bump django from 3.0.6 to 3.0.7 (#52)
Bumps [django](https://github.com/django/django) from 3.0.6 to 3.0.7.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.0.6...3.0.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-04 09:15:11 +02:00
790139f8bc stages/email: Add test to check if user is pending 2020-06-03 21:00:04 +02:00
f4be007803 build(deps): bump boto3 from 1.13.20 to 1.13.21 (#47)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.20 to 1.13.21.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.20...1.13.21)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-03 08:56:13 +02:00
448ca62661 build(deps): bump kombu from 4.6.9 to 4.6.10 (#48)
Bumps [kombu](https://kombu.readthedocs.io) from 4.6.9 to 4.6.10.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-03 08:55:23 +02:00
4824e5c8ba docs: add docs for flows and all stages 2020-06-02 23:52:02 +02:00
c4facd53b4 Merge branch 'master' into docs-flows 2020-06-02 20:25:43 +02:00
ef913abc7a sources/ldap: add option to disable user sync, move connection init to model 2020-06-02 17:15:59 +02:00
55fc5a6068 policies: rewrite cache_key to prevent wrong cache
# Conflicts:
#	passbook/core/signals.py
#	passbook/policies/engine.py
#	passbook/policies/process.py
2020-06-02 17:06:18 +02:00
f22c89c998 crypto: re-add default self-signed keypair 2020-06-02 17:01:15 +02:00
2a78d2d0a0 crypto: fix being unable to save with private key 2020-06-02 17:01:15 +02:00
b0ddc6a8c0 admin: fix missing stage count 2020-06-02 17:00:26 +02:00
8080b0380e providers/saml: re-add default PropertyMappings 2020-06-02 17:00:03 +02:00
ddfa2abbaa sources/ldap: re-add default PropertyMappings 2020-06-02 17:00:03 +02:00
5fc5e54f47 sources/oauth: fix typing errors
# Conflicts:
#	passbook/sources/oauth/clients.py
2020-06-02 16:57:38 +02:00
7462d56182 policies/engine: add more verbosity 2020-06-02 16:56:58 +02:00
3f92d1c420 flows: Correctly check initial policies on flow with context
# Conflicts:
#	passbook/flows/planner.py
#	passbook/flows/tests/test_planner.py
#	passbook/flows/tests/test_views.py
#	passbook/flows/views.py
2020-06-02 16:53:40 +02:00
d4fa60f509 core: only show user delete button if an unenrollment flow exists 2020-06-02 16:44:29 +02:00
052bf88c3d core: create default user
# Conflicts:
#	README.md
2020-06-02 16:42:44 +02:00
07d047c887 stages/identification: fix *_flows missing in edit form 2020-06-02 16:39:55 +02:00
23193314f1 Merge branch 'master' into docs-flows 2020-06-02 15:20:24 +02:00
1912b29dc5 policies/expression: fix lint error 2020-06-02 15:20:02 +02:00
46410428d9 build(deps): bump boto3 from 1.13.19 to 1.13.20 (#45)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.19 to 1.13.20.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.19...1.13.20)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-02 09:38:17 +02:00
1774e33c24 build(deps): bump kombu from 4.6.8 to 4.6.9 (#46)
Bumps [kombu](https://kombu.readthedocs.io) from 4.6.8 to 4.6.9.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-02 09:37:53 +02:00
c961327d27 stages/identification: fix recovery template 2020-06-01 19:08:14 +02:00
fe1ff7fc76 core: fix form not showing general errors 2020-06-01 19:08:01 +02:00
82d12ecfdf policies/expression: use pb_message() for messages instead of returning a tuple 2020-06-01 15:25:38 +02:00
6ed822fa38 build(deps-dev): bump autopep8 from 1.5.2 to 1.5.3 (#44)
Bumps [autopep8](https://github.com/hhatto/autopep8) from 1.5.2 to 1.5.3.
- [Release notes](https://github.com/hhatto/autopep8/releases)
- [Commits](https://github.com/hhatto/autopep8/compare/v1.5.2...v1.5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-01 12:40:08 +02:00
4d1658b35e stages/identification: explicitly define enrollment and recovery 2020-05-31 23:01:08 +02:00
08302a04bf docs: add docs for flows, start docs for stages 2020-05-30 22:36:01 +02:00
8b4558fcd0 build(deps): bump boto3 from 1.13.18 to 1.13.19 (#41)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.18 to 1.13.19.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.18...1.13.19)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-29 08:53:41 +02:00
80a36a3441 stages/user_write: don't use create_user, create empty object and set attributes 2020-05-28 23:22:15 +02:00
b1b63fbb7f stages/prompt: add prompt order field 2020-05-28 23:21:43 +02:00
fe6bfb1620 stages/identification: fix wrong link 2020-05-28 22:44:42 +02:00
e6d64bf5b3 stages/identification: fix sources and recovery/enrollment not being shown 2020-05-28 22:43:44 +02:00
270739a45a admin: fix policy testing form not showing the correct result 2020-05-28 21:50:43 +02:00
df8995deed policies/*: remove Policy.negate, order, timeout (#39)
policies: rewrite engine to use PolicyBinding for order/negate/timeout
policies: rewrite engine to use PolicyResult instead of tuple
2020-05-28 21:45:54 +02:00
fdfc6472d2 admin: fixup some urls 2020-05-28 10:36:27 +02:00
bc495828e7 build(deps): bump django-redis from 4.11.0 to 4.12.1 (#38)
Bumps [django-redis](https://github.com/jazzband/django-redis) from 4.11.0 to 4.12.1.
- [Release notes](https://github.com/jazzband/django-redis/releases)
- [Changelog](https://github.com/jazzband/django-redis/blob/master/CHANGES.rst)
- [Commits](https://github.com/jazzband/django-redis/compare/4.11.0...4.12.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-28 09:39:28 +02:00
fa138a273f build(deps): bump boto3 from 1.13.17 to 1.13.18 (#37)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.17 to 1.13.18.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.17...1.13.18)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-28 08:59:19 +02:00
5555dd4dde root: update readme 2020-05-27 17:57:43 +02:00
084ff2994f ci: add cleanup to CI to remove old tags 2020-05-27 17:33:27 +02:00
ee8a61d164 root: update readme 2020-05-27 17:26:35 +02:00
60a363e4bf build(deps): bump boto3 from 1.13.15 to 1.13.17 (#35)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.15 to 1.13.17.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.15...1.13.17)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-27 11:53:59 +02:00
e2804b9755 root: fix linting errors 2020-05-27 11:26:48 +02:00
9330a03273 root: fix pylint not correctly ignoring migrations 2020-05-27 11:15:10 +02:00
beabba2890 flows: Load Stages without refreshing the whole page (#33)
* flows: initial implementation of FlowExecutorShell

* flows: load messages dynamically upon card refresh
2020-05-24 00:57:25 +02:00
eeeb14a045 admin: fix urls for applications 2020-05-22 23:22:40 +02:00
ff3f126832 build(deps): bump boto3 from 1.13.14 to 1.13.15 (#31)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.14 to 1.13.15.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.14...1.13.15)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-22 09:55:25 +02:00
73225917cd build(deps): bump codemirror in /passbook/static/static (#29)
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.53.2 to 5.54.0.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.53.2...5.54.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-21 19:12:14 +02:00
8b7a285f4a build(deps): bump swagger-spec-validator from 2.5.0 to 2.6.0 (#28)
Bumps [swagger-spec-validator](https://github.com/Yelp/swagger_spec_validator) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/Yelp/swagger_spec_validator/releases)
- [Changelog](https://github.com/Yelp/swagger_spec_validator/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/Yelp/swagger_spec_validator/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-21 19:11:27 +02:00
3a4c6a5d67 build(deps): bump boto3 from 1.13.13 to 1.13.14 (#30)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.13 to 1.13.14.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.13...1.13.14)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-21 14:20:51 +02:00
0664f0b6b2 flows: add support for default_context, etc
default_context can be used to influence policies during the planning. This should be used when the Planner is called from other views to correctly preseed the plan.
This also checks if there is a PENDING_USER set, and uses that user for the cache key instead
2020-05-20 16:15:16 +02:00
fab9f1cb1b root: clear cache on debug startup 2020-05-20 16:13:48 +02:00
b3c54308b7 policies/expression: validate syntax on form clean 2020-05-20 16:13:36 +02:00
c1a72a0474 crypto: fix clean_ methods not returning values 2020-05-20 16:10:12 +02:00
4377d1e86e admin: fix flow ordering, fix source URLs 2020-05-20 16:09:53 +02:00
cafe2f1e1f admin: fix linting 2020-05-20 13:59:56 +02:00
c329a724e8 admin: fix wrong name in template 2020-05-20 13:55:17 +02:00
969da05437 admin: show object's docstring on inheritance based lists 2020-05-20 13:47:58 +02:00
c61c84e9f3 admin: remove inline JS, add CodeMirror widget for jinja and yaml 2020-05-20 13:00:45 +02:00
e08c5ff875 helm: fully remove domain, add log_level 2020-05-20 12:16:56 +02:00
d1fd616b8d build(deps): bump packaging from 20.3 to 20.4 (#25)
Bumps [packaging](https://github.com/pypa/packaging) from 20.3 to 20.4.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/20.3...20.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-20 09:41:31 +02:00
cc4bbc049b build(deps): bump boto3 from 1.13.12 to 1.13.13 (#27)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.12 to 1.13.13.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.12...1.13.13)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-20 09:38:04 +02:00
24a3e787dd migrate to per-model UUID Primary key, remove UUIDModel (#26)
* *: migrate to per-model UUID Primary key, remove UUIDModel

* *: fix import order, fix unittests
2020-05-20 09:17:06 +02:00
13a20478fd sources/oauth: add OIDC client 2020-05-19 21:53:46 +02:00
f58ee7fb52 sources/oauth: fix handling of sources with spaces in their name 2020-05-19 21:53:36 +02:00
4d45dc31a9 helm: limit deployments to their respective components 2020-05-19 21:25:56 +02:00
f6b5fbc2cf admin: fix sidebar 2020-05-19 21:02:10 +02:00
db69c3e38d admin: add generic tests 2020-05-19 20:59:43 +02:00
5ba55356a9 admin: cleanup templates, cleanup templatetags 2020-05-19 20:29:02 +02:00
6104b7c9ba build(deps): bump lxml from 4.5.0 to 4.5.1 (#24)
Bumps [lxml](https://github.com/lxml/lxml) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-4.5.0...lxml-4.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-19 15:58:30 +02:00
bcef8369ff build(deps): bump boto3 from 1.13.11 to 1.13.12 (#23)
Bumps [boto3](https://github.com/boto/boto3) from 1.13.11 to 1.13.12.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.13.11...1.13.12)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-19 07:40:00 +02:00
0fe555a76e Merge pull request #17 from BeryJu/dependabot/pip/pylint-2.5.2
build(deps-dev): bump pylint from 2.4.4 to 2.5.2
2020-05-19 07:33:17 +02:00
c903c81bd5 root: update pylint ignore list 2020-05-18 18:15:39 +02:00
2c43bfb13f Merge pull request #22 from BeryJu/dependabot/pip/urllib3-1.25.9
build(deps): bump urllib3 from 1.25.8 to 1.25.9
2020-05-18 16:04:40 +02:00
24ecab0878 Merge pull request #21 from BeryJu/dependabot/pip/sentry-sdk-0.14.4
build(deps): bump sentry-sdk from 0.14.3 to 0.14.4
2020-05-18 15:53:32 +02:00
6285c79341 Merge pull request #20 from BeryJu/dependabot/pip/django-otp-0.9.1
build(deps): bump django-otp from 0.8.1 to 0.9.1
2020-05-18 15:35:23 +02:00
5f7c56fab4 Merge pull request #19 from BeryJu/dependabot/pip/coverage-5.1
build(deps-dev): bump coverage from 5.0.4 to 5.1
2020-05-18 15:01:29 +02:00
6338b14a5b Merge pull request #18 from BeryJu/dependabot/pip/django-3.0.6
build(deps): bump django from 3.0.5 to 3.0.6
2020-05-18 14:51:44 +02:00
62ff3667f9 build(deps): bump urllib3 from 1.25.8 to 1.25.9
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.25.8 to 1.25.9.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/master/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.25.8...1.25.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 12:29:33 +00:00
3b97eef641 build(deps): bump sentry-sdk from 0.14.3 to 0.14.4
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 0.14.3 to 0.14.4.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGES.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/0.14.3...0.14.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 12:28:09 +00:00
b05d66466b build(deps): bump django-otp from 0.8.1 to 0.9.1
Bumps [django-otp](https://github.com/django-otp/django-otp) from 0.8.1 to 0.9.1.
- [Release notes](https://github.com/django-otp/django-otp/releases)
- [Changelog](https://github.com/django-otp/django-otp/blob/master/CHANGES.rst)
- [Commits](https://github.com/django-otp/django-otp/compare/v0.8.1...v0.9.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 12:26:41 +00:00
220144c919 build(deps-dev): bump coverage from 5.0.4 to 5.1
Bumps [coverage](https://github.com/nedbat/coveragepy) from 5.0.4 to 5.1.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/coverage-5.0.4...coverage-5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 12:25:16 +00:00
3b9402420d build(deps): bump django from 3.0.5 to 3.0.6
Bumps [django](https://github.com/django/django) from 3.0.5 to 3.0.6.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.0.5...3.0.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 12:23:42 +00:00
1324143146 Merge pull request #13 from BeryJu/dependabot/pip/autopep8-1.5.2
build(deps-dev): bump autopep8 from 1.5.1 to 1.5.2
2020-05-18 14:20:01 +02:00
595144cdd9 build(deps-dev): bump pylint from 2.4.4 to 2.5.2
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.4.4 to 2.5.2.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/master/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/pylint-2.4.4...pylint-2.5.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 12:19:16 +00:00
b3e714a741 Merge pull request #12 from BeryJu/dependabot/npm_and_yarn/passbook/static/static/codemirror-5.53.2
build(deps): bump codemirror from 5.52.0 to 5.53.2 in /passbook/static/static
2020-05-18 14:15:29 +02:00
2b3ae1e2d5 Merge pull request #10 from BeryJu/dependabot/pip/pylint-django-2.0.15
build(deps-dev): bump pylint-django from 2.0.14 to 2.0.15
2020-05-18 14:15:18 +02:00
886845306a build(deps): bump codemirror in /passbook/static/static
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.52.0 to 5.53.2.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.52.0...5.53.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 11:50:33 +00:00
de6aaec088 Merge pull request #11 from BeryJu/dependabot/npm_and_yarn/passbook/static/static/fortawesome/fontawesome-free-5.13.0
build(deps): bump @fortawesome/fontawesome-free from 5.12.1 to 5.13.0 in /passbook/static/static
2020-05-18 13:49:21 +02:00
137e0854ab Merge pull request #15 from BeryJu/dependabot/pip/bumpversion-0.6.0
build(deps-dev): bump bumpversion from 0.5.3 to 0.6.0
2020-05-18 13:48:52 +02:00
b6a0e895bc build(deps): bump @fortawesome/fontawesome-free
Bumps [@fortawesome/fontawesome-free](https://github.com/FortAwesome/Font-Awesome) from 5.12.1 to 5.13.0.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/compare/5.12.1...5.13.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 11:03:22 +00:00
05c791b1c0 build(deps-dev): bump bumpversion from 0.5.3 to 0.6.0
Bumps [bumpversion](https://github.com/peritus/bumpversion) from 0.5.3 to 0.6.0.
- [Release notes](https://github.com/peritus/bumpversion/releases)
- [Commits](https://github.com/peritus/bumpversion/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 11:02:36 +00:00
669f3051e8 build(deps-dev): bump pylint-django from 2.0.14 to 2.0.15
Bumps [pylint-django](https://github.com/PyCQA/pylint-django) from 2.0.14 to 2.0.15.
- [Release notes](https://github.com/PyCQA/pylint-django/releases)
- [Changelog](https://github.com/PyCQA/pylint-django/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/PyCQA/pylint-django/compare/v2.0.14...v2.0.15)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 11:02:17 +00:00
d945b56561 Merge pull request #14 from BeryJu/dependabot/npm_and_yarn/passbook/static/static/patternfly/patternfly-2.71.6
build(deps): bump @patternfly/patternfly from 2.65.3 to 2.71.6 in /passbook/static/static
2020-05-18 13:02:08 +02:00
27333bbff1 build(deps-dev): bump autopep8 from 1.5.1 to 1.5.2
Bumps [autopep8](https://github.com/hhatto/autopep8) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/hhatto/autopep8/releases)
- [Commits](https://github.com/hhatto/autopep8/compare/v1.5.1...v1.5.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 11:02:07 +00:00
80c800f233 Merge pull request #16 from BeryJu/dependabot/pip/boto3-1.13.11
build(deps): bump boto3 from 1.12.39 to 1.13.11
2020-05-18 12:58:40 +02:00
5d17d3dd31 build(deps): bump boto3 from 1.12.39 to 1.13.11
Bumps [boto3](https://github.com/boto/boto3) from 1.12.39 to 1.13.11.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.12.39...1.13.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 09:51:42 +00:00
8c64fcd17f build(deps): bump @patternfly/patternfly in /passbook/static/static
Bumps [@patternfly/patternfly](https://github.com/patternfly/patternfly) from 2.65.3 to 2.71.6.
- [Release notes](https://github.com/patternfly/patternfly/releases)
- [Changelog](https://github.com/patternfly/patternfly/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/patternfly/patternfly/compare/v2.65.3...v2.71.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-18 09:49:27 +00:00
753110583e admin: fix incorrect URL 2020-05-17 23:55:42 +02:00
4206a8c43e stages/password: fix button without labeling 2020-05-17 23:55:25 +02:00
7cc9ae45bc static: fix any click on menu triggering collapse 2020-05-17 23:55:13 +02:00
09ef58350c flows: rename AuthenticationStage to StageView as its a general base view 2020-05-17 00:03:06 +02:00
0b70007926 docs: add upgrade page 2020-05-16 23:46:25 +02:00
2c5d6e8187 docker: fix import order on wait_for_db 2020-05-16 22:56:14 +02:00
10d3f9ac2f root: rewrite bootstrap in python, remove management command 2020-05-16 22:46:17 +02:00
a6301055f0 root: remove need for PASSBOOK_DOMAIN 2020-05-16 21:47:51 +02:00
9a1b8eb7c8 admin: add empty views to all lists, move invitation to stage-invitations 2020-05-16 21:43:48 +02:00
2ee45f388c crypto: validate PEM data before saving 2020-05-16 21:25:10 +02:00
9a9feea5ff static: make hamburger menu work 2020-05-16 21:11:48 +02:00
8540e787d3 admin: fix create buttons disappearing on small screens 2020-05-16 21:11:38 +02:00
db26ce808e core: remove policies from application form 2020-05-16 20:48:45 +02:00
802d568273 admin: fix urls applications and sources 2020-05-16 20:48:30 +02:00
7b6524357f stages/user_logout: fix dependency on PENDING_USER 2020-05-16 20:46:21 +02:00
83013f063d core: remove stub data from user settings 2020-05-16 20:45:43 +02:00
333ee3eed4 dc: fix compose, remove SESSION_COOKIE_DOMAIN 2020-05-16 20:39:38 +02:00
e68352b09c admin: add flow-stage-bindings, add policy-bindings, add prompts 2020-05-16 19:55:59 +02:00
df1cb88abc admin: fix slow load for templates using get_links 2020-05-16 19:00:43 +02:00
5596caedbc Merge pull request #9 from BeryJu/db-reset
DB Reset
2020-05-16 18:20:03 +02:00
7bd65120b9 *: migrate from PolicyModel to PolicyBindingModel, move Policy to passbook_policies 2020-05-16 18:07:00 +02:00
227966e727 core: rename nonce to token 2020-05-16 16:11:53 +02:00
406f69080b Revert "*: providers and sources -> channels, PolicyModel to PolicyBindingModel that uses custom M2M through"
This reverts commit 7ed3ceb960.
2020-05-16 16:02:42 +02:00
2ce31dfaa5 root: upgrade django-dbbackup 2020-05-16 14:54:18 +02:00
b23afcfc3b helm: expose backup settings through helm chart 2020-05-16 14:17:43 +02:00
7ed3ceb960 *: providers and sources -> channels, PolicyModel to PolicyBindingModel that uses custom M2M through 2020-05-16 14:03:57 +02:00
615cd7870d stages/email: add field to select E-Mail and subject 2020-05-15 14:50:23 +02:00
b907105f4a policies/expression: expose python requests via expression, remove webhook policy 2020-05-15 12:02:41 +02:00
023423c6e7 stages/password: show password forgotten prompt when recovery flow configured 2020-05-15 11:01:02 +02:00
a5319fc2fe *: rename templatetags to clearly identify 2020-05-15 10:54:31 +02:00
8cfd3f9a2b Merge pull request #8 from BeryJu/flows-stage1
Flows Stage 1
2020-05-14 16:07:22 +02:00
814c797c64 stages/prompt: add policy verification logic 2020-05-14 15:48:23 +02:00
776ad3cfbf policies/expression: add pb_log function to debug 2020-05-14 15:48:08 +02:00
74ddf70cb7 policy: add context to PolicyRequest 2020-05-14 15:47:56 +02:00
a7a839a29c stages/prompt: promptstage based on PolicyBindingModel 2020-05-14 13:51:35 +02:00
9859c5db0a policies: add API for policybindings 2020-05-14 13:51:05 +02:00
fe503c8de0 root: add swagger to repository 2020-05-14 13:45:46 +02:00
43a583e2d2 stages/invitation: add unittests 2020-05-13 23:20:27 +02:00
f289025d8e stages/user_delete: fix missing API, fix missing tests 2020-05-13 23:20:07 +02:00
19cb310446 gh/actions: run coverage xml before codecov 2020-05-13 22:40:09 +02:00
47f6d0ac59 gh/actions: fix invalid path for codecov 2020-05-13 22:27:18 +02:00
922cbf932d : update readme 2020-05-13 19:05:01 +02:00
c104eeebe6 gh/actions: add codecov 2020-05-13 18:52:37 +02:00
80c3246333 policies/expression: add pb_flow_plan variable 2020-05-13 18:44:36 +02:00
461fed5567 admin: remove more duplicate code 2020-05-13 13:45:57 +02:00
d5f6714ed7 admin: remove redundant code 2020-05-13 11:57:19 +02:00
c42ed6bc99 admin: sort types, minor fixups 2020-05-13 11:57:10 +02:00
57fed2b92b stages/user_delete: fix formatting 2020-05-12 15:23:17 +02:00
e45b33c6c2 stages/user_delete: add user delete stage, remove view from core 2020-05-12 14:50:00 +02:00
137e90355b flows: default-auth -> default-authentication 2020-05-12 14:49:47 +02:00
7500e622f6 stages/invitation: start extracting invitation from core 2020-05-11 21:58:02 +02:00
d49c58f326 flows: fix linting 2020-05-11 21:27:46 +02:00
9814d3be03 flows: add Planner and Executor unittests 2020-05-11 15:01:14 +02:00
fc9f86cccc lib: use TemplateResponse for bad_request_message 2020-05-11 14:08:04 +02:00
6fd19c0a37 flows: add caching of plan, add planner unittests 2020-05-11 11:39:58 +02:00
10cb412532 flows: fix linting of migrations 2020-05-11 09:08:15 +02:00
e12780f78f flows: add invalidation designation, use as default logout action 2020-05-11 01:12:57 +02:00
9dec13c225 stages/user_logout: add logout stage 2020-05-11 01:12:14 +02:00
69120da45c core: remove redundant views/forms 2020-05-11 00:49:48 +02:00
5b2bf7519a stages/user_create -> user_write: Stage can create and update existing users 2020-05-10 23:38:15 +02:00
631cf77f89 stages/captcha: add tests 2020-05-10 21:43:30 +02:00
6676e95011 stages/email: add tests, cleanup 2020-05-10 21:43:22 +02:00
3219cffb52 stages/email: add logic to verify token 2020-05-10 21:00:04 +02:00
d4f149bc02 stages/email: add form for sending email to prevent spam
stages/email: make token validity configurable
2020-05-10 20:50:27 +02:00
206cf4967d stages/identification: add more templates 2020-05-10 20:24:47 +02:00
a67c53f46a stages/email: start rewriting templates, add template tags to embed CSS and images 2020-05-10 20:16:58 +02:00
e989c61793 core: reduce default nonce time to 30 minutes 2020-05-10 20:15:56 +02:00
99bab03cce flows: add check if current plan matches current flow 2020-05-10 20:15:24 +02:00
a7567ad8c6 stages/identification: add recovery support 2020-05-10 18:45:16 +02:00
2ffa2fc6b8 admin: update templates and stage views 2020-05-10 18:44:58 +02:00
8de87d9acb stages/identification: test signup notice 2020-05-10 18:17:24 +02:00
1d03b36750 stages/identification: show sign up url when related flow exists 2020-05-10 18:14:10 +02:00
8dc3c49a2f stages/user_create: add stage to create user after prompts 2020-05-10 18:04:23 +02:00
f6461b08d7 stages/prompt: add unittests 2020-05-10 17:52:26 +02:00
a3a3dde1c8 stages/dummy: add unittests
stages/password: improve coverage
stages/user_login: improve coverage
2020-05-10 17:02:01 +02:00
f111604b70 stages/login: -> stages/user_login: rename login to user_login for user_create stage 2020-05-10 16:20:44 +02:00
4315d1a03c stages/prompt: add prompt stage: dynamically created forms based on database 2020-05-10 16:20:17 +02:00
9def45c8d7 stages/identification: fix label for inputs 2020-05-10 15:29:27 +02:00
358922b09b lib/ui: fix human_list for lists with one item 2020-05-10 15:29:10 +02:00
fbc3ac6b30 flows: make FlowExecutor fully working without pending user 2020-05-10 15:28:52 +02:00
4f785da452 root: fix migrations for CI 2020-05-10 13:06:38 +02:00
ff4bd1c91f root: increase testing verbosity to debug CI 2020-05-10 12:50:58 +02:00
7a96f9e894 policies/dummy: fix migrations 2020-05-10 12:27:10 +02:00
c27d257146 core: fix migrations 2020-05-10 12:07:40 +02:00
9bccf9bb0a policies/dummy: separate dummy policy from core into app 2020-05-10 02:14:55 +02:00
c0b05a62f4 stages/password: add unittests 2020-05-10 02:00:38 +02:00
c140c39d07 stages/login: add unittests 2020-05-10 01:02:04 +02:00
9a700e506b stages/identification: simplify unittests 2020-05-10 01:01:58 +02:00
8e488670ad stages/identification: load uid_fields from stage in form, add more unit tests 2020-05-10 00:05:36 +02:00
fd5b2298e5 flows: fix unittests and migrations 2020-05-09 23:31:35 +02:00
caeaf8d5a9 stages/identification: optimise User lookup query 2020-05-09 23:20:20 +02:00
c46f0781fc flows: separate final login step from flow executor 2020-05-09 23:19:36 +02:00
0aad0604d8 stages/identification: migrate from core to separate stage 2020-05-09 21:31:29 +02:00
131c3fdb32 stages/password: fix broken authentication 2020-05-09 21:30:12 +02:00
8a6009c278 flows: enum to django TextChoices 2020-05-09 20:54:56 +02:00
3456527f10 providers/saml: fix minor typing issue 2020-05-09 20:54:11 +02:00
28b913136d root: set log level based on DEBUG flag 2020-05-09 20:53:47 +02:00
f700899640 stages/password: fix possibility of password in logs 2020-05-09 20:53:32 +02:00
5ba45d3037 root: add fossa 2020-05-08 21:08:36 +02:00
212e966dd4 factors: -> stage 2020-05-08 20:59:51 +02:00
08c0eb2ec6 admin: add flows 2020-05-08 18:45:53 +02:00
872ecd93a6 flows: add to api and add forms 2020-05-08 18:29:18 +02:00
f8af9d6ce0 flows: make sure flow_slug is logged consistently 2020-05-08 17:18:43 +02:00
c3e43a7c2f flows: fix denied view not being registered 2020-05-08 16:50:50 +02:00
273af0f1cb core/auth: fix unittests for flows 2020-05-08 16:43:22 +02:00
2a85e5ae87 flows: complete migration to FlowExecutorView, fully use context 2020-05-08 16:10:27 +02:00
114bb1b0bd flows: implement planner, start new executor 2020-05-08 14:33:14 +02:00
97b5d120f8 providers/oauth: fix default cors settings 2020-05-08 11:26:26 +02:00
e1f0fe45cb static: fix dashes being removed from slugs 2020-05-08 11:26:12 +02:00
bc0f4973d8 helm: fix bootstrap not being used correctly 2020-05-08 09:42:21 +02:00
5400882d78 flows/: more migration progress, consolidate views 2020-05-07 21:30:52 +02:00
8de66b27ad flows/*: Initial flows stage1 implementation 2020-05-07 20:51:06 +02:00
179f0097c0 provider/samlv2: more samlv2 progres 2020-05-07 19:25:15 +02:00
b40bffdf38 providers/samlv2: start implementing new SAML Provider 2020-05-07 01:20:08 +02:00
2e9496bb74 gh/ci: run pyright in python venv 2020-05-07 00:38:41 +02:00
d9d765c6cd gh/ci: fix ci syntax 2020-05-07 00:36:13 +02:00
10cc6856a4 gh/ci: fix pyright not being installed 2020-05-07 00:33:42 +02:00
813dd2894f *: add pyright type checking 2020-05-07 00:32:03 +02:00
80d90b91e8 core: add general admin.py loader, remove individual files 2020-05-07 00:05:10 +02:00
fff05e35ac providers/saml: optionally verify SAML Signature 2020-05-06 18:03:12 +02:00
75bb59a22a ui: fix help text not being shown for checkboxes 2020-05-06 17:59:19 +02:00
3ce69bb391 compose: remove explicit bootstrapping 2020-04-22 11:45:43 +02:00
4eb7c5f94b helm: remove explicit bootstrapping 2020-04-22 11:45:36 +02:00
aac7e6be90 lib: fix ram usage due to bootstrap
bootstrap now exits (0) when all services are up, instead continuously running. This is combined with a simple bash script, which does this job instead.

This also adds /bootstrap.sh as docker ENTRYPOINT
2020-04-22 11:45:11 +02:00
c77f4204c0 new release: 0.8.15-beta 2020-04-10 21:57:20 +02:00
5f4452470b providers/saml: fix metadata rendering when no singing keypair is selected
closes PASSBOOK-44
2020-04-10 21:54:23 +02:00
9a1270c693 providers/saml: fix wrong signing property being checked
closes PASSBOOK-45
2020-04-10 21:52:03 +02:00
7b9d1a1159 new release: 0.8.14-beta 2020-04-10 21:23:55 +02:00
cdbe1f6161 bump dependencies 2020-04-10 21:17:31 +02:00
e43db2e065 new release: 0.8.13-beta 2020-04-10 21:11:24 +02:00
d1c74d2160 lib: fix imports being changed every time 2020-03-05 17:28:03 +01:00
f2119ce567 providers/saml: fix signing_kp typo 2020-03-05 17:09:08 +01:00
2c4dcb9cf0 actions: remove cache 2020-03-04 21:15:44 +01:00
93b8266821 actions: install pipenv as root 2020-03-04 21:13:40 +01:00
443797d9b0 actions: install wheel package 2020-03-04 21:11:26 +01:00
a4365ca02c actions: don't update system pip 2020-03-04 21:09:46 +01:00
3750083667 actions: don't setup custom python, use system pip 2020-03-04 21:08:24 +01:00
66ef067ecf actions: don't update pip to fix CI 2020-03-04 20:10:46 +01:00
b489b0e691 Merge pull request #7 from BeryJu/crypto
generic cert management
2020-03-04 19:43:52 +01:00
f2154d9875 crypto: add property for private_key 2020-03-04 19:43:18 +01:00
80a50f9bdb providers/saml: switch to new crypto 2020-03-03 23:35:50 +01:00
dc8b89a6b9 sources/saml: switch to new crypto 2020-03-03 23:35:38 +01:00
8df55f22aa crypto: implement simple certificate-key pair for easier management 2020-03-03 23:35:25 +01:00
f6c322be27 providers/oidc: fix skip_authorization not being synced to oidc_client 2020-03-02 17:40:38 +01:00
a144552059 providers/oidc: fill claims with userinfo 2020-03-01 22:55:56 +01:00
535d529193 ui: fix title, fix navigation on user settings 2020-02-29 14:46:58 +01:00
6ed2e137a2 new release: 0.8.12-beta 2020-02-28 11:54:03 +01:00
45bd63c720 api: update old field names 2020-02-28 11:48:55 +01:00
736e13fc35 ui: add template for csrf errors 2020-02-28 11:41:28 +01:00
966fff008c ui: re-enable branding on navbar 2020-02-28 11:37:07 +01:00
64f15eadbd providers/saml: fix CSRF errors with POST binding 2020-02-28 10:50:16 +01:00
81b66ecdcd core: remove some more dead code, add more help texts for factors 2020-02-27 16:39:30 +01:00
53e5cf7826 admin: fix some models not being paginated 2020-02-27 15:30:28 +01:00
82654b3fd9 ui: re-organize some of the navigation to make it cleaner for end-users 2020-02-27 14:59:34 +01:00
9b72c604dd docs: fix some typos 2020-02-27 13:00:55 +01:00
587 changed files with 19250 additions and 6993 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.8.11-beta
current_version = 0.9.0-pre1
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)

20
.fossa.yml Executable file
View File

@ -0,0 +1,20 @@
# Generated by FOSSA CLI (https://github.com/fossas/fossa-cli)
# Visit https://fossa.com to learn more
version: 2
cli:
server: https://app.fossa.com
fetcher: custom
project: git@github.com:BeryJu/passbook.git
analyze:
modules:
- name: static
type: npm
target: passbook/static/static
path: passbook/static/static
- name: .
type: pip
target: .
path: .
options:
strategy: pipenv

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
custom: ["https://www.paypal.me/octocat"]

38
.github/workflows/ci-cleanup.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: passbook-ci-cleanup
on:
- delete
jobs:
delete-server:
runs-on: ubuntu-latest
steps:
- name: Delete docker tag
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: curl
-u $DOCKER_USERNAME:$DOCKER_PASSWORD
-X "DELETE"
"https://hub.docker.com/v2/repositories/$DOCKER_USERNAME/passbook/tags/${GITHUB_REF##*/}/"
delete-gatekeeper:
runs-on: ubuntu-latest
steps:
- name: Delete docker tag
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: curl
-u $DOCKER_USERNAME:$DOCKER_PASSWORD
-X "DELETE"
"https://hub.docker.com/v2/repositories/$DOCKER_USERNAME/passbook-gatekeeper/tags/${GITHUB_REF##*/}/"
delete-static:
runs-on: ubuntu-latest
steps:
- name: Delete docker tag
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: curl
-u $DOCKER_USERNAME:$DOCKER_PASSWORD
-X "DELETE"
"https://hub.docker.com/v2/repositories/$DOCKER_USERNAME/passbook-static/tags/${GITHUB_REF##*/}/"

View File

@ -15,14 +15,8 @@ jobs:
- uses: actions/setup-python@v1
with:
python-version: '3.8'
- uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs/
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
restore-keys: |
${{ runner.os }}-pipenv-
- name: Install dependencies
run: pip install -U pip pipenv && pipenv install --dev
run: sudo pip install -U wheel pipenv && pipenv install --dev
- name: Lint with pylint
run: pipenv run pylint passbook
black:
@ -32,14 +26,8 @@ jobs:
- uses: actions/setup-python@v1
with:
python-version: '3.8'
- uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs/
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
restore-keys: |
${{ runner.os }}-pipenv-
- name: Install dependencies
run: pip install -U pip pipenv && pipenv install --dev
run: sudo pip install -U wheel pipenv && pipenv install --dev
- name: Lint with black
run: pipenv run black --check passbook
prospector:
@ -49,14 +37,8 @@ jobs:
- uses: actions/setup-python@v1
with:
python-version: '3.8'
- uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs/
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
restore-keys: |
${{ runner.os }}-pipenv-
- name: Install dependencies
run: pip install -U pip pipenv && pipenv install --dev && pipenv install --dev prospector --skip-lock
run: sudo pip install -U wheel pipenv && pipenv install --dev && pipenv install --dev prospector --skip-lock
- name: Lint with prospector
run: pipenv run prospector
bandit:
@ -66,16 +48,26 @@ jobs:
- uses: actions/setup-python@v1
with:
python-version: '3.8'
- uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs/
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
restore-keys: |
${{ runner.os }}-pipenv-
- name: Install dependencies
run: pip install -U pip pipenv && pipenv install --dev
run: sudo pip install -U wheel pipenv && pipenv install --dev
- name: Lint with bandit
run: pipenv run bandit -r passbook
pyright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- uses: actions/setup-python@v1
with:
python-version: '3.8'
- name: Install pyright
run: npm install -g pyright
- name: Show pyright version
run: pyright --version
- name: Install dependencies
run: sudo pip install -U wheel pipenv && pipenv install --dev
- name: Lint with pyright
run: pipenv run pyright
# Actual CI tests
migrations:
needs:
@ -101,14 +93,8 @@ jobs:
- uses: actions/setup-python@v1
with:
python-version: '3.8'
- uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs/
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
restore-keys: |
${{ runner.os }}-pipenv-
- name: Install dependencies
run: pip install -U pip pipenv && pipenv install --dev
run: sudo pip install -U wheel pipenv && pipenv install --dev
- name: Run migrations
run: pipenv run ./manage.py migrate
coverage:
@ -135,16 +121,15 @@ jobs:
- uses: actions/setup-python@v1
with:
python-version: '3.8'
- uses: actions/cache@v1
with:
path: ~/.local/share/virtualenvs/
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
restore-keys: |
${{ runner.os }}-pipenv-
- name: Install dependencies
run: pip install -U pip pipenv && pipenv install --dev
run: sudo pip install -U wheel pipenv && pipenv install --dev
- name: Run coverage
run: pipenv run ./scripts/coverage.sh
- name: Create XML Report
run: pipenv run coverage xml
- uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
# Build
build-server:
needs:

View File

@ -16,11 +16,11 @@ jobs:
- name: Building Docker Image
run: docker build
--no-cache
-t beryju/passbook:0.8.11-beta
-t beryju/passbook:0.9.0-pre1
-t beryju/passbook:latest
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook:0.8.11-beta
run: docker push beryju/passbook:0.9.0-pre1
- name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook:latest
build-gatekeeper:
@ -37,11 +37,11 @@ jobs:
cd gatekeeper
docker build \
--no-cache \
-t beryju/passbook-gatekeeper:0.8.11-beta \
-t beryju/passbook-gatekeeper:0.9.0-pre1 \
-t beryju/passbook-gatekeeper:latest \
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook-gatekeeper:0.8.11-beta
run: docker push beryju/passbook-gatekeeper:0.9.0-pre1
- name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook-gatekeeper:latest
build-static:
@ -66,11 +66,11 @@ jobs:
run: docker build
--no-cache
--network=$(docker network ls | grep github | awk '{print $1}')
-t beryju/passbook-static:0.8.11-beta
-t beryju/passbook-static:0.9.0-pre1
-t beryju/passbook-static:latest
-f static.Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook-static:0.8.11-beta
run: docker push beryju/passbook-static:0.9.0-pre1
- name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook-static:latest
test-release:

View File

@ -8,4 +8,4 @@ ignore-paths:
- node_modules
uses:
- django
- django

View File

@ -1,9 +1,9 @@
[MASTER]
disable=redefined-outer-name,arguments-differ,no-self-use,cyclic-import,fixme,locally-disabled,unpacking-non-sequence,too-many-ancestors,too-many-branches,too-few-public-methods,import-outside-toplevel,bad-continuation
disable=redefined-outer-name,arguments-differ,no-self-use,cyclic-import,fixme,locally-disabled,too-many-ancestors,too-few-public-methods,import-outside-toplevel,bad-continuation,signature-differs
load-plugins=pylint_django,pylint.extensions.bad_builtin
extension-pkg-whitelist=lxml
const-rgx=[a-zA-Z0-9_]{1,40}$
ignored-modules=django-otp
jobs=4
jobs=12
ignore=migrations

View File

@ -25,7 +25,11 @@ RUN apt-get update && \
COPY ./passbook/ /app/passbook
COPY ./manage.py /app/
COPY ./docker/uwsgi.ini /app/
COPY ./docker/bootstrap.sh /bootstrap.sh
COPY ./docker/wait_for_db.py /app/wait_for_db.py
WORKDIR /app/
USER passbook
ENTRYPOINT [ "/bootstrap.sh" ]

View File

@ -40,7 +40,6 @@ signxml = "*"
structlog = "*"
swagger-spec-validator = "*"
urllib3 = {extras = ["secure"],version = "*"}
jinja2 = "*"
[requires]
python_version = "3.8"

836
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,92 @@
# passbook
<img src="passbook/static/static/passbook/logo.svg" height="50" alt="passbook logo"><img src="passbook/static/static/passbook/brand_inverted.svg" height="50" alt="passbook">
![](https://github.com/BeryJu/passbook/workflows/passbook-ci/badge.svg)
![](https://img.shields.io/github/workflow/status/beryju/passbook/passbook-ci?style=flat-square)
![](https://img.shields.io/docker/pulls/beryju/passbook.svg?style=flat-square)
![](https://img.shields.io/docker/pulls/beryju/passbook-gatekeeper.svg?style=flat-square)
![](https://img.shields.io/docker/pulls/beryju/passbook-static.svg?style=flat-square)
![](https://img.shields.io/docker/v/beryju/passbook?sort=semver&style=flat-square)
![](https://img.shields.io/codecov/c/gh/beryju/passbook?style=flat-square)
## Quick instance
## What is passbook?
passbook is an open-source Identity Provider focused on flexibility and versatility. You can use passbook in an existing environment to add support for new protocols. passbook is also a great solution for implementing signup/recovery/etc in your application, so you don't have to deal with it.
## Installation
For small/test setups it is recommended to use docker-compose.
```
export PASSBOOK_DOMAIN=domain.tld
wget https://raw.githubusercontent.com/BeryJu/passbook/master/docker-compose.yml
# Optionally enable Error-reporting
# export PASSBOOK_ERROR_REPORTING=true
# Optionally deploy a different version
# export PASSBOOK_TAG=0.8.15-beta
# If this is a productive installation, set a different PostgreSQL Password
# export PG_PASS=$(pwgen 40 1)
docker-compose pull
docker-compose up -d
docker-compose exec server ./manage.py migrate
docker-compose exec server ./manage.py createsuperuser
```
For bigger setups, there is a Helm Chart in the `helm/` directory. This is documented [here](https://passbook.beryju.org//installation/kubernetes/)
## Screenshots
![](docs/images/screen_apps.png)
![](docs/images/screen_admin.png)
## Development
To develop on passbook, you need a system with Python 3.7+ (3.8 is recommended). passbook uses [pipenv](https://pipenv.pypa.io/en/latest/) for managing dependencies.
To get started, run
```
python3 -m pip install pipenv
git clone https://github.com/BeryJu/passbook.git
cd passbook
pipenv shell
pipenv sync -d
```
Since passbook uses PostgreSQL-specific fields, you also need a local PostgreSQL instance to develop. passbook also uses redis for caching and message queueing.
For these databases you can use [Postgres.app](https://postgresapp.com/) and [Redis.app](https://jpadilla.github.io/redisapp/) on macOS or use it via docker-comppose:
```yaml
version: '3.7'
services:
postgresql:
container_name: postgres
image: postgres:11
volumes:
- db-data:/var/lib/postgresql/data
ports:
- 127.0.0.1:5432:5432
restart: always
redis:
container_name: redis
image: redis
ports:
- 127.0.0.1:6379:6379
restart: always
volumes:
db-data:
driver: local
```
To tell passbook about these databases, create a file in the project root called `local.env.yml` with the following contents:
```yaml
debug: true
postgresql:
user: postgres
log_level: debug
error_reporting: false
```
## Security
See [SECURITY.md](SECURITY.md)

13
SECURITY.md Normal file
View File

@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
As passbook is currently in a pre-stable, only the latest "stable" version is supported. After passbook 1.0, this will change.
| Version | Supported |
| -------- | ------------------ |
| 0.8.15 | :white_check_mark: |
## Reporting a Vulnerability
To report a vulnerability, send am email to [security@beryju.org](mailto:security@beryju.org)

View File

@ -21,14 +21,11 @@ services:
labels:
- traefik.enable=false
server:
image: beryju/passbook:${SERVER_TAG:-latest}
image: beryju/passbook:${PASSBOOK_TAG:-latest}
command:
- ./manage.py
- bootstrap
- uwsgi
- uwsgi.ini
environment:
- PASSBOOK_DOMAIN=${PASSBOOK_DOMAIN}
- PASSBOOK_REDIS__HOST=redis
- PASSBOOK_ERROR_REPORTING=${PASSBOOK_ERROR_REPORTING:-false}
- PASSBOOK_POSTGRESQL__HOST=postgresql
@ -42,10 +39,8 @@ services:
- traefik.docker.network=internal
- traefik.frontend.rule=PathPrefix:/
worker:
image: beryju/passbook:${SERVER_TAG:-latest}
image: beryju/passbook:${PASSBOOK_TAG:-latest}
command:
- ./manage.py
- bootstrap
- celery
- worker
- --autoscale=10,3
@ -58,7 +53,6 @@ services:
labels:
- traefik.enable=false
environment:
- PASSBOOK_DOMAIN=${PASSBOOK_DOMAIN}
- PASSBOOK_REDIS__HOST=redis
- PASSBOOK_ERROR_REPORTING=${PASSBOOK_ERROR_REPORTING:-false}
- PASSBOOK_POSTGRESQL__HOST=postgresql
@ -69,7 +63,7 @@ services:
- internal
labels:
- traefik.frontend.rule=PathPrefix:/static, /robots.txt
- traefik.port=8080
- traefik.port=80
- traefik.docker.network=internal
traefik:
image: traefik:1.7

3
docker/bootstrap.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash -ex
/app/wait_for_db.py
"$@"

41
docker/wait_for_db.py Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env python
"""This file needs to be run from the root of the project to correctly
import passbook. This is done by the dockerfile."""
from time import sleep
from psycopg2 import OperationalError, connect
from redis import Redis
from redis.exceptions import RedisError
from structlog import get_logger
from passbook.lib.config import CONFIG
LOGGER = get_logger()
while True:
try:
conn = connect(
dbname=CONFIG.y("postgresql.name"),
user=CONFIG.y("postgresql.user"),
password=CONFIG.y("postgresql.password"),
host=CONFIG.y("postgresql.host"),
)
conn.cursor()
break
except OperationalError:
sleep(1)
LOGGER.warning("PostgreSQL Connection failed, retrying...")
while True:
try:
redis = Redis(
host=CONFIG.y("redis.host"),
port=6379,
db=CONFIG.y("redis.message_queue_db"),
password=CONFIG.y("redis.password"),
)
redis.ping()
break
except RedisError:
sleep(1)
LOGGER.warning("Redis Connection failed, retrying...")

View File

@ -1,14 +0,0 @@
FROM python:3.8-slim-buster as builder
WORKDIR /mkdocs
RUN pip install mkdocs mkdocs-material
COPY docs/ docs
COPY mkdocs.yml .
RUN mkdocs build
FROM nginx
COPY --from=builder /mkdocs/site /usr/share/nginx/html

3
docs/build.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash -x
pip install -U mkdocs mkdocs-material
mkdocs gh-deploy

55
docs/expressions/index.md Normal file
View File

@ -0,0 +1,55 @@
# Expressions
Expressions allow you to write custom Logic using Python code.
Expressions are used in different places throughout passbook, and can do different things.
!!! info
These functions/objects are available wherever expressions are used. For more specific information, see [Expression Policies](../policies/expression.md) and [Property Mappings](../property-mappings/expression.md)
## Global objects
- `pb_logger`: structlog BoundLogger. ([ref](https://www.structlog.org/en/stable/api.html#structlog.BoundLogger))
- `requests`: requests Session object. ([ref](https://requests.readthedocs.io/en/master/user/advanced/))
## Generally available functions
### `regex_match(value: Any, regex: str) -> bool`
Check if `value` matches Regular Expression `regex`.
Example:
```python
return regex_match(request.user.username, '.*admin.*')
```
### `regex_replace(value: Any, regex: str, repl: str) -> str`
Replace anything matching `regex` within `value` with `repl` and return it.
Example:
```python
user_email_local = regex_replace(request.user.email, '(.+)@.+', '')
```
### `pb_is_group_member(user: User, **group_filters) -> bool`
Check if `user` is member of a group matching `**group_filters`.
Example:
```python
return pb_is_group_member(request.user, name="test_group")
```
### `pb_user_by(**filters) -> Optional[User]`
Fetch a user matching `**filters`. Returns None if no user was found.
Example:
```python
other_user = pb_user_by(username="other_user")
```

View File

@ -15,6 +15,7 @@ The User object has the following attributes:
List all the User's Group Names
```jinja2
[{% for group in user.groups.all() %}'{{ group.name }}',{% endfor %}]
```python
for group in user.groups.all():
yield group.name
```

View File

@ -1,23 +0,0 @@
# Factors
A factor represents a single authenticating factor for a user. Common examples of this would be a password or an OTP. These factors can be combined in any order, and can be dynamically enabled using policies.
## Password Factor
This is the standard Password Factor. It allows you to select which Backend the password is checked with. here you can also specify which Policies are used to check the password. You can also specify which Factors a User has to pass to recover their account.
## Dummy Factor
This factor waits a random amount of time. Mostly used for debugging.
## E-Mail Factor
This factor is mostly for recovery, and used in conjunction with the Password Factor.
## OTP Factor
This is your typical One-Time Password implementation, compatible with Authy and Google Authenticator. You can enfore this Factor so that every user has to configure it, or leave it optional.
## Captcha Factor
While this factor doesn't really authenticate a user, it is part of the Authentication Flow. passbook uses Google's reCaptcha implementation.

View File

@ -0,0 +1,36 @@
# Login Flow
This document describes how a simple authentication flow can be created.
This flow is created automatically when passbook is installed.
1. Create an **Identification** stage
> Here you can select whichever fields the user can identify themselves with
> Select the Template **Default Login**, as this template shows the (optional) Flows
> Here you can also link optional enrollment and recovery flows.
2. Create a **Password** stage
> Select the Backend you want the password to be checked against. Select "passbook-internal Userdatabase".
3. Create a **User Login** stage
> This stage doesn't have any options.
4. Create a flow
> Create a flow with the delegation of **Authentication**
> Assign a name and a slug. The slug is used in the URL when the flow is executed.
5. Bind the stages to the flow
> Bind the **Identification** Stage with an order of 0
> Bind the **Password** Stage with an order of 1
> Bind the **User Login** Stage with an order of 2
![](login.png)
!!! notice
This flow can used by any user, authenticated and un-authenticated. This means any authenticated user that visits this flow can login again.

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

45
docs/flow/flows.md Normal file
View File

@ -0,0 +1,45 @@
# Flows
Flows are a method of describing a sequence of stages. A stage represents a single verification or logic step. They are used to authenticate users, enroll them, and more.
Upon Flow execution, a plan is generated, which contains all stages. This means upon execution, all attached policies are evaluated. This behaviour can be altered by enabling the **Re-evaluate Policies** option on the binding.
To determine which flow is linked, passbook searches all Flows with the required designation and chooses the first instance the current user has access to.
## Permissions
Flows can have policies assigned to them, which determines if the current user is allowed to see and use this flow.
## Designation
Flows are designated for a single Purpose. This designation changes when a Flow is used. The following designations are available:
### Authentication
This is designates a flow to be used for authentication.
The authentication flow should always contain a [**User Login**](stages/user_login.md) stage, which attaches the staged user to the current session.
### Invalidation
This designates a flow to be used for the invalidation of a session.
This stage should always contain a [**User Logout**](stages/user_logout.md) stage, which resets the current session.
### Enrollment
This designates a flow for enrollment. This flow can contain any amount of Prompt stages, E-Mail verification or Captchas. At the end to create the user, you can use the [**User Write**](stages/user_write.md) stage, which either updates the currently staged user, or if none exists, creates a new one.
### Unenrollment
This designates a flow for unenrollment. This flow can contain any amount of verification, like [**E-Mail**](stages/email/index.md) or [**Captcha**](stages/captcha/index.md). To finally delete the account, use the [**User Delete**](stages/user_delete.md) stage.
### Recovery
This designates a flow for recovery. This flow normally contains an [**Identification**](stages/identification/index.md) stage to find the user. Then it can contain any amount of verification, like [**E-Mail**](stages/email/index.md) or [**Captcha**](stages/captcha/index.md).
Afterwards, use the [**Prompt**](stages/prompt/index.md) stage to ask the user for a new password and use [**User Write**](stages/user_write.md) to update the password.
### Change Password
This designates a flow for password changing. This flow can contain any amount of verification, like [**E-Mail**](stages/email/index.md) or [**Captcha**](stages/captcha/index.md).
Afterwards, use the [**Prompt**](stages/prompt/index.md) stage to ask the user for a new password and use [**User Write**](stages/user_write.md) to update the password.

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View File

@ -0,0 +1,7 @@
# Captcha stage
This stage adds a form of verification using [Google's ReCaptcha](https://www.google.com/recaptcha/intro/v3.html).
This stage has two required fields. You need a Public and a Private key, both of which you can acquire at https://www.google.com/recaptcha/admin.
![](captcha-admin.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -0,0 +1,5 @@
# Dummy stage
This stage is used for development, and has no function. It presents the User with a form, that requires a single confirmation.
![](dummy.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -0,0 +1,5 @@
# E-Mail
This stage can be used for E-Mail verification. passbook's background worker will send an E-Mail using the specified connection details. When an E-Mail can't be delivered, it is automatically periodically retried.
![](email-recovery.png)

View File

@ -0,0 +1,25 @@
# Identification
This stage provides a ready-to-go form for users to identify themselves.
## Options
### User Fields
Select which fields the user can use to identify themselves. Multiple fields can be specified and separated with a comma.
Valid choices:
- email
- username
### Template
This specifies which template is rendered. Currently there are two templates.
The `Login` template shows configured Sources below the login form, as well as linking to the defined Enrollment and Recovery flows.
The `Recovery` template shows only the form.
### Enrollment/Recovery Flow
These fields specify if and which flows are linked on the form. The enrollment flow is linked as `Need an account? Sign up.`, and the recovery flow is linked as `Forgot username or password?`.

View File

@ -0,0 +1,7 @@
# Invitation Stage
This stage can be used to invite users. You can use this enroll users with preset values.
If the option `Continue Flow without Invitation`, this stage will continue when no invitation token is present.
If you want to check if a user has used an invitation within a policy, you can check `request.context.invitation_in_effect`.

View File

@ -0,0 +1,7 @@
# OTP Stage
This stage offers a generic Time-based One-time Password authentication step.
You can optionally enforce this step, which will force every user without OTP setup to configure it.
This stage uses a 6-digit Code with a 30 second time-drift. This is currently not changeable.

View File

@ -0,0 +1,3 @@
# Password Stage
This is a generic password prompt, which authenticates the currently `pending_user`. This stage allows the selection of the Backend the user is authenticated against.

View File

@ -0,0 +1,42 @@
# Prompt Stage
This stage is used to show the user arbitrary prompts.
## Prompt
The prompt can be any of the following types:
| | |
|----------|------------------------------------------------------------------|
| text | Arbitrary text, no client-side validation is done. |
| email | E-Mail input, requires a valid E-Mail adress |
| password | Password Input |
| number | Number Input, any number is allowed |
| checkbox | Simple Checkbox |
| hidden | Hidden Input field, allows for the pre-setting of default values |
A Prompt has the following attributes:
### `field_key`
HTML name used for the prompt. This key is also used to later retrieve the data in expression policies:
```python
request.context.get('prompt_data').get('<field_key>')
```
### `label`
Label used to describe the Field. This might not be shown depending on the template selected.
### `required`
Flag that decides whether or not this field is required.
### `placeholder`
Field placeholder, shown within the input field. This field is also used by the `hidden` type as the actual value.
### `order`
Numerical index of the prompt. This applies to all stages this prompt is a part of.

View File

@ -0,0 +1,16 @@
# Prompt Validation
Further validation of prompts can be done using policies.
To validate that two password fields are identical, create the following expression policy:
```python
if request.context.get('prompt_data').get('password') == request.context.get('prompt_data').get('password_repeat'):
return True
pb_message("Passwords don't match.")
return False
```
This policy expects you two have two password fields with `field_key` set to `password` and `password_repeat`.
Afterwards bind this policy to the prompt stage you want to validate.

View File

@ -0,0 +1,8 @@
# User Delete Stage
!!! danger
This stage deletes the `pending_user` without any confirmation. You have to make sure the user is aware of this.
This stage is intended for an unenrollment flow. It deletes the currently pending user.
The pending user is also removed from the current session.

View File

@ -0,0 +1,5 @@
# User Login Stage
This stage attaches a currently pending user to the current session.
It can be used after `user_write` during an enrollment flow, or after a `password` stage during an authentication flow.

View File

@ -0,0 +1,3 @@
# User Logout Stage
Opposite stage of [User Login Stages](user_login.md). It removes the user from the current session.

View File

@ -0,0 +1,3 @@
# User Write Stage
This stages writes data from the current context to the current pending user. If no user is pending, a new one is created.

2
docs/images/brand.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

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

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

BIN
docs/images/screen_apps.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View File

@ -1,31 +1,16 @@
# Welcome
#
![passbook logo](images/logo.svg){: style="height:50px"}
![passbook brand](images/brand.svg){: style="height:50px"}
Welcome to the passbook Documentation. passbook is an open-source Identity Provider and Usermanagement software. It can be used as a central directory for users or customers and it can integrate with your existing Directory.
## What is passbook?
passbook can also be used as part of an Application to facilitate User Enrollment, Password recovery and Social Login.
passbook is an open-source Identity Provider focused on flexibility and versatility. You can use passbook in an existing environment to add support for new protocols. passbook is also a great solution for implementing signup/recovery/etc in your application, so you don't have to deal with it.
passbook uses the following Terminology:
## Installation
### Policy
See [Docker-compose](installation/docker-compose.md) or [Kubernetes](installation/kubernetes.md)
A Policy is at a base level a yes/no gate. It will either evaluate to True or False depending on the Policy Kind and settings. For example, a "Group Membership Policy" evaluates to True if the User is member of the specified Group and False if not. This can be used to conditionally apply Factors and grant/deny access.
## Screenshots
### Provider
A Provider is a way for other Applications to authenticate against passbook. Common Providers are OpenID Connect (OIDC) and SAML.
### Source
Sources are ways to get users into passbook. This might be an LDAP Connection to import Users from Active Directory, or an OAuth2 Connection to allow Social Logins.
### Application
An application links together Policies with a Provider, allowing you to control access. It also holds Information like UI Name, Icon and more.
### Factors
Factors represent Authentication Factors, like a Password or OTP. These Factors can be dynamically enabled using policies. This allows you to, for example, force users from a certain IP ranges to complete a Captcha to authenticate.
### Property Mappings
Property Mappings allow you to make Information available for external Applications. For example, if you want to login to AWS with passbook, you'd use Property Mappings to set the User's Roles based on their Groups.
![](images/screen_apps.png)
![](images/screen_admin.png)

View File

@ -1,6 +0,0 @@
# Installation
There are two supported ways to install passbook:
- [docker-compose](docker-compose.md) for test- or small productive setups
- [Kubernetes](./kubernetes.md) for larger Productive setups

View File

@ -1,3 +1,61 @@
# Kubernetes
For a mid to high-load Installation, Kubernetes is recommended. passbook is installed using a helm-chart.
```
# Default values for passbook.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
# passbook version to use. Defaults to latest stable version
# image:
# tag:
nameOverride: ""
config:
# Optionally specify fixed secret_key, otherwise generated automatically
# secret_key: _k*@6h2u2@q-dku57hhgzb7tnx*ba9wodcb^s9g0j59@=y(@_o
# Enable error reporting
error_reporting: false
# Log level used by web and worker
# Can be either debug, info, warning, error
log_level: warning
# This Helm chart ships with built-in Prometheus ServiceMonitors and Rules.
# This requires the CoreOS Prometheus Operator.
monitoring:
enabled: false
# Enable Database Backups to S3
# backup:
# access_key: access-key
# secret_key: secret-key
# bucket: s3-bucket
# host: s3-host
ingress:
enabled: false
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
path: /
hosts:
- passbook.k8s.local
tls: []
# - secretName: chart-example-tls
# hosts:
# - passbook.k8s.local
# These settings configure the packaged PostgreSQL and Redis chart.
postgresql:
postgresqlDatabase: passbook
redis:
cluster:
enabled: false
master:
persistence:
enabled: false
# https://stackoverflow.com/a/59189742
disableCommands: []
```

View File

@ -1,33 +0,0 @@
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: passbook-docs
namespace: prod-passbook-docs
labels:
app.kubernetes.io/name: passbook-docs
app.kubernetes.io/managed-by: passbook-docs
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: passbook-docs
template:
metadata:
labels:
app.kubernetes.io/name: passbook-docs
spec:
containers:
- name: passbook-docs
image: "beryju/passbook-docs:latest"
ports:
- name: http
containerPort: 80
protocol: TCP
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi

View File

@ -1,21 +0,0 @@
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
labels:
app.kubernetes.io/name: passbook-docs
name: passbook-docs
namespace: prod-passbook-docs
spec:
rules:
- host: docs.passbook.beryju.org
http:
paths:
- backend:
serviceName: passbook-docs-http
servicePort: http
path: /
tls:
- hosts:
- docs.passbook.beryju.org
secretName: passbook-docs-acme

View File

@ -1,17 +0,0 @@
---
apiVersion: v1
kind: Service
metadata:
name: passbook-docs-http
namespace: prod-passbook-docs
labels:
app.kubernetes.io/name: passbook-docs
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: passbook-docs

View File

@ -0,0 +1,30 @@
# Expression Policies
!!! notice
These variables are available in addition to the common variables/functions defined in [**Expressions**](../expressions/index.md)
The passing of the policy is determined by the return value of the code. Use `return True` to pass a policy and `return False` to fail it.
### Available Functions
#### `pb_message(message: str)`
Add a message, visible by the end user. This can be used to show the reason why they were denied.
Example:
```python
pb_message("Access denied")
return False
```
### Context variables
- `request`: A PolicyRequest object, which has the following properties:
- `request.user`: The current User, which the Policy is applied against. ([ref](../expressions/reference/user-object.md))
- `request.http_request`: The Django HTTP Request. ([ref](https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects))
- `request.obj`: A Django Model instance. This is only set if the Policy is ran against an object.
- `request.context`: A dictionary with dynamic data. This depends on the origin of the execution.
- `pb_is_sso_flow`: Boolean which is true if request was initiated by authenticating through an external Provider.
- `pb_client_ip`: Client's IP Address or '255.255.255.255' if no IP Address could be extracted.
- `pb_flow_plan`: Current Plan if Policy is called from the Flow Planner.

View File

@ -1,21 +0,0 @@
# Expression Policy
Expression Policies allows you to write custom Policy Logic using Jinja2 Templating language.
For a language reference, see [here](https://jinja.palletsprojects.com/en/2.11.x/templates/).
The following objects are passed into the variable:
- `request`: A PolicyRequest object, which has the following properties:
- `request.user`: The current User, which the Policy is applied against. ([ref](../../property-mappings/reference/user-object.md))
- `request.http_request`: The Django HTTP Request, as documented [here](https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects).
- `request.obj`: A Django Model instance. This is only set if the Policy is ran against an object.
- `pb_is_sso_flow`: Boolean which is true if request was initiated by authenticating through an external Provider.
- `pb_is_group_member(user, group_name)`: Function which checks if `user` is member of a Group with Name `gorup_name`.
- `pb_logger`: Standard Python Logger Object, which can be used to debug expressions.
- `pb_client_ip`: Client's IP Address.
There are also the following custom filters available:
- `regex_match(regex)`: Return True if value matches `regex`
- `regex_replace(regex, repl)`: Replace string matched by `regex` with `repl`

View File

@ -8,10 +8,6 @@ There are two different Kind of policies, a Standard Policy and a Password Polic
---
### Group-Membership Policy
This policy evaluates to True if the current user is a Member of the selected group.
### Reputation Policy
passbook keeps track of failed login attempts by Source IP and Attempted Username. These values are saved as scores. Each failed login decreases the Score for the Client IP as well as the targeted Username by one.
@ -20,11 +16,7 @@ This policy can be used to for example prompt Clients with a low score to pass a
## Expression Policy
See [Expression Policy](expression/index.md).
### Webhook Policy
This policy allows you to send an arbitrary HTTP Request to any URL. You can then use JSONPath to extract the result you need.
See [Expression Policy](expression.md).
## Password Policies
@ -35,11 +27,11 @@ This policy allows you to send an arbitrary HTTP Request to any URL. You can the
This Policy allows you to specify Password rules, like Length and required Characters.
The following rules can be set:
- Minimum amount of Uppercase Characters
- Minimum amount of Lowercase Characters
- Minimum amount of Symbols Characters
- Minimum Length
- Symbol charset (define which characters are counted as symbols)
- Minimum amount of Uppercase Characters
- Minimum amount of Lowercase Characters
- Minimum amount of Symbols Characters
- Minimum Length
- Symbol charset (define which characters are counted as symbols)
### Have I Been Pwned Policy

View File

@ -0,0 +1,12 @@
# Property Mapping Expressions
The property mapping should return a value that is expected by the Provider/Source. What types are supported, is documented in the individual Provider/Source. Returning `None` is always accepted, this simply skips this mapping.
!!! notice
These variables are available in addition to the common variables/functions defined in [**Expressions**](../expressions/index.md)
### Context Variables
- `user`: The current user, this might be `None` if there is no contextual user. ([ref](../expressions/reference/user-object.md))
- `request`: The current request, this might be `None` if there is no contextual request. ([ref](https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects))
- Arbitrary other arguments given by the provider, this is documented on the Provider/Source.

View File

@ -12,10 +12,10 @@ You can find examples [here](integrations/)
LDAP Property Mappings are used when you define a LDAP Source. These Mappings define which LDAP Property maps to which passbook Property. By default, these mappings are created:
- Autogenerated LDAP Mapping: givenName -> first_name
- Autogenerated LDAP Mapping: mail -> email
- Autogenerated LDAP Mapping: name -> name
- Autogenerated LDAP Mapping: sAMAccountName -> username
- Autogenerated LDAP Mapping: sn -> last_name
- Autogenerated LDAP Mapping: givenName -> first_name
- Autogenerated LDAP Mapping: mail -> email
- Autogenerated LDAP Mapping: name -> name
- Autogenerated LDAP Mapping: sAMAccountName -> username
- Autogenerated LDAP Mapping: sn -> last_name
These are configured for the most common LDAP Setups.

2
docs/requirements.txt Normal file
View File

@ -0,0 +1,2 @@
mkdocs
mkdocs-material

1
docs/runtime.txt Normal file
View File

@ -0,0 +1 @@
3.7

27
docs/terminology.md Normal file
View File

@ -0,0 +1,27 @@
### Policy
A Policy is at a base level a yes/no gate. It will either evaluate to True or False depending on the Policy Kind and settings. For example, a "Group Membership Policy" evaluates to True if the User is member of the specified Group and False if not. This can be used to conditionally apply Stages, grant/deny access to various objects and is also used for other custom logic.
### Provider
A Provider is a way for other Applications to authenticate against passbook. Common Providers are OpenID Connect (OIDC) and SAML.
### Source
Sources are ways to get users into passbook. This might be an LDAP Connection to import Users from Active Directory, or an OAuth2 Connection to allow Social Logins.
### Application
An application links together Policies with a Provider, allowing you to control access. It also holds Information like UI Name, Icon and more.
### Flows
Flows are a method of describing a sequence of stages. These flows can be used to defined how a user authenticates, enrolls, etc.
### Stages
A stage represents a single verification or logic step. They are used to authenticate users, enroll them, and more. These stages can optionally be applied to a flow via policies.
### Property Mappings
Property Mappings allow you to make Information available for external Applications. For example, if you want to login to AWS with passbook, you'd use Property Mappings to set the User's Roles based on their Groups.

View File

@ -0,0 +1,33 @@
# Upgrading from 0.8.x
Due to some database changes that had to be rather sooner than later, there is no possibility to directly upgrade. You must extract the data before hand and import it again. It is recommended to spin up a second instance of passbook to do this.
To export data from your old instance, run this command:
(with docker-compose)
```
docker-compose exec server ./manage.py dumpdata -o /tmp/passbook_dump.json passbook_core.User passbook_core.Group passbook_crypto.CertificateKeyPair passbook_audit.Event
docker cp passbook_server_1:/tmp/passbook_dump.json passbook_dump.json
```
(with kubernetes)
```
kubectl exec -it passbook-web-... -- ./manage.py dumpdata -o /tmp/passbook_dump.json passbook_core.User passbook_core.Group passbook_crypto.CertificateKeyPair passbook_audit.Event
kubectl cp passbook-web-...:/tmp/passbook_dump.json passbook_dump.json
```
After that, create a new passbook instance in a different namespace (kubernetes) or in a different folder (docker-compose). Once this instance is running, you can use the following commands to restore the data. On docker-compose, you still have to run the `migrate` command, to create all database structures.
(docker-compose)
```
docker cp passbook_dump.json new_passbook_server_1:/tmp/passbook_dump.json
docker-compose exec server ./manage.py loaddata /tmp/passbook_dump.json
```
(with kubernetes)
```
kubectl cp passbook_dump.json passbook-web-...:/tmp/passbook_dump.json
kubectl exec -it passbook-web-... -- ./manage.py loaddata /tmp/passbook_dump.json
```
Now, you should be able to login to the new passbook instance, and migrate the rest of the data over.

View File

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

View File

@ -8,9 +8,16 @@ data:
host: "{{ .Release.Name }}-postgresql"
name: "{{ .Values.postgresql.postgresqlDatabase }}"
user: postgres
{{- if .Values.backup }}
backup:
access_key: "{{ .Values.backup.access_key }}"
secret_key: "{{ .Values.backup.secret_key }}"
bucket: "{{ .Values.backup.bucket }}"
host: "{{ .Values.backup.host }}"
{{- end}}
redis:
host: "{{ .Release.Name }}-redis-master"
cache_db: 0
message_queue_db: 1
error_reporting: {{ .Values.config.error_reporting }}
domain: ".{{ index .Values.ingress.hosts 0 }}"
log_level: "{{ .Values.config.log_level }}"

View File

@ -7,11 +7,13 @@ metadata:
helm.sh/chart: {{ include "passbook.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
k8s.passbook.io/component: static
spec:
selector:
matchLabels:
app.kubernetes.io/name: {{ include "passbook.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
k8s.passbook.io/component: static
template:
metadata:
labels:

View File

@ -7,12 +7,14 @@ metadata:
helm.sh/chart: {{ include "passbook.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
k8s.passbook.io/component: web
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: {{ include "passbook.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
k8s.passbook.io/component: web
template:
metadata:
labels:
@ -28,10 +30,7 @@ spec:
- name: passbook-database-migrations
image: "beryju/passbook:{{ .Values.image.tag }}"
imagePullPolicy: Always
command:
- ./manage.py
args:
- bootstrap
- ./manage.py
- migrate
volumeMounts:
@ -61,10 +60,7 @@ spec:
- name: {{ .Chart.Name }}
image: "beryju/passbook:{{ .Values.image.tag }}"
imagePullPolicy: Always
command:
- ./manage.py
args:
- bootstrap
- uwsgi
- uwsgi.ini
volumeMounts:

View File

@ -7,12 +7,14 @@ metadata:
helm.sh/chart: {{ include "passbook.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
k8s.passbook.io/component: worker
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: {{ include "passbook.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
k8s.passbook.io/component: worker
template:
metadata:
labels:
@ -28,10 +30,7 @@ spec:
- name: {{ .Chart.Name }}
image: "beryju/passbook:{{ .Values.image.tag }}"
imagePullPolicy: IfNotPresent
command:
- ./manage.py
args:
- bootstrap
- celery
- worker
- --autoscale=10,3

View File

@ -2,7 +2,7 @@
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
image:
tag: 0.8.11-beta
tag: 0.9.0-pre1
nameOverride: ""
@ -11,14 +11,22 @@ config:
# secret_key: _k*@6h2u2@q-dku57hhgzb7tnx*ba9wodcb^s9g0j59@=y(@_o
# Enable error reporting
error_reporting: false
email:
host: localhost
# Log level used by web and worker
# Can be either debug, info, warning, error
log_level: warning
# This Helm chart ships with built-in Prometheus ServiceMonitors and Rules.
# This requires the CoreOS Prometheus Operator.
monitoring:
enabled: false
# Enable Database Backups to S3
# backup:
# access_key: access-key
# secret_key: secret-key
# bucket: s3-bucket
# host: s3-host
ingress:
enabled: false
annotations: {}

View File

@ -6,8 +6,8 @@ from defusedxml import defuse_stdlib
defuse_stdlib()
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'passbook.root.settings')
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "passbook.root.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:

View File

@ -1,39 +1,73 @@
site_name: passbook Docs
site_url: https://beryju.github.io/passbook
site_url: https://passbook.beryju.org/
copyright: "Copyright &copy; 2019 - 2020 BeryJu.org"
nav:
- Home: index.md
- Terminology: terminology.md
- Installation:
- Installation: installation/install.md
- docker-compose: installation/docker-compose.md
- Kubernetes: installation/kubernetes.md
- docker-compose: installation/docker-compose.md
- Kubernetes: installation/kubernetes.md
- Flows:
Overview: flow/flows.md
Examples:
- Login: flow/examples/login.md
- Stages:
- Captcha Stage: flow/stages/captcha/index.md
- Dummy Stage: flow/stages/dummy/index.md
- E-Mail Stage: flow/stages/email/index.md
- Identification Stage: flow/stages/identification/index.md
- Invitation Stage: flow/stages/invitation/index.md
- OTP Stage: flow/stages/otp/index.md
- Password Stage: flow/stages/password/index.md
- Prompt Stage: flow/stages/prompt/index.md
- Prompt Stage Validation: flow/stages/prompt/validation.md
- User Delete Stage: flow/stages/user_delete.md
- User Login Stage: flow/stages/user_login.md
- User Logout Stage: flow/stages/user_logout.md
- User Write Stage: flow/stages/user_write.md
- Sources: sources.md
- Providers: providers.md
- Expressions:
- Overview: expressions/index.md
- Reference:
- User Object: expressions/reference/user-object.md
- Property Mappings:
- Overview: property-mappings/index.md
- Reference:
- User Object: property-mappings/reference/user-object.md
- Factors: factors.md
- Overview: property-mappings/index.md
- Expressions: property-mappings/expression.md
- Policies:
- Overview: policies/index.md
- Expression: policies/expression/index.md
- Overview: policies/index.md
- Expression: policies/expression.md
- Integrations:
- as Provider:
- Amazon Web Services: integrations/services/aws/index.md
- GitLab: integrations/services/gitlab/index.md
- Rancher: integrations/services/rancher/index.md
- Harbor: integrations/services/harbor/index.md
- Sentry: integrations/services/sentry/index.md
- Ansible Tower/AWX: integrations/services/tower-awx/index.md
- as Provider:
- Amazon Web Services: integrations/services/aws/index.md
- GitLab: integrations/services/gitlab/index.md
- Rancher: integrations/services/rancher/index.md
- Harbor: integrations/services/harbor/index.md
- Sentry: integrations/services/sentry/index.md
- Ansible Tower/AWX: integrations/services/tower-awx/index.md
- Upgrading from v0.8.x: upgrading-from-0.8.x.md
repo_name: "BeryJu.org/passbook"
repo_name: "BeryJu/passbook"
repo_url: https://github.com/BeryJu/passbook
theme:
name: "material"
logo: "images/logo.svg"
name: material
logo: images/logo.svg
favicon: images/logo.svg
palette:
scheme: slate
primary: white
markdown_extensions:
- toc:
permalink: "¶"
- admonition
- codehilite
- pymdownx.betterem:
smart_enable: all
- pymdownx.inlinehilite
- pymdownx.magiclink
- attr_list
plugins:
- search

View File

@ -1,2 +1,2 @@
"""passbook"""
__version__ = "0.8.11-beta"
__version__ = "0.9.0-pre1"

View File

@ -4,6 +4,27 @@ from django import forms
from django.utils.translation import gettext_lazy as _
class CodeMirrorWidget(forms.Textarea):
"""Custom Textarea-based Widget that triggers a CodeMirror editor"""
# CodeMirror mode to enable
mode: str
def __init__(self, *args, mode="yaml", **kwargs):
super().__init__(*args, **kwargs)
self.mode = mode
def render(self, *args, **kwargs):
if "attrs" not in kwargs:
kwargs["attrs"] = {}
attrs = kwargs["attrs"]
if "class" not in attrs:
attrs["class"] = ""
attrs["class"] += " codemirror"
attrs["data-cm-mode"] = self.mode
return super().render(*args, **kwargs)
class InvalidYAMLInput(str):
"""Invalid YAML String type"""

View File

@ -1,6 +1,7 @@
"""passbook administration forms"""
from django import forms
from passbook.admin.fields import CodeMirrorWidget, YAMLField
from passbook.core.models import User
@ -8,3 +9,4 @@ class PolicyTestForm(forms.Form):
"""Form to test policies against user"""
user = forms.ModelChoiceField(queryset=User.objects.all())
context = YAMLField(widget=CodeMirrorWidget(), required=False, initial=dict)

View File

@ -1,4 +1,17 @@
"""passbook core source form fields"""
SOURCE_FORM_FIELDS = ["name", "slug", "enabled"]
SOURCE_SERIALIZER_FIELDS = ["pk", "name", "slug", "enabled"]
SOURCE_FORM_FIELDS = [
"name",
"slug",
"enabled",
"authentication_flow",
"enrollment_flow",
]
SOURCE_SERIALIZER_FIELDS = [
"pk",
"name",
"slug",
"enabled",
"authentication_flow",
"enrollment_flow",
]

View File

@ -2,7 +2,7 @@
from django import forms
from passbook.admin.fields import YAMLField
from passbook.admin.fields import CodeMirrorWidget, YAMLField
from passbook.core.models import User
@ -15,6 +15,7 @@ class UserForm(forms.ModelForm):
fields = ["username", "name", "email", "is_staff", "is_active", "attributes"]
widgets = {
"name": forms.TextInput,
"attributes": CodeMirrorWidget,
}
field_classes = {
"attributes": YAMLField,

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
@ -15,8 +15,9 @@
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<div class="pf-c-toolbar__bulk-select">
<a href="{% url 'passbook_admin:application-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
@ -62,6 +63,18 @@
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Applications.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no applications exist. Click the button below to create one.' %}
</div>
<a href="{% url 'passbook_admin:application-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,66 +1,68 @@
{% extends "administration/base.html" %}
{% extends "base/page.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-catalog"></i>
{% trans 'Audit Log' %}
</h1>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
{% include 'partials/pagination.html' %}
{% block page_content %}
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-catalog"></i>
{% trans 'Audit Log' %}
</h1>
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Action' %}</th>
<th role="columnheader" scope="col">{% trans 'Context' %}</th>
<th role="columnheader" scope="col">{% trans 'User' %}</th>
<th role="columnheader" scope="col">{% trans 'Creation Date' %}</th>
<th role="columnheader" scope="col">{% trans 'Client IP' %}</th>
</tr>
</thead>
<tbody role="rowgroup">
{% for entry in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ entry.action }}</div>
<small>{{ entry.app|default:'-' }}</small>
</div>
</th>
<td role="cell">
<code>{{ entry.context }}</code>
</td>
<td role="cell">
<span>
{{ entry.user }}
</span>
</td>
<td role="cell">
<span>
{{ entry.created }}
</span>
</td>
<td role="cell">
<span>
{{ entry.client_ip }}
</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Action' %}</th>
<th role="columnheader" scope="col">{% trans 'Context' %}</th>
<th role="columnheader" scope="col">{% trans 'User' %}</th>
<th role="columnheader" scope="col">{% trans 'Creation Date' %}</th>
<th role="columnheader" scope="col">{% trans 'Client IP' %}</th>
</tr>
</thead>
<tbody role="rowgroup">
{% for entry in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ entry.action }}</div>
<small>{{ entry.app|default:'-' }}</small>
</div>
</th>
<td role="cell">
<code>{{ entry.context }}</code>
</td>
<td role="cell">
<span>
{{ entry.user }}
</span>
</td>
<td role="cell">
<span>
{{ entry.created }}
</span>
</td>
<td role="cell">
<span>
{{ entry.client_ip }}
</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
</div>
</div>
</section>
</section>
</main>
{% endblock %}

View File

@ -1,6 +1,10 @@
{% extends "overview/base.html" %}
{% extends "base/page.html" %}
{% load static %}
{% load i18n %}
{% load passbook_is_active %}
{% load passbook_utils %}
{% block head %}
{{ block.super }}
@ -10,5 +14,132 @@
<link rel="stylesheet" href="{% static 'node_modules/codemirror/theme/monokai.css' %}">
<script src="{% static 'node_modules/codemirror/mode/xml/xml.js' %}"></script>
<script src="{% static 'node_modules/codemirror/mode/yaml/yaml.js' %}"></script>
<script src="{% static 'node_modules/codemirror/mode/jinja2/jinja2.js' %}"></script>
<script src="{% static 'node_modules/codemirror/mode/python/python.js' %}"></script>
{% endblock %}
{% block page_content %}
<div class="pf-c-page__sidebar">
<div class="pf-c-page__sidebar-body">
<nav class="pf-c-nav" id="page-default-nav-example-primary-nav" aria-label="Global">
<ul class="pf-c-nav__list">
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:overview' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:overview' %}">
{% trans 'System Status' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:applications' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:applications' 'passbook_admin:application-create' 'passbook_admin:application-update' 'passbook_admin:application-delete' %}">
{% trans 'Applications' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:sources' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:sources' 'passbook_admin:source-create' 'passbook_admin:source-update' 'passbook_admin:source-delete' %}">
{% trans 'Sources' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:providers' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:providers' 'passbook_admin:provider-create' 'passbook_admin:provider-update' 'passbook_admin:provider-delete' %}">
{% trans 'Providers' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:property-mappings' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:property-mappings' 'passbook_admin:property-mapping-create' 'passbook_admin:property-mapping-update' 'passbook_admin:property-mapping-delete' %}">
{% trans 'Property Mappings' %}
</a>
</li>
<li class="pf-c-nav__item pf-m-expandable pf-m-expanded">
<a href="#" class="pf-c-nav__link" aria-expanded="true">{% trans 'Flows' %}
<span class="pf-c-nav__toggle">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
</a>
<section class="pf-c-nav__subnav">
<ul class="pf-c-nav__simple-list">
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:flows' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:flows' 'passbook_admin:flow-create' 'passbook_admin:flow-update' 'passbook_admin:flow-delete' %}">
{% trans 'Flows' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:stage-bindings' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:stage-bindings' 'passbook_admin:stage-binding-create' 'passbook_admin:stage-binding-update' 'passbook_admin:stage-binding-delete' %}">
{% trans 'Bindings' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:stages' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:stages' 'passbook_admin:stage-create' 'passbook_admin:stage-update' 'passbook_admin:stage-delete' %}">
{% trans 'Stages' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:stage-prompts' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:stage-prompts' 'passbook_admin:stage-prompt-create' 'passbook_admin:stage-prompt-update' 'passbook_admin:stage-prompt-delete' %}">
{% trans 'Prompts' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:stage-invitations' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:stage-invitations' 'passbook_admin:stage-invitation-create' 'passbook_admin:stage-invitation-delete' %}">
{% trans 'Invitations' %}
</a>
</li>
</ul>
</section>
</li>
<li class="pf-c-nav__item pf-m-expandable pf-m-expanded">
<a href="#" class="pf-c-nav__link" aria-expanded="true">{% trans 'Policies' %}
<span class="pf-c-nav__toggle">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
</a>
<section class="pf-c-nav__subnav" aria-labelledby="subnav-title1">
<ul class="pf-c-nav__simple-list">
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:policies' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:policies' 'passbook_admin:policy-create' 'passbook_admin:policy-update' 'passbook_admin:policy-delete' 'passbook_admin:policy-test' %}">
{% trans 'Policies' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:policies-bindings' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:policies-bindings' 'passbook_admin:policy-binding-create' 'passbook_admin:policy-binding-update' 'passbook_admin:policy-binding-delete' %}">
{% trans 'Bindings' %}
</a>
</li>
</ul>
</section>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:certificate_key_pair' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:certificate_key_pair' 'passbook_admin:certificatekeypair-create' 'passbook_admin:certificatekeypair-update' 'passbook_admin:certificatekeypair-delete' %}">
{% trans 'Certificates' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:users' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:users' 'passbook_admin:user-update' 'passbook_admin:user-delete' %}">
{% trans 'Users' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:groups' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:groups' 'passbook_admin:group-update' 'passbook_admin:group-delete' %}">
{% trans 'Groups' %}
</a>
</li>
</ul>
</nav>
</div>
</div>
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
{% block content %}
{% endblock %}
</main>
{% endblock %}

View File

@ -0,0 +1,82 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-key"></i>
{% trans 'Certificate-Key Pairs' %}
</h1>
<p>{% trans "Import certificates of external providers or create certificates to sign requests with." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__bulk-select">
<a href="{% url 'passbook_admin:certificatekeypair-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Private Key available' %}</th>
<th role="columnheader" scope="col">{% trans 'Fingerprint' %}</th>
<th role="columnheader" scope="col">{% trans 'Provider Type' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for kp in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ kp.name }}</div>
</div>
</th>
<td role="cell">
<span>
{% if kp.key_data is not None %}
{% trans 'Yes' %}
{% else %}
{% trans 'No' %}
{% endif %}
</span>
</td>
<td role="cell">
<span>
{{ kp.fingerprint }}
</span>
</td>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:certificatekeypair-update' pk=kp.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:certificatekeypair-delete' pk=kp.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Certificates.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no certificates exist. Click the button below to create one.' %}
</div>
<a href="{% url 'passbook_admin:certificatekeypair-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">

View File

@ -1,83 +0,0 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-plugged"></i>
{% trans 'Factors' %}
</h1>
<p>{% trans "Factors required for a user to successfully authenticate." %}
</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:factor-create' %}?type={{ type }}&back={{ request.get_full_path }}">{{ name }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Order' %}</th>
<th role="columnheader" scope="col">{% trans 'Enabled' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for factor in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ factor.name }} ({{ factor.slug }})</div>
<small>{{ factor|verbose_name }}</small>
</div>
</th>
<td role="cell">
<span>
{{ factor.order }}
</span>
</td>
<td role="cell">
<span>
{{ factor.enabled }}
</span>
</td>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:factor-update' pk=factor.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:factor-delete' pk=factor.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
{% get_links factor as links %}
{% for name, href in links.items %}
<a class="pf-c-button pf-m-tertiary" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,85 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-process-automation"></i>
{% trans 'Flows' %}
</h1>
<p>{% trans "Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__bulk-select">
<a href="{% url 'passbook_admin:flow-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Designation' %}</th>
<th role="columnheader" scope="col">{% trans 'Stages' %}</th>
<th role="columnheader" scope="col">{% trans 'Policies' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for flow in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ flow.name }}</div>
<small>{{ flow.slug }}</small>
</div>
</th>
<td role="cell">
<span>
{{ flow.designation }}
</span>
</td>
<td role="cell">
<span>
{{ flow.stages.all|length }}
</span>
</td>
<td role="cell">
<span>
{{ flow.policies.all|length }}
</span>
</td>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:flow-update' pk=flow.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:flow-delete' pk=flow.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Flows.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no flows exist. Click the button below to create one.' %}
</div>
<a href="{% url 'passbook_admin:flow-create' %}?back={{ request.get_full_path }}"
class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
@ -16,8 +16,9 @@
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<div class="pf-c-toolbar__bulk-select">
<a href="{% url 'passbook_admin:group-create' %}?back={{ request.get_full_path }}"
class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
@ -61,6 +62,18 @@
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Groups.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no group exist. Click the button below to create one.' %}
</div>
<a href="{% url 'passbook_admin:group-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -48,22 +48,33 @@
</div>
</a>
<a href="{% url 'passbook_admin:factors' %}" class="pf-c-card pf-m-hoverable pf-m-compact">
<a href="{% url 'passbook_admin:stages' %}" class="pf-c-card pf-m-hoverable pf-m-compact">
<div class="pf-c-card__head">
<div class="pf-c-card__head-main">
<i class="pf-icon pf-icon-plugged"></i> {% trans 'Factors' %}
<i class="pf-icon pf-icon-plugged"></i> {% trans 'Stages' %}
</div>
</div>
<div class="pf-c-card__body">
{% if factor_count < 1 %}
<i class="pficon-error-circle-o"></i> {{ factor_count }}
<p>{% trans 'No Factors configured. No Users will be able to login.' %}"></p>
{% if stage_count < 1 %}
<i class="pficon-error-circle-o"></i> {{ stage_count }}
<p>{% trans 'No Stages configured. No Users will be able to login.' %}"></p>
{% else %}
<i class="pf-icon pf-icon-ok"></i> {{ factor_count }}
<i class="pf-icon pf-icon-ok"></i> {{ stage_count }}
{% endif %}
</div>
</a>
<a href="{% url 'passbook_admin:stages' %}" class="pf-c-card pf-m-hoverable pf-m-compact">
<div class="pf-c-card__head">
<div class="pf-c-card__head-main">
<i class="pf-icon pf-icon-topology"></i> {% trans 'Flows' %}
</div>
</div>
<div class="pf-c-card__body">
<i class="pf-icon pf-icon-ok"></i> {{ flow_count }}
</div>
</a>
<a href="{% url 'passbook_admin:policies' %}" class="pf-c-card pf-m-hoverable pf-m-compact">
<div class="pf-c-card__head">
<div class="pf-c-card__head-main">
@ -80,7 +91,7 @@
</div>
</a>
<a href="{% url 'passbook_admin:invitations' %}" class="pf-c-card pf-m-hoverable pf-m-compact">
<a href="{% url 'passbook_admin:stage-invitations' %}" class="pf-c-card pf-m-hoverable pf-m-compact">
<div class="pf-c-card__head">
<div class="pf-c-card__head-main">
<i class="pf-icon pf-icon-migration"></i> {% trans 'Invitation' %}

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
@ -10,13 +10,14 @@
<i class="pf-icon pf-icon-infrastructure"></i>
{% trans 'Policies' %}
</h1>
<p>{% trans "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Factors." %}</p>
<p>{% trans "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<div class="pf-c-toolbar__bulk-select">
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
@ -25,7 +26,12 @@
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:policy-create' %}?type={{ type }}&back={{ request.get_full_path }}">{{ name }}</a>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:policy-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
@ -47,12 +53,12 @@
<th role="columnheader">
<div>
<div>{{ policy.name }}</div>
{% if not policy.policymodel_set.exists %}
{% if not policy.bindings.exists %}
<i class="pf-icon pf-icon-warning-triangle"></i>
<small>{% trans 'Warning: Policy is not assigned.' %}</small>
{% else %}
<i class="pf-icon pf-icon-ok"></i>
<small>{% blocktrans with object_count=policy.policymodel_set.all|length %}Assigned to {{ object_count }} objects.{% endblocktrans %}</small>
<small>{% blocktrans with object_count=policy.bindings.all|length %}Assigned to {{ object_count }} objects.{% endblocktrans %}</small>
{% endif %}
</div>
</th>
@ -73,6 +79,36 @@
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Policies.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no policies exist. Click the button below to create one.' %}
</div>
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item"
href="{% url 'passbook_admin:policy-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,72 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-infrastructure"></i>
{% trans 'Policy Bindings' %}
</h1>
<p>{% trans "Bind existing Policies to Models accepting policies." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__bulk-select">
<a href="{% url 'passbook_admin:policy-binding-create' %}?back={{ request.get_full_path }}"
class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Enabled' %}</th>
<th role="columnheader" scope="col">{% trans 'Policy' %}</th>
<th role="columnheader" scope="col">{% trans 'Target' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup" class="pf-m-expanded">
{% for binding in object_list %}
<tr role="row pf-c-table__expandable-row pf-m-expanded">
<th role="cell">
<div>{{ binding.enabled }}</div>
</th>
<th role="cell">
<div>{{ binding.policy }}</div>
</th>
<th role="cell">
<div>{{ binding.target|verbose_name }}</div>
</th>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:policy-binding-update' pk=binding.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:policy-binding-delete' pk=binding.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Policy Bindings.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no policy bindings exist. Click the button below to create one.' %}
</div>
<a href="{% url 'passbook_admin:policy-binding-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
@ -16,8 +16,9 @@
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<div class="pf-c-toolbar__bulk-select">
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
@ -27,7 +28,12 @@
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item"
href="{% url 'passbook_admin:property-mapping-create' %}?type={{ type }}&back={{ request.get_full_path }}">{{ name }}</a>
href="{% url 'passbook_admin:property-mapping-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
@ -67,6 +73,36 @@
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Property Mappings.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no property mappings exist. Click the button below to create one.' %}
</div>
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item"
href="{% url 'passbook_admin:property-mapping-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% load admin_reflection %}
{% block content %}
@ -17,8 +17,9 @@
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<div class="pf-c-toolbar__bulk-select">
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
@ -27,7 +28,12 @@
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:provider-create' %}?type={{ type }}&back={{ request.get_full_path }}">{{ name }}</a>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:provider-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
@ -86,6 +92,35 @@
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Providers.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no providers exist. Click the button below to create one.' %}
</div>
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:provider-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% load admin_reflection %}
{% block content %}
@ -17,8 +17,9 @@
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<div class="pf-c-toolbar__bulk-select">
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
@ -27,7 +28,12 @@
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:source-create' %}?type={{ type }}&back={{ request.get_full_path }}">{{ name }}</a>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:source-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
@ -80,6 +86,35 @@
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Sources.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no sources exist. Click the button below to create one.' %}
</div>
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:source-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,117 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load passbook_utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-plugged"></i>
{% trans 'Stages' %}
</h1>
<p>{% trans "Stages are single steps of a Flow that a user is guided through." %}
</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__bulk-select">
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item" href="{% url 'passbook_admin:stage-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Flows' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for stage in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ stage.name }}</div>
<small>{{ stage|verbose_name }}</small>
</div>
</th>
<td role="cell">
<ul>
{% for flow in stage.flow_set.all %}
<li><a href="{% url 'passbook_admin:flow-update' pk=flow.pk %}">{{ flow.slug }}</a></li>
{% empty %}
<li>-</li>
{% endfor %}
</ul>
</td>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:stage-update' pk=stage.stage_uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:stage-delete' pk=stage.stage_uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
{% get_links stage as links %}
{% for name, href in links.items %}
<a class="pf-c-button pf-m-tertiary" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Stages.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no stages exist. Click the button below to create one.' %}
</div>
<div class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<a class="pf-c-dropdown__menu-item"
href="{% url 'passbook_admin:stage-create' %}?type={{ type }}&back={{ request.get_full_path }}">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,99 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-infrastructure"></i>
{% trans 'Stage Bindings' %}
</h1>
<p>{% trans "Bind existing Stages to Flows." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__bulk-select">
<a href="{% url 'passbook_admin:stage-binding-create' %}?back={{ request.get_full_path }}"
class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Order' %}</th>
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Stage Type' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% regroup object_list by flow as grouped_bindings %}
{% for flow in grouped_bindings %}
<tr role="role">
<td>
{% blocktrans with name=flow.grouper.name %}
Flow {{ name }}
{% endblocktrans %}
</td>
<td></td>
<td></td>
<td></td>
</tr>
{% for binding in flow.list %}
<tr class="pf-c-table__expandable-row pf-m-expanded" role="row">
<td role="cell">
<span>
{{ binding.order }}
</span>
</td>
<th role="columnheader">
<div>
<div>{{ binding.flow.name }}</div>
<small>
{{ binding.flow }}
</small>
</div>
</th>
<td role="cell">
<div>
<div>
{{ binding.stage.name }}
</div>
<small>
{{ binding.stage }}
</small>
</div>
</td>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:stage-binding-update' pk=binding.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:stage-binding-delete' pk=binding.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Flow-Stage Bindings.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no flow-stage bindings exist. Click the button below to create one.' %}
</div>
<a href="{% url 'passbook_admin:certificatekeypair-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
@ -10,15 +10,16 @@
<i class="pf-icon pf-icon-migration"></i>
{% trans 'Invitations' %}
</h1>
<p>{% trans "Create Invitation Links to enroll Users, and optionally force a username or expire on a set date." %}
<p>{% trans "Create Invitation Links to enroll Users, and optionally force specific attributes of their account." %}
</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<a href="{% url 'passbook_admin:invitation-create' %}?back={{ request.get_full_path }}"
<div class="pf-c-toolbar__bulk-select">
<a href="{% url 'passbook_admin:stage-invitation-create' %}?back={{ request.get_full_path }}"
class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
@ -45,7 +46,7 @@
</span>
</td>
<td>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:invitation-delete' pk=invitation.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:stage-invitation-delete' pk=invitation.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
@ -54,6 +55,18 @@
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Invitations.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no invitations exist. Click the button below to create one.' %}
</div>
<a href="{% url 'passbook_admin:stage-invitation-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,98 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load passbook_utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-plugged"></i>
{% trans 'Prompts' %}
</h1>
<p>{% trans "Single Prompts that can be used for Prompt Stages." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__bulk-select">
<a href="{% url 'passbook_admin:stage-prompt-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Field' %}</th>
<th role="columnheader" scope="col">{% trans 'Label' %}</th>
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
<th role="columnheader" scope="col">{% trans 'Order' %}</th>
<th role="columnheader" scope="col">{% trans 'Flows' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for prompt in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ prompt.field_key }}</div>
</div>
</th>
<td role="cell">
<div>
{{ prompt.label }}
</div>
</td>
<td role="cell">
<div>
{{ prompt.type }}
</div>
</td>
<td role="cell">
<div>
{{ prompt.order }}
</div>
</td>
<td role="cell">
<ul>
{% for flow in prompt.flow_set.all %}
<li><a href="{% url 'passbook_admin:flow-update' pk=flow.pk %}">{{ flow.slug }}</a></li>
{% empty %}
<li>-</li>
{% endfor %}
</ul>
</td>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:stage-prompt-update' pk=prompt.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:stage-prompt-delete' pk=prompt.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
{% get_links prompt as links %}
{% for name, href in links.items %}
<a class="pf-c-button pf-m-tertiary" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Stage Prompts.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no stage prompts exist. Click the button below to create one.' %}
</div>
<a href="{% url 'passbook_admin:stage-prompt-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
@ -14,8 +14,9 @@
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<div class="pf-c-toolbar__bulk-select">
<a href="{% url 'passbook_admin:user-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
@ -61,6 +62,18 @@
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-empty-state">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Users.' %}
</h1>
<div class="pf-c-empty-state__body">
{% trans 'Currently no users exist. How did you even get here.' %}
</div>
<a href="{% url 'passbook_admin:user-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,12 +1,18 @@
{% extends base_template|default:"generic/form.html" %}
{% load utils %}
{% load passbook_utils %}
{% load i18n %}
{% block above_form %}
<h1>{% blocktrans with type=form|form_verbose_name %}Create {{ type }}{% endblocktrans %}</h1>
<h1>
{% blocktrans with type=form|form_verbose_name %}
Create {{ type }}
{% endblocktrans %}
</h1>
{% endblock %}
{% block action %}
{% blocktrans with type=form|form_verbose_name %}Create {{ type }}{% endblocktrans %}
{% blocktrans with type=form|form_verbose_name %}
Create {{ type }}
{% endblocktrans %}
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% load passbook_utils %}
{% load static %}
{% block head %}
@ -48,28 +48,6 @@
</div>
</div>
</section>
<script>
const attributes = document.getElementsByName('attributes');
if (attributes.length > 0) {
// https://github.com/codemirror/CodeMirror/issues/5092
attributes[0].removeAttribute("required");
const attributesCM = CodeMirror.fromTextArea(attributes[0], {
mode: 'yaml',
theme: 'monokai',
lineNumbers: true,
});
}
const expressions = document.getElementsByName('expression');
if (expressions.length > 0) {
// https://github.com/codemirror/CodeMirror/issues/5092
expressions[0].removeAttribute("required");
const expressionCM = CodeMirror.fromTextArea(expressions[0], {
mode: 'jinja2',
theme: 'monokai',
lineNumbers: true,
});
}
</script>
{% endblock %}
{% block scripts %}

View File

@ -1,12 +1,18 @@
{% extends base_template|default:"generic/form.html" %}
{% load utils %}
{% load passbook_utils %}
{% load i18n %}
{% block above_form %}
<h1>{% blocktrans with type=form|form_verbose_name %}Update {{ type }}{% endblocktrans %}</h1>
<h1>
{% blocktrans with type=form|form_verbose_name|title inst=form.instance %}
Update {{ inst }}
{% endblocktrans %}
</h1>
{% endblock %}
{% block action %}
{% blocktrans with type=form|form_verbose_name %}Update {{ type }}{% endblocktrans %}
{% blocktrans with type=form|form_verbose_name|title %}
Update {{ type }}
{% endblocktrans %}
{% endblock %}

View File

@ -1,6 +1,4 @@
"""passbook admin templatetags"""
import inspect
from django import template
from django.db.models import Model
from django.utils.html import mark_safe
@ -21,14 +19,16 @@ def get_links(model_instance):
return links
try:
for name, method in inspect.getmembers(
model_instance, predicate=inspect.ismethod
):
if name.startswith(prefix):
human_name = name.replace(prefix, "").replace("_", " ").capitalize()
link = method()
if link:
links[human_name] = link
for name in dir(model_instance):
if not name.startswith(prefix):
continue
value = getattr(model_instance, name)
if not callable(value):
continue
human_name = name.replace(prefix, "").replace("_", " ").capitalize()
link = value()
if link:
links[human_name] = link
except NotImplementedError:
pass
@ -46,11 +46,14 @@ def get_htmls(context, model_instance):
return htmls
try:
for name, method in inspect.getmembers(
model_instance, predicate=inspect.ismethod
):
for name in dir(model_instance):
if not name.startswith(prefix):
continue
value = getattr(model_instance, name)
if not callable(value):
continue
if name.startswith(prefix):
html = method(context.get("request"))
html = value(context.get("request"))
if html:
htmls.append(mark_safe(html))
except NotImplementedError:

37
passbook/admin/tests.py Normal file
View File

@ -0,0 +1,37 @@
"""admin tests"""
from typing import Callable
from django.shortcuts import reverse
from django.test import Client, TestCase
from django.urls.exceptions import NoReverseMatch
from passbook.admin.urls import urlpatterns
from passbook.core.models import User
class TestAdmin(TestCase):
"""Generic admin tests"""
def setUp(self):
self.user = User.objects.create_superuser(username="test")
self.client = Client()
self.client.force_login(self.user)
def generic_view_tester(view_name: str) -> Callable:
"""This is used instead of subTest for better visibility"""
def tester(self: TestAdmin):
try:
full_url = reverse(f"passbook_admin:{view_name}")
response = self.client.get(full_url)
self.assertTrue(response.status_code < 500)
except NoReverseMatch:
pass
return tester
for url in urlpatterns:
method_name = url.name.replace("-", "_")
setattr(TestAdmin, f"test_{method_name}", generic_view_tester(url.name))

View File

@ -4,15 +4,20 @@ from django.urls import path
from passbook.admin.views import (
applications,
audit,
certificate_key_pair,
debug,
factors,
flows,
groups,
invitations,
overview,
policy,
policies,
policies_bindings,
property_mapping,
providers,
sources,
stages,
stages_bindings,
stages_invitations,
stages_prompts,
users,
)
@ -51,20 +56,43 @@ urlpatterns = [
name="source-delete",
),
# Policies
path("policies/", policy.PolicyListView.as_view(), name="policies"),
path("policies/create/", policy.PolicyCreateView.as_view(), name="policy-create"),
path("policies/", policies.PolicyListView.as_view(), name="policies"),
path("policies/create/", policies.PolicyCreateView.as_view(), name="policy-create"),
path(
"policies/<uuid:pk>/update/",
policy.PolicyUpdateView.as_view(),
policies.PolicyUpdateView.as_view(),
name="policy-update",
),
path(
"policies/<uuid:pk>/delete/",
policy.PolicyDeleteView.as_view(),
policies.PolicyDeleteView.as_view(),
name="policy-delete",
),
path(
"policies/<uuid:pk>/test/", policy.PolicyTestView.as_view(), name="policy-test"
"policies/<uuid:pk>/test/",
policies.PolicyTestView.as_view(),
name="policy-test",
),
# Policy bindings
path(
"policies/bindings/",
policies_bindings.PolicyBindingListView.as_view(),
name="policies-bindings",
),
path(
"policies/bindings/create/",
policies_bindings.PolicyBindingCreateView.as_view(),
name="policy-binding-create",
),
path(
"policies/bindings/<uuid:pk>/update/",
policies_bindings.PolicyBindingUpdateView.as_view(),
name="policy-binding-update",
),
path(
"policies/bindings/<uuid:pk>/delete/",
policies_bindings.PolicyBindingDeleteView.as_view(),
name="policy-binding-delete",
),
# Providers
path("providers/", providers.ProviderListView.as_view(), name="providers"),
@ -83,20 +111,87 @@ urlpatterns = [
providers.ProviderDeleteView.as_view(),
name="provider-delete",
),
# Factors
path("factors/", factors.FactorListView.as_view(), name="factors"),
path("factors/create/", factors.FactorCreateView.as_view(), name="factor-create"),
# Stages
path("stages/", stages.StageListView.as_view(), name="stages"),
path("stages/create/", stages.StageCreateView.as_view(), name="stage-create"),
path(
"factors/<uuid:pk>/update/",
factors.FactorUpdateView.as_view(),
name="factor-update",
"stages/<uuid:pk>/update/",
stages.StageUpdateView.as_view(),
name="stage-update",
),
path(
"factors/<uuid:pk>/delete/",
factors.FactorDeleteView.as_view(),
name="factor-delete",
"stages/<uuid:pk>/delete/",
stages.StageDeleteView.as_view(),
name="stage-delete",
),
# Factors
# Stage bindings
path(
"stages/bindings/",
stages_bindings.StageBindingListView.as_view(),
name="stage-bindings",
),
path(
"stages/bindings/create/",
stages_bindings.StageBindingCreateView.as_view(),
name="stage-binding-create",
),
path(
"stages/bindings/<uuid:pk>/update/",
stages_bindings.StageBindingUpdateView.as_view(),
name="stage-binding-update",
),
path(
"stages/bindings/<uuid:pk>/delete/",
stages_bindings.StageBindingDeleteView.as_view(),
name="stage-binding-delete",
),
# Stage Prompts
path(
"stages/prompts/",
stages_prompts.PromptListView.as_view(),
name="stage-prompts",
),
path(
"stages/prompts/create/",
stages_prompts.PromptCreateView.as_view(),
name="stage-prompt-create",
),
path(
"stages/prompts/<uuid:pk>/update/",
stages_prompts.PromptUpdateView.as_view(),
name="stage-prompt-update",
),
path(
"stages/prompts/<uuid:pk>/delete/",
stages_prompts.PromptDeleteView.as_view(),
name="stage-prompt-delete",
),
# Stage Invitations
path(
"stages/invitations/",
stages_invitations.InvitationListView.as_view(),
name="stage-invitations",
),
path(
"stages/invitations/create/",
stages_invitations.InvitationCreateView.as_view(),
name="stage-invitation-create",
),
path(
"stages/invitations/<uuid:pk>/delete/",
stages_invitations.InvitationDeleteView.as_view(),
name="stage-invitation-delete",
),
# Flows
path("flows/", flows.FlowListView.as_view(), name="flows"),
path("flows/create/", flows.FlowCreateView.as_view(), name="flow-create",),
path(
"flows/<uuid:pk>/update/", flows.FlowUpdateView.as_view(), name="flow-update",
),
path(
"flows/<uuid:pk>/delete/", flows.FlowDeleteView.as_view(), name="flow-delete",
),
# Property Mappings
path(
"property-mappings/",
property_mapping.PropertyMappingListView.as_view(),
@ -117,18 +212,6 @@ urlpatterns = [
property_mapping.PropertyMappingDeleteView.as_view(),
name="property-mapping-delete",
),
# Invitations
path("invitations/", invitations.InvitationListView.as_view(), name="invitations"),
path(
"invitations/create/",
invitations.InvitationCreateView.as_view(),
name="invitation-create",
),
path(
"invitations/<uuid:pk>/delete/",
invitations.InvitationDeleteView.as_view(),
name="invitation-delete",
),
# Users
path("users/", users.UserListView.as_view(), name="users"),
path("users/create/", users.UserCreateView.as_view(), name="user-create"),
@ -148,6 +231,27 @@ urlpatterns = [
path(
"group/<uuid:pk>/delete/", groups.GroupDeleteView.as_view(), name="group-delete"
),
# Certificate-Key Pairs
path(
"crypto/certificates/",
certificate_key_pair.CertificateKeyPairListView.as_view(),
name="certificate_key_pair",
),
path(
"crypto/certificates/create/",
certificate_key_pair.CertificateKeyPairCreateView.as_view(),
name="certificatekeypair-create",
),
path(
"crypto/certificates/<uuid:pk>/update/",
certificate_key_pair.CertificateKeyPairUpdateView.as_view(),
name="certificatekeypair-update",
),
path(
"crypto/certificates/<uuid:pk>/delete/",
certificate_key_pair.CertificateKeyPairDeleteView.as_view(),
name="certificatekeypair-delete",
),
# Audit Log
path("audit/", audit.EventListView.as_view(), name="audit-log"),
# Groups

View File

@ -12,4 +12,4 @@ class EventListView(PermissionListMixin, ListView):
template_name = "administration/audit/list.html"
permission_required = "passbook_audit.view_event"
ordering = "-created"
paginate_by = 10
paginate_by = 20

View File

@ -0,0 +1,77 @@
"""passbook CertificateKeyPair administration"""
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.crypto.forms import CertificateKeyPairForm
from passbook.crypto.models import CertificateKeyPair
from passbook.lib.views import CreateAssignPermView
class CertificateKeyPairListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all keypairs"""
model = CertificateKeyPair
permission_required = "passbook_crypto.view_certificatekeypair"
ordering = "name"
paginate_by = 40
template_name = "administration/certificatekeypair/list.html"
class CertificateKeyPairCreateView(
SuccessMessageMixin,
LoginRequiredMixin,
DjangoPermissionRequiredMixin,
CreateAssignPermView,
):
"""Create new CertificateKeyPair"""
model = CertificateKeyPair
form_class = CertificateKeyPairForm
permission_required = "passbook_crypto.add_certificatekeypair"
template_name = "generic/create.html"
success_url = reverse_lazy("passbook_admin:certificate_key_pair")
success_message = _("Successfully created CertificateKeyPair")
def get_context_data(self, **kwargs):
kwargs["type"] = "Certificate-Key Pair"
return super().get_context_data(**kwargs)
class CertificateKeyPairUpdateView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
):
"""Update certificatekeypair"""
model = CertificateKeyPair
form_class = CertificateKeyPairForm
permission_required = "passbook_crypto.change_certificatekeypair"
template_name = "generic/update.html"
success_url = reverse_lazy("passbook_admin:certificate_key_pair")
success_message = _("Successfully updated Certificate-Key Pair")
class CertificateKeyPairDeleteView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
):
"""Delete certificatekeypair"""
model = CertificateKeyPair
permission_required = "passbook_crypto.delete_certificatekeypair"
template_name = "generic/delete.html"
success_url = reverse_lazy("passbook_admin:certificate_key_pair")
success_message = _("Successfully deleted Certificate-Key Pair")
def delete(self, request, *args, **kwargs):
messages.success(self.request, self.success_message)
return super().delete(request, *args, **kwargs)

View File

@ -1,115 +0,0 @@
"""passbook Factor administration"""
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
)
from django.contrib.messages.views import SuccessMessageMixin
from django.http import Http404
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
from django.views.generic import DeleteView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.core.models import Factor
from passbook.lib.utils.reflection import path_to_class
from passbook.lib.views import CreateAssignPermView
def all_subclasses(cls):
"""Recursively return all subclassess of cls"""
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)]
)
class FactorListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all factors"""
model = Factor
template_name = "administration/factor/list.html"
permission_required = "passbook_core.view_factor"
ordering = "order"
paginate_by = 40
def get_context_data(self, **kwargs):
kwargs["types"] = {
x.__name__: x._meta.verbose_name for x in all_subclasses(Factor)
}
return super().get_context_data(**kwargs)
def get_queryset(self):
return super().get_queryset().select_subclasses()
class FactorCreateView(
SuccessMessageMixin,
LoginRequiredMixin,
DjangoPermissionRequiredMixin,
CreateAssignPermView,
):
"""Create new Factor"""
model = Factor
template_name = "generic/create.html"
permission_required = "passbook_core.add_factor"
success_url = reverse_lazy("passbook_admin:factors")
success_message = _("Successfully created Factor")
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)
factor_type = self.request.GET.get("type")
model = next(x for x in all_subclasses(Factor) if x.__name__ == factor_type)
kwargs["type"] = model._meta.verbose_name
return kwargs
def get_form_class(self):
factor_type = self.request.GET.get("type")
model = next(x for x in all_subclasses(Factor) if x.__name__ == factor_type)
if not model:
raise Http404
return path_to_class(model.form)
class FactorUpdateView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
):
"""Update factor"""
model = Factor
permission_required = "passbook_core.update_application"
template_name = "generic/update.html"
success_url = reverse_lazy("passbook_admin:factors")
success_message = _("Successfully updated Factor")
def get_form_class(self):
form_class_path = self.get_object().form
form_class = path_to_class(form_class_path)
return form_class
def get_object(self, queryset=None):
return (
Factor.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
)
class FactorDeleteView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
):
"""Delete factor"""
model = Factor
template_name = "generic/delete.html"
permission_required = "passbook_core.delete_factor"
success_url = reverse_lazy("passbook_admin:factors")
success_message = _("Successfully deleted Factor")
def get_object(self, queryset=None):
return (
Factor.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
)
def delete(self, request, *args, **kwargs):
messages.success(self.request, self.success_message)
return super().delete(request, *args, **kwargs)

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