Compare commits

...

88 Commits

Author SHA1 Message Date
65d9f690cd release: 0.13.5-stable 2020-12-26 00:52:26 +01:00
f96c2db5df core: show multi-select notice for SelectMultiple Widgets 2020-12-26 00:43:29 +01:00
5647f53140 core: fix anonymous user being included in User API
# Conflicts:
#	authentik/admin/views/applications.py
2020-12-26 00:26:24 +01:00
4e20cd0fee core: fix error during migrations 2020-12-25 23:51:51 +01:00
49636f8fa0 stages/invitation: fix optional field being required 2020-12-25 23:42:21 +01:00
cd8157ea08 stages/password: fix PasswordStageForm not showing backends 2020-12-25 23:42:21 +01:00
2a94ad7782 release: 0.13.4-stable 2020-12-24 15:36:39 +01:00
07eb5ffb4b core: fix missing import for application api 2020-12-24 15:35:57 +01:00
8cc68928b8 outposts: validate kubeconfig before saving 2020-12-24 13:25:08 +01:00
221db12f85 outposts: allow blank kubeconfig 2020-12-24 13:25:04 +01:00
34166d3c20 core: make application's provider not required
# Conflicts:
#	authentik/core/api/applications.py
2020-12-24 13:24:52 +01:00
94972d64e6 web: fix sidebar being overlayed over modal backdrop 2020-12-22 20:38:30 +01:00
253eaa382c admin: fix policy test button in dark theme 2020-12-20 22:32:53 +01:00
fc4f9733d1 policies/expression: fix missing ak_logger 2020-12-20 22:32:45 +01:00
8d784afcd1 web: expand sidebar by default on desktop, auto collapse 2020-12-20 22:32:37 +01:00
e23afd18e4 release: 0.13.3-stable 2020-12-19 16:55:07 +01:00
c2a30b760a web: allow Sidebar to be opened on mobile (#417)
* web: initial sidebar trigger on mobile

* web: render hamburger button as overlay top right
2020-12-19 16:54:25 +01:00
6e24856d45 flows: fix redirect when un-authenticated user uses external authentication (#416)
* flows: add PLAN_CONTEXT_REDIRECT so final redirect can be set from within flow

* sources/*: use PLAN_CONTEXT_REDIRECT

* flows: fallback when flow plan is empty
2020-12-19 16:42:39 +01:00
98a58b74e3 core: ensure generic error template fills screen 2020-12-19 14:28:20 +01:00
5f3ab22bea providers/oauth2: fix incorrect background set on end session screen 2020-12-19 14:24:28 +01:00
1ed5d5da35 build(deps): bump @sentry/browser from 5.29.1 to 5.29.2 in /web (#413)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 5.29.1 to 5.29.2.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/5.29.1...5.29.2)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-18 09:58:34 +01:00
76193e0031 build(deps): bump boto3 from 1.16.38 to 1.16.39 (#412)
Bumps [boto3](https://github.com/boto/boto3) from 1.16.38 to 1.16.39.
- [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.16.38...1.16.39)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-18 09:44:54 +01:00
50109ca7ad build(deps): bump @sentry/tracing from 5.29.1 to 5.29.2 in /web (#414)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 5.29.1 to 5.29.2.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/5.29.1...5.29.2)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-18 09:44:33 +01:00
e4b66d991c release: 0.13.2-stable 2020-12-17 20:20:47 +01:00
68adc2d5a5 admin: fix warning during swagger generation 2020-12-17 19:49:35 +01:00
349a3a67d5 flows: use to_stage_response in _flow_done() 2020-12-17 19:34:15 +01:00
e1394207e7 flows: fix inconsistent behaviour when flow is empty 2020-12-17 19:22:24 +01:00
f265c1f10b admin: fix cache clean views erroring 2020-12-17 19:03:32 +01:00
1aecdc7f8f web: fix css for policy tertiary buttons and text on flow card 2020-12-17 14:31:45 +01:00
a18edaf62b build(deps): bump @sentry/tracing from 5.29.0 to 5.29.1 in /web (#411)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 5.29.0 to 5.29.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/5.29.0...5.29.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-17 11:41:19 +01:00
c91abe448c build(deps): bump celery from 5.0.4 to 5.0.5 (#407)
Bumps [celery](https://github.com/celery/celery) from 5.0.4 to 5.0.5.
- [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/v5.0.4...v5.0.5)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-17 09:46:11 +01:00
e531e52403 build(deps): bump django-storages from 1.10.1 to 1.11 (#408)
Bumps [django-storages](https://github.com/jschneier/django-storages) from 1.10.1 to 1.11.
- [Release notes](https://github.com/jschneier/django-storages/releases)
- [Changelog](https://github.com/jschneier/django-storages/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jschneier/django-storages/compare/1.10.1...1.11)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-17 09:45:58 +01:00
cae536fa65 build(deps): bump boto3 from 1.16.37 to 1.16.38 (#409)
Bumps [boto3](https://github.com/boto/boto3) from 1.16.37 to 1.16.38.
- [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.16.37...1.16.38)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-17 09:45:45 +01:00
316b15b8a9 build(deps): bump @sentry/browser from 5.29.0 to 5.29.1 in /web (#410)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 5.29.0 to 5.29.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/5.29.0...5.29.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-17 09:45:29 +01:00
e6ccd4fa76 web: fix file name casing 2020-12-17 00:18:24 +01:00
86aabba3ed web: fix file name casing 2020-12-17 00:18:03 +01:00
0b36aad5c8 admin: ensure clean_expired_models is called during tests 2020-12-17 00:17:20 +01:00
64d2a216f0 web: fix linting 2020-12-16 23:50:23 +01:00
a5e5e140d6 admin: add full api tests 2020-12-16 23:42:44 +01:00
29f98abd00 root: update swagger 2020-12-16 23:32:14 +01:00
7b5ce4e98a web: use colours for icons, move users to separate card 2020-12-16 23:28:04 +01:00
d7fa52ebf3 admin: remove old admin overview 2020-12-16 23:21:38 +01:00
2ffaa94825 web: fix typo 2020-12-16 23:08:40 +01:00
b80b2626a6 web: fix rendering of version 2020-12-16 23:08:35 +01:00
3b7bba5a62 web: make sure naming matches backend 2020-12-16 23:03:06 +01:00
2d9efe035e web: migrate admin overview cards to separate files 2020-12-16 23:00:32 +01:00
48438e28fd admin: separate overview API into WorkerAPI and VersionAPI 2020-12-16 22:53:53 +01:00
885a2f0a58 web: add flow and policy cache card 2020-12-16 22:30:37 +01:00
cf46ee06b7 api: create dedicated api for cached flows and policies 2020-12-16 22:18:36 +01:00
9e33b49d29 web: rewrite aggregate cards to separate components 2020-12-16 22:00:40 +01:00
1179ba4ef2 api: remove counters from overview api and allow filtering on object apis 2020-12-16 22:00:29 +01:00
3c12c8b3ff core: make Provider SerializerModel 2020-12-16 21:38:40 +01:00
4d22659b6e web: re-organise sidebar 2020-12-16 16:04:11 +01:00
2c0709eeee web: render SidebarItem from the item 2020-12-16 16:04:02 +01:00
c24d1b6b84 outposts: fix incorrect timeout for state cache 2020-12-16 12:14:34 +01:00
040e148a73 release: 0.13.1-stable 2020-12-16 11:26:15 +01:00
b85d550ee0 build(deps-dev): bump pytest from 6.2.0 to 6.2.1 (#405)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.2.0...6.2.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-16 09:15:14 +01:00
ce95139d66 build(deps): bump boto3 from 1.16.36 to 1.16.37 (#404)
Bumps [boto3](https://github.com/boto/boto3) from 1.16.36 to 1.16.37.
- [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.16.36...1.16.37)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-16 09:14:58 +01:00
46436a5780 build(deps): bump @types/chart.js from 2.9.28 to 2.9.29 in /web (#406)
Bumps [@types/chart.js](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chart.js) from 2.9.28 to 2.9.29.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chart.js)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-16 09:14:43 +01:00
835a9aaaf2 outposts: fix circular import 2020-12-16 00:00:36 +01:00
42005e7def outposts: ensure all Service Connection state updates are done by the task 2020-12-15 23:39:52 +01:00
d9956e1e9c outpost: fix invalid incluster config causing Outpost Service Connection list to fail 2020-12-15 21:17:33 +01:00
4b1e73251a root: fix messages showing for all sessions of a user 2020-12-15 15:19:15 +01:00
736dbdca33 build(deps-dev): bump @rollup/plugin-typescript in /web (#401)
Bumps [@rollup/plugin-typescript](https://github.com/rollup/plugins) from 8.0.0 to 8.1.0.
- [Release notes](https://github.com/rollup/plugins/releases)
- [Commits](https://github.com/rollup/plugins/compare/eslint-v8.0.0...typescript-v8.1.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 10:54:31 +01:00
789b8e5d3e build(deps-dev): bump @typescript-eslint/eslint-plugin in /web (#402)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.9.1 to 4.10.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.10.0/packages/eslint-plugin)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 10:28:11 +01:00
074b55f66b build(deps): bump boto3 from 1.16.35 to 1.16.36 (#398)
Bumps [boto3](https://github.com/boto/boto3) from 1.16.35 to 1.16.36.
- [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.16.35...1.16.36)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 08:59:52 +01:00
d9bc5ea4d1 build(deps): bump rollup from 2.34.2 to 2.35.1 in /web (#399)
Bumps [rollup](https://github.com/rollup/rollup) from 2.34.2 to 2.35.1.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v2.34.2...v2.35.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 08:59:42 +01:00
716bb9f188 build(deps): bump @patternfly/patternfly from 4.65.6 to 4.70.2 in /web (#400)
Bumps [@patternfly/patternfly](https://github.com/patternfly/patternfly) from 4.65.6 to 4.70.2.
- [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/prerelease-v4.65.6...prerelease-v4.70.2)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 08:59:30 +01:00
dd496619a2 build(deps-dev): bump @typescript-eslint/parser in /web (#403)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.9.1 to 4.10.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.10.0/packages/parser)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 08:59:04 +01:00
51d07f7913 proxy: output JSON logs 2020-12-14 19:41:32 +01:00
5c4163579b root: fix application icons now showing with docker-compose 2020-12-14 19:32:48 +01:00
5a73413d58 web: fix brand not showing on firefox 2020-12-14 19:26:02 +01:00
51a5d4bf49 docs: fix issues when overscrolling 2020-12-14 14:16:00 +01:00
8bbb854073 root: make docker-compose database name and username configurable 2020-12-14 12:27:33 +01:00
9f2e9e8444 release: 0.13.0-stable 2020-12-14 11:20:47 +01:00
a3d361f500 outposts: fix controller not using token.key 2020-12-14 11:03:49 +01:00
e9bb583b32 providers/proxy: ensure pb_proxy is deleted and ak_proxy is created 2020-12-14 10:47:49 +01:00
efccf47c83 build(deps): bump packaging from 20.7 to 20.8 (#388)
Bumps [packaging](https://github.com/pypa/packaging) from 20.7 to 20.8.
- [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.7...20.8)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 10:07:23 +01:00
a5b144cf8f build(deps): bump boto3 from 1.16.34 to 1.16.35 (#391)
Bumps [boto3](https://github.com/boto/boto3) from 1.16.34 to 1.16.35.
- [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.16.34...1.16.35)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 10:07:12 +01:00
afc5a17fc2 build(deps): bump github.com/recws-org/recws in /proxy (#394)
Bumps [github.com/recws-org/recws](https://github.com/recws-org/recws) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/recws-org/recws/releases)
- [Commits](https://github.com/recws-org/recws/compare/v1.2.1...v1.2.2)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 10:06:59 +01:00
b3e0884b2e build(deps-dev): bump eslint-plugin-lit from 1.2.4 to 1.3.0 in /web (#396)
Bumps [eslint-plugin-lit](https://github.com/43081j/eslint-plugin-lit) from 1.2.4 to 1.3.0.
- [Release notes](https://github.com/43081j/eslint-plugin-lit/releases)
- [Commits](https://github.com/43081j/eslint-plugin-lit/compare/v1.2.4...v1.3.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 10:06:34 +01:00
078d648551 build(deps): bump uvicorn from 0.13.0 to 0.13.1 (#390)
* build(deps): bump uvicorn from 0.13.0 to 0.13.1

Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.13.0 to 0.13.1.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.13.0...0.13.1)

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

* root: remove asgi workaround when websocket is closed during connect

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org>
2020-12-14 10:05:07 +01:00
41f9097592 build(deps-dev): bump pytest from 6.1.2 to 6.2.0 (#389)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.1.2 to 6.2.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.1.2...6.2.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 08:53:18 +01:00
562175741c build(deps): bump github.com/getsentry/sentry-go in /proxy (#392)
Bumps [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) from 0.7.0 to 0.9.0.
- [Release notes](https://github.com/getsentry/sentry-go/releases)
- [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-go/compare/v0.7.0...v0.9.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 08:52:56 +01:00
24e24cb97e build(deps-dev): bump typescript from 4.1.2 to 4.1.3 in /web (#395)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/commits)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 08:52:40 +01:00
69b0a23a7d build(deps-dev): bump bandit from 1.6.3 to 1.7.0 (#387)
Bumps [bandit](https://github.com/PyCQA/bandit) from 1.6.3 to 1.7.0.
- [Release notes](https://github.com/PyCQA/bandit/releases)
- [Commits](https://github.com/PyCQA/bandit/compare/1.6.3...1.7.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-14 08:11:24 +01:00
f0f3245388 root: fix links to docs 2020-12-14 00:45:02 +01:00
99ca0d1f9f helm: fix missing /media/ route 2020-12-14 00:11:49 +01:00
113 changed files with 1536 additions and 1032 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 0.13.0-rc4 current_version = 0.13.5-stable
tag = True tag = True
commit = True commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*) parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)

View File

@ -18,11 +18,11 @@ jobs:
- name: Building Docker Image - name: Building Docker Image
run: docker build run: docker build
--no-cache --no-cache
-t beryju/authentik:0.13.0-rc4 -t beryju/authentik:0.13.5-stable
-t beryju/authentik:latest -t beryju/authentik:latest
-f Dockerfile . -f Dockerfile .
- name: Push Docker Container to Registry (versioned) - name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik:0.13.0-rc4 run: docker push beryju/authentik:0.13.5-stable
- name: Push Docker Container to Registry (latest) - name: Push Docker Container to Registry (latest)
run: docker push beryju/authentik:latest run: docker push beryju/authentik:latest
build-proxy: build-proxy:
@ -48,11 +48,11 @@ jobs:
cd proxy/ cd proxy/
docker build \ docker build \
--no-cache \ --no-cache \
-t beryju/authentik-proxy:0.13.0-rc4 \ -t beryju/authentik-proxy:0.13.5-stable \
-t beryju/authentik-proxy:latest \ -t beryju/authentik-proxy:latest \
-f Dockerfile . -f Dockerfile .
- name: Push Docker Container to Registry (versioned) - name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik-proxy:0.13.0-rc4 run: docker push beryju/authentik-proxy:0.13.5-stable
- name: Push Docker Container to Registry (latest) - name: Push Docker Container to Registry (latest)
run: docker push beryju/authentik-proxy:latest run: docker push beryju/authentik-proxy:latest
build-static: build-static:
@ -69,11 +69,11 @@ jobs:
cd web/ cd web/
docker build \ docker build \
--no-cache \ --no-cache \
-t beryju/authentik-static:0.13.0-rc4 \ -t beryju/authentik-static:0.13.5-stable \
-t beryju/authentik-static:latest \ -t beryju/authentik-static:latest \
-f Dockerfile . -f Dockerfile .
- name: Push Docker Container to Registry (versioned) - name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik-static:0.13.0-rc4 run: docker push beryju/authentik-static:0.13.5-stable
- name: Push Docker Container to Registry (latest) - name: Push Docker Container to Registry (latest)
run: docker push beryju/authentik-static:latest run: docker push beryju/authentik-static:latest
test-release: test-release:
@ -107,5 +107,5 @@ jobs:
SENTRY_PROJECT: authentik SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org SENTRY_URL: https://sentry.beryju.org
with: with:
tagName: 0.13.0-rc4 tagName: 0.13.5-stable
environment: beryjuorg-prod environment: beryjuorg-prod

150
Pipfile.lock generated
View File

@ -53,10 +53,10 @@
}, },
"autobahn": { "autobahn": {
"hashes": [ "hashes": [
"sha256:24ce276d313e84d68241c3aef30d484f352b90a40168981b3640312c821df77b", "sha256:491238c31f78721eaa9d0593909ab455a4ea68127aadd76ecf67185143f5f298",
"sha256:86bbce30cdd407137c57670993a8f9bfdfe3f8e994b889181d85e844d5aa8dfb" "sha256:72b68a1ce1e10e3cbcc3b280aae86d5b2e7a1f409febab1ab91a8a3274113f6e"
], ],
"version": "==20.7.1" "version": "==20.12.2"
}, },
"automat": { "automat": {
"hashes": [ "hashes": [
@ -74,18 +74,18 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:616cde1e326949020da85a5bacaa7ad287e9f117d10ac9c5bfb9150a98dfe1a7", "sha256:a05614300fd404c7952a55ae92e106b9400ae65886425aaab3104527be833848",
"sha256:ddad9ada00eccae1fc2da28c69531ba202fead562994ddcd9a9a232e993cd8a2" "sha256:c7556b0861d982b71043fbc0df024644320c817ad796391c442d0c2f15a77223"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.16.34" "version": "==1.16.39"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:49f5e56a7382a65ee0873371edcd91bdba8fc3f70abe102ebc1a0da2e6fbed06", "sha256:449e4196160ff58ee27d2a626a7ce4cfff2640fe1806d7a279e73a30ad286347",
"sha256:4d81d92127ef646ae0f0ee84c9c220c92fa82312e765c29f8cb3b000fdbdd038" "sha256:e0d0386098a072abd7b6c087e6149d997377c969a823ebe01b3f5bfabe9bfac0"
], ],
"version": "==1.19.34" "version": "==1.19.39"
}, },
"cachetools": { "cachetools": {
"hashes": [ "hashes": [
@ -96,11 +96,11 @@
}, },
"celery": { "celery": {
"hashes": [ "hashes": [
"sha256:45bb7909061862305cefec94289fabc1b89ac004680f4dc7d9dea642a2507e53", "sha256:5e8d364e058554e83bbb116e8377d90c79be254785f357cb2cec026e79febe13",
"sha256:533f3635065b7ed362ffc04228635b4c82d53a9ab812118ccdedb5eae281fb97" "sha256:f4efebe6f8629b0da2b8e529424de376494f5b7a743c321c8a2ddc2b1414921c"
], ],
"index": "pypi", "index": "pypi",
"version": "==5.0.4" "version": "==5.0.5"
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
@ -168,10 +168,10 @@
}, },
"chardet": { "chardet": {
"hashes": [ "hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
], ],
"version": "==3.0.4" "version": "==4.0.0"
}, },
"click": { "click": {
"hashes": [ "hashes": [
@ -343,11 +343,11 @@
}, },
"django-storages": { "django-storages": {
"hashes": [ "hashes": [
"sha256:12de8fb2605b9b57bfaf54b075280d7cbb3b3ee1ca4bc9b9add147af87fe3a2c", "sha256:056ec3e9e2b0c6f363913976072ffba2923e79e4859578047da139ba1637497e",
"sha256:652275ab7844538c462b62810276c0244866f345878256a9e0e86f5b1283ae18" "sha256:7af56611c62a1c174aab4e862efb7fdd98296dccf76f42135f5b6851fc313c97"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.10.1" "version": "==1.11"
}, },
"djangorestframework": { "djangorestframework": {
"hashes": [ "hashes": [
@ -396,10 +396,10 @@
}, },
"google-auth": { "google-auth": {
"hashes": [ "hashes": [
"sha256:5176db85f1e7e837a646cd9cede72c3c404ccf2e3373d9ee14b2db88febad440", "sha256:0b0e026b412a0ad096e753907559e4bdb180d9ba9f68dd9036164db4fdc4ad2e",
"sha256:b728625ff5dfce8f9e56a499c8a4eb51443a67f20f6d28b67d5774c310ec4b6b" "sha256:ce752cc51c31f479dbf9928435ef4b07514b20261b021c7383bee4bda646acb8"
], ],
"version": "==1.23.0" "version": "==1.24.0"
}, },
"gunicorn": { "gunicorn": {
"hashes": [ "hashes": [
@ -646,26 +646,46 @@
}, },
"msgpack": { "msgpack": {
"hashes": [ "hashes": [
"sha256:002a0d813e1f7b60da599bdf969e632074f9eec1b96cbed8fb0973a63160a408", "sha256:01835e300967e5ad6fdbfc36eafe74df67ff47e16e0d6dee8766630550315903",
"sha256:25b3bc3190f3d9d965b818123b7752c5dfb953f0d774b454fd206c18fe384fb8", "sha256:03c5554315317d76c25a15569dd52ac6047b105df71e861f24faf9675672b72d",
"sha256:271b489499a43af001a2e42f42d876bb98ccaa7e20512ff37ca78c8e12e68f84", "sha256:0968b368a9a9081435bfcb7a57a1e8b75c7bf038ef911b369acd2e038c7f873a",
"sha256:39c54fdebf5fa4dda733369012c59e7d085ebdfe35b6cf648f09d16708f1be5d", "sha256:1d7ab166401f7789bf11262439336c0a01b878f0d602e48f35c35d2e3a555820",
"sha256:4233b7f86c1208190c78a525cd3828ca1623359ef48f78a6fea4b91bb995775a", "sha256:1e8d27bac821f8aa909904a704a67e5e8bc2e42b153415fc3621b7afbc06702b",
"sha256:5bea44181fc8e18eed1d0cd76e355073f00ce232ff9653a0ae88cb7d9e643322", "sha256:1fc9f21da9fd77088ebfd3c9941b044ca3f4a048e85f7ff5727f26bcdbffed61",
"sha256:5dba6d074fac9b24f29aaf1d2d032306c27f04187651511257e7831733293ec2", "sha256:20196229acc193939223118c7420838749d5b0cece49cd397739a3a6ffcfe2d1",
"sha256:7a22c965588baeb07242cb561b63f309db27a07382825fc98aecaf0827c1538e", "sha256:2933443313289725f16bd7b99a8c3aa6a2cca1549e661d7407f056a0af80bf7b",
"sha256:908944e3f038bca67fcfedb7845c4a257c7749bf9818632586b53bcf06ba4b97", "sha256:2966b155356fd231fa441131d7301e1596ee38974ad56dc57fd752fdbe2bb63f",
"sha256:9534d5cc480d4aff720233411a1f765be90885750b07df772380b34c10ecb5c0", "sha256:29a6fb3729215b6fcab786ef4f460a5406a5c056f7021191f70ff7712a3f6ba4",
"sha256:aa5c057eab4f40ec47ea6f5a9825846be2ff6bf34102c560bad5cad5a677c5be", "sha256:35cbefa7d7bddfb4b0770a1b9ff721cd8dfe9a680947a68457974d5e3e6acc2f",
"sha256:b3758dfd3423e358bbb18a7cccd1c74228dffa7a697e5be6cb9535de625c0dbf", "sha256:35ff1ac162a77fb78be360d9f771d36cbf1202e94fc6d70e284ad5db6ab72608",
"sha256:c901e8058dd6653307906c5f157f26ed09eb94a850dddd989621098d347926ab", "sha256:40dd1ac7420f071e96b3e4a4a7b8e69546a6f8065ff5995dbacf53f86207eb98",
"sha256:cec8bf10981ed70998d98431cd814db0ecf3384e6b113366e7f36af71a0fca08", "sha256:4bea1938e484c9caca9585105f447d6807c496c153b7244fa726b3cc4a68ec9e",
"sha256:db685187a415f51d6b937257474ca72199f393dad89534ebbdd7d7a3b000080e", "sha256:4e58b9f4a99bc3a90859bb006ec4422448a5ce39e5cd6e7498c56de5dcec9c34",
"sha256:e35b051077fc2f3ce12e7c6a34cf309680c63a842db3a0616ea6ed25ad20d272", "sha256:66d47e952856bfcde46d8351380d0b5b928a73112b66bc06d5367dfcc077c06a",
"sha256:e7bbdd8e2b277b77782f3ce34734b0dfde6cbe94ddb74de8d733d603c7f9e2b1", "sha256:69f6aa503378548ea1e760c11aeb6fc91952bf3634fd806a38a0e47edb507fcd",
"sha256:ea41c9219c597f1d2bf6b374d951d310d58684b5de9dc4bd2976db9e1e22c140" "sha256:7033215267a0e9f60f4a5e4fb2228a932c404f237817caff8dc3115d9e7cd975",
"sha256:7b50afd767cc053ad92fad39947c3670db27305fd1c49acded44d9d9ac8b56fd",
"sha256:99ea9e65876546743b2b8bb5bc7adefbb03b9da78a899827467da197a48f790b",
"sha256:abcc62303ac4d789878d4aac4cdba1bbe2adb478d67be99cd4a6d56ac3a4028f",
"sha256:b107f9b36665bf7d7c6176a938a361a7aba16aa179d833919448f77287866484",
"sha256:b5b27923b6c98a2616b7e906a29e4e10e1b4424aea87a0e0d5636327dc6ea315",
"sha256:bf8eedc7bfbf63cbc9abe58287c32d78780a347835e82c23033c68f11f80bb05",
"sha256:c144ff4954a6ea40aa603600c8be175349588fc68696092889fa34ab6e055060",
"sha256:c4e5f96a1d0d916ce7a16decb7499e8923ddef007cf7d68412fb68767766648a",
"sha256:c60e8b2bf754b8dcc1075c5bee0b177ed9193e7cbd2377faaf507120a948e697",
"sha256:c82fc6cdba5737eb6ed0c926a30a5d56e7b050297375a16d6c5ad89b576fd979",
"sha256:ce4ebe2c79411cd5671b20862831880e7850a2de699cff6626f48853fde61ae6",
"sha256:d113c6b1239c62669ef3063693842605a3edbfebc39a333cf91ba60d314afe6d",
"sha256:d3cea07ad16919a44e8d1ea67efa5244855cdce807d672f41694acc24d08834e",
"sha256:d76672602db16e3f44bc1a85c7ee5f15d79e02fcf5bc9d133c2954753be6eddc",
"sha256:decf2091b75987ca2564e3b742f9614eb7d57e39ff04eaa68af7a3fc5648f7ed",
"sha256:e13b9007af66a3f62574bc0a13843df0e4402f5ee4b00a02aa1803f01d26b9fb",
"sha256:e157edf4213dacafb0f862e0b7a3a18448250cec91aa1334f432f49028acc650",
"sha256:e234ff83628ca3ab345bf97fb36ccbf6d2f1700f5e08868643bf4489edc960f8",
"sha256:f08d9dd3ce0c5e972dc4653f0fb66d2703941e65356388c13032b578dd718261",
"sha256:f20d7d4f1f0728560408ba6933154abccf0c20f24642a2404b43d5c23e4119ab"
], ],
"version": "==1.0.0" "version": "==1.0.1"
}, },
"oauthlib": { "oauthlib": {
"hashes": [ "hashes": [
@ -676,11 +696,11 @@
}, },
"packaging": { "packaging": {
"hashes": [ "hashes": [
"sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236", "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858",
"sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376" "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"
], ],
"index": "pypi", "index": "pypi",
"version": "==20.7" "version": "==20.8"
}, },
"prometheus-client": { "prometheus-client": {
"hashes": [ "hashes": [
@ -855,10 +875,10 @@
}, },
"pyopenssl": { "pyopenssl": {
"hashes": [ "hashes": [
"sha256:898aefbde331ba718570244c3b01dcddb1b31a3b336613436a45e52e27d9a82d", "sha256:4c231c759543ba02560fcd2480c48dcec4dae34c9da7d3747c508227e0624b51",
"sha256:92f08eccbd73701cf744e8ffd6989aa7842d48cbe3fea8a7c031c5647f590ac5" "sha256:818ae18e06922c066f777a33f1fca45786d85edfe71cd043de6379337a7f274b"
], ],
"version": "==20.0.0" "version": "==20.0.1"
}, },
"pyparsing": { "pyparsing": {
"hashes": [ "hashes": [
@ -930,10 +950,10 @@
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8", "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998" "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
], ],
"version": "==2.25.0" "version": "==2.25.1"
}, },
"requests-oauthlib": { "requests-oauthlib": {
"hashes": [ "hashes": [
@ -948,7 +968,7 @@
"sha256:109ea5a66744dd859bf16fe904b8d8b627adafb9408753161e766a92e7d681fa", "sha256:109ea5a66744dd859bf16fe904b8d8b627adafb9408753161e766a92e7d681fa",
"sha256:6166864e23d6b5195a5cfed6cd9fed0fe774e226d8f854fcb23b7bbef0350233" "sha256:6166864e23d6b5195a5cfed6cd9fed0fe774e226d8f854fcb23b7bbef0350233"
], ],
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.6'",
"version": "==4.6" "version": "==4.6"
}, },
"ruamel.yaml": { "ruamel.yaml": {
@ -1073,11 +1093,11 @@
"standard" "standard"
], ],
"hashes": [ "hashes": [
"sha256:28420526640d800aabe648038f8e2ea8ba2a8bdc363002eecd5dfc57a0f75ab7", "sha256:2a7b17f4d9848d6557ccc2274a5f7c97f1daf037d130a0c6918f67cd9bc8cdf5",
"sha256:5123606e0f1d15ffbe0f63161c5078f7c28f350c5eb102435671eae58046db0f" "sha256:6fcce74c00b77d4f4b3ed7ba1b2a370d27133bfdb46f835b7a76dfe0a8c110ae"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.13.0" "version": "==0.13.1"
}, },
"uvloop": { "uvloop": {
"hashes": [ "hashes": [
@ -1263,11 +1283,11 @@
}, },
"bandit": { "bandit": {
"hashes": [ "hashes": [
"sha256:2ff3fe35fe3212c0be5fc9c4899bd0108e2b5239c5ff62fb174639e4660fe958", "sha256:216be4d044209fa06cf2a3e51b319769a51be8318140659719aa7a115c35ed07",
"sha256:d02dfe250f4aa2d166c127ad81d192579e2bfcdb8501717c0e2005e35a6bcf60" "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.6.3" "version": "==1.7.0"
}, },
"black": { "black": {
"hashes": [ "hashes": [
@ -1453,11 +1473,11 @@
}, },
"packaging": { "packaging": {
"hashes": [ "hashes": [
"sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236", "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858",
"sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376" "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"
], ],
"index": "pypi", "index": "pypi",
"version": "==20.7" "version": "==20.8"
}, },
"pathspec": { "pathspec": {
"hashes": [ "hashes": [
@ -1496,10 +1516,10 @@
}, },
"py": { "py": {
"hashes": [ "hashes": [
"sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2", "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
"sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342" "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
], ],
"version": "==1.9.0" "version": "==1.10.0"
}, },
"pycodestyle": { "pycodestyle": {
"hashes": [ "hashes": [
@ -1566,11 +1586,11 @@
}, },
"pytest": { "pytest": {
"hashes": [ "hashes": [
"sha256:4288fed0d9153d9646bfcdf0c0428197dba1ecb27a33bb6e031d002fa88653fe", "sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8",
"sha256:c0a7e94a8cdbc5422a51ccdad8e6f1024795939cc89159a0ae7f0b316ad3823e" "sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"
], ],
"index": "pypi", "index": "pypi",
"version": "==6.1.2" "version": "==6.2.1"
}, },
"pytest-django": { "pytest-django": {
"hashes": [ "hashes": [

View File

@ -1,2 +1,2 @@
"""authentik""" """authentik"""
__version__ = "0.13.0-rc4" __version__ = "0.13.5-stable"

View File

@ -1,4 +1,4 @@
"""authentik administration overview""" """authentik administration metrics"""
import time import time
from collections import Counter from collections import Counter
from datetime import timedelta from datetime import timedelta
@ -47,7 +47,7 @@ def get_events_per_1h(**filter_kwargs) -> List[Dict[str, int]]:
class AdministrationMetricsSerializer(Serializer): class AdministrationMetricsSerializer(Serializer):
"""Overview View""" """Login Metrics per 1h"""
logins_per_1h = SerializerMethodField() logins_per_1h = SerializerMethodField()
logins_failed_per_1h = SerializerMethodField() logins_failed_per_1h = SerializerMethodField()
@ -68,12 +68,12 @@ class AdministrationMetricsSerializer(Serializer):
class AdministrationMetricsViewSet(ViewSet): class AdministrationMetricsViewSet(ViewSet):
"""Return single instance of AdministrationMetricsSerializer""" """Login Metrics per 1h"""
permission_classes = [IsAdminUser] permission_classes = [IsAdminUser]
@swagger_auto_schema(responses={200: AdministrationMetricsSerializer(many=True)}) @swagger_auto_schema(responses={200: AdministrationMetricsSerializer(many=True)})
def list(self, request: Request) -> Response: def list(self, request: Request) -> Response:
"""Return single instance of AdministrationMetricsSerializer""" """Login Metrics per 1h"""
serializer = AdministrationMetricsSerializer(True) serializer = AdministrationMetricsSerializer(True)
return Response(serializer.data) return Response(serializer.data)

View File

@ -1,79 +0,0 @@
"""authentik administration overview"""
from django.core.cache import cache
from drf_yasg2.utils import swagger_auto_schema
from rest_framework.fields import SerializerMethodField
from rest_framework.permissions import IsAdminUser
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import Serializer
from rest_framework.viewsets import ViewSet
from authentik import __version__
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
from authentik.core.models import Provider
from authentik.policies.models import Policy
from authentik.root.celery import CELERY_APP
class AdministrationOverviewSerializer(Serializer):
"""Overview View"""
version = SerializerMethodField()
version_latest = SerializerMethodField()
worker_count = SerializerMethodField()
providers_without_application = SerializerMethodField()
policies_without_binding = SerializerMethodField()
cached_policies = SerializerMethodField()
cached_flows = SerializerMethodField()
def get_version(self, _) -> str:
"""Get current version"""
return __version__
def get_version_latest(self, _) -> str:
"""Get latest version from cache"""
version_in_cache = cache.get(VERSION_CACHE_KEY)
if not version_in_cache:
update_latest_version.delay()
return __version__
return version_in_cache
def get_worker_count(self, _) -> int:
"""Ping workers"""
return len(CELERY_APP.control.ping(timeout=0.5))
def get_providers_without_application(self, _) -> int:
"""Count of providers without application"""
return len(Provider.objects.filter(application=None))
def get_policies_without_binding(self, _) -> int:
"""Count of policies not bound or use in prompt stages"""
return len(
Policy.objects.filter(bindings__isnull=True, promptstage__isnull=True)
)
def get_cached_policies(self, _) -> int:
"""Get cached policy count"""
return len(cache.keys("policy_*"))
def get_cached_flows(self, _) -> int:
"""Get cached flow count"""
return len(cache.keys("flow_*"))
def create(self, request: Request) -> Response:
raise NotImplementedError
def update(self, request: Request) -> Response:
raise NotImplementedError
class AdministrationOverviewViewSet(ViewSet):
"""Return single instance of AdministrationOverviewSerializer"""
permission_classes = [IsAdminUser]
@swagger_auto_schema(responses={200: AdministrationOverviewSerializer(many=True)})
def list(self, request: Request) -> Response:
"""Return single instance of AdministrationOverviewSerializer"""
serializer = AdministrationOverviewSerializer(True)
return Response(serializer.data)

View File

@ -66,7 +66,7 @@ class TaskViewSet(ViewSet):
"successful": True, "successful": True,
} }
) )
except ImportError: except ImportError: # pragma: no cover
# if we get an import error, the module path has probably changed # if we get an import error, the module path has probably changed
task.delete() task.delete()
return Response({"successful": False}) return Response({"successful": False})

View File

@ -0,0 +1,60 @@
"""authentik administration overview"""
from django.core.cache import cache
from drf_yasg2.utils import swagger_auto_schema
from packaging.version import parse
from rest_framework.fields import SerializerMethodField
from rest_framework.mixins import ListModelMixin
from rest_framework.permissions import IsAdminUser
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import Serializer
from rest_framework.viewsets import GenericViewSet
from authentik import __version__
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
class VersionSerializer(Serializer):
"""Get running and latest version."""
version_current = SerializerMethodField()
version_latest = SerializerMethodField()
outdated = SerializerMethodField()
def get_version_current(self, _) -> str:
"""Get current version"""
return __version__
def get_version_latest(self, _) -> str:
"""Get latest version from cache"""
version_in_cache = cache.get(VERSION_CACHE_KEY)
if not version_in_cache: # pragma: no cover
update_latest_version.delay()
return __version__
return version_in_cache
def get_outdated(self, instance) -> bool:
"""Check if we're running the latest version"""
return parse(self.get_version_current(instance)) < parse(
self.get_version_latest(instance)
)
def create(self, request: Request) -> Response:
raise NotImplementedError
def update(self, request: Request) -> Response:
raise NotImplementedError
class VersionViewSet(ListModelMixin, GenericViewSet):
"""Get running and latest version."""
permission_classes = [IsAdminUser]
def get_queryset(self):
return None
@swagger_auto_schema(responses={200: VersionSerializer(many=True)})
def list(self, request: Request) -> Response:
"""Get running and latest version."""
return Response(VersionSerializer(True).data)

View File

@ -0,0 +1,25 @@
"""authentik administration overview"""
from rest_framework.mixins import ListModelMixin
from rest_framework.permissions import IsAdminUser
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import Serializer
from rest_framework.viewsets import GenericViewSet
from authentik.root.celery import CELERY_APP
class WorkerViewSet(ListModelMixin, GenericViewSet):
"""Get currently connected worker count."""
serializer_class = Serializer
permission_classes = [IsAdminUser]
def get_queryset(self):
return None
def list(self, request: Request) -> Response:
"""Get currently connected worker count."""
return Response(
{"pagination": {"count": len(CELERY_APP.control.ping(timeout=0.5))}}
)

View File

@ -1,230 +0,0 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load static %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>{% trans 'System Overview' %}</h1>
</div>
</section>
<section class="pf-c-page__main-section">
<div class="pf-l-gallery pf-m-gutter">
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" style="grid-column-end: span 3;grid-row-end: span 2;">
<div class="pf-c-card__header">
<div class="pf-c-card__header-main">
<i class="pf-icon pf-icon-server"></i> {% trans 'Logins over the last 24 hours' %}
</div>
</div>
<div class="pf-c-card__body">
<ak-admin-logins-chart url="{% url 'authentik_api:admin_metrics-list' %}"></ak-admin-logins-chart>
</div>
</div>
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" style="grid-column-end: span 2;grid-row-end: span 3;">
<div class="pf-c-card__header">
<div class="pf-c-card__header-main">
<i class="pf-icon pf-icon-server"></i> {% trans 'Apps with most usage' %}
</div>
</div>
<div class="pf-c-card__body">
<table class="pf-c-table pf-m-compact" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Application' %}</th>
<th role="columnheader" scope="col">{% trans 'Logins' %}</th>
<th role="columnheader" scope="col"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for app in most_used_applications %}
<tr role="row">
<td role="cell">
{{ app.application.name }}
</td>
<td role="cell">
{{ app.total_logins }}
</td>
<td role="cell">
<progress value="{{ app.total_logins }}" max="{{ most_used_applications.0.total_logins }}"></progress>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
<div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
<div class="pf-c-card__header-main">
<i class="pf-icon pf-icon-plugged"></i> {% trans 'Providers' %}
</div>
<a href="{% url 'authentik_admin:providers' %}">
<i class="fa fa-external-link-alt"> </i>
</a>
</div>
<div class="pf-c-card__body">
{% if providers_without_application.exists %}
<p class="ak-aggregate-card">
<i class="fa fa-exclamation-triangle"></i> {{ provider_count }}
</p>
<p>{% trans 'Warning: At least one Provider has no application assigned.' %}</p>
{% else %}
<p class="ak-aggregate-card">
<i class="fa fa-check-circle"></i> {{ provider_count }}
</p>
{% endif %}
</div>
</div>
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
<div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
<div class="pf-c-card__header-main">
<i class="pf-icon pf-icon-infrastructure"></i> {% trans 'Policies' %}
</div>
<a href="{% url 'authentik_admin:policies' %}">
<i class="fa fa-external-link-alt"> </i>
</a>
</div>
<div class="pf-c-card__body">
{% if policies_without_binding %}
<p class="ak-aggregate-card">
<i class="fa fa-exclamation-triangle"></i> {{ policy_count }}
</p>
<p>{% trans 'Policies without binding exist.' %}</p>
{% else %}
<p class="ak-aggregate-card">
<i class="fa fa-check-circle"></i> {{ policy_count }}
</p>
{% endif %}
</div>
</div>
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
<div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
<div class="pf-c-card__header-main">
<i class="pf-icon pf-icon-user"></i> {% trans 'Users' %}
</div>
<a href="{% url 'authentik_admin:users' %}">
<i class="fa fa-external-link-alt"> </i>
</a>
</div>
<div class="pf-c-card__body">
<p class="ak-aggregate-card">
<i class="fa fa-check-circle"></i> {{ user_count }}
</p>
</div>
</div>
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
<div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
<div class="pf-c-card__header-main">
<i class="pf-icon pf-icon-bundle"></i> {% trans 'Version' %}
</div>
<a href="https://github.com/BeryJu/authentik/releases" target="_blank">
<i class="fa fa-external-link-alt"> </i>
</a>
</div>
<div class="pf-c-card__body">
<p class="ak-aggregate-card">
{% if version >= version_latest %}
<i class="fa fa-check-circle"></i> {{ version }}
{% else %}
<i class="fa fa-exclamation-triangle"></i> {{ version }}
{% endif %}
</p>
{% if version >= version_latest %}
{% blocktrans %}
Up-to-date!
{% endblocktrans %}
{% else %}
{% blocktrans with latest=version_latest %}
{{ latest }} is available!
{% endblocktrans %}
{% endif %}
</div>
</div>
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
<div class="pf-c-card__header">
<div class="pf-c-card__header-main">
<i class="pf-icon pf-icon-server"></i> {% trans 'Workers' %}
</div>
</div>
<fetch-fill-slot class="pf-c-card__body" url="{% url 'authentik_api:admin_overview-list' %}" key="worker_count">
<div slot="value < 1">
<p class="ak-aggregate-card">
<i class="fa fa-exclamation-triangle"></i> <span data-value></span>
</p>
<p>{% trans 'No workers connected.' %}</p>
</div>
<div slot="value >= 1">
<p class="ak-aggregate-card">
<i class="fa fa-check-circle"></i> <span data-value></span>
</p>
</div>
<div>
<span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>
</div>
</fetch-fill-slot>
</div>
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
<div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
<div class="pf-c-card__header-main">
<i class="pf-icon pf-icon-server"></i> {% trans 'Cached Policies' %}
</div>
<ak-modal-button href="{% url 'authentik_admin:overview-clear-policy-cache' %}">
<a slot="trigger">
<i class="fa fa-trash"> </i>
</a>
<div slot="modal"></div>
</ak-modal-button>
</div>
<div class="pf-c-card__body">
{% if cached_policies < 1 %}
<p class="ak-aggregate-card">
<i class="fa fa-exclamation-triangle"></i> {{ cached_policies }}
</p>
<p>{% trans 'No policies cached. Users may experience slow response times.' %}</p>
{% else %}
<p class="ak-aggregate-card">
<i class="fa fa-check-circle"></i> {{ cached_policies }}
</p>
{% endif %}
</div>
</div>
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
<div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
<div class="pf-c-card__header-main">
<i class="pf-icon pf-icon-server"></i> {% trans 'Cached Flows' %}
</div>
<ak-modal-button href="{% url 'authentik_admin:overview-clear-flow-cache' %}">
<a slot="trigger">
<i class="fa fa-trash"> </i>
</a>
<div slot="modal"></div>
</ak-modal-button>
</div>
<div class="pf-c-card__body">
{% if cached_flows < 1 %}
<p class="ak-aggregate-card">
<span class="fa fa-exclamation-triangle"></span> {{ cached_flows }}
</p>
<p>{% trans 'No flows cached.' %}</p>
{% else %}
<p class="ak-aggregate-card">
<i class="fa fa-check-circle"></i> {{ cached_flows }}
</p>
{% endif %}
</div>
</div>
</div>
</section>
{% endblock %}

View File

@ -81,7 +81,7 @@
<div slot="modal"></div> <div slot="modal"></div>
</ak-modal-button> </ak-modal-button>
<ak-modal-button href="{% url 'authentik_admin:policy-test' pk=policy.pk %}"> <ak-modal-button href="{% url 'authentik_admin:policy-test' pk=policy.pk %}">
<ak-spinner-button slot="trigger" class="pf-m-tertiary"> <ak-spinner-button slot="trigger" class="pf-m-secondary">
{% trans 'Test' %} {% trans 'Test' %}
</ak-spinner-button> </ak-spinner-button>
<div slot="modal"></div> <div slot="modal"></div>

View File

@ -37,8 +37,9 @@
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid"> <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead> <thead>
<tr role="row"> <tr role="row">
<th role="columnheader" scope="col">{% trans 'ID' %}</th>
<th role="columnheader" scope="col">{% trans 'Created by' %}</th>
<th role="columnheader" scope="col">{% trans 'Expiry' %}</th> <th role="columnheader" scope="col">{% trans 'Expiry' %}</th>
<th role="columnheader" scope="col">{% trans 'Link' %}</th>
<th role="cell"></th> <th role="cell"></th>
</tr> </tr>
</thead> </thead>
@ -47,12 +48,17 @@
<tr role="row"> <tr role="row">
<td role="cell"> <td role="cell">
<span> <span>
{{ invitation.expiry }} {{ invitation.invite_uuid }}
</span> </span>
</td> </td>
<td role="cell"> <td role="cell">
<span> <span>
{{ invitation.Link }} {{ invitation.created_by }}
</span>
</td>
<td role="cell">
<span>
{{ invitation.expiry|default:"-" }}
</span> </span>
</td> </td>
<td> <td>

View File

@ -6,6 +6,7 @@ from django.test import TestCase
from authentik import __version__ from authentik import __version__
from authentik.core.models import Group, User from authentik.core.models import Group, User
from authentik.core.tasks import clean_expired_models
class TestAdminAPI(TestCase): class TestAdminAPI(TestCase):
@ -19,19 +20,54 @@ class TestAdminAPI(TestCase):
self.group.save() self.group.save()
self.client.force_login(self.user) self.client.force_login(self.user)
def test_overview(self): def test_tasks(self):
"""Test Overview API""" """Test Task API"""
response = self.client.get(reverse("authentik_api:admin_overview-list")) clean_expired_models.delay()
response = self.client.get(reverse("authentik_api:admin_system_tasks-list"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
body = loads(response.content) body = loads(response.content)
self.assertEqual(body["version"], __version__) self.assertTrue(
any([task["task_name"] == "clean_expired_models" for task in body])
)
def test_tasks_retry(self):
"""Test Task API (retry)"""
clean_expired_models.delay()
response = self.client.post(
reverse(
"authentik_api:admin_system_tasks-retry",
kwargs={"pk": "clean_expired_models"},
)
)
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertTrue(body["successful"])
def test_tasks_retry_404(self):
"""Test Task API (retry, 404)"""
response = self.client.post(
reverse(
"authentik_api:admin_system_tasks-retry",
kwargs={"pk": "qwerqewrqrqewrqewr"},
)
)
self.assertEqual(response.status_code, 404)
def test_version(self):
"""Test Version API"""
response = self.client.get(reverse("authentik_api:admin_version-list"))
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertEqual(body["version_current"], __version__)
def test_workers(self):
"""Test Workers API"""
response = self.client.get(reverse("authentik_api:admin_workers-list"))
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertEqual(body["pagination"]["count"], 0)
def test_metrics(self): def test_metrics(self):
"""Test metrics API""" """Test metrics API"""
response = self.client.get(reverse("authentik_api:admin_metrics-list")) response = self.client.get(reverse("authentik_api:admin_metrics-list"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_tasks(self):
"""Test tasks metrics API"""
response = self.client.get(reverse("authentik_api:admin_system_tasks-list"))
self.assertEqual(response.status_code, 200)

View File

@ -34,7 +34,6 @@ urlpatterns = [
overview.PolicyCacheClearView.as_view(), overview.PolicyCacheClearView.as_view(),
name="overview-clear-policy-cache", name="overview-clear-policy-cache",
), ),
path("overview/", overview.AdministrationOverviewView.as_view(), name="overview"),
# Applications # Applications
path( path(
"applications/", applications.ApplicationListView.as_view(), name="applications" "applications/", applications.ApplicationListView.as_view(), name="applications"

View File

@ -1,65 +1,25 @@
"""authentik administration overview""" """authentik administration overview"""
from typing import Union
from django.conf import settings
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.core.cache import cache from django.core.cache import cache
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.http.response import HttpResponse from django.http.response import HttpResponse
from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import FormView, TemplateView from django.views.generic import FormView
from packaging.version import LegacyVersion, Version, parse
from structlog import get_logger from structlog import get_logger
from authentik import __version__
from authentik.admin.forms.overview import FlowCacheClearForm, PolicyCacheClearForm from authentik.admin.forms.overview import FlowCacheClearForm, PolicyCacheClearForm
from authentik.admin.mixins import AdminRequiredMixin from authentik.admin.mixins import AdminRequiredMixin
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
from authentik.core.models import Provider, User
from authentik.policies.models import Policy
LOGGER = get_logger() LOGGER = get_logger()
class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
"""Overview View"""
template_name = "administration/overview.html"
def get_latest_version(self) -> Union[LegacyVersion, Version]:
"""Get latest version from cache"""
version_in_cache = cache.get(VERSION_CACHE_KEY)
if not version_in_cache:
if not settings.DEBUG:
update_latest_version.delay()
return parse(__version__)
return parse(version_in_cache)
def get_context_data(self, **kwargs):
kwargs["policy_count"] = len(Policy.objects.all())
kwargs["user_count"] = len(User.objects.all()) - 1 # Remove anonymous user
kwargs["provider_count"] = len(Provider.objects.all())
kwargs["version"] = parse(__version__)
kwargs["version_latest"] = self.get_latest_version()
kwargs["providers_without_application"] = Provider.objects.filter(
application=None
)
kwargs["policies_without_binding"] = len(
Policy.objects.filter(bindings__isnull=True, promptstage__isnull=True)
)
kwargs["cached_policies"] = len(cache.keys("policy_*"))
kwargs["cached_flows"] = len(cache.keys("flow_*"))
return super().get_context_data(**kwargs)
class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView): class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
"""View to clear Policy cache""" """View to clear Policy cache"""
form_class = PolicyCacheClearForm form_class = PolicyCacheClearForm
template_name = "generic/form_non_model.html" template_name = "generic/form_non_model.html"
success_url = reverse_lazy("authentik_admin:overview") success_url = "/"
success_message = _("Successfully cleared Policy cache") success_message = _("Successfully cleared Policy cache")
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
@ -75,7 +35,7 @@ class FlowCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
form_class = FlowCacheClearForm form_class = FlowCacheClearForm
template_name = "generic/form_non_model.html" template_name = "generic/form_non_model.html"
success_url = reverse_lazy("authentik_admin:overview") success_url = "/"
success_message = _("Successfully cleared Flow cache") success_message = _("Successfully cleared Flow cache")
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:

View File

@ -5,9 +5,10 @@ from drf_yasg2.views import get_schema_view
from rest_framework import routers from rest_framework import routers
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from authentik.admin.api.overview import AdministrationOverviewViewSet from authentik.admin.api.metrics import AdministrationMetricsViewSet
from authentik.admin.api.overview_metrics import AdministrationMetricsViewSet
from authentik.admin.api.tasks import TaskViewSet from authentik.admin.api.tasks import TaskViewSet
from authentik.admin.api.version import VersionViewSet
from authentik.admin.api.workers import WorkerViewSet
from authentik.api.v2.config import ConfigsViewSet from authentik.api.v2.config import ConfigsViewSet
from authentik.api.v2.messages import MessagesViewSet from authentik.api.v2.messages import MessagesViewSet
from authentik.audit.api import EventViewSet from authentik.audit.api import EventViewSet
@ -19,13 +20,22 @@ from authentik.core.api.sources import SourceViewSet
from authentik.core.api.tokens import TokenViewSet from authentik.core.api.tokens import TokenViewSet
from authentik.core.api.users import UserViewSet from authentik.core.api.users import UserViewSet
from authentik.crypto.api import CertificateKeyPairViewSet from authentik.crypto.api import CertificateKeyPairViewSet
from authentik.flows.api import FlowStageBindingViewSet, FlowViewSet, StageViewSet from authentik.flows.api import (
FlowCacheViewSet,
FlowStageBindingViewSet,
FlowViewSet,
StageViewSet,
)
from authentik.outposts.api import ( from authentik.outposts.api import (
DockerServiceConnectionViewSet, DockerServiceConnectionViewSet,
KubernetesServiceConnectionViewSet, KubernetesServiceConnectionViewSet,
OutpostViewSet, OutpostViewSet,
) )
from authentik.policies.api import PolicyBindingViewSet, PolicyViewSet from authentik.policies.api import (
PolicyBindingViewSet,
PolicyCacheViewSet,
PolicyViewSet,
)
from authentik.policies.dummy.api import DummyPolicyViewSet from authentik.policies.dummy.api import DummyPolicyViewSet
from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet
from authentik.policies.expression.api import ExpressionPolicyViewSet from authentik.policies.expression.api import ExpressionPolicyViewSet
@ -63,9 +73,8 @@ router = routers.DefaultRouter()
router.register("root/messages", MessagesViewSet, basename="messages") router.register("root/messages", MessagesViewSet, basename="messages")
router.register("root/config", ConfigsViewSet, basename="configs") router.register("root/config", ConfigsViewSet, basename="configs")
router.register( router.register("admin/version", VersionViewSet, basename="admin_version")
"admin/overview", AdministrationOverviewViewSet, basename="admin_overview" router.register("admin/workers", WorkerViewSet, basename="admin_workers")
)
router.register("admin/metrics", AdministrationMetricsViewSet, basename="admin_metrics") router.register("admin/metrics", AdministrationMetricsViewSet, basename="admin_metrics")
router.register("admin/system_tasks", TaskViewSet, basename="admin_system_tasks") router.register("admin/system_tasks", TaskViewSet, basename="admin_system_tasks")
@ -82,6 +91,7 @@ router.register(
router.register("outposts/proxy", ProxyOutpostConfigViewSet) router.register("outposts/proxy", ProxyOutpostConfigViewSet)
router.register("flows/instances", FlowViewSet) router.register("flows/instances", FlowViewSet)
router.register("flows/cached", FlowCacheViewSet, basename="flows_cache")
router.register("flows/bindings", FlowStageBindingViewSet) router.register("flows/bindings", FlowStageBindingViewSet)
router.register("crypto/certificatekeypairs", CertificateKeyPairViewSet) router.register("crypto/certificatekeypairs", CertificateKeyPairViewSet)
@ -94,6 +104,7 @@ router.register("sources/saml", SAMLSourceViewSet)
router.register("sources/oauth", OAuthSourceViewSet) router.register("sources/oauth", OAuthSourceViewSet)
router.register("policies/all", PolicyViewSet) router.register("policies/all", PolicyViewSet)
router.register("policies/cached", PolicyCacheViewSet, basename="policies_cache")
router.register("policies/bindings", PolicyBindingViewSet) router.register("policies/bindings", PolicyBindingViewSet)
router.register("policies/expression", ExpressionPolicyViewSet) router.register("policies/expression", ExpressionPolicyViewSet)
router.register("policies/group_membership", GroupMembershipPolicyViewSet) router.register("policies/group_membership", GroupMembershipPolicyViewSet)

View File

@ -11,8 +11,9 @@ from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from rest_framework_guardian.filters import ObjectPermissionsFilter from rest_framework_guardian.filters import ObjectPermissionsFilter
from authentik.admin.api.overview_metrics import get_events_per_1h from authentik.admin.api.metrics import get_events_per_1h
from authentik.audit.models import EventAction from authentik.audit.models import EventAction
from authentik.core.api.providers import ProviderSerializer
from authentik.core.models import Application from authentik.core.models import Application
from authentik.policies.engine import PolicyEngine from authentik.policies.engine import PolicyEngine
@ -21,6 +22,7 @@ class ApplicationSerializer(ModelSerializer):
"""Application Serializer""" """Application Serializer"""
launch_url = SerializerMethodField() launch_url = SerializerMethodField()
provider = ProviderSerializer(source="get_provider", required=False)
def get_launch_url(self, instance: Application) -> str: def get_launch_url(self, instance: Application) -> str:
"""Get generated launch URL""" """Get generated launch URL"""

View File

@ -1,6 +1,6 @@
"""Provider API Views""" """Provider API Views"""
from rest_framework.serializers import ModelSerializer, SerializerMethodField from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.core.models import Provider from authentik.core.models import Provider
@ -14,17 +14,33 @@ class ProviderSerializer(ModelSerializer):
"""Get object type so that we know which API Endpoint to use to get the full object""" """Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace("provider", "") return obj._meta.object_name.lower().replace("provider", "")
def to_representation(self, instance: Provider):
# pyright: reportGeneralTypeIssues=false
if instance.__class__ == Provider:
return super().to_representation(instance)
return instance.serializer(instance=instance).data
class Meta: class Meta:
model = Provider model = Provider
fields = ["pk", "name", "authorization_flow", "property_mappings", "__type__"] fields = [
"pk",
"name",
"application",
"authorization_flow",
"property_mappings",
"__type__",
]
class ProviderViewSet(ReadOnlyModelViewSet): class ProviderViewSet(ModelViewSet):
"""Provider Viewset""" """Provider Viewset"""
queryset = Provider.objects.all() queryset = Provider.objects.all()
serializer_class = ProviderSerializer serializer_class = ProviderSerializer
filterset_fields = {
"application": ["isnull"],
}
def get_queryset(self): def get_queryset(self):
return Provider.objects.select_subclasses() return Provider.objects.select_subclasses()

View File

@ -1,5 +1,6 @@
"""User API Views""" """User API Views"""
from drf_yasg2.utils import swagger_auto_schema from drf_yasg2.utils import swagger_auto_schema
from guardian.utils import get_anonymous_user
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
@ -33,9 +34,12 @@ class UserSerializer(ModelSerializer):
class UserViewSet(ModelViewSet): class UserViewSet(ModelViewSet):
"""User Viewset""" """User Viewset"""
queryset = User.objects.all() queryset = User.objects.all().exclude(pk=get_anonymous_user().pk)
serializer_class = UserSerializer serializer_class = UserSerializer
def get_queryset(self):
return User.objects.all().exclude(pk=get_anonymous_user().pk)
@swagger_auto_schema(responses={200: UserSerializer(many=False)}) @swagger_auto_schema(responses={200: UserSerializer(many=False)})
@action(detail=False) @action(detail=False)
# pylint: disable=invalid-name # pylint: disable=invalid-name

View File

@ -14,6 +14,7 @@ from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from guardian.mixins import GuardianUserMixin from guardian.mixins import GuardianUserMixin
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from rest_framework.serializers import Serializer
from structlog import get_logger from structlog import get_logger
from authentik.core.exceptions import PropertyMappingExpressionException from authentik.core.exceptions import PropertyMappingExpressionException
@ -127,7 +128,7 @@ class User(GuardianUserMixin, AbstractUser):
verbose_name_plural = _("Users") verbose_name_plural = _("Users")
class Provider(models.Model): class Provider(SerializerModel):
"""Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application""" """Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application"""
name = models.TextField() name = models.TextField()
@ -156,6 +157,11 @@ class Provider(models.Model):
"""Return Form class used to edit this object""" """Return Form class used to edit this object"""
raise NotImplementedError raise NotImplementedError
@property
def serializer(self) -> Type[Serializer]:
"""Get serializer for this model"""
raise NotImplementedError
def __str__(self): def __str__(self):
return self.name return self.name

View File

@ -3,6 +3,15 @@
{% load i18n %} {% load i18n %}
{% load authentik_utils %} {% load authentik_utils %}
{% block head %}
{{ block.super }}
<style>
.pf-c-empty-state {
height: 100vh;
}
</style>
{% endblock %}
{% block body %} {% block body %}
<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl"> <section class="pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<div class="pf-c-empty-state"> <div class="pf-c-empty-state">

View File

@ -31,7 +31,7 @@
<p class="pf-c-form__helper-text">{{ field.help_text }}</p> <p class="pf-c-form__helper-text">{{ field.help_text }}</p>
{% endif %} {% endif %}
</div> </div>
{% elif field.field.widget|fieldtype == 'Select' %} {% elif field.field.widget|fieldtype == 'Select' or field.field.widget|fieldtype == "SelectMultiple" %}
<div class="pf-c-form__group-label"> <div class="pf-c-form__group-label">
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}"> <label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">
<span class="pf-c-form__label-text">{{ field.label }}</span> <span class="pf-c-form__label-text">{{ field.label }}</span>
@ -46,6 +46,9 @@
{% if field.help_text %} {% if field.help_text %}
<p class="pf-c-form__helper-text">{{ field.help_text|safe }}</p> <p class="pf-c-form__helper-text">{{ field.help_text|safe }}</p>
{% endif %} {% endif %}
{% if field.field.widget|fieldtype == 'SelectMultiple' %}
<p class="pf-c-form__helper-text">{% trans 'Hold control/command to select multiple items.' %}</p>
{% endif %}
</div> </div>
</div> </div>
{% elif field.field.widget|fieldtype == 'CheckboxInput' %} {% elif field.field.widget|fieldtype == 'CheckboxInput' %}

View File

@ -1,7 +1,14 @@
"""Flow API Views""" """Flow API Views"""
from django.core.cache import cache from django.core.cache import cache
from rest_framework.serializers import ModelSerializer, SerializerMethodField from rest_framework.mixins import ListModelMixin
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import (
ModelSerializer,
Serializer,
SerializerMethodField,
)
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
from authentik.flows.models import Flow, FlowStageBinding, Stage from authentik.flows.models import Flow, FlowStageBinding, Stage
from authentik.flows.planner import cache_key from authentik.flows.planner import cache_key
@ -98,3 +105,14 @@ class FlowStageBindingViewSet(ModelViewSet):
queryset = FlowStageBinding.objects.all() queryset = FlowStageBinding.objects.all()
serializer_class = FlowStageBindingSerializer serializer_class = FlowStageBindingSerializer
filterset_fields = "__all__" filterset_fields = "__all__"
class FlowCacheViewSet(ListModelMixin, GenericViewSet):
"""Info about cached flows"""
queryset = Flow.objects.none()
serializer_class = Serializer
def list(self, request: Request) -> Response:
"""Info about cached flows"""
return Response(data={"pagination": {"count": len(cache.keys("flow_*"))}})

View File

@ -19,6 +19,7 @@ LOGGER = get_logger()
PLAN_CONTEXT_PENDING_USER = "pending_user" PLAN_CONTEXT_PENDING_USER = "pending_user"
PLAN_CONTEXT_SSO = "is_sso" PLAN_CONTEXT_SSO = "is_sso"
PLAN_CONTEXT_REDIRECT = "redirect"
PLAN_CONTEXT_APPLICATION = "application" PLAN_CONTEXT_APPLICATION = "application"

View File

@ -8,7 +8,7 @@ from django.test.client import RequestFactory
from django.utils.encoding import force_str from django.utils.encoding import force_str
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException from authentik.flows.exceptions import FlowNonApplicableException
from authentik.flows.markers import ReevaluateMarker, StageMarker from authentik.flows.markers import ReevaluateMarker, StageMarker
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
from authentik.flows.planner import FlowPlan, FlowPlanner from authentik.flows.planner import FlowPlan, FlowPlanner
@ -40,6 +40,10 @@ class TestFlowExecutor(TestCase):
def setUp(self): def setUp(self):
self.request_factory = RequestFactory() self.request_factory = RequestFactory()
@patch(
"authentik.flows.views.to_stage_response",
TO_STAGE_RESPONSE_MOCK,
)
def test_existing_plan_diff_flow(self): def test_existing_plan_diff_flow(self):
"""Check that a plan for a different flow cancels the current plan""" """Check that a plan for a different flow cancels the current plan"""
flow = Flow.objects.create( flow = Flow.objects.create(
@ -62,7 +66,7 @@ class TestFlowExecutor(TestCase):
"authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug} "authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug}
), ),
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 302)
self.assertEqual(cancel_mock.call_count, 2) self.assertEqual(cancel_mock.call_count, 2)
@patch( @patch(
@ -105,10 +109,13 @@ class TestFlowExecutor(TestCase):
response = self.client.get( response = self.client.get(
reverse("authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug}), reverse("authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug}),
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 302)
self.assertIsInstance(response, AccessDeniedResponse) self.assertEqual(response.url, reverse("authentik_core:shell"))
self.assertInHTML(EmptyFlowException.__doc__, response.rendered_content)
@patch(
"authentik.flows.views.to_stage_response",
TO_STAGE_RESPONSE_MOCK,
)
def test_invalid_flow_redirect(self): def test_invalid_flow_redirect(self):
"""Tests that an invalid flow still redirects""" """Tests that an invalid flow still redirects"""
flow = Flow.objects.create( flow = Flow.objects.create(
@ -121,11 +128,8 @@ class TestFlowExecutor(TestCase):
dest = "/unique-string" dest = "/unique-string"
url = reverse("authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug}) url = reverse("authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug})
response = self.client.get(url + f"?{NEXT_ARG_NAME}={dest}") response = self.client.get(url + f"?{NEXT_ARG_NAME}={dest}")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 302)
self.assertJSONEqual( self.assertEqual(response.url, reverse("authentik_core:shell"))
force_str(response.content),
{"type": "redirect", "to": dest},
)
def test_multi_stage_flow(self): def test_multi_stage_flow(self):
"""Test a full flow with multiple stages""" """Test a full flow with multiple stages"""
@ -161,6 +165,10 @@ class TestFlowExecutor(TestCase):
plan: FlowPlan = session[SESSION_KEY_PLAN] plan: FlowPlan = session[SESSION_KEY_PLAN]
self.assertEqual(len(plan.stages), 1) self.assertEqual(len(plan.stages), 1)
@patch(
"authentik.flows.views.to_stage_response",
TO_STAGE_RESPONSE_MOCK,
)
def test_reevaluate_remove_last(self): def test_reevaluate_remove_last(self):
"""Test planner with re-evaluate (last stage is removed)""" """Test planner with re-evaluate (last stage is removed)"""
flow = Flow.objects.create( flow = Flow.objects.create(

View File

@ -21,7 +21,12 @@ from authentik.audit.models import cleanse_dict
from authentik.core.models import USER_ATTRIBUTE_DEBUG from authentik.core.models import USER_ATTRIBUTE_DEBUG
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
from authentik.flows.models import ConfigurableStage, Flow, FlowDesignation, Stage from authentik.flows.models import ConfigurableStage, Flow, FlowDesignation, Stage
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan, FlowPlanner from authentik.flows.planner import (
PLAN_CONTEXT_PENDING_USER,
PLAN_CONTEXT_REDIRECT,
FlowPlan,
FlowPlanner,
)
from authentik.lib.utils.reflection import class_to_path from authentik.lib.utils.reflection import class_to_path
from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs
from authentik.policies.http import AccessDeniedResponse from authentik.policies.http import AccessDeniedResponse
@ -83,7 +88,9 @@ class FlowExecutorView(View):
return to_stage_response(self.request, self.handle_invalid_flow(exc)) return to_stage_response(self.request, self.handle_invalid_flow(exc))
except EmptyFlowException as exc: except EmptyFlowException as exc:
LOGGER.warning("f(exec): Flow is empty", exc=exc) LOGGER.warning("f(exec): Flow is empty", exc=exc)
return to_stage_response(self.request, self.handle_invalid_flow(exc)) # To match behaviour with loading an empty flow plan from cache,
# we don't show an error message here, but rather call _flow_done()
return self._flow_done()
# We don't save the Plan after getting the next stage # We don't save the Plan after getting the next stage
# as it hasn't been successfully passed yet # as it hasn't been successfully passed yet
next_stage = self.plan.next(self.request) next_stage = self.plan.next(self.request)
@ -143,11 +150,15 @@ class FlowExecutorView(View):
"""User Successfully passed all stages""" """User Successfully passed all stages"""
# Since this is wrapped by the ExecutorShell, the next argument is saved in the session # Since this is wrapped by the ExecutorShell, the next argument is saved in the session
# extract the next param before cancel as that cleans it # extract the next param before cancel as that cleans it
next_param = self.request.session.get(SESSION_KEY_GET, {}).get( next_param = None
NEXT_ARG_NAME, "authentik_core:shell" if self.plan:
) next_param = self.plan.context.get(PLAN_CONTEXT_REDIRECT)
if not next_param:
next_param = self.request.session.get(SESSION_KEY_GET, {}).get(
NEXT_ARG_NAME, "authentik_core:shell"
)
self.cancel() self.cancel()
return redirect_with_qs(next_param) return to_stage_response(self.request, redirect_with_qs(next_param))
def stage_ok(self) -> HttpResponse: def stage_ok(self) -> HttpResponse:
"""Callback called by stages upon successful completion. """Callback called by stages upon successful completion.

View File

@ -46,7 +46,7 @@ class SecretReconciler(KubernetesObjectReconciler[V1Secret]):
"authentik_host_insecure": b64string( "authentik_host_insecure": b64string(
str(self.controller.outpost.config.authentik_host_insecure) str(self.controller.outpost.config.authentik_host_insecure)
), ),
"token": b64string(self.controller.outpost.token.token_uuid.hex), "token": b64string(self.controller.outpost.token.key),
}, },
) )

View File

@ -1,7 +1,11 @@
"""Outpost forms""" """Outpost forms"""
from django import forms from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from kubernetes.client.configuration import Configuration
from kubernetes.config.config_exception import ConfigException
from kubernetes.config.kube_config import load_kube_config_from_dict
from authentik.admin.fields import CodeMirrorWidget, YAMLField from authentik.admin.fields import CodeMirrorWidget, YAMLField
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
@ -71,6 +75,23 @@ class DockerServiceConnectionForm(forms.ModelForm):
class KubernetesServiceConnectionForm(forms.ModelForm): class KubernetesServiceConnectionForm(forms.ModelForm):
"""Kubernetes service-connection form""" """Kubernetes service-connection form"""
def clean_kubeconfig(self):
"""Validate kubeconfig by attempting to load it"""
kubeconfig = self.cleaned_data["kubeconfig"]
if kubeconfig == {}:
if not self.cleaned_data["local"]:
raise ValidationError(
_("You can only use an empty kubeconfig when local is enabled.")
)
# Empty kubeconfig is valid
return kubeconfig
config = Configuration()
try:
load_kube_config_from_dict(kubeconfig, client_configuration=config)
except ConfigException:
raise ValidationError(_("Invalid kubeconfig"))
return kubeconfig
class Meta: class Meta:
model = KubernetesServiceConnection model = KubernetesServiceConnection

View File

@ -0,0 +1,21 @@
# Generated by Django 3.1.4 on 2020-12-24 12:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_outposts", "0014_auto_20201213_1407"),
]
operations = [
migrations.AlterField(
model_name="kubernetesserviceconnection",
name="kubeconfig",
field=models.JSONField(
blank=True,
help_text="Paste your kubeconfig here. authentik will automatically use the currently selected context.",
),
),
]

View File

@ -113,17 +113,24 @@ class OutpostServiceConnection(models.Model):
objects = InheritanceManager() objects = InheritanceManager()
@property
def state_key(self) -> str:
"""Key used to save connection state in cache"""
return f"outpost_service_connection_{self.pk.hex}"
@property @property
def state(self) -> OutpostServiceConnectionState: def state(self) -> OutpostServiceConnectionState:
"""Get state of service connection""" """Get state of service connection"""
state_key = f"outpost_service_connection_{self.pk.hex}" from authentik.outposts.tasks import outpost_service_connection_state
state = cache.get(state_key, None)
state = cache.get(self.state_key, None)
if not state: if not state:
state = self._get_state() outpost_service_connection_state.delay(self.pk)
cache.set(state_key, state, timeout=0) return OutpostServiceConnectionState("", False)
return state return state
def _get_state(self) -> OutpostServiceConnectionState: def fetch_state(self) -> OutpostServiceConnectionState:
"""Fetch current Service Connection state"""
raise NotImplementedError raise NotImplementedError
@property @property
@ -203,7 +210,7 @@ class DockerServiceConnection(OutpostServiceConnection):
raise ServiceConnectionInvalid from exc raise ServiceConnectionInvalid from exc
return client return client
def _get_state(self) -> OutpostServiceConnectionState: def fetch_state(self) -> OutpostServiceConnectionState:
try: try:
client = self.client() client = self.client()
return OutpostServiceConnectionState( return OutpostServiceConnectionState(
@ -227,7 +234,8 @@ class KubernetesServiceConnection(OutpostServiceConnection):
"Paste your kubeconfig here. authentik will automatically use " "Paste your kubeconfig here. authentik will automatically use "
"the currently selected context." "the currently selected context."
) )
) ),
blank=True,
) )
@property @property
@ -239,7 +247,7 @@ class KubernetesServiceConnection(OutpostServiceConnection):
def __str__(self) -> str: def __str__(self) -> str:
return f"Kubernetes Service-Connection {self.name}" return f"Kubernetes Service-Connection {self.name}"
def _get_state(self) -> OutpostServiceConnectionState: def fetch_state(self) -> OutpostServiceConnectionState:
try: try:
client = self.client() client = self.client()
api_instance = VersionApi(client) api_instance = VersionApi(client)
@ -247,7 +255,7 @@ class KubernetesServiceConnection(OutpostServiceConnection):
return OutpostServiceConnectionState( return OutpostServiceConnectionState(
version=version.git_version, healthy=True version=version.git_version, healthy=True
) )
except (OpenApiException, HTTPError): except (OpenApiException, HTTPError, ServiceConnectionInvalid):
return OutpostServiceConnectionState(version="", healthy=False) return OutpostServiceConnectionState(version="", healthy=False)
def client(self) -> ApiClient: def client(self) -> ApiClient:

View File

@ -35,21 +35,22 @@ def outpost_controller_all():
@CELERY_APP.task() @CELERY_APP.task()
def outpost_service_connection_state(state_pk: Any): def outpost_service_connection_state(connection_pk: Any):
"""Update cached state of a service connection""" """Update cached state of a service connection"""
connection: OutpostServiceConnection = ( connection: OutpostServiceConnection = (
OutpostServiceConnection.objects.filter(pk=state_pk).select_subclasses().first() OutpostServiceConnection.objects.filter(pk=connection_pk)
.select_subclasses()
.first()
) )
cache.delete(f"outpost_service_connection_{connection.pk.hex}") state = connection.fetch_state()
_ = connection.state cache.set(connection.state_key, state, timeout=None)
@CELERY_APP.task(bind=True, base=MonitoredTask) @CELERY_APP.task(bind=True, base=MonitoredTask)
def outpost_service_connection_monitor(self: MonitoredTask): def outpost_service_connection_monitor(self: MonitoredTask):
"""Regularly check the state of Outpost Service Connections""" """Regularly check the state of Outpost Service Connections"""
for connection in OutpostServiceConnection.objects.select_subclasses(): for connection in OutpostServiceConnection.objects.all():
cache.delete(f"outpost_service_connection_{connection.pk.hex}") outpost_service_connection_state.delay(connection.pk)
_ = connection.state
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL)) self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL))

View File

@ -1,11 +1,16 @@
"""policy API Views""" """policy API Views"""
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from rest_framework.mixins import ListModelMixin
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ( from rest_framework.serializers import (
ModelSerializer, ModelSerializer,
PrimaryKeyRelatedField, PrimaryKeyRelatedField,
Serializer,
SerializerMethodField, SerializerMethodField,
) )
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
from authentik.policies.forms import GENERAL_FIELDS from authentik.policies.forms import GENERAL_FIELDS
from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel
@ -68,6 +73,10 @@ class PolicyViewSet(ReadOnlyModelViewSet):
queryset = Policy.objects.all() queryset = Policy.objects.all()
serializer_class = PolicySerializer serializer_class = PolicySerializer
filterset_fields = {
"bindings": ["isnull"],
"promptstage": ["isnull"],
}
def get_queryset(self): def get_queryset(self):
return Policy.objects.select_subclasses() return Policy.objects.select_subclasses()
@ -98,3 +107,14 @@ class PolicyBindingViewSet(ModelViewSet):
serializer_class = PolicyBindingSerializer serializer_class = PolicyBindingSerializer
filterset_fields = ["policy", "target", "enabled", "order", "timeout"] filterset_fields = ["policy", "target", "enabled", "order", "timeout"]
search_fields = ["policy__name"] search_fields = ["policy__name"]
class PolicyCacheViewSet(ListModelMixin, GenericViewSet):
"""Info about cached policies"""
queryset = Policy.objects.none()
serializer_class = Serializer
def list(self, request: Request) -> Response:
"""Info about cached policies"""
return Response(data={"pagination": {"count": len(cache.keys("policy_*"))}})

View File

@ -21,6 +21,7 @@ class PolicyEvaluator(BaseEvaluator):
def __init__(self, policy_name: str): def __init__(self, policy_name: str):
super().__init__() super().__init__()
self._messages = [] self._messages = []
self._context["ak_logger"] = get_logger(policy_name)
self._context["ak_message"] = self.expr_func_message self._context["ak_message"] = self.expr_func_message
self._context["ip_address"] = ip_address self._context["ip_address"] = ip_address
self._context["ip_network"] = ip_network self._context["ip_network"] = ip_network

View File

@ -7,7 +7,7 @@
<label for="" class="pf-c-form__label"></label> <label for="" class="pf-c-form__label"></label>
<div class="c-form__horizontal-group"> <div class="c-form__horizontal-group">
<p> <p>
Expression using Python. See <a target="_blank" href="https://goauthentik.io/policies/expression/">here</a> for a list of all variables. Expression using Python. See <a target="_blank" href="https://goauthentik.io/docs/policies/expression/">here</a> for a list of all variables.
</p> </p>
</div> </div>
</div> </div>

View File

@ -18,6 +18,7 @@ from django.utils import dateformat, timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from jwkest.jwk import Key, RSAKey, SYMKey, import_rsa_key from jwkest.jwk import Key, RSAKey, SYMKey, import_rsa_key
from jwkest.jws import JWS from jwkest.jws import JWS
from rest_framework.serializers import Serializer
from authentik.core.models import ExpiringModel, PropertyMapping, Provider, User from authentik.core.models import ExpiringModel, PropertyMapping, Provider, User
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
@ -263,6 +264,12 @@ class OAuth2Provider(Provider):
launch_url = urlparse(main_url) launch_url = urlparse(main_url)
return main_url.replace(launch_url.path, "") return main_url.replace(launch_url.path, "")
@property
def serializer(self) -> Type[Serializer]:
from authentik.providers.oauth2.api import OAuth2ProviderSerializer
return OAuth2ProviderSerializer
@property @property
def form(self) -> Type[ModelForm]: def form(self) -> Type[ModelForm]:
from authentik.providers.oauth2.forms import OAuth2ProviderForm from authentik.providers.oauth2.forms import OAuth2ProviderForm

View File

@ -4,6 +4,16 @@
{% load i18n %} {% load i18n %}
{% load authentik_utils %} {% load authentik_utils %}
{% block head %}
{{ block.super }}
<style>
.pf-c-background-image::before {
background-image: url("{% static 'dist/assets/images/flow_background.jpg' %}");
background-position: center;
}
</style>
{% endblock %}
{% block title %} {% block title %}
{% trans 'End session' %} {% trans 'End session' %}
{% endblock %} {% endblock %}

View File

@ -7,7 +7,7 @@
<label for="" class="pf-c-form__label"></label> <label for="" class="pf-c-form__label"></label>
<div class="c-form__horizontal-group"> <div class="c-form__horizontal-group">
<p> <p>
Expression using Python. See <a href="https://goauthentik.io/property-mappings/expression/">here</a> for a list of all variables. Expression using Python. See <a href="https://goauthentik.io/docs/property-mappings/expression/">here</a> for a list of all variables.
</p> </p>
</div> </div>
</div> </div>

View File

@ -1,34 +1,6 @@
# Generated by Django 3.1.1 on 2020-09-30 08:10 # Generated by Django 3.1.1 on 2020-09-30 08:10
from django.apps.registry import Apps
from django.db import migrations, models from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
SCOPE_AK_PROXY_EXPRESSION = """return {
"ak_proxy": {
"user_attributes": user.group_attributes()
}
}"""
def create_proxy_scope(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from authentik.providers.proxy.models import SCOPE_AK_PROXY, ProxyProvider
ScopeMapping = apps.get_model("authentik_providers_oauth2", "ScopeMapping")
ScopeMapping.objects.update_or_create(
scope_name=SCOPE_AK_PROXY,
defaults={
"name": "Autogenerated OAuth2 Mapping: authentik Proxy",
"scope_name": SCOPE_AK_PROXY,
"description": "",
"expression": SCOPE_AK_PROXY_EXPRESSION,
},
)
for provider in ProxyProvider.objects.all():
provider.set_oauth_defaults()
provider.save()
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -74,5 +46,4 @@ class Migration(migrations.Migration):
verbose_name="HTTP-Basic Username", verbose_name="HTTP-Basic Username",
), ),
), ),
migrations.RunPython(create_proxy_scope),
] ]

View File

@ -0,0 +1,41 @@
# Generated by Django 3.1.4 on 2020-12-14 09:42
from django.apps.registry import Apps
from django.db import migrations
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
SCOPE_AK_PROXY_EXPRESSION = """return {
"ak_proxy": {
"user_attributes": user.group_attributes()
}
}"""
def create_proxy_scope(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from authentik.providers.proxy.models import SCOPE_AK_PROXY, ProxyProvider
ScopeMapping = apps.get_model("authentik_providers_oauth2", "ScopeMapping")
ScopeMapping.objects.filter(scope_name="pb_proxy").delete()
ScopeMapping.objects.update_or_create(
scope_name=SCOPE_AK_PROXY,
defaults={
"name": "Autogenerated OAuth2 Mapping: authentik Proxy",
"scope_name": SCOPE_AK_PROXY,
"description": "",
"expression": SCOPE_AK_PROXY_EXPRESSION,
},
)
for provider in ProxyProvider.objects.all():
provider.set_oauth_defaults()
provider.save()
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_proxy", "0009_auto_20201007_1721"),
]
operations = [migrations.RunPython(create_proxy_scope)]

View File

@ -8,6 +8,7 @@ from django.db import models
from django.forms import ModelForm from django.forms import ModelForm
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from rest_framework.serializers import Serializer
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.lib.models import DomainlessURLValidator from authentik.lib.models import DomainlessURLValidator
@ -108,6 +109,12 @@ class ProxyProvider(OutpostModel, OAuth2Provider):
return ProxyProviderForm return ProxyProviderForm
@property
def serializer(self) -> Type[Serializer]:
from authentik.providers.proxy.api import ProxyProviderSerializer
return ProxyProviderSerializer
@property @property
def launch_url(self) -> Optional[str]: def launch_url(self) -> Optional[str]:
"""Use external_host as launch URL""" """Use external_host as launch URL"""

View File

@ -7,6 +7,7 @@ from django.forms import ModelForm
from django.http import HttpRequest from django.http import HttpRequest
from django.shortcuts import reverse from django.shortcuts import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from structlog import get_logger from structlog import get_logger
from authentik.core.models import PropertyMapping, Provider from authentik.core.models import PropertyMapping, Provider
@ -145,6 +146,12 @@ class SAMLProvider(Provider):
launch_url = urlparse(self.acs_url) launch_url = urlparse(self.acs_url)
return self.acs_url.replace(launch_url.path, "") return self.acs_url.replace(launch_url.path, "")
@property
def serializer(self) -> Type[Serializer]:
from authentik.providers.saml.api import SAMLPropertyMappingSerializer
return SAMLPropertyMappingSerializer
@property @property
def form(self) -> Type[ModelForm]: def form(self) -> Type[ModelForm]:
from authentik.providers.saml.forms import SAMLProviderForm from authentik.providers.saml.forms import SAMLProviderForm

View File

@ -7,7 +7,7 @@
<label for="" class="pf-c-form__label"></label> <label for="" class="pf-c-form__label"></label>
<div class="c-form__horizontal-group"> <div class="c-form__horizontal-group">
<p> <p>
Expression using Python. See <a href="https://goauthentik.io/property-mappings/expression/">here</a> for a list of all variables. Expression using Python. See <a href="https://goauthentik.io/docs/property-mappings/expression/">here</a> for a list of all variables.
</p> </p>
</div> </div>
</div> </div>

View File

@ -105,16 +105,7 @@ class ASGILogger:
# https://code.djangoproject.com/ticket/31508 # https://code.djangoproject.com/ticket/31508
# https://github.com/encode/uvicorn/issues/266 # https://github.com/encode/uvicorn/issues/266
return return
try: await self.app(scope, receive, send_hooked)
await self.app(scope, receive, send_hooked)
except TypeError as exc:
# https://github.com/encode/uvicorn/issues/244
if exc.args == (
"An asyncio.Future, a coroutine or an awaitable is required",
):
pass
else:
raise exc
def _get_ip(self) -> str: def _get_ip(self) -> str:
client_ip = None client_ip = None

View File

@ -7,13 +7,16 @@ class MessageConsumer(JsonWebsocketConsumer):
"""Consumer which sends django.contrib.messages Messages over WS. """Consumer which sends django.contrib.messages Messages over WS.
channel_name is saved into cache with user_id, and when a add_message is called""" channel_name is saved into cache with user_id, and when a add_message is called"""
session_key: str
def connect(self): def connect(self):
self.accept() self.accept()
cache.set(f"user_{self.scope['user'].pk}_messages_{self.channel_name}", True) self.session_key = self.scope["session"].session_key
cache.set(f"user_{self.session_key}_messages_{self.channel_name}", True, None)
# pylint: disable=unused-argument # pylint: disable=unused-argument
def disconnect(self, close_code): def disconnect(self, close_code):
cache.delete(f"user_{self.scope['user'].pk}_messages_{self.channel_name}") cache.delete(f"user_{self.session_key}_messages_{self.channel_name}")
def event_update(self, event: dict): def event_update(self, event: dict):
"""Event handler which is called by Messages Storage backend""" """Event handler which is called by Messages Storage backend"""

View File

@ -16,7 +16,7 @@ class ChannelsStorage(FallbackStorage):
self.channel = get_channel_layer() self.channel = get_channel_layer()
def _store(self, messages: list[Message], response, *args, **kwargs): def _store(self, messages: list[Message], response, *args, **kwargs):
prefix = f"user_{self.request.user.pk}_messages_" prefix = f"user_{self.request.session.session_key}_messages_"
keys = cache.keys(f"{prefix}*") keys = cache.keys(f"{prefix}*")
if len(keys) < 1: if len(keys) < 1:
return super()._store(messages, response, *args, **kwargs) return super()._store(messages, response, *args, **kwargs)

View File

@ -7,7 +7,7 @@
<label for="" class="pf-c-form__label"></label> <label for="" class="pf-c-form__label"></label>
<div class="c-form__horizontal-group"> <div class="c-form__horizontal-group">
<p> <p>
Expression using Python. See <a href="https://goauthentik.io/property-mappings/expression/">here</a> for a list of all variables. Expression using Python. See <a href="https://goauthentik.io/docs/property-mappings/expression/">here</a> for a list of all variables.
</p> </p>
</div> </div>
</div> </div>

View File

@ -15,10 +15,11 @@ from authentik.core.models import User
from authentik.flows.models import Flow, in_memory_stage from authentik.flows.models import Flow, in_memory_stage
from authentik.flows.planner import ( from authentik.flows.planner import (
PLAN_CONTEXT_PENDING_USER, PLAN_CONTEXT_PENDING_USER,
PLAN_CONTEXT_REDIRECT,
PLAN_CONTEXT_SSO, PLAN_CONTEXT_SSO,
FlowPlanner, FlowPlanner,
) )
from authentik.flows.views import SESSION_KEY_PLAN from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs from authentik.lib.utils.urls import redirect_with_qs
from authentik.policies.utils import delete_none_keys from authentik.policies.utils import delete_none_keys
from authentik.sources.oauth.auth import AuthorizedServiceBackend from authentik.sources.oauth.auth import AuthorizedServiceBackend
@ -135,11 +136,17 @@ class OAuthCallback(OAuthClientMixin, View):
def handle_login_flow(self, flow: Flow, **kwargs) -> HttpResponse: def handle_login_flow(self, flow: Flow, **kwargs) -> HttpResponse:
"""Prepare Authentication Plan, redirect user FlowExecutor""" """Prepare Authentication Plan, redirect user FlowExecutor"""
# Ensure redirect is carried through when user was trying to
# authorize application
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
NEXT_ARG_NAME, "authentik_core:shell"
)
kwargs.update( kwargs.update(
{ {
# Since we authenticate the user by their token, they have no backend set # Since we authenticate the user by their token, they have no backend set
PLAN_CONTEXT_AUTHENTICATION_BACKEND: "django.contrib.auth.backends.ModelBackend", PLAN_CONTEXT_AUTHENTICATION_BACKEND: "django.contrib.auth.backends.ModelBackend",
PLAN_CONTEXT_SSO: True, PLAN_CONTEXT_SSO: True,
PLAN_CONTEXT_REDIRECT: final_redirect,
} }
) )
# We run the Flow planner here so we can pass the Pending user in the context # We run the Flow planner here so we can pass the Pending user in the context

View File

@ -13,10 +13,11 @@ from authentik.core.models import User
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.flows.planner import ( from authentik.flows.planner import (
PLAN_CONTEXT_PENDING_USER, PLAN_CONTEXT_PENDING_USER,
PLAN_CONTEXT_REDIRECT,
PLAN_CONTEXT_SSO, PLAN_CONTEXT_SSO,
FlowPlanner, FlowPlanner,
) )
from authentik.flows.views import SESSION_KEY_PLAN from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs from authentik.lib.utils.urls import redirect_with_qs
from authentik.policies.utils import delete_none_keys from authentik.policies.utils import delete_none_keys
from authentik.sources.saml.exceptions import ( from authentik.sources.saml.exceptions import (
@ -54,11 +55,14 @@ class ResponseProcessor:
_root: Any _root: Any
_root_xml: str _root_xml: str
_http_request: HttpRequest
def __init__(self, source: SAMLSource): def __init__(self, source: SAMLSource):
self._source = source self._source = source
def parse(self, request: HttpRequest): def parse(self, request: HttpRequest):
"""Check if `request` contains SAML Response data, parse and validate it.""" """Check if `request` contains SAML Response data, parse and validate it."""
self._http_request = request
# First off, check if we have any SAML Data at all. # First off, check if we have any SAML Data at all.
raw_response = request.POST.get("SAMLResponse", None) raw_response = request.POST.get("SAMLResponse", None)
if not raw_response: if not raw_response:
@ -187,6 +191,11 @@ class ResponseProcessor:
name_id_filter = self._get_name_id_filter() name_id_filter = self._get_name_id_filter()
matching_users = User.objects.filter(**name_id_filter) matching_users = User.objects.filter(**name_id_filter)
# Ensure redirect is carried through when user was trying to
# authorize application
final_redirect = self._http_request.session.get(SESSION_KEY_GET, {}).get(
NEXT_ARG_NAME, "authentik_core:shell"
)
if matching_users.exists(): if matching_users.exists():
# User exists already, switch to authentication flow # User exists already, switch to authentication flow
return self._flow_response( return self._flow_response(
@ -195,6 +204,7 @@ class ResponseProcessor:
**{ **{
PLAN_CONTEXT_PENDING_USER: matching_users.first(), PLAN_CONTEXT_PENDING_USER: matching_users.first(),
PLAN_CONTEXT_AUTHENTICATION_BACKEND: DEFAULT_BACKEND, PLAN_CONTEXT_AUTHENTICATION_BACKEND: DEFAULT_BACKEND,
PLAN_CONTEXT_REDIRECT: final_redirect,
}, },
) )
return self._flow_response( return self._flow_response(

View File

@ -1,6 +1,5 @@
"""authentik flows invitation forms""" """authentik flows invitation forms"""
from django import forms from django import forms
from django.utils.translation import gettext as _
from authentik.admin.fields import CodeMirrorWidget, YAMLField from authentik.admin.fields import CodeMirrorWidget, YAMLField
from authentik.stages.invitation.models import Invitation, InvitationStage from authentik.stages.invitation.models import Invitation, InvitationStage
@ -25,8 +24,5 @@ class InvitationForm(forms.ModelForm):
model = Invitation model = Invitation
fields = ["expires", "fixed_data"] fields = ["expires", "fixed_data"]
labels = {
"fixed_data": _("Optional fixed data to enforce on user enrollment."),
}
widgets = {"fixed_data": CodeMirrorWidget()} widgets = {"fixed_data": CodeMirrorWidget()}
field_classes = {"fixed_data": YAMLField} field_classes = {"fixed_data": YAMLField}

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.4 on 2020-12-25 21:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_invitation", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="invitation",
name="fixed_data",
field=models.JSONField(blank=True, default=dict),
),
]

View File

@ -61,7 +61,11 @@ class Invitation(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE) created_by = models.ForeignKey(User, on_delete=models.CASCADE)
expires = models.DateTimeField(default=None, blank=True, null=True) expires = models.DateTimeField(default=None, blank=True, null=True)
fixed_data = models.JSONField(default=dict) fixed_data = models.JSONField(
default=dict,
blank=True,
help_text=_("Optional fixed data to enforce on user enrollment."),
)
def __str__(self): def __str__(self):
return f"Invitation {self.invite_uuid.hex} created by {self.created_by}" return f"Invitation {self.invite_uuid.hex} created by {self.created_by}"

View File

@ -53,5 +53,5 @@ class PasswordStageForm(forms.ModelForm):
fields = ["name", "backends", "configure_flow", "failed_attempts_before_cancel"] fields = ["name", "backends", "configure_flow", "failed_attempts_before_cancel"]
widgets = { widgets = {
"name": forms.TextInput(), "name": forms.TextInput(),
"backends": forms.SelectMultiple(get_authentication_backends()), "backends": forms.SelectMultiple(choices=get_authentication_backends()),
} }

View File

@ -10,8 +10,8 @@ services:
- internal - internal
environment: environment:
- POSTGRES_PASSWORD=${PG_PASS:-thisisnotagoodpassword} - POSTGRES_PASSWORD=${PG_PASS:-thisisnotagoodpassword}
- POSTGRES_USER=authentik - POSTGRES_USER=${PG_USER:-authentik}
- POSTGRES_DB=authentik - POSTGRES_DB=${PG_DB:-authentik}
env_file: env_file:
- .env - .env
redis: redis:
@ -19,11 +19,13 @@ services:
networks: networks:
- internal - internal
server: server:
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.0-rc4} image: beryju/authentik:${AUTHENTIK_TAG:-0.13.5-stable}
command: server command: server
environment: environment:
AUTHENTIK_REDIS__HOST: redis AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
volumes: volumes:
- ./media:/media - ./media:/media
@ -42,13 +44,15 @@ services:
env_file: env_file:
- .env - .env
worker: worker:
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.0-rc4} image: beryju/authentik:${AUTHENTIK_TAG:-0.13.5-stable}
command: worker command: worker
networks: networks:
- internal - internal
environment: environment:
AUTHENTIK_REDIS__HOST: redis AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
volumes: volumes:
- ./backups:/backups - ./backups:/backups
@ -56,7 +60,7 @@ services:
env_file: env_file:
- .env - .env
static: static:
image: beryju/authentik-static:${AUTHENTIK_TAG:-0.13.0-rc4} image: beryju/authentik-static:${AUTHENTIK_TAG:-0.13.5-stable}
networks: networks:
- internal - internal
labels: labels:
@ -68,7 +72,7 @@ services:
traefik.http.services.static-service.loadbalancer.healthcheck.path: / traefik.http.services.static-service.loadbalancer.healthcheck.path: /
traefik.http.services.static-service.loadbalancer.server.port: '80' traefik.http.services.static-service.loadbalancer.server.port: '80'
volumes: volumes:
- ./media:/media - ./media:/usr/share/nginx/html/media
traefik: traefik:
image: traefik:2.3 image: traefik:2.3
command: command:
@ -81,7 +85,6 @@ services:
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
ports: ports:
- "0.0.0.0:80:80"
- "0.0.0.0:443:443" - "0.0.0.0:443:443"
- "127.0.0.1:8080:8080" - "127.0.0.1:8080:8080"
networks: networks:

View File

@ -4,7 +4,7 @@ name: authentik
home: https://goauthentik.io home: https://goauthentik.io
sources: sources:
- https://github.com/BeryJu/authentik - https://github.com/BeryJu/authentik
version: "0.13.0-rc4" version: "0.13.5-stable"
icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg
dependencies: dependencies:
- name: postgresql - name: postgresql

View File

@ -4,7 +4,7 @@
|-----------------------------------|-------------------------|-------------| |-----------------------------------|-------------------------|-------------|
| image.name | beryju/authentik | Image used to run the authentik server and worker | | image.name | beryju/authentik | Image used to run the authentik server and worker |
| image.name_static | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) | | image.name_static | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) |
| image.tag | 0.13.0-rc4 | Image tag | | image.tag | 0.13.5-stable | Image tag |
| image.pullPolicy | IfNotPresent | Image Pull Policy used for all deployments | | image.pullPolicy | IfNotPresent | Image Pull Policy used for all deployments |
| serverReplicas | 1 | Replicas for the Server deployment | | serverReplicas | 1 | Replicas for the Server deployment |
| workerReplicas | 1 | Replicas for the Worker deployment | | workerReplicas | 1 | Replicas for the Worker deployment |

View File

@ -36,6 +36,10 @@ spec:
backend: backend:
serviceName: {{ $fullName }}-static serviceName: {{ $fullName }}-static
servicePort: http servicePort: http
- path: /media/
backend:
serviceName: {{ $fullName }}-static
servicePort: http
- path: /robots.txt - path: /robots.txt
backend: backend:
serviceName: {{ $fullName }}-static serviceName: {{ $fullName }}-static

View File

@ -5,7 +5,7 @@ image:
name: beryju/authentik name: beryju/authentik
name_static: beryju/authentik-static name_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended
tag: 0.13.0-rc4 tag: 0.13.5-stable
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
serverReplicas: 1 serverReplicas: 1

View File

@ -5,12 +5,12 @@ go 1.14
require ( require (
cloud.google.com/go v0.64.0 // indirect cloud.google.com/go v0.64.0 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc v2.2.1+incompatible
github.com/getsentry/sentry-go v0.7.0 github.com/getsentry/sentry-go v0.9.0
github.com/go-openapi/errors v0.19.9 github.com/go-openapi/errors v0.19.9 // indirect
github.com/go-openapi/runtime v0.19.24 github.com/go-openapi/runtime v0.19.24
github.com/go-openapi/strfmt v0.19.11 github.com/go-openapi/strfmt v0.19.11
github.com/go-openapi/swag v0.19.12 github.com/go-openapi/swag v0.19.12 // indirect
github.com/go-openapi/validate v0.19.15 github.com/go-openapi/validate v0.19.15 // indirect
github.com/go-redis/redis/v7 v7.4.0 // indirect github.com/go-redis/redis/v7 v7.4.0 // indirect
github.com/go-swagger/go-swagger v0.25.0 // indirect github.com/go-swagger/go-swagger v0.25.0 // indirect
github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/handlers v1.5.1 // indirect
@ -22,7 +22,7 @@ require (
github.com/oauth2-proxy/oauth2-proxy v1.1.2-0.20200817154438-5fa5b3186f39 github.com/oauth2-proxy/oauth2-proxy v1.1.2-0.20200817154438-5fa5b3186f39
github.com/pelletier/go-toml v1.8.1 // indirect github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f // indirect github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f // indirect
github.com/recws-org/recws v1.2.1 github.com/recws-org/recws v1.2.2
github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus v1.7.0
github.com/spf13/afero v1.4.1 // indirect github.com/spf13/afero v1.4.1 // indirect
github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cast v1.3.1 // indirect

View File

@ -35,13 +35,17 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BeryJu/authentik v0.0.0-20201213234502-f0f32453882b h1:tLc7ERt2fWSu14nXdsER4EP62KUPXwAB0OeLVAA4Rx0=
github.com/BeryJu/authentik v0.0.0-20201214075318-41f9097592da h1:XB/MCgb0K+ngEETjBYKGiXcN2des8YtANetYboeT4Lg=
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw=
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc= github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI= github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
@ -139,6 +143,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/getsentry/sentry-go v0.7.0 h1:MR2yfR4vFfv/2+iBuSnkdQwVg7N9cJzihZ6KJu7srwQ= github.com/getsentry/sentry-go v0.7.0 h1:MR2yfR4vFfv/2+iBuSnkdQwVg7N9cJzihZ6KJu7srwQ=
github.com/getsentry/sentry-go v0.7.0/go.mod h1:pLFpD2Y5RHIKF9Bw3KH6/68DeN2K/XBJd8awjdPnUwg= github.com/getsentry/sentry-go v0.7.0/go.mod h1:pLFpD2Y5RHIKF9Bw3KH6/68DeN2K/XBJd8awjdPnUwg=
github.com/getsentry/sentry-go v0.9.0 h1:KIfpY/D9hX3gWAEd3d8z6ImuHNWtqEsjlpdF8zXFsHM=
github.com/getsentry/sentry-go v0.9.0/go.mod h1:kELm/9iCblqUYh+ZRML7PNdCvEuw24wBvJPYyi86cws=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
@ -409,6 +415,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@ -438,9 +446,14 @@ github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
@ -448,6 +461,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
@ -496,6 +510,7 @@ github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa h1:hI1uC2A3vJFjwvB
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa/go.mod h1:8vxFeeg++MqgCHwehSuwTlYCF0ALyDJbYJ1JsKi7v6s= github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa/go.mod h1:8vxFeeg++MqgCHwehSuwTlYCF0ALyDJbYJ1JsKi7v6s=
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@ -519,8 +534,11 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@ -577,6 +595,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/recws-org/recws v1.2.1 h1:bYocRkAsS71hlQ9AMCVS+hYXHEgEyQsAbYKXf394gZ8= github.com/recws-org/recws v1.2.1 h1:bYocRkAsS71hlQ9AMCVS+hYXHEgEyQsAbYKXf394gZ8=
github.com/recws-org/recws v1.2.1/go.mod h1:SxTgwQU/jqYSzEgUh4ifDxq/7enApS150f8nZ5Sczk8= github.com/recws-org/recws v1.2.1/go.mod h1:SxTgwQU/jqYSzEgUh4ifDxq/7enApS150f8nZ5Sczk8=
github.com/recws-org/recws v1.2.2 h1:TkyyCEgMjsr1D2fnutY/DPhGnUKCLpJeXDAGy6rLmGE=
github.com/recws-org/recws v1.2.2/go.mod h1:SxTgwQU/jqYSzEgUh4ifDxq/7enApS150f8nZ5Sczk8=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -584,6 +604,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@ -711,6 +732,7 @@ golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
@ -1046,6 +1068,7 @@ gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@ -1070,6 +1093,7 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -59,6 +59,7 @@ func getCommonOptions() *options.Options {
} }
func doGlobalSetup(config map[string]interface{}) { func doGlobalSetup(config map[string]interface{}) {
log.SetFormatter(&log.JSONFormatter{})
switch config[ConfigLogLevel].(string) { switch config[ConfigLogLevel].(string) {
case "debug": case "debug":
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)

View File

@ -1,3 +1,3 @@
package pkg package pkg
const VERSION = "0.13.0-rc4" const VERSION = "0.13.5-stable"

View File

@ -22,11 +22,11 @@ paths:
/admin/metrics/: /admin/metrics/:
get: get:
operationId: admin_metrics_list operationId: admin_metrics_list
description: Return single instance of AdministrationMetricsSerializer description: Login Metrics per 1h
parameters: [] parameters: []
responses: responses:
'200': '200':
description: Overview View description: Login Metrics per 1h
schema: schema:
description: '' description: ''
type: array type: array
@ -35,22 +35,6 @@ paths:
tags: tags:
- admin - admin
parameters: [] parameters: []
/admin/overview/:
get:
operationId: admin_overview_list
description: Return single instance of AdministrationOverviewSerializer
parameters: []
responses:
'200':
description: Overview View
schema:
description: ''
type: array
items:
$ref: '#/definitions/AdministrationOverview'
tags:
- admin
parameters: []
/admin/system_tasks/: /admin/system_tasks/:
get: get:
operationId: admin_system_tasks_list operationId: admin_system_tasks_list
@ -82,6 +66,95 @@ paths:
in: path in: path
required: true required: true
type: string type: string
/admin/version/:
get:
operationId: admin_version_list
description: Get running and latest version.
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: A page number within the paginated result set.
required: false
type: integer
- name: page_size
in: query
description: Number of results to return per page.
required: false
type: integer
responses:
'200':
description: Get running and latest version.
schema:
description: ''
type: array
items:
$ref: '#/definitions/Version'
tags:
- admin
parameters: []
/admin/workers/:
get:
operationId: admin_workers_list
description: Get currently connected worker count.
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: A page number within the paginated result set.
required: false
type: integer
- name: page_size
in: query
description: Number of results to return per page.
required: false
type: integer
responses:
'200':
description: ''
schema:
required:
- count
- results
type: object
properties:
count:
type: integer
next:
type: string
format: uri
x-nullable: true
previous:
type: string
format: uri
x-nullable: true
results:
type: array
items:
description: ''
type: object
properties: {}
tags:
- admin
parameters: []
/audit/events/: /audit/events/:
get: get:
operationId: audit_events_list operationId: audit_events_list
@ -1062,6 +1135,59 @@ paths:
required: true required: true
type: string type: string
format: uuid format: uuid
/flows/cached/:
get:
operationId: flows_cached_list
description: Info about cached flows
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: A page number within the paginated result set.
required: false
type: integer
- name: page_size
in: query
description: Number of results to return per page.
required: false
type: integer
responses:
'200':
description: ''
schema:
required:
- count
- results
type: object
properties:
count:
type: integer
next:
type: string
format: uri
x-nullable: true
previous:
type: string
format: uri
x-nullable: true
results:
type: array
items:
description: ''
type: object
properties: {}
tags:
- flows
parameters: []
/flows/instances/: /flows/instances/:
get: get:
operationId: flows_instances_list operationId: flows_instances_list
@ -1702,6 +1828,16 @@ paths:
operationId: policies_all_list operationId: policies_all_list
description: Policy Viewset description: Policy Viewset
parameters: parameters:
- name: bindings__isnull
in: query
description: ''
required: false
type: string
- name: promptstage__isnull
in: query
description: ''
required: false
type: string
- name: ordering - name: ordering
in: query in: query
description: Which field to use when ordering the results. description: Which field to use when ordering the results.
@ -1919,6 +2055,59 @@ paths:
required: true required: true
type: string type: string
format: uuid format: uuid
/policies/cached/:
get:
operationId: policies_cached_list
description: Info about cached policies
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: A page number within the paginated result set.
required: false
type: integer
- name: page_size
in: query
description: Number of results to return per page.
required: false
type: integer
responses:
'200':
description: ''
schema:
required:
- count
- results
type: object
properties:
count:
type: integer
next:
type: string
format: uri
x-nullable: true
previous:
type: string
format: uri
x-nullable: true
results:
type: array
items:
description: ''
type: object
properties: {}
tags:
- policies
parameters: []
/policies/dummy/: /policies/dummy/:
get: get:
operationId: policies_dummy_list operationId: policies_dummy_list
@ -3264,6 +3453,11 @@ paths:
operationId: providers_all_list operationId: providers_all_list
description: Provider Viewset description: Provider Viewset
parameters: parameters:
- name: application__isnull
in: query
description: ''
required: false
type: string
- name: ordering - name: ordering
in: query in: query
description: Which field to use when ordering the results. description: Which field to use when ordering the results.
@ -3309,6 +3503,22 @@ paths:
$ref: '#/definitions/Provider' $ref: '#/definitions/Provider'
tags: tags:
- providers - providers
post:
operationId: providers_all_create
description: Provider Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Provider'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/Provider'
tags:
- providers
parameters: [] parameters: []
/providers/all/{id}/: /providers/all/{id}/:
get: get:
@ -3322,6 +3532,47 @@ paths:
$ref: '#/definitions/Provider' $ref: '#/definitions/Provider'
tags: tags:
- providers - providers
put:
operationId: providers_all_update
description: Provider Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Provider'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/Provider'
tags:
- providers
patch:
operationId: providers_all_partial_update
description: Provider Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Provider'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/Provider'
tags:
- providers
delete:
operationId: providers_all_delete
description: Provider Viewset
parameters: []
responses:
'204':
description: ''
tags:
- providers
parameters: parameters:
- name: id - name: id
in: path in: path
@ -6421,7 +6672,7 @@ paths:
format: uuid format: uuid
definitions: definitions:
AdministrationMetrics: AdministrationMetrics:
description: Overview View description: Login Metrics per 1h
type: object type: object
properties: properties:
logins_per_1h: logins_per_1h:
@ -6432,38 +6683,6 @@ definitions:
title: Logins failed per 1h title: Logins failed per 1h
type: string type: string
readOnly: true readOnly: true
AdministrationOverview:
description: Overview View
type: object
properties:
version:
title: Version
type: string
readOnly: true
version_latest:
title: Version latest
type: string
readOnly: true
worker_count:
title: Worker count
type: integer
readOnly: true
providers_without_application:
title: Providers without application
type: integer
readOnly: true
policies_without_binding:
title: Policies without binding
type: integer
readOnly: true
cached_policies:
title: Cached policies
type: integer
readOnly: true
cached_flows:
title: Cached flows
type: integer
readOnly: true
Task: Task:
description: Serialize TaskInfo and TaskResult description: Serialize TaskInfo and TaskResult
required: required:
@ -6494,6 +6713,22 @@ definitions:
type: array type: array
items: items:
type: string type: string
Version:
description: Get running and latest version.
type: object
properties:
version_current:
title: Version current
type: string
readOnly: true
version_latest:
title: Version latest
type: string
readOnly: true
outdated:
title: Outdated
type: boolean
readOnly: true
Event: Event:
description: Event Serializer description: Event Serializer
required: required:
@ -7110,7 +7345,6 @@ definitions:
description: KubernetesServiceConnection Serializer description: KubernetesServiceConnection Serializer
required: required:
- name - name
- kubeconfig
type: object type: object
properties: properties:
pk: pk:
@ -7480,6 +7714,7 @@ definitions:
description: Provider Serializer description: Provider Serializer
required: required:
- name - name
- application
- authorization_flow - authorization_flow
type: object type: object
properties: properties:
@ -7491,6 +7726,9 @@ definitions:
title: Name title: Name
type: string type: string
minLength: 1 minLength: 1
application:
title: Application
type: string
authorization_flow: authorization_flow:
title: Authorization flow title: Authorization flow
description: Flow used when authorizing this provider. description: Flow used when authorizing this provider.
@ -8357,6 +8595,7 @@ definitions:
x-nullable: true x-nullable: true
fixed_data: fixed_data:
title: Fixed data title: Fixed data
description: Optional fixed data to enforce on user enrollment.
type: object type: object
OTPStaticStage: OTPStaticStage:
description: OTPStaticStage Serializer description: OTPStaticStage Serializer

298
web/package-lock.json generated
View File

@ -102,14 +102,14 @@
} }
}, },
"@patternfly/patternfly": { "@patternfly/patternfly": {
"version": "4.65.6", "version": "4.70.2",
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.65.6.tgz", "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.70.2.tgz",
"integrity": "sha512-dENO2nZbf5SoEH68coW9U+6FpZmdVnFVjztl7rUeWUPSBUuF1eWld5LT03Q6PVoZuWqqbJxFJodyFKwLb+L9vw==" "integrity": "sha512-XKCHnOjx1JThY3s98AJhsApSsGHPvEdlY7r+b18OecqUnmThVGw3nslzYYrwfCGlJ/xQtV5so29SduH2/uhHzA=="
}, },
"@rollup/plugin-typescript": { "@rollup/plugin-typescript": {
"version": "8.0.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.0.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.1.0.tgz",
"integrity": "sha512-2L/kKvM5U4VOm+yVMvPIBF3yMZtQUyopf4YIT+KQbqZBZ8Fsdm7X6yeezy92PMyvvHQG1Pa322MVwxPojQvukA==", "integrity": "sha512-pyQlcGQYRsONUDwXK3ckGPHjPzmjlq4sinzr7emW8ZMb2oZjg9WLcdcP8wyHSvBjvHrLzMayyPy079RROqb4vw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@rollup/pluginutils": "^3.1.0", "@rollup/pluginutils": "^3.1.0",
@ -142,13 +142,13 @@
} }
}, },
"@sentry/browser": { "@sentry/browser": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.29.2.tgz",
"integrity": "sha512-kRlt1mE2wrYjspnIupNnPxqsUrRuy02SuXhbpP7J6uu8QasoEmJ78hk0hHz4jOZRmuWwfs2zIXD4tLGgWOKq8A==", "integrity": "sha512-uxZ7y7rp85tJll+RZtXRhXPbnFnOaxZqJEv05vJlXBtBNLQtlczV5iCtU9mZRLVHDtmZ5VVKUV8IKXntEqqDpQ==",
"requires": { "requires": {
"@sentry/core": "5.29.0", "@sentry/core": "5.29.2",
"@sentry/types": "5.29.0", "@sentry/types": "5.29.2",
"@sentry/utils": "5.29.0", "@sentry/utils": "5.29.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -160,14 +160,14 @@
} }
}, },
"@sentry/core": { "@sentry/core": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.29.2.tgz",
"integrity": "sha512-a1sZBJ2u3NG0YDlGvOTwUCWiNjhfmDtAQiKK1o6RIIbcrWy9TlSps7CYDkBP239Y3A4pnvohjEEKEP3v3L3LZQ==", "integrity": "sha512-7WYkoxB5IdlNEbwOwqSU64erUKH4laavPsM0/yQ+jojM76ErxlgEF0u//p5WaLPRzh3iDSt6BH+9TL45oNZeZw==",
"requires": { "requires": {
"@sentry/hub": "5.29.0", "@sentry/hub": "5.29.2",
"@sentry/minimal": "5.29.0", "@sentry/minimal": "5.29.2",
"@sentry/types": "5.29.0", "@sentry/types": "5.29.2",
"@sentry/utils": "5.29.0", "@sentry/utils": "5.29.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -179,12 +179,12 @@
} }
}, },
"@sentry/hub": { "@sentry/hub": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.2.tgz",
"integrity": "sha512-kcDPQsRG4cFdmqDh+TzjeO7lWYxU8s1dZYAbbl1J4uGKmhNB0J7I4ak4SGwTsXLY6fhbierxr6PRaoNojCxjPw==", "integrity": "sha512-LaAIo2hwUk9ykeh9RF0cwLy6IRw+DjEee8l1HfEaDFUM6TPGlNNGObMJNXb9/95jzWp7jWwOpQjoIE3jepdQJQ==",
"requires": { "requires": {
"@sentry/types": "5.29.0", "@sentry/types": "5.29.2",
"@sentry/utils": "5.29.0", "@sentry/utils": "5.29.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -196,12 +196,12 @@
} }
}, },
"@sentry/minimal": { "@sentry/minimal": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.2.tgz",
"integrity": "sha512-nhXofdjtO41/caiF1wk1oT3p/QuhOZDYdF/b29DoD2MiAMK9IjhhOXI/gqaRpDKkXlDvd95fDTcx4t/MqqcKXA==", "integrity": "sha512-0aINSm8fGA1KyM7PavOBe1GDZDxrvnKt+oFnU0L+bTcw8Lr+of+v6Kwd97rkLRNOLw621xP076dL/7LSIzMuhw==",
"requires": { "requires": {
"@sentry/hub": "5.29.0", "@sentry/hub": "5.29.2",
"@sentry/types": "5.29.0", "@sentry/types": "5.29.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -213,48 +213,48 @@
} }
}, },
"@sentry/tracing": { "@sentry/tracing": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.29.2.tgz",
"integrity": "sha512-2ZITUH7Eur7IkmRAd5gw8Xt2Sfc28btCnT7o2P2J8ZPD65e99ATqjxXPokx0+6zEkTsstIDD3mbyuwkpbuvuTA==", "integrity": "sha512-iumYbVRpvoU3BUuIooxibydeaOOjl5ysc+mzsqhRs2NGW/C3uKAsFXdvyNfqt3bxtRQwJEhwJByLP2u3pLThpw==",
"requires": { "requires": {
"@sentry/hub": "5.29.0", "@sentry/hub": "5.29.2",
"@sentry/minimal": "5.29.0", "@sentry/minimal": "5.29.2",
"@sentry/types": "5.29.0", "@sentry/types": "5.29.2",
"@sentry/utils": "5.29.0", "@sentry/utils": "5.29.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
"@sentry/hub": { "@sentry/hub": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.2.tgz",
"integrity": "sha512-kcDPQsRG4cFdmqDh+TzjeO7lWYxU8s1dZYAbbl1J4uGKmhNB0J7I4ak4SGwTsXLY6fhbierxr6PRaoNojCxjPw==", "integrity": "sha512-LaAIo2hwUk9ykeh9RF0cwLy6IRw+DjEee8l1HfEaDFUM6TPGlNNGObMJNXb9/95jzWp7jWwOpQjoIE3jepdQJQ==",
"requires": { "requires": {
"@sentry/types": "5.29.0", "@sentry/types": "5.29.2",
"@sentry/utils": "5.29.0", "@sentry/utils": "5.29.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
"@sentry/minimal": { "@sentry/minimal": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.2.tgz",
"integrity": "sha512-nhXofdjtO41/caiF1wk1oT3p/QuhOZDYdF/b29DoD2MiAMK9IjhhOXI/gqaRpDKkXlDvd95fDTcx4t/MqqcKXA==", "integrity": "sha512-0aINSm8fGA1KyM7PavOBe1GDZDxrvnKt+oFnU0L+bTcw8Lr+of+v6Kwd97rkLRNOLw621xP076dL/7LSIzMuhw==",
"requires": { "requires": {
"@sentry/hub": "5.29.0", "@sentry/hub": "5.29.2",
"@sentry/types": "5.29.0", "@sentry/types": "5.29.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
"@sentry/types": { "@sentry/types": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.2.tgz",
"integrity": "sha512-iDkxT/9sT3UF+Xb+JyLjZ5caMXsgLfRyV9VXQEiR2J6mgpMielj184d9jeF3bm/VMuAf/VFFqrHlcVsVgmrrMw==" "integrity": "sha512-dM9wgt8wy4WRty75QkqQgrw9FV9F+BOMfmc0iaX13Qos7i6Qs2Q0dxtJ83SoR4YGtW8URaHzlDtWlGs5egBiMA=="
}, },
"@sentry/utils": { "@sentry/utils": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.2.tgz",
"integrity": "sha512-b2B1gshw2u3EHlAi84PuI5sfmLKXW1z9enMMhNuuNT/CoRp+g5kMAcUv/qYTws7UNnYSvTuVGuZG30v1e0hP9A==", "integrity": "sha512-nEwQIDjtFkeE4k6yIk4Ka5XjGRklNLThWLs2xfXlL7uwrYOH2B9UBBOOIRUraBm/g/Xrra3xsam/kRxuiwtXZQ==",
"requires": { "requires": {
"@sentry/types": "5.29.0", "@sentry/types": "5.29.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
@ -266,16 +266,16 @@
} }
}, },
"@sentry/types": { "@sentry/types": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.2.tgz",
"integrity": "sha512-iDkxT/9sT3UF+Xb+JyLjZ5caMXsgLfRyV9VXQEiR2J6mgpMielj184d9jeF3bm/VMuAf/VFFqrHlcVsVgmrrMw==" "integrity": "sha512-dM9wgt8wy4WRty75QkqQgrw9FV9F+BOMfmc0iaX13Qos7i6Qs2Q0dxtJ83SoR4YGtW8URaHzlDtWlGs5egBiMA=="
}, },
"@sentry/utils": { "@sentry/utils": {
"version": "5.29.0", "version": "5.29.2",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.2.tgz",
"integrity": "sha512-b2B1gshw2u3EHlAi84PuI5sfmLKXW1z9enMMhNuuNT/CoRp+g5kMAcUv/qYTws7UNnYSvTuVGuZG30v1e0hP9A==", "integrity": "sha512-nEwQIDjtFkeE4k6yIk4Ka5XjGRklNLThWLs2xfXlL7uwrYOH2B9UBBOOIRUraBm/g/Xrra3xsam/kRxuiwtXZQ==",
"requires": { "requires": {
"@sentry/types": "5.29.0", "@sentry/types": "5.29.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -287,9 +287,9 @@
} }
}, },
"@types/chart.js": { "@types/chart.js": {
"version": "2.9.28", "version": "2.9.29",
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.28.tgz", "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.29.tgz",
"integrity": "sha512-9YYhsxRngRJb0dkuaU5BezkF+zvvVHnwdRw+rtlahtFb4zqNf9YSgWsOq+dLYeh0fqsWmHUYLR64eNigh02F+w==", "integrity": "sha512-WOZMitUU3gHDM0oQsCsVivX+oDsIki93szcTmmUPBm39cCvAELBjokjSDVOoA3xiIEbb+jp17z/3S2tIqruwOQ==",
"requires": { "requires": {
"moment": "^2.10.2" "moment": "^2.10.2"
} }
@ -393,156 +393,70 @@
} }
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "4.9.1", "version": "4.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.10.0.tgz",
"integrity": "sha512-QRLDSvIPeI1pz5tVuurD+cStNR4sle4avtHhxA+2uyixWGFjKzJ+EaFVRW6dA/jOgjV5DTAjOxboQkRDE8cRlQ==", "integrity": "sha512-h6/V46o6aXpKRlarP1AiJEXuCJ7cMQdlpfMDrcllIgX3dFkLwEBTXAoNP98ZoOmqd1xvymMVRAI4e7yVvlzWEg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "4.9.1", "@typescript-eslint/experimental-utils": "4.10.0",
"@typescript-eslint/scope-manager": "4.9.1", "@typescript-eslint/scope-manager": "4.10.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0", "regexpp": "^3.0.0",
"semver": "^7.3.2", "semver": "^7.3.2",
"tsutils": "^3.17.1" "tsutils": "^3.17.1"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz",
"integrity": "sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.9.1",
"@typescript-eslint/visitor-keys": "4.9.1"
}
},
"@typescript-eslint/types": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.1.tgz",
"integrity": "sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==",
"dev": true
},
"@typescript-eslint/visitor-keys": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz",
"integrity": "sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.9.1",
"eslint-visitor-keys": "^2.0.0"
}
}
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "4.9.1", "version": "4.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.10.0.tgz",
"integrity": "sha512-c3k/xJqk0exLFs+cWSJxIjqLYwdHCuLWhnpnikmPQD2+NGAx9KjLYlBDcSI81EArh9FDYSL6dslAUSwILeWOxg==", "integrity": "sha512-opX+7ai1sdWBOIoBgpVJrH5e89ra1KoLrJTz0UtWAa4IekkKmqDosk5r6xqRaNJfCXEfteW4HXQAwMdx+jjEmw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.3", "@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.9.1", "@typescript-eslint/scope-manager": "4.10.0",
"@typescript-eslint/types": "4.9.1", "@typescript-eslint/types": "4.10.0",
"@typescript-eslint/typescript-estree": "4.9.1", "@typescript-eslint/typescript-estree": "4.10.0",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0" "eslint-utils": "^2.0.0"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz",
"integrity": "sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.9.1",
"@typescript-eslint/visitor-keys": "4.9.1"
}
},
"@typescript-eslint/types": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.1.tgz",
"integrity": "sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz",
"integrity": "sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.9.1",
"@typescript-eslint/visitor-keys": "4.9.1",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
"lodash": "^4.17.15",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz",
"integrity": "sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.9.1",
"eslint-visitor-keys": "^2.0.0"
}
},
"globby": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
"integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
"dev": true,
"requires": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.1.1",
"ignore": "^5.1.4",
"merge2": "^1.3.0",
"slash": "^3.0.0"
}
}
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "4.9.1", "version": "4.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.9.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.10.0.tgz",
"integrity": "sha512-Gv2VpqiomvQ2v4UL+dXlQcZ8zCX4eTkoIW+1aGVWT6yTO+6jbxsw7yQl2z2pPl/4B9qa5JXeIbhJpONKjXIy3g==", "integrity": "sha512-amBvUUGBMadzCW6c/qaZmfr3t9PyevcSWw7hY2FuevdZVp5QPw/K76VSQ5Sw3BxlgYCHZcK6DjIhSZK0PQNsQg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/scope-manager": "4.9.1", "@typescript-eslint/scope-manager": "4.10.0",
"@typescript-eslint/types": "4.9.1", "@typescript-eslint/types": "4.10.0",
"@typescript-eslint/typescript-estree": "4.9.1", "@typescript-eslint/typescript-estree": "4.10.0",
"debug": "^4.1.1" "debug": "^4.1.1"
} }
}, },
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.9.1", "version": "4.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.10.0.tgz",
"integrity": "sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==", "integrity": "sha512-WAPVw35P+fcnOa8DEic0tQUhoJJsgt+g6DEcz257G7vHFMwmag58EfowdVbiNcdfcV27EFR0tUBVXkDoIvfisQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.9.1", "@typescript-eslint/types": "4.10.0",
"@typescript-eslint/visitor-keys": "4.9.1" "@typescript-eslint/visitor-keys": "4.10.0"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.9.1", "version": "4.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.10.0.tgz",
"integrity": "sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==", "integrity": "sha512-+dt5w1+Lqyd7wIPMa4XhJxUuE8+YF+vxQ6zxHyhLGHJjHiunPf0wSV8LtQwkpmAsRi1lEOoOIR30FG5S2HS33g==",
"dev": true "dev": true
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "4.9.1", "version": "4.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.10.0.tgz",
"integrity": "sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw==", "integrity": "sha512-mGK0YRp9TOk6ZqZ98F++bW6X5kMTzCRROJkGXH62d2azhghmq+1LNLylkGe6uGUOQzD452NOAEth5VAF6PDo5g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.9.1", "@typescript-eslint/types": "4.10.0",
"@typescript-eslint/visitor-keys": "4.9.1", "@typescript-eslint/visitor-keys": "4.10.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"globby": "^11.0.1", "globby": "^11.0.1",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -568,12 +482,12 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.9.1", "version": "4.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.10.0.tgz",
"integrity": "sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==", "integrity": "sha512-hPyz5qmDMuZWFtHZkjcCpkAKHX8vdu1G3YsCLEd25ryZgnJfj6FQuJ5/O7R+dB1ueszilJmAFMtlU4CA6se3Jg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.9.1", "@typescript-eslint/types": "4.10.0",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
} }
}, },
@ -1215,9 +1129,9 @@
"dev": true "dev": true
}, },
"eslint-plugin-lit": { "eslint-plugin-lit": {
"version": "1.2.4", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.2.4.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.3.0.tgz",
"integrity": "sha512-LfmtuaW9ZcE8R2ji5cMj+SG5bSjEY+IBexuAOKKE+IUB8b6N0AnhVATbfaAX5V2KibeKvIBit2Xpo3QabpeiuA==", "integrity": "sha512-fy6Lr5vYI3kvCYaDXA20lwyKAp1keS9UjR5ntj8U2TeV+1yUta3S7xxXe+rABKRPbcNzi1ZvQLE1LmNKc9yr4Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"parse5": "^6.0.1", "parse5": "^6.0.1",
@ -2680,9 +2594,9 @@
} }
}, },
"rollup": { "rollup": {
"version": "2.34.2", "version": "2.35.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.34.2.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.35.1.tgz",
"integrity": "sha512-mvtQLqu3cNeoctS+kZ09iOPxrc1P1/Bt1z15enuQ5feyKOdM3MJAVFjjsygurDpSWn530xB4AlA83TWIzRstXA==", "integrity": "sha512-q5KxEyWpprAIcainhVy6HfRttD9kutQpHbeqDTWnqAFNJotiojetK6uqmcydNMymBEtC4I8bCYR+J3mTMqeaUA==",
"requires": { "requires": {
"fsevents": "~2.1.2" "fsevents": "~2.1.2"
} }
@ -3317,9 +3231,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "4.1.2", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {

View File

@ -8,35 +8,35 @@
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^5.15.1", "@fortawesome/fontawesome-free": "^5.15.1",
"@patternfly/patternfly": "^4.65.6", "@patternfly/patternfly": "^4.70.2",
"@sentry/browser": "^5.29.0", "@sentry/browser": "^5.29.2",
"@sentry/tracing": "^5.29.0", "@sentry/tracing": "^5.29.2",
"@types/chart.js": "^2.9.28", "@types/chart.js": "^2.9.29",
"@types/codemirror": "0.0.102", "@types/codemirror": "0.0.102",
"chart.js": "^2.9.4", "chart.js": "^2.9.4",
"codemirror": "^5.58.3", "codemirror": "^5.58.3",
"construct-style-sheets-polyfill": "^2.4.3", "construct-style-sheets-polyfill": "^2.4.3",
"lit-element": "^2.4.0", "lit-element": "^2.4.0",
"lit-html": "^1.3.0", "lit-html": "^1.3.0",
"rollup": "^2.34.2", "rollup": "^2.35.1",
"rollup-plugin-copy": "^3.3.0", "rollup-plugin-copy": "^3.3.0",
"rollup-plugin-cssimport": "^1.0.2", "rollup-plugin-cssimport": "^1.0.2",
"rollup-plugin-external-globals": "^0.6.1", "rollup-plugin-external-globals": "^0.6.1",
"tslib": "^2.0.3" "tslib": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-typescript": "^8.0.0", "@rollup/plugin-typescript": "^8.1.0",
"@typescript-eslint/eslint-plugin": "^4.9.1", "@typescript-eslint/eslint-plugin": "^4.10.0",
"@typescript-eslint/parser": "^4.9.1", "@typescript-eslint/parser": "^4.10.0",
"eslint": "^7.15.0", "eslint": "^7.15.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-lit": "^1.2.4", "eslint-plugin-lit": "^1.3.0",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-minify-html-literals": "^1.2.5", "rollup-plugin-minify-html-literals": "^1.2.5",
"rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-sourcemaps": "^0.6.3", "rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",
"typescript": "^4.1.2" "typescript": "^4.1.3"
} }
} }

View File

@ -1,4 +1,4 @@
import { DefaultClient, PBResponse, QueryArguments } from "./client"; import { DefaultClient, PBResponse, QueryArguments } from "./Client";
export class Application { export class Application {
pk: string; pk: string;

View File

@ -1,4 +1,4 @@
import { NotFoundError, RequestError } from "./errors"; import { NotFoundError, RequestError } from "./Error";
export const VERSION = "v2beta"; export const VERSION = "v2beta";

View File

@ -1,4 +1,4 @@
import { DefaultClient } from "./client"; import { DefaultClient } from "./Client";
import * as Sentry from "@sentry/browser"; import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing"; import { Integrations } from "@sentry/tracing";
import { VERSION } from "../constants"; import { VERSION } from "../constants";

View File

@ -1,4 +1,4 @@
import { DefaultClient } from "./client"; import { DefaultClient } from "./Client";
export class AuditEvent { export class AuditEvent {
//audit/events/top_per_user/?filter_action=authorize_application //audit/events/top_per_user/?filter_action=authorize_application

View File

@ -1,4 +1,4 @@
import { DefaultClient, PBResponse, QueryArguments } from "./client"; import { DefaultClient, PBResponse, QueryArguments } from "./Client";
export enum FlowDesignation { export enum FlowDesignation {
Authentication = "authentication", Authentication = "authentication",
@ -33,6 +33,12 @@ export class Flow {
static list(filter?: QueryArguments): Promise<PBResponse<Flow>> { static list(filter?: QueryArguments): Promise<PBResponse<Flow>> {
return DefaultClient.fetch<PBResponse<Flow>>(["flows", "instances"], filter); return DefaultClient.fetch<PBResponse<Flow>>(["flows", "instances"], filter);
} }
static cached(): Promise<number> {
return DefaultClient.fetch<PBResponse<Flow>>(["flows", "cached"]).then(r => {
return r.pagination.count;
});
}
} }
export class Stage { export class Stage {

24
web/src/api/Policies.ts Normal file
View File

@ -0,0 +1,24 @@
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
export class Policy {
pk: string;
name: string;
constructor() {
throw Error();
}
static get(pk: string): Promise<Policy> {
return DefaultClient.fetch<Policy>(["policies", "all", pk]);
}
static list(filter?: QueryArguments): Promise<PBResponse<Policy>> {
return DefaultClient.fetch<PBResponse<Policy>>(["policies", "all"], filter);
}
static cached(): Promise<number> {
return DefaultClient.fetch<PBResponse<Policy>>(["policies", "cached"]).then(r => {
return r.pagination.count;
});
}
}

View File

@ -1,10 +1,5 @@
import { DefaultClient, PBResponse, QueryArguments } from "./client"; import { DefaultClient, PBResponse, QueryArguments } from "./Client";
import { Policy } from "./Policies";
export interface Policy {
pk: string;
name: string;
[key: string]: unknown;
}
export class PolicyBinding { export class PolicyBinding {
pk: string; pk: string;

19
web/src/api/Providers.ts Normal file
View File

@ -0,0 +1,19 @@
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
export class Provider {
pk: number;
name: string;
authorization_flow: string;
constructor() {
throw Error();
}
static get(slug: string): Promise<Provider> {
return DefaultClient.fetch<Provider>(["providers", "all", slug]);
}
static list(filter?: QueryArguments): Promise<PBResponse<Provider>> {
return DefaultClient.fetch<PBResponse<Provider>>(["providers", "all"], filter);
}
}

View File

@ -1,4 +1,4 @@
import { DefaultClient, PBResponse, QueryArguments } from "./client"; import { DefaultClient, PBResponse, QueryArguments } from "./Client";
export class Source { export class Source {
pk: string; pk: string;

View File

@ -1,4 +1,4 @@
import { DefaultClient } from "./client"; import { DefaultClient } from "./Client";
interface TokenResponse { interface TokenResponse {
key: string; key: string;

View File

@ -1,4 +1,4 @@
import { DefaultClient, PBResponse } from "./client"; import { DefaultClient, PBResponse } from "./Client";
let _globalMePromise: Promise<User>; let _globalMePromise: Promise<User>;

17
web/src/api/Versions.ts Normal file
View File

@ -0,0 +1,17 @@
import { DefaultClient } from "./Client";
export class Version {
version_current: string;
version_latest: string;
outdated: boolean;
constructor() {
throw Error();
}
static get(): Promise<Version> {
return DefaultClient.fetch<Version>(["admin", "version"]);
}
}

View File

@ -1,20 +0,0 @@
import { DefaultClient } from "./client";
export class AdminOverview {
version: string;
version_latest: string;
worker_count: number;
providers_without_application: number;
policies_without_binding: number;
cached_policies: number;
cached_flows: number;
constructor() {
throw Error();
}
static get(): Promise<AdminOverview> {
return DefaultClient.fetch<AdminOverview>(["admin", "overview"]);
}
}

View File

@ -5,11 +5,6 @@ html {
--pf-c-nav__link--PaddingLeft: 0.5rem; --pf-c-nav__link--PaddingLeft: 0.5rem;
} }
/* Fix patternfly sidebar and header with open Modal */
.pf-c-page__sidebar {
z-index: 0;
}
.pf-c-page__header { .pf-c-page__header {
z-index: 0; z-index: 0;
} }
@ -86,6 +81,10 @@ select[multiple] {
font-size: var(--pf-global--FontSize--sm); font-size: var(--pf-global--FontSize--sm);
} }
.pf-c-page__main {
z-index: auto !important;
}
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root { :root {
--ak-dark-foreground: #fafafa; --ak-dark-foreground: #fafafa;
@ -162,10 +161,12 @@ select[multiple] {
background-color: var(--ak-dark-background-light); background-color: var(--ak-dark-background-light);
color: var(--ak-dark-foreground); color: var(--ak-dark-foreground);
} }
.pf-m-tertiary,
.pf-c-button.pf-m-tertiary { .pf-c-button.pf-m-tertiary {
--pf-c-button--after--BorderColor: var(--ak-dark-foreground-darker); --pf-c-button--after--BorderColor: var(--ak-dark-foreground-darker);
color: var(--ak-dark-foreground-darker); color: var(--ak-dark-foreground-darker);
} }
.pf-m-tertiary:hover,
.pf-c-button.pf-m-tertiary:hover { .pf-c-button.pf-m-tertiary:hover {
--pf-c-button--after--BorderColor: var(--ak-dark-background-lighter); --pf-c-button--after--BorderColor: var(--ak-dark-background-lighter);
} }
@ -197,6 +198,9 @@ select[multiple] {
.pf-c-login__main { .pf-c-login__main {
background-color: var(--ak-dark-background); background-color: var(--ak-dark-background);
} }
.pf-c-login__main-body {
color: var(--ak-dark-foreground);
}
.pf-c-login__main-footer-links-item-link > img { .pf-c-login__main-footer-links-item-link > img {
filter: invert(1); filter: invert(1);
} }

View File

@ -28,4 +28,4 @@ export const ColorStyles = css`
background-color: var(--pf-global--danger-color--100); background-color: var(--pf-global--danger-color--100);
} }
`; `;
export const VERSION = "0.13.0-rc4"; export const VERSION = "0.13.5-stable";

View File

@ -3,7 +3,7 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
// @ts-ignore // @ts-ignore
import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
import { tokenByIdentifier } from "../../api/token"; import { tokenByIdentifier } from "../../api/Tokens";
import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants";
@customElement("ak-token-copy-button") @customElement("ak-token-copy-button")

View File

@ -2,6 +2,7 @@ import { gettext } from "django";
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import { COMMON_STYLES } from "../../common/styles"; import { COMMON_STYLES } from "../../common/styles";
import { ColorStyles } from "../../constants";
@customElement("ak-aggregate-card") @customElement("ak-aggregate-card")
export class AggregateCard extends LitElement { export class AggregateCard extends LitElement {
@ -24,22 +25,26 @@ export class AggregateCard extends LitElement {
text-align: center; text-align: center;
color: var(--pf-global--Color--100); color: var(--pf-global--Color--100);
} }
`]); `, ColorStyles]);
} }
renderInner(): TemplateResult { renderInner(): TemplateResult {
return html`<slot></slot>`; return html`<slot></slot>`;
} }
renderHeaderLink(): TemplateResult {
return html`${this.headerLink ? html`<a href="${this.headerLink}">
<i class="fa fa-external-link-alt"> </i>
</a>` : ""}`;
}
render(): TemplateResult { render(): TemplateResult {
return html`<div class="pf-c-card pf-c-card-aggregate"> return html`<div class="pf-c-card pf-c-card-aggregate">
<div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between"> <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
<div class="pf-c-card__header-main"> <div class="pf-c-card__header-main">
<i class="${ifDefined(this.icon)}"></i> ${this.header ? gettext(this.header) : ""} <i class="${ifDefined(this.icon)}"></i> ${this.header ? gettext(this.header) : ""}
</div> </div>
${this.headerLink ? html`<a href="${this.headerLink}"> ${this.renderHeaderLink()}
<i class="fa fa-external-link-alt"> </i>
</a>` : ""}
</div> </div>
<div class="pf-c-card__body center-value"> <div class="pf-c-card__body center-value">
${this.renderInner()} ${this.renderInner()}

View File

@ -1,6 +1,6 @@
import { gettext } from "django"; import { gettext } from "django";
import { LitElement, html, customElement, TemplateResult, property } from "lit-element"; import { LitElement, html, customElement, TemplateResult, property } from "lit-element";
import { DefaultClient } from "../../api/client"; import { DefaultClient } from "../../api/Client";
import "./Message"; import "./Message";
import { APIMessage } from "./Message"; import { APIMessage } from "./Message";

View File

@ -1,8 +1,8 @@
import { gettext } from "django"; import { gettext } from "django";
import { customElement, html, property, TemplateResult } from "lit-element"; import { customElement, html, property, TemplateResult } from "lit-element";
import { PBResponse } from "../../api/client"; import { PBResponse } from "../../api/Client";
import { PolicyBinding } from "../../api/policy_binding";
import { Table } from "../../elements/table/Table"; import { Table } from "../../elements/table/Table";
import { PolicyBinding } from "../../api/PolicyBindings";
import "../../elements/Tabs"; import "../../elements/Tabs";
import "../../elements/AdminLoginsChart"; import "../../elements/AdminLoginsChart";

View File

@ -65,6 +65,32 @@ export class SidebarItem {
} }
}); });
} }
async render(activePath: string): Promise<TemplateResult> {
if (this.condition) {
const result = await this.condition();
if (!result) {
return html``;
}
}
return html` <li class="pf-c-nav__item ${this.hasChildren() ? "pf-m-expandable pf-m-expanded" : ""}">
${this.path ?
html`<a href="#${this.path}" class="pf-c-nav__link ${this.isActive(activePath) ? "pf-m-current" : ""}">
${this.name}
</a>` :
html`<a class="pf-c-nav__link" aria-expanded="true">
${this.name}
<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">
${this._children.map((i) => until(i.render(activePath), html``))}
</ul>
</section>`}
</li>`;
}
} }
@customElement("ak-sidebar") @customElement("ak-sidebar")
@ -118,37 +144,11 @@ export class Sidebar extends LitElement {
}); });
} }
async renderItem(item: SidebarItem): Promise<TemplateResult> {
if (item.condition) {
const result = await item.condition();
if (!result) {
return html``;
}
}
return html` <li class="pf-c-nav__item ${item.hasChildren() ? "pf-m-expandable pf-m-expanded" : ""}">
${item.path ?
html`<a href="#${item.path}" class="pf-c-nav__link ${item.isActive(this.activePath) ? "pf-m-current": ""}">
${item.name}
</a>` :
html`<a class="pf-c-nav__link" aria-expanded="true">
${item.name}
<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">
${item._children.map((i) => until(this.renderItem(i), html``))}
</ul>
</section>`}
</li>`;
}
render(): TemplateResult { render(): TemplateResult {
return html`<nav class="pf-c-nav" aria-label="Global"> return html`<nav class="pf-c-nav" aria-label="Global">
<ak-sidebar-brand></ak-sidebar-brand> <ak-sidebar-brand></ak-sidebar-brand>
<ul class="pf-c-nav__list"> <ul class="pf-c-nav__list">
${this.items.map((i) => until(this.renderItem(i), html``))} ${this.items.map((i) => until(i.render(this.activePath), html``))}
</ul> </ul>
<ak-sidebar-user></ak-sidebar-user> <ak-sidebar-user></ak-sidebar-user>
</nav>`; </nav>`;

View File

@ -3,7 +3,7 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu
import PageStyle from "@patternfly/patternfly/components/Page/page.css"; import PageStyle from "@patternfly/patternfly/components/Page/page.css";
// @ts-ignore // @ts-ignore
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
import { Config } from "../../api/config"; import { Config } from "../../api/Config";
export const DefaultConfig: Config = { export const DefaultConfig: Config = {
branding_logo: " /static/dist/assets/icons/icon_left_brand.svg", branding_logo: " /static/dist/assets/icons/icon_left_brand.svg",
@ -33,6 +33,7 @@ export class SidebarBrand extends LitElement {
.pf-c-brand img { .pf-c-brand img {
width: 100%; width: 100%;
padding: 0 .5rem; padding: 0 .5rem;
height: 42px;
} }
`, `,
]; ];

View File

@ -0,0 +1,35 @@
import { css, CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element";
import { COMMON_STYLES } from "../../common/styles";
@customElement("ak-sidebar-hamburger")
export class SidebarHamburger extends LitElement {
static get styles(): CSSResult[] {
return COMMON_STYLES.concat(
css`
:host {
position: absolute;
top: var(--pf-c-page__main-section--PaddingTop);
right: var(--pf-c-page__main-section--PaddingRight);
z-index: 250;
}
`
);
}
onClick(): void {
this.dispatchEvent(
new CustomEvent("ak-sidebar-toggle", {
bubbles: true,
composed: true,
})
);
}
render(): TemplateResult {
return html`<button @click=${() => (this.onClick())} class="pf-c-button pf-m-plain" type="button">
<i class="fas fa-bars" aria-hidden="true"></i>
</button>`;
}
}

View File

@ -5,7 +5,7 @@ import NavStyle from "@patternfly/patternfly/components/Nav/nav.css";
import fa from "@fortawesome/fontawesome-free/css/all.css"; import fa from "@fortawesome/fontawesome-free/css/all.css";
// @ts-ignore // @ts-ignore
import AvatarStyle from "@patternfly/patternfly/components/Avatar/avatar.css"; import AvatarStyle from "@patternfly/patternfly/components/Avatar/avatar.css";
import { User } from "../../api/user"; import { User } from "../../api/Users";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
@customElement("ak-sidebar-user") @customElement("ak-sidebar-user")

View File

@ -1,6 +1,6 @@
import { gettext } from "django"; import { gettext } from "django";
import { CSSResult, html, LitElement, property, TemplateResult } from "lit-element"; import { CSSResult, html, LitElement, property, TemplateResult } from "lit-element";
import { PBResponse } from "../../api/client"; import { PBResponse } from "../../api/Client";
import { COMMON_STYLES } from "../../common/styles"; import { COMMON_STYLES } from "../../common/styles";
import "./TablePagination"; import "./TablePagination";

View File

@ -1,6 +1,6 @@
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { COMMON_STYLES } from "../../common/styles"; import { COMMON_STYLES } from "../../common/styles";
import { PBPagination } from "../../api/client"; import { PBPagination } from "../../api/Client";
@customElement("ak-table-pagination") @customElement("ak-table-pagination")
export class TablePagination extends LitElement { export class TablePagination extends LitElement {

View File

@ -1,17 +1,19 @@
import { customElement } from "lit-element"; import { customElement } from "lit-element";
import { User } from "../api/user"; import { User } from "../api/Users";
import { SidebarItem } from "../elements/sidebar/Sidebar"; import { SidebarItem } from "../elements/sidebar/Sidebar";
import { SLUG_REGEX } from "../elements/router/Route"; import { SLUG_REGEX } from "../elements/router/Route";
import { Interface } from "./Interface"; import { Interface } from "./Interface";
export const SIDEBAR_ITEMS: SidebarItem[] = [ export const SIDEBAR_ITEMS: SidebarItem[] = [
new SidebarItem("Library", "/library/"), new SidebarItem("Library", "/library/"),
new SidebarItem("Monitor", "/audit/audit").when((): Promise<boolean> => { new SidebarItem("Monitor").children(
new SidebarItem("Overview", "/administration/overview/"),
new SidebarItem("System Tasks", "/administration/tasks/"),
new SidebarItem("Events", "/audit/audit"),
).when((): Promise<boolean> => {
return User.me().then(u => u.is_superuser); return User.me().then(u => u.is_superuser);
}), }),
new SidebarItem("Administration").children( new SidebarItem("Administration").children(
new SidebarItem("Overview", "/administration/overview-ng/"),
new SidebarItem("System Tasks", "/administration/tasks/"),
new SidebarItem("Applications", "/administration/applications/").activeWhen( new SidebarItem("Applications", "/administration/applications/").activeWhen(
`^/applications/(?<slug>${SLUG_REGEX})/$` `^/applications/(?<slug>${SLUG_REGEX})/$`
), ),
@ -19,27 +21,29 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [
`^/sources/(?<slug>${SLUG_REGEX})/$`, `^/sources/(?<slug>${SLUG_REGEX})/$`,
), ),
new SidebarItem("Providers", "/administration/providers/"), new SidebarItem("Providers", "/administration/providers/"),
new SidebarItem("Flows").children( new SidebarItem("Outposts", "/administration/outposts/"),
new SidebarItem("Flows", "/administration/flows/").activeWhen(`^/flows/(?<slug>${SLUG_REGEX})/$`), new SidebarItem("Outpost Service Connections", "/administration/outposts/service_connections/"),
new SidebarItem("Stages", "/administration/stages/"),
new SidebarItem("Prompts", "/administration/stages/prompts/"),
new SidebarItem("Invitations", "/administration/stages/invitations/"),
),
new SidebarItem("User Management").children(
new SidebarItem("User", "/administration/users/"),
new SidebarItem("Groups", "/administration/groups/")
),
new SidebarItem("Outposts").children(
new SidebarItem("Outposts", "/administration/outposts/"),
new SidebarItem("Service Connections", "/administration/outposts/service_connections/")
),
new SidebarItem("Policies", "/administration/policies/"), new SidebarItem("Policies", "/administration/policies/"),
new SidebarItem("Property Mappings", "/administration/property-mappings"), new SidebarItem("Property Mappings", "/administration/property-mappings"),
new SidebarItem("Certificates", "/administration/crypto/certificates"), new SidebarItem("Certificates", "/administration/crypto/certificates"),
new SidebarItem("Tokens", "/administration/tokens/"), new SidebarItem("Tokens", "/administration/tokens/"),
).when((): Promise<boolean> => { ).when((): Promise<boolean> => {
return User.me().then(u => u.is_superuser); return User.me().then(u => u.is_superuser);
}) }),
new SidebarItem("Flows").children(
new SidebarItem("Flows", "/administration/flows/").activeWhen(`^/flows/(?<slug>${SLUG_REGEX})/$`),
new SidebarItem("Stages", "/administration/stages/"),
new SidebarItem("Prompts", "/administration/stages/prompts/"),
new SidebarItem("Invitations", "/administration/stages/invitations/"),
).when((): Promise<boolean> => {
return User.me().then(u => u.is_superuser);
}),
new SidebarItem("User Management").children(
new SidebarItem("User", "/administration/users/"),
new SidebarItem("Groups", "/administration/groups/")
).when((): Promise<boolean> => {
return User.me().then(u => u.is_superuser);
}),
]; ];
@customElement("ak-interface-admin") @customElement("ak-interface-admin")

View File

@ -1,11 +1,14 @@
import { gettext } from "django"; import { gettext } from "django";
import { html, LitElement, TemplateResult } from "lit-element"; import { html, LitElement, property, TemplateResult } from "lit-element";
import { SidebarItem } from "../elements/sidebar/Sidebar"; import { SidebarItem } from "../elements/sidebar/Sidebar";
import "../elements/router/RouterOutlet"; import "../elements/router/RouterOutlet";
import "../elements/messages/MessageContainer"; import "../elements/messages/MessageContainer";
import "../elements/sidebar/SidebarHamburger";
export abstract class Interface extends LitElement { export abstract class Interface extends LitElement {
@property({type: Boolean})
sidebarOpen?: boolean;
abstract get sidebar(): SidebarItem[]; abstract get sidebar(): SidebarItem[];
@ -13,11 +16,24 @@ export abstract class Interface extends LitElement {
return this; return this;
} }
constructor() {
super();
this.sidebarOpen = window.outerWidth >= 1280;
window.addEventListener("resize", () => {
this.sidebarOpen = window.outerWidth >= 1280;
});
window.addEventListener("ak-sidebar-toggle", () => {
this.sidebarOpen = !this.sidebarOpen;
});
}
render(): TemplateResult { render(): TemplateResult {
return html`<ak-message-container></ak-message-container> return html`<ak-message-container></ak-message-container>
<div class="pf-c-page"> <div class="pf-c-page">
<a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content">${gettext("Skip to content")}</a> <a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content">${gettext("Skip to content")}</a>
<ak-sidebar class="pf-c-page__sidebar" .items=${this.sidebar}> <ak-sidebar-hamburger>
</ak-sidebar-hamburger>
<ak-sidebar class="pf-c-page__sidebar ${this.sidebarOpen ? "pf-m-expanded" : "pf-m-collapsed"}" .items=${this.sidebar}>
</ak-sidebar> </ak-sidebar>
<main class="pf-c-page__main"> <main class="pf-c-page__main">
<ak-router-outlet role="main" class="pf-c-page__main" tabindex="-1" id="main-content" defaultUrl="/library/"> <ak-router-outlet role="main" class="pf-c-page__main" tabindex="-1" id="main-content" defaultUrl="/library/">

View File

@ -1,8 +1,8 @@
import { gettext } from "django"; import { gettext } from "django";
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import { Application } from "../api/application"; import { Application } from "../api/Applications";
import { PBResponse } from "../api/client"; import { PBResponse } from "../api/Client";
import { COMMON_STYLES } from "../common/styles"; import { COMMON_STYLES } from "../common/styles";
import { loading, truncate } from "../utils"; import { loading, truncate } from "../utils";

View File

@ -1,71 +1,27 @@
import { gettext } from "django"; import { gettext } from "django";
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element";
import { AdminOverview } from "../../api/admin_overview"; import { DefaultClient } from "../../api/Client";
import { DefaultClient } from "../../api/client";
import { User } from "../../api/user";
import { COMMON_STYLES } from "../../common/styles"; import { COMMON_STYLES } from "../../common/styles";
import { AggregatePromiseCard } from "../../elements/cards/AggregatePromiseCard";
import { SpinnerSize } from "../../elements/Spinner";
import "../../elements/AdminLoginsChart"; import "../../elements/AdminLoginsChart";
import "../../elements/cards/AggregatePromiseCard";
import "./TopApplicationsTable"; import "./TopApplicationsTable";
import "./cards/AdminStatusCard";
@customElement("ak-admin-status-card") import "./cards/FlowCacheStatusCard";
export class AdminStatusCard extends AggregatePromiseCard { import "./cards/PolicyCacheStatusCard";
import "./cards/PolicyUnboundStatusCard";
@property({type: Number}) import "./cards/ProviderStatusCard";
value?: number; import "./cards/UserCountStatusCard";
import "./cards/VersionStatusCard";
@property() import "./cards/WorkerStatusCard";
warningText?: string;
@property({type: Number})
lessThanThreshold?: number;
renderNone(): TemplateResult {
return html`<ak-spinner size=${SpinnerSize.Large}></ak-spinner>`;
}
renderGood(): TemplateResult {
return html`<p class="ak-aggregate-card">
<i class="fa fa-check-circle"></i> ${this.value}
</p>`;
}
renderBad(): TemplateResult {
return html`<p class="ak-aggregate-card">
<i class="fa fa-exclamation-triangle"></i> ${this.value}
</p>
<p class="subtext">${this.warningText ? gettext(this.warningText) : ""}</p>`;
}
renderInner(): TemplateResult {
if (!this.value) {
return this.renderNone();
}
return html``;
}
}
@customElement("ak-admin-overview") @customElement("ak-admin-overview")
export class AdminOverviewPage extends LitElement { export class AdminOverviewPage extends LitElement {
@property({attribute: false})
data?: AdminOverview;
@property({attribute: false})
users?: Promise<number>;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return COMMON_STYLES; return COMMON_STYLES;
} }
firstUpdated(): void {
AdminOverview.get().then(value => this.data = value);
this.users = User.count();
}
render(): TemplateResult { render(): TemplateResult {
return html`<section class="pf-c-page__main-section pf-m-light"> return html`<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content"> <div class="pf-c-content">
@ -80,48 +36,20 @@ export class AdminOverviewPage extends LitElement {
<ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;"> <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;">
<ak-top-applications-table></ak-top-applications-table> <ak-top-applications-table></ak-top-applications-table>
</ak-aggregate-card> </ak-aggregate-card>
<ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Workers"> <ak-admin-status-card-provider class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Providers" headerLink="#/administration/providers/">
${this.data ? </ak-admin-status-card-provider>
this.data?.worker_count < 1 ? <ak-admin-status-card-policy-unbound class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-infrastructure" header="Policies" headerLink="#/administration/policies/">
html`<p class="ak-aggregate-card"> </ak-admin-status-card-policy-unbound>
<i class="fa fa-exclamation-triangle"></i> ${this.data?.worker_count} <ak-admin-status-card-user-count class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-user" header="Users" headerLink="#/administration/users/">
</p> </ak-admin-status-card-user-count>
<p class="subtext">${gettext("No workers connected.")}</p>` : <ak-admin-status-version class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-bundle" header="Version" headerLink="https://github.com/BeryJu/authentik/releases">
html`<p class="ak-aggregate-card"> </ak-admin-status-version>
<i class="fa fa-check-circle"></i> ${this.data?.worker_count} <ak-admin-status-card-workers class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Workers">
</p>` </ak-admin-status-card-workers>
: html`<ak-spinner size=${SpinnerSize.Large}></ak-spinner>`} <ak-admin-status-card-policy-cache class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Cached Policies">
</ak-aggregate-card> </ak-admin-status-card-policy-cache>
<ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Providers" headerLink="#/administration/providers/"> <ak-admin-status-card-flow-cache class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Cached Flows">
${this.data ? </ak-admin-status-card-flow-cache>
this.data?.providers_without_application > 1 ?
html`<p class="ak-aggregate-card">
<i class="fa fa-exclamation-triangle"></i> 0
</p>
<p class="subtext">${gettext("At least one Provider has no application assigned.")}</p>` :
html`<p class="ak-aggregate-card">
<i class="fa fa-check-circle"></i> 0
</p>`
: html`<ak-spinner size=${SpinnerSize.Large}></ak-spinner>`}
</ak-aggregate-card>
<ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Policies" headerLink="#/administration/policies/">
${this.data ?
this.data?.policies_without_binding > 1 ?
html`<p class="ak-aggregate-card">
<i class="fa fa-exclamation-triangle"></i> 0
</p>
<p class="subtext">${gettext("Policies without binding exist.")}</p>` :
html`<p class="ak-aggregate-card">
<i class="fa fa-check-circle"></i> 0
</p>`
: html`<ak-spinner size=${SpinnerSize.Large}></ak-spinner>`}
</ak-aggregate-card>
<ak-aggregate-card-promise
icon="pf-icon pf-icon-user"
header="Users"
headerLink="#/administration/users/"
.promise=${this.users}>
</ak-aggregate-card-promise>
</div> </div>
</section>`; </section>`;
} }

View File

@ -1,6 +1,6 @@
import { gettext } from "django"; import { gettext } from "django";
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { AuditEvent, TopNEvent } from "../../api/events"; import { AuditEvent, TopNEvent } from "../../api/Events";
import { COMMON_STYLES } from "../../common/styles"; import { COMMON_STYLES } from "../../common/styles";
import "../../elements/Spinner"; import "../../elements/Spinner";

View File

@ -0,0 +1,37 @@
import { html, TemplateResult } from "lit-html";
import { until } from "lit-html/directives/until";
import { AggregateCard } from "../../../elements/cards/AggregateCard";
import { SpinnerSize } from "../../../elements/Spinner";
export interface AdminStatus {
icon: string;
message?: string;
}
export abstract class AdminStatusCard<T> extends AggregateCard {
abstract getPrimaryValue(): Promise<T>;
abstract getStatus(value: T): Promise<AdminStatus>;
value?: T;
renderValue(): TemplateResult {
return html`${this.value}`;
}
renderInner(): TemplateResult {
return html`<p class="center-value">
${until(this.getPrimaryValue().then((v) => {
this.value = v;
return this.getStatus(v);
}).then((status) => {
return html`<p class="ak-aggregate-card">
<i class="${status.icon}"></i> ${this.renderValue()}
</p>
${status.message ? html`<p class="subtext">${status.message}</p>` : html``}`;
}), html`<ak-spinner size="${SpinnerSize.Large}"></ak-spinner>`)}
</p>`;
}
}

View File

@ -0,0 +1,36 @@
import { gettext } from "django";
import { customElement, html, TemplateResult } from "lit-element";
import { Flow } from "../../../api/Flows";
import { AdminStatus, AdminStatusCard } from "./AdminStatusCard";
import "../../../elements/buttons/ModalButton";
@customElement("ak-admin-status-card-flow-cache")
export class FlowCacheStatusCard extends AdminStatusCard<number> {
getPrimaryValue(): Promise<number> {
return Flow.cached();
}
getStatus(value: number): Promise<AdminStatus> {
if (value < 1) {
return Promise.resolve<AdminStatus>({
icon: "fa fa-exclamation-triangle pf-m-warning",
message: gettext("No flows cached."),
});
} else {
return Promise.resolve<AdminStatus>({
icon: "fa fa-check-circle pf-m-success"
});
}
}
renderHeaderLink(): TemplateResult {
return html`<ak-modal-button href="/administration/overview/cache/flow/">
<a slot="trigger">
<i class="fa fa-trash"> </i>
</a>
<div slot="modal"></div>
</ak-modal-button>`;
}
}

View File

@ -0,0 +1,37 @@
import { gettext } from "django";
import { customElement } from "lit-element";
import { TemplateResult, html } from "lit-html";
import { Policy } from "../../../api/Policies";
import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
import "../../../elements/buttons/ModalButton";
@customElement("ak-admin-status-card-policy-cache")
export class PolicyCacheStatusCard extends AdminStatusCard<number> {
getPrimaryValue(): Promise<number> {
return Policy.cached();
}
getStatus(value: number): Promise<AdminStatus> {
if (value < 1) {
return Promise.resolve<AdminStatus>({
icon: "fa fa-exclamation-triangle pf-m-warning",
message: gettext("No policies cached. Users may experience slow response times."),
});
} else {
return Promise.resolve<AdminStatus>({
icon: "fa fa-check-circle pf-m-success"
});
}
}
renderHeaderLink(): TemplateResult {
return html`<ak-modal-button href="/administration/overview/cache/policy/">
<a slot="trigger">
<i class="fa fa-trash"> </i>
</a>
<div slot="modal"></div>
</ak-modal-button>`;
}
}

View File

@ -0,0 +1,31 @@
import { gettext } from "django";
import { customElement } from "lit-element";
import { Policy } from "../../../api/Policies";
import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
@customElement("ak-admin-status-card-policy-unbound")
export class PolicyUnboundStatusCard extends AdminStatusCard<number> {
getPrimaryValue(): Promise<number> {
return Policy.list({
"bindings__isnull": true,
"promptstage__isnull": true,
}).then((response) => {
return response.pagination.count;
});
}
getStatus(value: number): Promise<AdminStatus> {
if (value > 0) {
return Promise.resolve<AdminStatus>({
icon: "fa fa-exclamation-triangle pf-m-warning",
message: gettext("Policies without binding exist."),
});
} else {
return Promise.resolve<AdminStatus>({
icon: "fa fa-check-circle pf-m-success"
});
}
}
}

View File

@ -0,0 +1,30 @@
import { gettext } from "django";
import { customElement } from "lit-element";
import { Provider } from "../../../api/Providers";
import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
@customElement("ak-admin-status-card-provider")
export class ProviderStatusCard extends AdminStatusCard<number> {
getPrimaryValue(): Promise<number> {
return Provider.list({
"application__isnull": true
}).then((response) => {
return response.pagination.count;
});
}
getStatus(value: number): Promise<AdminStatus> {
if (value > 0) {
return Promise.resolve<AdminStatus>({
icon: "fa fa-exclamation-triangle pf-m-warning",
message: gettext("Warning: At least one Provider has no application assigned."),
});
} else {
return Promise.resolve<AdminStatus>({
icon: "fa fa-check-circle pf-m-success"
});
}
}
}

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