Compare commits

..

126 Commits

Author SHA1 Message Date
8f99891a9d release: 2021.5.1-rc10 2021-05-12 21:25:18 +02:00
99d5262d41 ci: install git in final test containers
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 21:24:35 +02:00
97a3c2d88b release: 2021.5.1-rc9 2021-05-12 20:50:29 +02:00
e91ff4566d Merge branch 'next' into version-2021.5
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

# Conflicts:
#	outpost/pkg/version.go
2021-05-12 20:49:58 +02:00
dc942b2f4c outposts: build as gh-<commit hash>
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 20:37:55 +02:00
a3fccbdaff outposts: add build_hash for docker image
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 20:36:18 +02:00
bdf9f26d07 outposts: compare build hash in outdated check
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 19:05:29 +02:00
901cea1453 outposts: send build hash as part of hello
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 19:02:04 +02:00
37b57ac28f outposts: include git commit hash in build from git branch
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 18:56:44 +02:00
e9aa37ba67 outposts/ldap: fix user info caching, fix mixed case DN
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#864
2021-05-12 18:49:15 +02:00
9a0aa4c79b outposts/ldap: add infinite loop prevention
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 18:31:44 +02:00
34ab68a169 outposts: cleanup logging
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 18:01:46 +02:00
52cf4890cf root: remove servername from backup files
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 17:53:23 +02:00
8e5d03cb86 outposts: remove legacy API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 16:41:54 +02:00
2190fa555b events/api: fix error when updating transports
closes #866

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 16:41:30 +02:00
ae1edde17b ci: install git in container for dbbackup
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 16:30:51 +02:00
3ad1c3f212 web/admin: fix AuthenticatorValidationStage's form not setting notConfiguredAction
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#802
2021-05-12 16:28:14 +02:00
3665e2fefa release: 2021.5.1-rc8 2021-05-12 14:52:34 +02:00
3dbe35cf9e stages/invitation: fix wrong serializer used for user model
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

# Conflicts:
#	swagger.yaml
2021-05-12 14:22:16 +02:00
c7f0ea8a4b root: update dbbackup to git version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 01:20:31 +02:00
0620324702 root: bump version of psf black
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 00:42:46 +02:00
5a802bcf83 web/admin: fix list of outpost status
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 22:59:45 +02:00
00c8054893 web/admin: fix border on dark mode in firefox
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 22:27:33 +02:00
dc2538f59d web/admin: fix outpost health not updating on refresh
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 21:53:19 +02:00
5a0e78c698 outposts: fix issue with duplicate outpost health
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 21:46:30 +02:00
fd4e8a59f4 web/admin: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 20:09:49 +02:00
dd1a6a81c8 outposts/proxy: improve host header detection
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 20:02:36 +02:00
84dfbcaaae providers/api: return redirect_uris for proxy provider
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 20:02:17 +02:00
e649e9fb03 core: don't use self.get_object for application permission check to prevent 404 when view permission is missing
closes #864

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 17:35:11 +02:00
266ef66a6f Merge branch 'master' into next 2021-05-11 14:57:52 +02:00
842fdb0b0c fixed session durations of more than 1 day (#863) 2021-05-11 14:57:33 +02:00
a270a84aae website/docs: update link for saml provider metadata
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#857
2021-05-11 14:23:39 +02:00
36f7cad23b Merge pull request #862 from goauthentik/form-refresh-on-save
Form refresh on save
2021-05-11 14:23:32 +02:00
e441ac1e43 web/admin: add download links for certificates
closes #861

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 14:21:48 +02:00
24f2932777 crypto: add ?download flag
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#861
2021-05-11 14:21:35 +02:00
a6c6f22221 web/admin: add button to copy saml metadata download link
closes #857

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:52:47 +02:00
abd5db8ad4 website/docs: update link for saml provider metadata
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#857
2021-05-11 13:44:51 +02:00
124ce80694 sources/plex: make plex_token readable from API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:32:28 +02:00
4352960f83 web/admin: fix error when updating oauth source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:31:33 +02:00
4e2443d60b flows: make cancel link always logout user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:13:05 +02:00
34a8408a4f Merge branch 'next' into form-refresh-on-save 2021-05-11 13:07:57 +02:00
17b65adcc5 lib: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:07:47 +02:00
6f8d129dea web/admin: migrate remaining forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:44:50 +02:00
59f339beda web/admin: migrate stage forms to ModelForm
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:35:53 +02:00
ce1c400022 web/admin: migrate policy forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:19:35 +02:00
c99afe0ad4 web/admin: remove unused imports
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:12:31 +02:00
ff9ff18c11 web/admin: migrate more forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:05:30 +02:00
4d11d82c6e web/admin: migrate more forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 11:55:25 +02:00
b4d750174f web/admin: add modelform as base, start migrating
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 11:48:34 +02:00
fd44765ff4 Merge branch 'next' into form-refresh-on-save 2021-05-11 11:47:29 +02:00
190ebb27e4 Merge branch 'master' into next 2021-05-11 11:47:10 +02:00
fb3c04d0c7 build(deps): bump postcss from 8.2.14 to 8.2.15 in /website (#858) 2021-05-11 10:46:06 +02:00
3ba8de61e0 build(deps): bump eslint-plugin-lit from 1.3.0 to 1.4.0 in /web (#859) 2021-05-11 10:45:46 +02:00
d4d2be84a3 build(deps): bump boto3 from 1.17.69 to 1.17.70 (#860) 2021-05-11 10:45:33 +02:00
96ea7ae09c root: allow configuration of s3 backup location
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 02:10:00 +02:00
172bfceb31 root: fix db backup failing when password has special chars
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 02:01:22 +02:00
932b19999e providers/proxy: missing @property for noop
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 01:26:01 +02:00
0f1cc86e71 outposts/ak: updater providers automatically every 150 seconds
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 01:07:26 +02:00
788fd00390 outposts: use noop flag in each reconciler instead of raising Disabled and force use of get_referecen_object
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 00:27:29 +02:00
f602e202b8 website/docs: use beryju.org directly for beta
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 00:11:42 +02:00
9b60fcb08b root: only install latest postgresql client, since they are backwards compatible
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 23:24:27 +02:00
a293a14f2a outposts: re-add _config for backwards compat
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 22:28:46 +02:00
65bfa589eb Merge branch 'master' into next 2021-05-10 20:35:11 +02:00
defca51d24 build(deps): bump @sentry/browser from 6.3.5 to 6.3.6 in /web (#855)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.3.5 to 6.3.6.
- [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/6.3.5...6.3.6)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:34:09 +02:00
d862028134 build(deps): bump @typescript-eslint/eslint-plugin in /web (#856)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.22.1 to 4.23.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.23.0/packages/eslint-plugin)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:33:59 +02:00
c19d7c37aa build(deps): bump @sentry/tracing from 6.3.5 to 6.3.6 in /web (#853)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.3.5 to 6.3.6.
- [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/6.3.5...6.3.6)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:30:55 +02:00
6fb3102d25 build(deps): bump @typescript-eslint/parser in /web (#854)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.22.1 to 4.23.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.23.0/packages/parser)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:30:41 +02:00
51e3453dca admin: fix linting in api tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 20:14:21 +02:00
6f58fdf158 api: add more tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:51:29 +02:00
5d4051f547 ci: test and lint at the same time
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:36:28 +02:00
219b8d1a57 outposts: allow individual components of managed outposts to be disabled
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:27:48 +02:00
c7d4e69669 root: make database port configurable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:25:15 +02:00
cd629dfbaa outposts: improve API validation for config attribute, ensure all required attributes are set
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:24:42 +02:00
8eaaaae2a7 outpost: add trace log level
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 18:09:52 +02:00
3d0a853449 Merge branch 'version-2021.5' into next 2021-05-10 18:07:39 +02:00
c2f8ff55cf outposts: fix outpost delete hanging thread, run cleanup in async task with info from cache with ability to retry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 17:11:31 +02:00
4b52697cfe web/elements: add refresh support to chart
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 15:57:52 +02:00
80fae44f47 release: 2021.5.1-rc7 2021-05-10 12:13:10 +02:00
afd7af557d ci: login to ghcr
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 12:13:03 +02:00
73eb97ca6e release: 2021.5.1-rc6 2021-05-10 11:44:23 +02:00
ebe90d8886 Merge branch 'next' into version-2021.5 2021-05-10 11:43:50 +02:00
a1a1b113b1 release: 2021.5.1-rc5 2021-05-10 11:34:00 +02:00
9adf8e88ba ci: remove arm v8
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 11:33:21 +02:00
72d87ee51d ci: test arm/v8 with libpq
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 11:23:15 +02:00
9654285535 Merge branch 'master' into next 2021-05-10 11:22:16 +02:00
6e47e69c62 build(deps-dev): bump prettier from 2.2.1 to 2.3.0 in /website (#852)
Bumps [prettier](https://github.com/prettier/prettier) from 2.2.1 to 2.3.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.2.1...2.3.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 09:05:00 +02:00
1ba89a02ee root: install libpq-dev in docker
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 00:38:58 +02:00
1fb3642701 sources/oauth: fix google tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 00:27:37 +02:00
847d97b813 sources/oauth: fix google tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 00:27:20 +02:00
253060def2 website: add service-account for outposts in other cluster
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 00:16:52 +02:00
2e70ea799a ci: try arm64 only
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 00:06:49 +02:00
7364914ae8 Merge branch 'master' into next 2021-05-10 00:02:53 +02:00
1f1d322958 *: fix api results when non-superuser
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 00:01:35 +02:00
e4841ce1a4 Merge branch 'version-2021.5' into next 2021-05-09 23:41:23 +02:00
af30b781b6 ci: only arm only v8
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 23:40:27 +02:00
5f490c563e ci: build for arm v6 and v8
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 23:32:52 +02:00
e33a5528f7 core: catch IntegrityError in flow_manager and deny request
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 23:31:39 +02:00
d4de243e3b ci: always run on release for version branches but don't push images
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 23:09:48 +02:00
317117ee68 build(deps): bump eslint from 7.25.0 to 7.26.0 in /web (#848)
Bumps [eslint](https://github.com/eslint/eslint) from 7.25.0 to 7.26.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v7.25.0...v7.26.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-09 23:03:39 +02:00
40d03a6124 build(deps): bump service-identity from 18.1.0 to 21.1.0 (#849)
Bumps [service-identity](https://github.com/pyca/service-identity) from 18.1.0 to 21.1.0.
- [Release notes](https://github.com/pyca/service-identity/releases)
- [Changelog](https://github.com/pyca/service-identity/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/service-identity/compare/18.1.0...21.1.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-09 23:03:03 +02:00
9cfeeb35ba ci: fix invalid workflow file
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 22:56:50 +02:00
b7d828702d sources/oauth: don't set username on google source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 22:56:44 +02:00
19dfeec782 build(deps): bump django-otp from 1.0.4 to 1.0.5 (#850)
Bumps [django-otp](https://github.com/django-otp/django-otp) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/django-otp/django-otp/releases)
- [Changelog](https://github.com/django-otp/django-otp/blob/master/CHANGES.rst)
- [Commits](https://github.com/django-otp/django-otp/compare/v1.0.4...v1.0.5)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-09 22:55:30 +02:00
07eef2869f build(deps): bump boto3 from 1.17.68 to 1.17.69 (#851)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.68 to 1.17.69.
- [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.17.68...1.17.69)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-09 22:55:19 +02:00
f7fd31cc84 release: 2021.5.1-rc4 2021-05-09 21:43:38 +02:00
465d9c2b93 ci: use local context for docker build
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 21:42:22 +02:00
04aae8f584 sources/oauth: make secret write_only
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 21:40:25 +02:00
bbca90c93a Merge branch 'next' into version-2021.5 2021-05-09 20:57:23 +02:00
dda1d4e0fb core: add more logs to flow_manager
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 20:27:37 +02:00
f072c600cc lifecycle: use URl for redis on startup to prevent errors with no paswords
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 20:13:58 +02:00
65b8a5bb8d outposts/proxy: redirect to protocol based on X-Forwarded-Proto
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 19:12:35 +02:00
92537a6c8d Merge branch 'next' into version-2021.5 2021-05-09 18:46:26 +02:00
72836ecd9d outposts: default to currently running namespace if possible
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 18:44:32 +02:00
251a97c77e Merge branch 'next' into version-2021.5 2021-05-09 18:13:52 +02:00
7f7046f0e4 outposts: lowercase k8s object names
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 18:13:21 +02:00
20e59158c2 root: add github actions to dependabot
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 18:08:06 +02:00
9a9e55ae32 ci: bump qemu action version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 17:53:57 +02:00
481260a5ca ci: bump checkout actions
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 17:51:56 +02:00
436adcce2e website/docs: fix URL for new chart repo
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 17:32:14 +02:00
cd3f02fd3b release: 2021.5.1-rc3 2021-05-09 17:25:48 +02:00
7abfd24150 ci: only build arm64 and arm
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 17:23:19 +02:00
d3feab9463 release: 2021.5.1-rc2 2021-05-09 16:43:36 +02:00
189427609f ci: fix paths for go build
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 16:41:52 +02:00
d76a9c211a ci: fix web api client not being generated for general build
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 16:41:45 +02:00
ef7d9c4d35 ci: fix mixed environment variables
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 16:37:03 +02:00
d4493c0ee9 web/admin: add new base form to handle refresh events
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 12:59:00 +02:00
171 changed files with 3743 additions and 3542 deletions

View File

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

View File

@ -1,5 +1,13 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
assignees:
- BeryJu
- package-ecosystem: gomod
directory: "/outpost"
schedule:

View File

@ -3,36 +3,49 @@ name: authentik-on-release
on:
release:
types: [published, created]
push:
branches:
- version-*
jobs:
# Build
build-server:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v1.1.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Docker Login Registry
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_PASSWORD }}
password: ${{ secrets.DOCKER_USERNAME }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: prepare ts api client
run: |
docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/api --additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0
- name: Building Docker Image
uses: docker/build-push-action@v2
with:
push: true
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik:2021.5.1-rc1,
beryju/authentik:2021.5.1-rc10,
beryju/authentik:latest,
ghcr.io/goauthentik/server:2021.5.1-rc1,
ghcr.io/goauthentik/server:2021.5.1-rc10,
ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
platforms: linux/amd64,linux/arm64
context: .
build-proxy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: "^1.15"
@ -41,32 +54,38 @@ jobs:
cd outpost
go get -u github.com/go-swagger/go-swagger/cmd/swagger
swagger generate client -f ../swagger.yaml -A authentik -t pkg/
go build -v .
go build -v ./cmd/proxy/server.go
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v1.1.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Docker Login Registry
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_PASSWORD }}
password: ${{ secrets.DOCKER_USERNAME }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Building Docker Image
uses: docker/build-push-action@v2
with:
push: true
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-proxy:2021.5.1-rc1,
beryju/authentik-proxy:2021.5.1-rc10,
beryju/authentik-proxy:latest,
ghcr.io/goauthentik/proxy:2021.5.1-rc1,
ghcr.io/goauthentik/proxy:2021.5.1-rc10,
ghcr.io/goauthentik/proxy:latest
context: outpost/
file: outpost/proxy.Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
platforms: linux/amd64,linux/arm64
build-ldap:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: "^1.15"
@ -75,36 +94,43 @@ jobs:
cd outpost
go get -u github.com/go-swagger/go-swagger/cmd/swagger
swagger generate client -f ../swagger.yaml -A authentik -t pkg/
go build -v .
go build -v ./cmd/ldap/server.go
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v1.1.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Docker Login Registry
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_PASSWORD }}
password: ${{ secrets.DOCKER_USERNAME }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Building Docker Image
uses: docker/build-push-action@v2
with:
push: true
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-ldap:2021.5.1-rc1,
beryju/authentik-ldap:2021.5.1-rc10,
beryju/authentik-ldap:latest,
ghcr.io/goauthentik/ldap:2021.5.1-rc1,
ghcr.io/goauthentik/ldap:2021.5.1-rc10,
ghcr.io/goauthentik/ldap:latest
context: outpost/
file: outpost/ldap.Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
platforms: linux/amd64,linux/arm64
test-release:
if: ${{ github.event_name == 'release' }}
needs:
- build-server
- build-proxy
- build-ldap
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Run test suite in final docker images
run: |
sudo apt-get install -y pwgen
@ -113,13 +139,14 @@ jobs:
docker-compose pull -q
docker-compose up --no-start
docker-compose start postgresql redis
docker-compose run -u root --entrypoint /bin/bash server -c "pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
docker-compose run -u root --entrypoint /bin/bash server -c "apt-get update && apt-get install -y --no-install-recommends git && pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
sentry-release:
if: ${{ github.event_name == 'release' }}
needs:
- test-release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Create a Sentry.io release
uses: getsentry/action-release@v1
env:
@ -128,5 +155,5 @@ jobs:
SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org
with:
version: authentik@2021.5.1-rc1
version: authentik@2021.5.1-rc10
environment: beryjuorg-prod

View File

@ -10,7 +10,7 @@ jobs:
name: Create Release from Tag
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: prepare ts api client
run: |
docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/api --additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0
@ -27,7 +27,7 @@ jobs:
-f Dockerfile .
docker-compose up --no-start
docker-compose start postgresql redis
docker-compose run -u root --entrypoint /bin/bash server -c "pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
docker-compose run -u root --entrypoint /bin/bash server -c "apt-get update && apt-get install -y --no-install-recommends git && pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
- name: Extract version number
id: get_version
uses: actions/github-script@0.2.0

View File

@ -48,15 +48,16 @@ ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
RUN apt-get update && \
apt-get install -y --no-install-recommends curl ca-certificates gnupg && \
apt-get install -y --no-install-recommends curl ca-certificates gnupg git && \
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
apt-get update && \
apt-get install -y --no-install-recommends postgresql-client-12 postgresql-client-11 build-essential libxmlsec1-dev pkg-config libmaxminddb0 && \
apt-get clean && \
apt-get install -y --no-install-recommends libpq-dev postgresql-client build-essential libxmlsec1-dev pkg-config libmaxminddb0 && \
pip install -r /requirements.txt --no-cache-dir && \
apt-get remove --purge -y build-essential && \
apt-get remove --purge -y build-essential git && \
apt-get autoremove --purge -y && \
apt-get clean && \
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \
# This is quite hacky, but docker has no guaranteed Group ID
# we could instead check for the GID of the socket and add the user dynamically,
# but then we have to drop permmissions later

View File

@ -1,3 +1,6 @@
.SHELLFLAGS += -x -e
PWD = $(shell pwd)
all: lint-fix lint test gen
test-integration:
@ -24,6 +27,14 @@ lint:
gen:
./manage.py generate_swagger -o swagger.yaml -f yaml
docker run \
--rm -v ${PWD}:/local \
openapitools/openapi-generator-cli generate \
-i /local/swagger.yaml \
-g typescript-fetch \
-o /local/web/api \
--additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0
cd web/api && npx tsc
local-stack:
export AUTHENTIK_TAG=testing

View File

@ -11,7 +11,7 @@ channels-redis = "*"
dacite = "*"
defusedxml = "*"
django = "*"
django-dbbackup = "*"
django-dbbackup = { git = 'https://github.com/django-dbbackup/django-dbbackup.git', ref = '9d1909c30a3271c8c9c8450add30d6e0b996e145' }
django-filter = "*"
django-guardian = "*"
django-model-utils = "*"
@ -50,7 +50,7 @@ python_version = "3.9"
[dev-packages]
bandit = "*"
black = "==20.8b1"
black = "==21.5b1"
bump2version = "*"
colorama = "*"
coverage = "*"

282
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "17be2923cf8d281e430ec1467aea723806ac6f7c58fc6553ede92317e43f4d14"
"sha256": "8a32708c1c04f8da03c817df973de28c37c97ee773f571ce0b3f3f834e1b7094"
},
"pipfile-spec": 6,
"requires": {
@ -56,6 +56,7 @@
"sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a",
"sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"
],
"markers": "python_version >= '3.6'",
"version": "==3.7.4.post0"
},
"aioredis": {
@ -70,6 +71,7 @@
"sha256:03e16e94f2b34c31f8bf1206d8ddd3ccaa4c315f7f6a1879b7b1210d229568c2",
"sha256:493a2ac6788ce270a2f6a765b017299f60c1998f5a8617908ee9be082f7300fb"
],
"markers": "python_version >= '3.6'",
"version": "==5.0.6"
},
"asgiref": {
@ -77,6 +79,7 @@
"sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee",
"sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"
],
"markers": "python_version >= '3.6'",
"version": "==3.3.4"
},
"async-timeout": {
@ -84,20 +87,23 @@
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
"markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1"
},
"attrs": {
"hashes": [
"sha256:3901be1cb7c2a780f14668691474d9252c070a756be0a9ead98cfeabfa11aeb8",
"sha256:8ee1e5f5a1afc5b19bdfae4fdf0c35ed324074bdce3500c939842c8f818645d9"
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
],
"version": "==21.1.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.2.0"
},
"autobahn": {
"hashes": [
"sha256:9195df8af03b0ff29ccd4b7f5abbde957ee90273465942205f9a1bad6c3f07ac",
"sha256:e126c1f583e872fb59e79d36977cfa1f2d0a8a79f90ae31f406faae7664b8e03"
],
"markers": "python_version >= '3.7'",
"version": "==21.3.1"
},
"automat": {
@ -116,24 +122,26 @@
},
"boto3": {
"hashes": [
"sha256:56f1766f1271b6b4e979c7b56225377f8912050e5935adc5c1c9e3a0338b949e",
"sha256:c61c809d288e88b9a0d926f56f803d0128b498aa9b45a42a6e03cd9a83e5c124"
"sha256:bcb1b76ca5a60586181ad202b19f4c50cb7c22ac68cae20f997abe311e22bf2a",
"sha256:edf9b3b36e08cd575a9458bf59871852335aceb5db2d07bfc8530bae3a97d045"
],
"index": "pypi",
"version": "==1.17.68"
"version": "==1.17.71"
},
"botocore": {
"hashes": [
"sha256:0f693f5ad6348ec1a62b3a66fee2840d3b722d66b44896022d644275ff8b143d",
"sha256:eb3544911cb0316a33b328a27d137130af278a9c0006be0c95e5e402b01d9865"
"sha256:414e1721d381095767db1cf673257fdfec639da3be9405a41d49cc859b817d68",
"sha256:b7afebca1fd6ca1f8af79f377a445d474e3bd2cf88e704169d6713a6362a304f"
],
"version": "==1.20.68"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.20.71"
},
"cachetools": {
"hashes": [
"sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001",
"sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"
],
"markers": "python_version ~= '3.5'",
"version": "==4.2.2"
},
"cbor2": {
@ -220,6 +228,7 @@
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0"
},
"click": {
@ -227,6 +236,7 @@
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==7.1.2"
},
"click-didyoumean": {
@ -300,6 +310,7 @@
"sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f",
"sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393"
],
"markers": "python_version >= '3.6'",
"version": "==3.0.2"
},
"defusedxml": {
@ -319,11 +330,11 @@
"version": "==3.2.2"
},
"django-dbbackup": {
"git": "https://github.com/django-dbbackup/django-dbbackup.git",
"hashes": [
"sha256:bb109735cae98b64ad084e5b461b7aca2d7b39992f10c9ed9435e3ebb6fb76c8"
],
"index": "pypi",
"version": "==3.3.0"
"ref": "9d1909c30a3271c8c9c8450add30d6e0b996e145"
},
"django-filter": {
"hashes": [
@ -351,11 +362,11 @@
},
"django-otp": {
"hashes": [
"sha256:04852c5301befb02d1d8ba4a31d375eb08d7c2cb6fe86b5f840867435ab1309c",
"sha256:3916fc7652c2f934b1cf3807dd8ed257ce7605c10dfefa27fadda5628d9a9c9e"
"sha256:75a815747a0542cc5442e3a6396dfd272c49a0866bee2149ac57ecc36ddd3961",
"sha256:cc657a0e7266cda6ab42f861bdc3840ed24f7e441bc7f249916174dd1a6375a0"
],
"index": "pypi",
"version": "==1.0.4"
"version": "==1.0.5"
},
"django-prometheus": {
"hashes": [
@ -425,6 +436,7 @@
"hashes": [
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.18.2"
},
"geoip2": {
@ -440,6 +452,7 @@
"sha256:588bdb03a41ecb4978472b847881e5518b5d9ec6153d3d679aa127a55e13b39f",
"sha256:9ad25fba07f46a628ad4d0ca09f38dcb262830df2ac95b217f9b0129c9e42206"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.30.0"
},
"gunicorn": {
@ -455,6 +468,7 @@
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
],
"markers": "python_version >= '3.6'",
"version": "==0.12.0"
},
"hiredis": {
@ -501,6 +515,7 @@
"sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0",
"sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a"
],
"markers": "python_version >= '3.6'",
"version": "==2.0.0"
},
"httptools": {
@ -549,6 +564,7 @@
"sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417",
"sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"
],
"markers": "python_version >= '3.5'",
"version": "==0.5.1"
},
"itypes": {
@ -560,16 +576,18 @@
},
"jinja2": {
"hashes": [
"sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
"sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
"sha256:2f2de5285cf37f33d33ecd4a9080b75c87cd0c1994d5a9c6df17131ea1f049c6",
"sha256:ea8d7dd814ce9df6de6a761ec7f1cac98afe305b8cdc4aaae4e114b8d8ce24c5"
],
"version": "==2.11.3"
"markers": "python_version >= '3.6'",
"version": "==3.0.0"
},
"jmespath": {
"hashes": [
"sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
"sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.0"
},
"jsonschema": {
@ -584,6 +602,7 @@
"sha256:6dc509178ac4269b0e66ab4881f70a2035c33d3a622e20585f965986a5182006",
"sha256:f4965fba0a4718d47d470beeb5d6446e3357a62402b16c510b6a2f251e05ac3c"
],
"markers": "python_version >= '3.6'",
"version": "==5.0.2"
},
"kubernetes": {
@ -597,6 +616,9 @@
"ldap3": {
"hashes": [
"sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91",
"sha256:4139c91f0eef9782df7b77c8cbc6243086affcb6a8a249b768a9658438e5da59",
"sha256:8c949edbad2be8a03e719ba48bd6779f327ec156929562814b3e84ab56889c8c",
"sha256:afc6fc0d01f02af82cd7bfabd3bbfd5dc96a6ae91e97db0a2dab8a0f1b436056",
"sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57"
],
"index": "pypi",
@ -656,65 +678,49 @@
},
"markupsafe": {
"hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
"sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f",
"sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014",
"sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85",
"sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850",
"sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1",
"sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
"sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5",
"sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c",
"sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
"sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
"sha256:007dc055dbce5b1104876acee177dbfd18757e19d562cd440182e1f492e96b95",
"sha256:031bf79a27d1c42f69c276d6221172417b47cb4b31cdc73d362a9bf5a1889b9f",
"sha256:161d575fa49395860b75da5135162481768b11208490d5a2143ae6785123e77d",
"sha256:24bbc3507fb6dfff663af7900a631f2aca90d5a445f272db5fc84999fa5718bc",
"sha256:2efaeb1baff547063bad2b2893a8f5e9c459c4624e1a96644bbba08910ae34e0",
"sha256:32200f562daaab472921a11cbb63780f1654552ae49518196fc361ed8e12e901",
"sha256:3261fae28155e5c8634dd7710635fe540a05b58f160cef7713c7700cb9980e66",
"sha256:3b54a9c68995ef4164567e2cd1a5e16db5dac30b2a50c39c82db8d4afaf14f63",
"sha256:3c352ff634e289061711608f5e474ec38dbaa21e3e168820d53d5f4015e5b91b",
"sha256:3fb47f97f1d338b943126e90b79cad50d4fcfa0b80637b5a9f468941dbbd9ce5",
"sha256:441ce2a8c17683d97e06447fcbccbdb057cbf587c78eb75ae43ea7858042fe2c",
"sha256:45535241baa0fc0ba2a43961a1ac7562ca3257f46c4c3e9c0de38b722be41bd1",
"sha256:4aca81a687975b35e3e80bcf9aa93fe10cd57fac37bf18b2314c186095f57e05",
"sha256:4cc563836f13c57f1473bc02d1e01fc37bab70ad4ee6be297d58c1d66bc819bf",
"sha256:4fae0677f712ee090721d8b17f412f1cbceefbf0dc180fe91bab3232f38b4527",
"sha256:58bc9fce3e1557d463ef5cee05391a05745fd95ed660f23c1742c711712c0abb",
"sha256:664832fb88b8162268928df233f4b12a144a0c78b01d38b81bdcf0fc96668ecb",
"sha256:70820a1c96311e02449591cbdf5cd1c6a34d5194d5b55094ab725364375c9eb2",
"sha256:79b2ae94fa991be023832e6bcc00f41dbc8e5fe9d997a02db965831402551730",
"sha256:83cf0228b2f694dcdba1374d5312f2277269d798e65f40344964f642935feac1",
"sha256:87de598edfa2230ff274c4de7fcf24c73ffd96208c8e1912d5d0fee459767d75",
"sha256:8f806bfd0f218477d7c46a11d3e52dc7f5fdfaa981b18202b7dc84bbc287463b",
"sha256:90053234a6479738fd40d155268af631c7fca33365f964f2208867da1349294b",
"sha256:a00dce2d96587651ef4fa192c17e039e8cfab63087c67e7d263a5533c7dad715",
"sha256:a08cd07d3c3c17cd33d9e66ea9dee8f8fc1c48e2d11bd88fd2dc515a602c709b",
"sha256:a19d39b02a24d3082856a5b06490b714a9d4179321225bbf22809ff1e1887cc8",
"sha256:d00a669e4a5bec3ee6dbeeeedd82a405ced19f8aeefb109a012ea88a45afff96",
"sha256:dab0c685f21f4a6c95bfc2afd1e7eae0033b403dd3d8c1b6d13a652ada75b348",
"sha256:df561f65049ed3556e5b52541669310e88713fdae2934845ec3606f283337958",
"sha256:e4570d16f88c7f3032ed909dc9e905a17da14a1c4cfd92608e3fda4cb1208bbd",
"sha256:e77e4b983e2441aff0c0d07ee711110c106b625f440292dfe02a2f60c8218bd6",
"sha256:e79212d09fc0e224d20b43ad44bb0a0a3416d1e04cf6b45fed265114a5d43d20",
"sha256:f58b5ba13a5689ca8317b98439fccfbcc673acaaf8241c1869ceea40f5d585bf",
"sha256:fef86115fdad7ae774720d7103aa776144cf9b66673b4afa9bcaa7af990ed07b"
],
"version": "==1.1.1"
"markers": "python_version >= '3.6'",
"version": "==2.0.0"
},
"maxminddb": {
"hashes": [
"sha256:47e86a084dd814fac88c99ea34ba3278a74bc9de5a25f4b815b608798747c7dc"
],
"markers": "python_version >= '3.6'",
"version": "==2.0.3"
},
"msgpack": {
@ -790,6 +796,7 @@
"sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281",
"sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"
],
"markers": "python_version >= '3.6'",
"version": "==5.1.0"
},
"oauthlib": {
@ -797,6 +804,7 @@
"sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889",
"sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.1.0"
},
"packaging": {
@ -812,6 +820,7 @@
"sha256:030e4f9df5f53db2292eec37c6255957eb76168c6f974e4176c711cf91ed34aa",
"sha256:b6c5a9643e3545bcbfd9451766cbaa5d9c67e7303c7bc32c750b6fa70ecb107d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.1"
},
"prompt-toolkit": {
@ -819,6 +828,7 @@
"sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04",
"sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"
],
"markers": "python_full_version >= '3.6.1'",
"version": "==3.0.18"
},
"psycopg2-binary": {
@ -864,15 +874,37 @@
},
"pyasn1": {
"hashes": [
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
"sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"
"sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
"sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
],
"version": "==0.4.8"
},
"pyasn1-modules": {
"hashes": [
"sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
"sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199",
"sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
"sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
"sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4",
"sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
"sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"
"sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74",
"sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb",
"sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
"sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
"sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
"sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
"sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"
],
"version": "==0.2.8"
},
@ -881,6 +913,7 @@
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.20"
},
"pycryptodome": {
@ -924,6 +957,7 @@
"sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316",
"sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29"
],
"markers": "python_version >= '3.5'",
"version": "==2.0.2"
},
"pyjwt": {
@ -946,12 +980,14 @@
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pyrsistent": {
"hashes": [
"sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"
],
"markers": "python_version >= '3.5'",
"version": "==0.17.3"
},
"python-dateutil": {
@ -959,6 +995,7 @@
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.1"
},
"python-dotenv": {
@ -1015,6 +1052,7 @@
"sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2",
"sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==3.5.3"
},
"requests": {
@ -1022,12 +1060,14 @@
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.25.1"
},
"requests-oauthlib": {
"hashes": [
"sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
"sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"
"sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a",
"sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"
],
"index": "pypi",
"version": "==1.3.0"
@ -1045,6 +1085,7 @@
"sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28",
"sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22"
],
"markers": "python_version >= '3'",
"version": "==0.17.4"
},
"ruamel.yaml.clib": {
@ -1081,7 +1122,7 @@
"sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2",
"sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f"
],
"markers": "platform_python_implementation == 'CPython' and python_version < '3.10'",
"markers": "python_version < '3.10' and platform_python_implementation == 'CPython'",
"version": "==0.2.2"
},
"s3transfer": {
@ -1101,17 +1142,18 @@
},
"service-identity": {
"hashes": [
"sha256:001c0707759cb3de7e49c078a7c0c9cd12594161d3bf06b9c254fdcb1a60dc36",
"sha256:0858a54aabc5b459d1aafa8a518ed2081a285087f349fe3e55197989232e2e2d"
"sha256:6e6c6086ca271dc11b033d17c3a8bea9f24ebff920c587da090afc9519419d34",
"sha256:f0b0caac3d40627c3c04d7a51b6e06721857a0e10a8775f2d1d7e72901b3a7db"
],
"index": "pypi",
"version": "==18.1.0"
"version": "==21.1.0"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"sqlparse": {
@ -1119,6 +1161,7 @@
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
],
"markers": "python_version >= '3.5'",
"version": "==0.4.1"
},
"structlog": {
@ -1174,6 +1217,7 @@
"sha256:7d6f89745680233f1c4db9ddb748df5e88d2a7a37962be174c0fd04c8dba1dc8",
"sha256:c16b55f9a67b2419cfdf8846576e2ec9ba94fe6978a83080c352a80db31c93fb"
],
"markers": "python_version >= '3.6'",
"version": "==21.2.1"
},
"typing-extensions": {
@ -1189,6 +1233,7 @@
"sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f",
"sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.0.1"
},
"urllib3": {
@ -1233,6 +1278,7 @@
"sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30",
"sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"
],
"markers": "python_version >= '3.6'",
"version": "==5.0.0"
},
"watchgod": {
@ -1262,6 +1308,7 @@
"sha256:2e50d26ca593f70aba7b13a489435ef88b8fc3b5c5643c1ce8808ff9b40f0b32",
"sha256:d376bd60eace9d437ab6d7ee16f4ab4e821c9dae591e1b783c58ebd8aaf80c5c"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.59.0"
},
"websockets": {
@ -1348,6 +1395,7 @@
"sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a",
"sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"
],
"markers": "python_version >= '3.6'",
"version": "==1.6.3"
},
"zope.interface": {
@ -1404,6 +1452,7 @@
"sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4",
"sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==5.4.0"
}
},
@ -1420,14 +1469,16 @@
"sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e",
"sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"
],
"markers": "python_version ~= '3.6'",
"version": "==2.5.6"
},
"attrs": {
"hashes": [
"sha256:3901be1cb7c2a780f14668691474d9252c070a756be0a9ead98cfeabfa11aeb8",
"sha256:8ee1e5f5a1afc5b19bdfae4fdf0c35ed324074bdce3500c939842c8f818645d9"
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
],
"version": "==21.1.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.2.0"
},
"bandit": {
"hashes": [
@ -1439,10 +1490,11 @@
},
"black": {
"hashes": [
"sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"
"sha256:23695358dbcb3deafe7f0a3ad89feee5999a46be5fec21f4f1d108be0bcdb3b1",
"sha256:8a60071a0043876a4ae96e6c69bd3a127dad2c1ca7c8083573eb82f92705d008"
],
"index": "pypi",
"version": "==20.8b1"
"version": "==21.5b1"
},
"bump2version": {
"hashes": [
@ -1464,6 +1516,7 @@
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0"
},
"click": {
@ -1471,6 +1524,7 @@
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==7.1.2"
},
"colorama": {
@ -1544,14 +1598,16 @@
"sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0",
"sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"
],
"markers": "python_version >= '3.4'",
"version": "==4.0.7"
},
"gitpython": {
"hashes": [
"sha256:05af150f47a5cca3f4b0af289b73aef8cf3c4fe2385015b06220cbcdee48bb6e",
"sha256:a77824e516d3298b04fb36ec7845e92747df8fcfee9cacc32dd6239f9652f867"
"sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b",
"sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"
],
"version": "==3.1.15"
"markers": "python_version >= '3.4'",
"version": "==3.1.14"
},
"idna": {
"hashes": [
@ -1572,6 +1628,7 @@
"sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6",
"sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"
],
"markers": "python_version >= '3.6' and python_version < '4.0'",
"version": "==5.8.0"
},
"lazy-object-proxy": {
@ -1599,6 +1656,7 @@
"sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93",
"sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.6.0"
},
"mccabe": {
@ -1635,6 +1693,7 @@
"sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd",
"sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"
],
"markers": "python_version >= '2.6'",
"version": "==5.6.0"
},
"pluggy": {
@ -1642,6 +1701,7 @@
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1"
},
"py": {
@ -1649,6 +1709,7 @@
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.10.0"
},
"pylint": {
@ -1679,6 +1740,7 @@
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pytest": {
@ -1783,6 +1845,7 @@
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.25.1"
},
"requests-mock": {
@ -1806,6 +1869,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"smmap": {
@ -1813,6 +1877,7 @@
"sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182",
"sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"
],
"markers": "python_version >= '3.5'",
"version": "==4.0.0"
},
"stevedore": {
@ -1820,6 +1885,7 @@
"sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee",
"sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"
],
"markers": "python_version >= '3.6'",
"version": "==3.3.0"
},
"toml": {
@ -1827,51 +1893,9 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2"
},
"typed-ast": {
"hashes": [
"sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace",
"sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff",
"sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266",
"sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528",
"sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6",
"sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808",
"sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4",
"sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363",
"sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341",
"sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04",
"sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41",
"sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e",
"sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3",
"sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899",
"sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805",
"sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c",
"sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c",
"sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39",
"sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a",
"sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3",
"sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7",
"sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f",
"sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075",
"sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0",
"sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40",
"sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428",
"sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927",
"sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3",
"sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f",
"sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"
],
"version": "==1.4.3"
},
"typing-extensions": {
"hashes": [
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
],
"version": "==3.10.0.0"
},
"urllib3": {
"extras": [
"secure"

View File

@ -1,3 +1,3 @@
"""authentik"""
__version__ = "2021.5.1-rc1"
__version__ = "2021.5.1-rc10"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -7,6 +7,7 @@ from django.urls import reverse
from authentik import __version__
from authentik.core.models import Group, User
from authentik.core.tasks import clean_expired_models
from authentik.events.monitored_tasks import TaskResultStatus
class TestAdminAPI(TestCase):
@ -30,6 +31,26 @@ class TestAdminAPI(TestCase):
any(task["task_name"] == "clean_expired_models" for task in body)
)
def test_tasks_single(self):
"""Test Task API (read single)"""
clean_expired_models.delay()
response = self.client.get(
reverse(
"authentik_api:admin_system_tasks-detail",
kwargs={"pk": "clean_expired_models"},
)
)
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertEqual(body["status"], TaskResultStatus.SUCCESSFUL.name)
self.assertEqual(body["task_name"], "clean_expired_models")
response = self.client.get(
reverse(
"authentik_api:admin_system_tasks-detail", kwargs={"pk": "qwerqwer"}
)
)
self.assertEqual(response.status_code, 404)
def test_tasks_retry(self):
"""Test Task API (retry)"""
clean_expired_models.delay()

View File

@ -54,4 +54,4 @@ class AuthentikTokenAuthentication(BaseAuthentication):
if not token:
return None
return (token.user, None)
return (token.user, None) # pragma: no cover

View File

@ -22,3 +22,10 @@ class TestSwaggerGeneration(APITestCase):
reverse("authentik_api:schema-json", kwargs={"format": ".json"}),
)
self.assertTrue(loads(response.content.decode()))
def test_browser(self):
"""Test API Browser"""
response = self.client.get(
reverse("authentik_api:swagger"),
)
self.assertEqual(response.status_code, 200)

View File

@ -4,6 +4,7 @@ from typing import Optional
from django.core.cache import cache
from django.db.models import QuerySet
from django.http.response import HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from drf_yasg import openapi
from drf_yasg.utils import no_body, swagger_auto_schema
from rest_framework.decorators import action
@ -101,7 +102,9 @@ class ApplicationViewSet(ModelViewSet):
# pylint: disable=unused-argument
def check_access(self, request: Request, slug: str) -> Response:
"""Check access to a single application by slug"""
application = self.get_object()
# Don't use self.get_object as that checks for view_application permission
# which the user might not have, even if they have access
application = get_object_or_404(Application, slug=slug)
engine = PolicyEngine(application, self.request.user, self.request)
engine.build()
if engine.passing:

View File

@ -3,6 +3,7 @@ from enum import Enum
from typing import Any, Optional, Type
from django.contrib import messages
from django.db import IntegrityError
from django.db.models.query_utils import Q
from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest
from django.shortcuts import redirect
@ -116,9 +117,11 @@ class SourceFlowManager:
)
return Action.DENY, None
query = Q(username__exact=self.enroll_info.get("username", None))
self._logger.debug("trying to link with existing user", query=query)
matching_users = User.objects.filter(query)
# No matching users, always enroll
if not matching_users.exists():
self._logger.debug("no matching users found, enrolling")
return Action.ENROLL, self.update_connection(new_connection, **kwargs)
user = matching_users.first()
@ -147,7 +150,11 @@ class SourceFlowManager:
def get_flow(self, **kwargs) -> HttpResponse:
"""Get the flow response based on user_matching_mode"""
action, connection = self.get_action(**kwargs)
try:
action, connection = self.get_action(**kwargs)
except IntegrityError as exc:
self._logger.warning("failed to get action", exc=exc)
return redirect("/")
self._logger.debug("get_action() says", action=action, connection=connection)
if connection:
if action == Action.LINK:

View File

@ -24,7 +24,7 @@ class TestApplicationsAPI(APITestCase):
)
def test_check_access(self):
"""Test check_access operation """
"""Test check_access operation"""
self.client.force_login(self.user)
response = self.client.get(
reverse(

View File

@ -21,7 +21,7 @@ class TestModels(TestCase):
self.assertTrue(token.is_expired)
def test_token_expire_no_expire(self):
"""Test token expiring with "expiring" set """
"""Test token expiring with "expiring" set"""
token = Token.objects.create(
expires=now(), user=get_anonymous_user(), expiring=False
)

View File

@ -3,7 +3,9 @@ import django_filters
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import load_pem_x509_certificate
from django.http.response import HttpResponse
from django.utils.translation import gettext_lazy as _
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.fields import (
@ -145,7 +147,16 @@ class CertificateKeyPairViewSet(ModelViewSet):
serializer = self.get_serializer(instance)
return Response(serializer.data)
@swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)})
@swagger_auto_schema(
manual_parameters=[
openapi.Parameter(
name="download",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
],
responses={200: CertificateDataSerializer(many=False)},
)
@action(detail=True, pagination_class=None, filter_backends=[])
# pylint: disable=invalid-name, unused-argument
def view_certificate(self, request: Request, pk: str) -> Response:
@ -156,11 +167,29 @@ class CertificateKeyPairViewSet(ModelViewSet):
secret=certificate,
type="certificate",
).from_http(request)
if "download" in request._request.GET:
# Mime type from https://pki-tutorial.readthedocs.io/en/latest/mime.html
response = HttpResponse(
certificate.certificate_data, content_type="application/x-pem-file"
)
response[
"Content-Disposition"
] = f'attachment; filename="{certificate.name}_certificate.pem"'
return response
return Response(
CertificateDataSerializer({"data": certificate.certificate_data}).data
)
@swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)})
@swagger_auto_schema(
manual_parameters=[
openapi.Parameter(
name="download",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
],
responses={200: CertificateDataSerializer(many=False)},
)
@action(detail=True, pagination_class=None, filter_backends=[])
# pylint: disable=invalid-name, unused-argument
def view_private_key(self, request: Request, pk: str) -> Response:
@ -171,4 +200,13 @@ class CertificateKeyPairViewSet(ModelViewSet):
secret=certificate,
type="private_key",
).from_http(request)
if "download" in request._request.GET:
# Mime type from https://pki-tutorial.readthedocs.io/en/latest/mime.html
response = HttpResponse(
certificate.key_data, content_type="application/x-pem-file"
)
response[
"Content-Disposition"
] = f'attachment; filename="{certificate.name}_private_key.pem"'
return response
return Response(CertificateDataSerializer({"data": certificate.key_data}).data)

View File

@ -2,7 +2,9 @@
import datetime
from django.test import TestCase
from django.urls import reverse
from authentik.core.models import User
from authentik.crypto.api import CertificateKeyPairSerializer
from authentik.crypto.builder import CertificateBuilder
from authentik.crypto.models import CertificateKeyPair
@ -47,3 +49,45 @@ class TestCrypto(TestCase):
now = datetime.datetime.today()
self.assertEqual(instance.name, "test-cert")
self.assertEqual((instance.certificate.not_valid_after - now).days, 2)
def test_certificate_download(self):
"""Test certificate export (download)"""
self.client.force_login(User.objects.get(username="akadmin"))
keypair = CertificateKeyPair.objects.first()
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-certificate",
kwargs={"pk": keypair.pk},
)
)
self.assertEqual(200, response.status_code)
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-certificate",
kwargs={"pk": keypair.pk},
)
+ "?download",
)
self.assertEqual(200, response.status_code)
self.assertIn("Content-Disposition", response)
def test_private_key_download(self):
"""Test private_key export (download)"""
self.client.force_login(User.objects.get(username="akadmin"))
keypair = CertificateKeyPair.objects.first()
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-private-key",
kwargs={"pk": keypair.pk},
)
)
self.assertEqual(200, response.status_code)
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-private-key",
kwargs={"pk": keypair.pk},
)
+ "?download",
)
self.assertEqual(200, response.status_code)
self.assertIn("Content-Disposition", response)

View File

@ -1,7 +1,9 @@
"""Notification API Views"""
from django_filters.rest_framework import DjangoFilterBackend
from guardian.utils import get_anonymous_user
from rest_framework import mixins
from rest_framework.fields import ReadOnlyField
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import GenericViewSet
@ -47,6 +49,11 @@ class NotificationViewSet(
"event",
"seen",
]
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
SearchFilter,
]
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()

View File

@ -2,22 +2,25 @@
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.groups import GroupSerializer
from authentik.events.models import NotificationRule
class NotificationRuleSerializer(ModelSerializer):
"""NotificationRule Serializer"""
group_obj = GroupSerializer(read_only=True, source="group")
class Meta:
model = NotificationRule
depth = 2
fields = [
"pk",
"name",
"transports",
"severity",
"group",
"group_obj",
]

View File

@ -298,7 +298,7 @@ class CancelView(View):
if SESSION_KEY_PLAN in request.session:
del request.session[SESSION_KEY_PLAN]
LOGGER.debug("Canceled current plan")
return redirect("authentik_core:root-redirect")
return redirect("authentik_core:default-invalidation")
class ToDefaultFlow(View):

View File

@ -3,6 +3,7 @@ postgresql:
host: localhost
name: authentik
user: authentik
port: 5432
password: 'env://POSTGRES_PASSWORD'
web:
@ -41,6 +42,7 @@ outposts:
# Placeholders:
# %(type)s: Outpost type; proxy, ldap, etc
# %(version)s: Current version; 2021.4.1
# %(build_hash)s: Build hash if you're running a beta version
docker_image_base: "beryju/authentik-%(type)s:%(version)s"
authentik:

View File

@ -1,23 +1,33 @@
"""Outpost API Views"""
from dacite.core import from_dict
from dacite.exceptions import DaciteError
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.fields import BooleanField, CharField, DateTimeField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import JSONField, ModelSerializer
from rest_framework.serializers import JSONField, ModelSerializer, ValidationError
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.utils import PassiveSerializer, is_dict
from authentik.outposts.models import Outpost, default_outpost_config
from authentik.outposts.models import Outpost, OutpostConfig, default_outpost_config
class OutpostSerializer(ModelSerializer):
"""Outpost Serializer"""
_config = JSONField(validators=[is_dict])
config = JSONField(validators=[is_dict], source="_config")
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
def validate_config(self, config) -> dict:
"""Check that the config has all required fields"""
try:
from_dict(OutpostConfig, config)
except DaciteError as exc:
raise ValidationError(f"Failed to validate config: {str(exc)}") from exc
return config
class Meta:
model = Outpost
@ -29,7 +39,7 @@ class OutpostSerializer(ModelSerializer):
"providers_obj",
"service_connection",
"token_identifier",
"_config",
"config",
]

View File

@ -42,6 +42,8 @@ class OutpostConsumer(AuthJsonConsumer):
outpost: Optional[Outpost] = None
last_uid: Optional[str] = None
def connect(self):
super().connect()
uuid = self.scope["url_route"]["kwargs"]["pk"]
@ -52,9 +54,7 @@ class OutpostConsumer(AuthJsonConsumer):
raise DenyConnection()
self.accept()
self.outpost = outpost.first()
OutpostState(
uid=self.channel_name, last_seen=datetime.now(), _outpost=self.outpost
).save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)
self.last_uid = self.channel_name
LOGGER.debug(
"added outpost instace to cache",
outpost=self.outpost,
@ -63,27 +63,29 @@ class OutpostConsumer(AuthJsonConsumer):
# pylint: disable=unused-argument
def disconnect(self, close_code):
if self.outpost:
OutpostState.for_channel(self.outpost, self.channel_name).delete()
if self.outpost and self.last_uid:
OutpostState.for_channel(self.outpost, self.last_uid).delete()
LOGGER.debug(
"removed outpost instance from cache",
outpost=self.outpost,
channel_name=self.channel_name,
instance_uuid=self.last_uid,
)
def receive_json(self, content: Data):
msg = from_dict(WebsocketMessage, content)
uid = msg.args.get("uuid", self.channel_name)
self.last_uid = uid
state = OutpostState(
uid=self.channel_name,
uid=uid,
last_seen=datetime.now(),
_outpost=self.outpost,
)
if msg.instruction == WebsocketMessageInstruction.HELLO:
state.version = msg.args.get("version", None)
state.build_hash = msg.args.get("buildHash", "")
elif msg.instruction == WebsocketMessageInstruction.ACK:
return
if state.version:
state.save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)
state.save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)
response = WebsocketMessage(instruction=WebsocketMessageInstruction.ACK)
self.send_json(asdict(response))

View File

@ -1,11 +1,12 @@
"""Base Controller"""
from dataclasses import dataclass
from os import environ
from typing import Optional
from structlog.stdlib import get_logger
from structlog.testing import capture_logs
from authentik import __version__
from authentik import ENV_GIT_HASH_KEY, __version__
from authentik.lib.config import CONFIG
from authentik.lib.sentry import SentryIgnoredException
from authentik.outposts.models import Outpost, OutpostServiceConnection
@ -56,6 +57,12 @@ class BaseController:
"""Handler to delete everything we've created"""
raise NotImplementedError
def down_with_logs(self) -> list[str]:
"""Call .down() but capture all log output and return it."""
with capture_logs() as logs:
self.down()
return [x["event"] for x in logs]
def get_static_deployment(self) -> str:
"""Return a static deployment configuration"""
raise NotImplementedError
@ -63,4 +70,8 @@ class BaseController:
def get_container_image(self) -> str:
"""Get container image to use for this outpost"""
image_name_template: str = CONFIG.y("outposts.docker_image_base")
return image_name_template % {"type": self.outpost.type, "version": __version__}
return image_name_template % {
"type": self.outpost.type,
"version": __version__,
"build_hash": environ.get(ENV_GIT_HASH_KEY, ""),
}

View File

@ -30,11 +30,6 @@ class NeedsUpdate(ReconcileTrigger):
"""Exception to trigger an update to the Kubernetes Object"""
class Disabled(SentryIgnoredException):
"""Exception which can be thrown in a reconciler to signal than an
object should not be created."""
class KubernetesObjectReconciler(Generic[T]):
"""Base Kubernetes Reconciler, handles the basic logic."""
@ -45,22 +40,29 @@ class KubernetesObjectReconciler(Generic[T]):
self.namespace = controller.outpost.config.kubernetes_namespace
self.logger = get_logger().bind(type=self.__class__.__name__)
@property
def noop(self) -> bool:
"""Return true if this object should not be created/updated/deleted in this cluster"""
return False
@property
def name(self) -> str:
"""Get the name of the object this reconciler manages"""
return self.controller.outpost.config.object_naming_template % {
"name": slugify(self.controller.outpost.name),
"uuid": self.controller.outpost.uuid.hex,
}
return (
self.controller.outpost.config.object_naming_template
% {
"name": slugify(self.controller.outpost.name),
"uuid": self.controller.outpost.uuid.hex,
}
).lower()
def up(self):
"""Create object if it doesn't exist, update if needed or recreate if needed."""
current = None
try:
reference = self.get_reference_object()
except Disabled:
self.logger.debug("Object not required")
if self.noop:
self.logger.debug("Object is noop")
return
reference = self.get_reference_object()
try:
try:
current = self.retrieve()
@ -89,11 +91,8 @@ class KubernetesObjectReconciler(Generic[T]):
def down(self):
"""Delete object if found"""
# Call self.get_reference_object to check if we even need to do anything
try:
self.get_reference_object()
except Disabled:
self.logger.debug("Object not required")
if self.noop:
self.logger.debug("Object is noop")
return
try:
current = self.retrieve()

View File

@ -0,0 +1,11 @@
"""k8s utils"""
from pathlib import Path
def get_namespace() -> str:
"""Get the namespace if we're running in a pod, otherwise default to default"""
path = Path("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
if path.exists():
with open(path, "r") as _namespace_file:
return _namespace_file.read()
return "default"

View File

@ -8,7 +8,7 @@ from structlog.testing import capture_logs
from yaml import dump_all
from authentik.outposts.controllers.base import BaseController, ControllerException
from authentik.outposts.controllers.k8s.base import Disabled, KubernetesObjectReconciler
from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
from authentik.outposts.controllers.k8s.secret import SecretReconciler
from authentik.outposts.controllers.k8s.service import ServiceReconciler
@ -49,6 +49,9 @@ class KubernetesController(BaseController):
try:
all_logs = []
for reconcile_key in self.reconcile_order:
if reconcile_key in self.outpost.config.kubernetes_disabled_components:
all_logs += [f"{reconcile_key.title()}: Disabled"]
continue
with capture_logs() as logs:
reconciler = self.reconcilers[reconcile_key](self)
reconciler.up()
@ -61,19 +64,34 @@ class KubernetesController(BaseController):
try:
for reconcile_key in self.reconcile_order:
reconciler = self.reconcilers[reconcile_key](self)
self.logger.debug("Tearing down object", name=reconcile_key)
reconciler.down()
except ApiException as exc:
raise ControllerException(str(exc)) from exc
def down_with_logs(self) -> list[str]:
try:
all_logs = []
for reconcile_key in self.reconcile_order:
if reconcile_key in self.outpost.config.kubernetes_disabled_components:
all_logs += [f"{reconcile_key.title()}: Disabled"]
continue
with capture_logs() as logs:
reconciler = self.reconcilers[reconcile_key](self)
reconciler.down()
all_logs += [f"{reconcile_key.title()}: {x['event']}" for x in logs]
return all_logs
except ApiException as exc:
raise ControllerException(str(exc)) from exc
def get_static_deployment(self) -> str:
documents = []
for reconcile_key in self.reconcile_order:
reconciler = self.reconcilers[reconcile_key](self)
try:
documents.append(reconciler.get_reference_object().to_dict())
except Disabled:
if reconciler.noop:
continue
documents.append(reconciler.get_reference_object().to_dict())
with StringIO() as _str:
dump_all(

View File

@ -1,6 +1,7 @@
"""Outpost models"""
from dataclasses import asdict, dataclass, field
from datetime import datetime
from os import environ
from typing import Iterable, Optional, Union
from uuid import uuid4
@ -26,13 +27,14 @@ from packaging.version import LegacyVersion, Version, parse
from structlog.stdlib import get_logger
from urllib3.exceptions import HTTPError
from authentik import __version__
from authentik import ENV_GIT_HASH_KEY, __version__
from authentik.core.models import USER_ATTRIBUTE_SA, Provider, Token, TokenIntents, User
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.config import CONFIG
from authentik.lib.models import InheritanceForeignKey
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.http import USER_ATTRIBUTE_CAN_OVERRIDE_IP
from authentik.outposts.controllers.k8s.utils import get_namespace
from authentik.outposts.docker_tls import DockerInlineTLS
OUR_VERSION = parse(__version__)
@ -41,7 +43,7 @@ LOGGER = get_logger()
class ServiceConnectionInvalid(SentryIgnoredException):
""""Exception raised when a Service Connection has invalid parameters"""
"""Exception raised when a Service Connection has invalid parameters"""
@dataclass
@ -59,10 +61,11 @@ class OutpostConfig:
object_naming_template: str = field(default="ak-outpost-%(name)s")
kubernetes_replicas: int = field(default=1)
kubernetes_namespace: str = field(default="default")
kubernetes_namespace: str = field(default_factory=get_namespace)
kubernetes_ingress_annotations: dict[str, str] = field(default_factory=dict)
kubernetes_ingress_secret_name: str = field(default="authentik-outpost-tls")
kubernetes_service_type: str = field(default="ClusterIP")
kubernetes_disabled_components: list[str] = field(default_factory=list)
class OutpostModel(Model):
@ -409,6 +412,7 @@ class OutpostState:
last_seen: Optional[datetime] = field(default=None)
version: Optional[str] = field(default=None)
version_should: Union[Version, LegacyVersion] = field(default=OUR_VERSION)
build_hash: str = field(default="")
_outpost: Optional[Outpost] = field(default=None)
@ -417,6 +421,8 @@ class OutpostState:
"""Check if outpost version matches our version"""
if not self.version:
return False
if self.build_hash != environ.get(ENV_GIT_HASH_KEY, ""):
return False
return parse(self.version) < OUR_VERSION
@staticmethod

View File

@ -1,5 +1,5 @@
"""authentik outpost signals"""
from django.conf import settings
from django.core.cache import cache
from django.db.models import Model
from django.db.models.signals import post_save, pre_delete, pre_save
from django.dispatch import receiver
@ -8,9 +8,12 @@ from structlog.stdlib import get_logger
from authentik.core.models import Provider
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.utils.reflection import class_to_path
from authentik.outposts.controllers.base import ControllerException
from authentik.outposts.models import Outpost, OutpostServiceConnection
from authentik.outposts.tasks import outpost_controller_down, outpost_post_save
from authentik.outposts.tasks import (
CACHE_KEY_OUTPOST_DOWN,
outpost_controller,
outpost_post_save,
)
LOGGER = get_logger()
UPDATE_TRIGGERING_MODELS = (
@ -39,7 +42,8 @@ def pre_save_outpost(sender, instance: Outpost, **_):
)
if bool(dirty):
LOGGER.info("Outpost needs re-deployment due to changes", instance=instance)
outpost_controller_down_wrapper(old_instance)
cache.set(CACHE_KEY_OUTPOST_DOWN % instance.pk.hex, old_instance)
outpost_controller.delay(instance.pk.hex, action="down", from_cache=True)
@receiver(post_save)
@ -63,23 +67,5 @@ def post_save_update(sender, instance: Model, **_):
def pre_delete_cleanup(sender, instance: Outpost, **_):
"""Ensure that Outpost's user is deleted (which will delete the token through cascade)"""
instance.user.delete()
outpost_controller_down_wrapper(instance)
def outpost_controller_down_wrapper(instance: Outpost):
"""To ensure that deployment is cleaned up *consistently* we call the controller, and wait
for it to finish. We don't want to call it in this thread, as we don't have the Outpost
Service connection here"""
try:
outpost_controller_down.delay(instance.pk.hex).get()
except RuntimeError: # pragma: no cover
# In e2e/integration tests, this might run inside a thread/process and
# trigger the celery `Never call result.get() within a task` detection
if settings.TEST:
pass
else:
raise
except ControllerException as exc:
LOGGER.warning(
"failed to cleanup outpost deployment", exc=exc, instance=instance
)
cache.set(CACHE_KEY_OUTPOST_DOWN % instance.pk.hex, instance)
outpost_controller.delay(instance.pk.hex, action="down", from_cache=True)

View File

@ -36,6 +36,7 @@ from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesCont
from authentik.root.celery import CELERY_APP
LOGGER = get_logger()
CACHE_KEY_OUTPOST_DOWN = "outpost_teardown_%s"
def controller_for_outpost(outpost: Outpost) -> Optional[BaseController]:
@ -56,13 +57,6 @@ def controller_for_outpost(outpost: Outpost) -> Optional[BaseController]:
return None
@CELERY_APP.task()
def outpost_controller_all():
"""Launch Controller for all Outposts which support it"""
for outpost in Outpost.objects.exclude(service_connection=None):
outpost_controller.delay(outpost.pk.hex)
@CELERY_APP.task()
def outpost_service_connection_state(connection_pk: Any):
"""Update cached state of a service connection"""
@ -89,17 +83,29 @@ def outpost_service_connection_monitor(self: MonitoredTask):
)
@CELERY_APP.task()
def outpost_controller_all():
"""Launch Controller for all Outposts which support it"""
for outpost in Outpost.objects.exclude(service_connection=None):
outpost_controller.delay(outpost.pk.hex, "up", from_cache=False)
@CELERY_APP.task(bind=True, base=MonitoredTask)
def outpost_controller(self: MonitoredTask, outpost_pk: str):
"""Create/update/monitor the deployment of an Outpost"""
def outpost_controller(
self: MonitoredTask, outpost_pk: str, action: str = "up", from_cache: bool = False
):
"""Create/update/monitor/delete the deployment of an Outpost"""
logs = []
outpost: Outpost = Outpost.objects.get(pk=outpost_pk)
if from_cache:
outpost: Outpost = cache.get(CACHE_KEY_OUTPOST_DOWN % outpost_pk)
else:
outpost: Outpost = Outpost.objects.get(pk=outpost_pk)
self.set_uid(slugify(outpost.name))
try:
controller = controller_for_outpost(outpost)
if not controller:
return
logs = controller.up_with_logs()
logs = getattr(controller, f"{action}_with_logs")()
LOGGER.debug("---------------Outpost Controller logs starting----------------")
for log in logs:
LOGGER.debug(log)
@ -110,16 +116,6 @@ def outpost_controller(self: MonitoredTask, outpost_pk: str):
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, logs))
@CELERY_APP.task()
def outpost_controller_down(outpost_pk: str):
"""Delete outpost objects before deleting the DB Object"""
outpost = Outpost.objects.get(pk=outpost_pk)
controller = controller_for_outpost(outpost)
if not controller:
return
controller.down()
@CELERY_APP.task(bind=True, base=MonitoredTask)
def outpost_token_ensurer(self: MonitoredTask):
"""Periodically ensure that all Outposts have valid Service Accounts

View File

@ -3,6 +3,10 @@ from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import PropertyMapping, User
from authentik.flows.models import Flow
from authentik.outposts.api.outposts import OutpostSerializer
from authentik.outposts.models import default_outpost_config
from authentik.providers.proxy.models import ProxyProvider
class TestOutpostServiceConnectionsAPI(APITestCase):
@ -22,3 +26,22 @@ class TestOutpostServiceConnectionsAPI(APITestCase):
reverse("authentik_api:outpostserviceconnection-types"),
)
self.assertEqual(response.status_code, 200)
def test_outpost_config(self):
"""Test Outpost's config field"""
provider = ProxyProvider.objects.create(
name="test", authorization_flow=Flow.objects.first()
)
invalid = OutpostSerializer(
data={"name": "foo", "providers": [provider.pk], "config": {}}
)
self.assertFalse(invalid.is_valid())
self.assertIn("config", invalid.errors)
valid = OutpostSerializer(
data={
"name": "foo",
"providers": [provider.pk],
"config": default_outpost_config("foo"),
}
)
self.assertTrue(valid.is_valid())

View File

@ -1,7 +1,9 @@
"""OAuth2Provider API Views"""
from django_filters.rest_framework import DjangoFilterBackend
from guardian.utils import get_anonymous_user
from rest_framework import mixins
from rest_framework.fields import CharField, ListField
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import GenericViewSet
@ -37,6 +39,11 @@ class AuthorizationCodeViewSet(
serializer_class = ExpiringBaseGrantModelSerializer
filterset_fields = ["user", "provider"]
ordering = ["provider", "expires"]
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
SearchFilter,
]
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()
@ -57,6 +64,11 @@ class RefreshTokenViewSet(
serializer_class = ExpiringBaseGrantModelSerializer
filterset_fields = ["user", "provider"]
ordering = ["provider", "expires"]
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
SearchFilter,
]
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()

View File

@ -33,6 +33,8 @@ class OpenIDConnectConfigurationSerializer(PassiveSerializer):
class ProxyProviderSerializer(ProviderSerializer):
"""ProxyProvider Serializer"""
redirect_uris = CharField(read_only=True)
def validate(self, attrs) -> dict[Any, str]:
"""Check that internal_host is set when forward_auth_mode is disabled"""
if (
@ -67,6 +69,7 @@ class ProxyProviderSerializer(ProviderSerializer):
"basic_auth_password_attribute",
"basic_auth_user_attribute",
"forward_auth_mode",
"redirect_uris",
]

View File

@ -17,7 +17,6 @@ from kubernetes.client.models.networking_v1beta1_ingress_rule import (
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import (
Disabled,
KubernetesObjectReconciler,
NeedsUpdate,
)
@ -137,9 +136,6 @@ class IngressReconciler(KubernetesObjectReconciler[NetworkingV1beta1Ingress]):
),
)
rules.append(rule)
if not rules:
self.logger.debug("No providers use proxying, no ingress needed")
raise Disabled()
tls_config = None
if tls_hosts:
tls_config = NetworkingV1beta1IngressTLS(

View File

@ -7,7 +7,6 @@ from kubernetes.client import ApiextensionsV1Api, CustomObjectsApi
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import (
Disabled,
KubernetesObjectReconciler,
NeedsUpdate,
)
@ -70,6 +69,19 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware])
self.api_ex = ApiextensionsV1Api(controller.client)
self.api = CustomObjectsApi(controller.client)
@property
def noop(self) -> bool:
if not ProxyProvider.objects.filter(
outpost__in=[self.controller.outpost],
forward_auth_mode=True,
).exists():
self.logger.debug("No providers with forward auth enabled.")
return True
if not self._crd_exists():
self.logger.debug("CRD doesn't exist")
return True
return False
def _crd_exists(self) -> bool:
"""Check if the traefik middleware exists"""
return bool(
@ -87,15 +99,6 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware])
def get_reference_object(self) -> TraefikMiddleware:
"""Get deployment object for outpost"""
if not ProxyProvider.objects.filter(
outpost__in=[self.controller.outpost],
forward_auth_mode=True,
).exists():
self.logger.debug("No providers with forward auth enabled.")
raise Disabled()
if not self._crd_exists():
self.logger.debug("CRD doesn't exist")
raise Disabled()
return TraefikMiddleware(
apiVersion=f"{CRD_GROUP}/{CRD_VERSION}",
kind="Middleware",

View File

@ -5,6 +5,7 @@ from defusedxml.ElementTree import fromstring
from django.http.response import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.fields import CharField, FileField, ReadOnlyField
@ -83,7 +84,14 @@ class SAMLProviderViewSet(ModelViewSet):
responses={
200: SAMLMetadataSerializer(many=False),
404: "Provider has no application assigned",
}
},
manual_parameters=[
openapi.Parameter(
name="download",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
],
)
@action(methods=["GET"], detail=True, permission_classes=[AllowAny])
# pylint: disable=invalid-name, unused-argument

View File

@ -23,7 +23,7 @@ def deflate_and_base64_encode(inflated: str, encoding="utf-8"):
def nice64(src: str) -> str:
"""Returns src base64-encoded and formatted nicely for our XML. """
"""Returns src base64-encoded and formatted nicely for our XML."""
return base64.b64encode(src.encode()).decode("utf-8").replace("\n", "")

View File

@ -248,6 +248,7 @@ DATABASES = {
"NAME": CONFIG.y("postgresql.name"),
"USER": CONFIG.y("postgresql.user"),
"PASSWORD": CONFIG.y("postgresql.password"),
"PORT": int(CONFIG.y("postgresql.port")),
}
}
@ -319,9 +320,7 @@ CELERY_RESULT_BACKEND = (
# Database backup
DBBACKUP_STORAGE = "django.core.files.storage.FileSystemStorage"
DBBACKUP_STORAGE_OPTIONS = {"location": "./backups" if DEBUG else "/backups"}
DBBACKUP_CONNECTOR_MAPPING = {
"django_prometheus.db.backends.postgresql": "dbbackup.db.postgresql.PgDumpConnector"
}
DBBACKUP_FILENAME_TEMPLATE = "authentik-backup-{datetime}.sql"
if CONFIG.y("postgresql.s3_backup"):
DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
DBBACKUP_STORAGE_OPTIONS = {
@ -331,9 +330,10 @@ if CONFIG.y("postgresql.s3_backup"):
"region_name": CONFIG.y("postgresql.s3_backup.region", "eu-central-1"),
"default_acl": "private",
"endpoint_url": CONFIG.y("postgresql.s3_backup.host"),
"location": CONFIG.y("postgresql.s3_backup.location", ""),
}
j_print(
"Database backup to S3 is configured.",
"Database backup to S3 is configured",
host=CONFIG.y("postgresql.s3_backup.host"),
)
@ -355,7 +355,7 @@ if _ERROR_REPORTING:
send_default_pii=CONFIG.y_bool("error_reporting.send_pii", False),
)
j_print(
"Error reporting is enabled.",
"Error reporting is enabled",
env=CONFIG.y("error_reporting.environment", "customer"),
)

View File

@ -75,6 +75,7 @@ class OAuthSourceSerializer(SourceSerializer):
"callback_url",
"type",
]
extra_kwargs = {"consumer_secret": {"write_only": True}}
class OAuthSourceViewSet(ModelViewSet):

View File

@ -1,5 +1,7 @@
"""OAuth Source Serializer"""
from django_filters.rest_framework import DjangoFilterBackend
from guardian.utils import get_anonymous_user
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.sources import SourceSerializer
@ -25,6 +27,11 @@ class UserOAuthSourceConnectionViewSet(ModelViewSet):
queryset = UserOAuthSourceConnection.objects.all()
serializer_class = UserOAuthSourceConnectionSerializer
filterset_fields = ["source__slug"]
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
SearchFilter,
]
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()

View File

@ -33,6 +33,5 @@ class TestTypeGoogle(TestCase):
def test_enroll_context(self):
"""Test Google Enrollment context"""
ak_context = GoogleOAuth2Callback().get_user_enroll_context(GOOGLE_USER)
self.assertEqual(ak_context["username"], GOOGLE_USER["email"])
self.assertEqual(ak_context["email"], GOOGLE_USER["email"])
self.assertEqual(ak_context["name"], GOOGLE_USER["name"])

View File

@ -23,7 +23,6 @@ class GoogleOAuth2Callback(OAuthCallback):
info: dict[str, Any],
) -> dict[str, Any]:
return {
"username": info.get("email"),
"email": info.get("email"),
"name": info.get("name"),
}

View File

@ -34,7 +34,6 @@ class PlexSourceSerializer(SourceSerializer):
"allow_friends",
"plex_token",
]
extra_kwargs = {"plex_token": {"write_only": True}}
class PlexTokenRedeemSerializer(PassiveSerializer):

View File

@ -1,6 +1,9 @@
"""AuthenticatorStaticStage API Views"""
from django_filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from django_otp.plugins.otp_static.models import StaticDevice
from guardian.utils import get_anonymous_user
from rest_framework.filters import SearchFilter
from rest_framework.permissions import IsAdminUser
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
@ -43,6 +46,11 @@ class StaticDeviceViewSet(ModelViewSet):
search_fields = ["name"]
filterset_fields = ["name"]
ordering = ["name"]
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
SearchFilter,
]
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()

View File

@ -1,6 +1,8 @@
"""AuthenticatorTOTPStage API Views"""
from django_filters.rest_framework import DjangoFilterBackend
from django_otp.plugins.otp_totp.models import TOTPDevice
from guardian.utils import get_anonymous_user
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.permissions import IsAdminUser
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
@ -46,6 +48,11 @@ class TOTPDeviceViewSet(ModelViewSet):
search_fields = ["name"]
filterset_fields = ["name"]
ordering = ["name"]
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
SearchFilter,
]
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()

View File

@ -1,5 +1,7 @@
"""AuthenticateWebAuthnStage API Views"""
from django_filters.rest_framework import DjangoFilterBackend
from guardian.utils import get_anonymous_user
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.permissions import IsAdminUser
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
@ -45,6 +47,11 @@ class WebAuthnDeviceViewSet(ModelViewSet):
search_fields = ["name"]
filterset_fields = ["name"]
ordering = ["name"]
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
SearchFilter,
]
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()

View File

@ -1,6 +1,8 @@
"""ConsentStage API Views"""
from django_filters.rest_framework import DjangoFilterBackend
from guardian.utils import get_anonymous_user
from rest_framework import mixins
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from authentik.core.api.applications import ApplicationSerializer
@ -49,6 +51,11 @@ class UserConsentViewSet(
serializer_class = UserConsentSerializer
filterset_fields = ["user", "application"]
ordering = ["application", "expires"]
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
SearchFilter,
]
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()

View File

@ -3,6 +3,7 @@ from rest_framework.fields import JSONField
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import is_dict
from authentik.flows.api.stages import StageSerializer
from authentik.stages.invitation.models import Invitation, InvitationStage
@ -29,6 +30,7 @@ class InvitationStageViewSet(ModelViewSet):
class InvitationSerializer(ModelSerializer):
"""Invitation Serializer"""
created_by = UserSerializer(read_only=True)
fixed_data = JSONField(validators=[is_dict], required=False)
class Meta:
@ -41,7 +43,6 @@ class InvitationSerializer(ModelSerializer):
"created_by",
"single_use",
]
depth = 2
class InvitationViewSet(ModelViewSet):

View File

@ -33,7 +33,7 @@ class UserLoginStageView(StageView):
backend=backend,
)
delta = timedelta_from_string(self.executor.current_stage.session_duration)
if delta.seconds == 0:
if delta.total_seconds() == 0:
self.request.session.set_expiry(0)
else:
self.request.session.set_expiry(delta)

View File

@ -19,7 +19,7 @@ variables:
branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }}
stages:
- stage: Lint
- stage: Lint_and_test
jobs:
- job: pylint
pool:
@ -118,8 +118,6 @@ stages:
- task: CmdLine@2
inputs:
script: pipenv run pyright e2e lifecycle
- stage: Test
jobs:
- job: migrations
pool:
vmImage: 'ubuntu-latest'

View File

@ -21,7 +21,7 @@ services:
networks:
- internal
server:
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc1}
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc10}
restart: unless-stopped
command: server
environment:
@ -52,7 +52,7 @@ services:
- "0.0.0.0:9000:9000"
- "0.0.0.0:9443:9443"
worker:
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc1}
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc10}
restart: unless-stopped
command: worker
networks:

View File

@ -1,3 +1,3 @@
package constants
const VERSION = "2021.5.1-rc1"
const VERSION = "2021.5.1-rc10"

View File

@ -41,11 +41,9 @@ while True:
while True:
try:
redis = Redis(
host=CONFIG.y("redis.host"),
port=6379,
db=CONFIG.y("redis.message_queue_db"),
password=CONFIG.y("redis.password"),
redis = Redis.from_url(
f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:6379"
f"/{CONFIG.y('redis.ws_db')}"
)
redis.ping()
break

View File

@ -116,7 +116,10 @@ stages:
command: 'buildAndPush'
Dockerfile: 'outpost/proxy.Dockerfile'
buildContext: 'outpost/'
tags: "gh-$(branchName)"
tags: |
gh-$(branchName)
gh-$(Build.SourceVersion)
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
- job: ldap_build_docker
pool:
vmImage: 'ubuntu-latest'
@ -141,4 +144,7 @@ stages:
command: 'buildAndPush'
Dockerfile: 'outpost/ldap.Dockerfile'
buildContext: 'outpost/'
tags: "gh-$(branchName)"
tags: |
gh-$(branchName)
gh-$(Build.SourceVersion)
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'

View File

@ -17,6 +17,7 @@ require (
github.com/go-redis/redis/v7 v7.4.0 // indirect
github.com/go-swagger/go-swagger v0.27.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/gorilla/websocket v1.4.2
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/justinas/alice v1.2.0

View File

@ -352,6 +352,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=

View File

@ -1,4 +1,6 @@
FROM golang:1.16.4 AS builder
ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
WORKDIR /work

View File

@ -1,13 +1,13 @@
package ak
import (
"fmt"
"math/rand"
"net/url"
"os"
"time"
"github.com/go-openapi/runtime"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/recws-org/recws"
"goauthentik.io/outpost/pkg"
@ -35,13 +35,14 @@ type APIController struct {
reloadOffset time.Duration
wsConn *recws.RecConn
wsConn *recws.RecConn
instanceUUID uuid.UUID
}
// NewAPIController initialise new API Controller instance from URL and API token
func NewAPIController(akURL url.URL, token string) *APIController {
transport := httptransport.New(akURL.Host, client.DefaultBasePath, []string{akURL.Scheme})
transport.Transport = SetUserAgent(getTLSTransport(), fmt.Sprintf("authentik-proxy@%s", pkg.VERSION))
transport.Transport = SetUserAgent(getTLSTransport(), pkg.UserAgent())
// create the transport
auth := httptransport.BearerToken(token)
@ -70,6 +71,7 @@ func NewAPIController(akURL url.URL, token string) *APIController {
logger: log,
reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
instanceUUID: uuid.New(),
}
ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset)
ac.initWS(akURL, outpost.Pk)
@ -90,6 +92,10 @@ func (a *APIController) Start() error {
a.logger.Debug("Starting WS Health notifier...")
a.startWSHealth()
}()
go func() {
a.logger.Debug("Starting Interval updater...")
a.startIntervalUpdater()
}()
go func() {
err := a.Server.Start()
if err != nil {

View File

@ -23,7 +23,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
header := http.Header{
"Authorization": []string{authHeader},
"User-Agent": []string{fmt.Sprintf("authentik-proxy@%s", pkg.VERSION)},
"User-Agent": []string{pkg.UserAgent()},
}
value, set := os.LookupEnv("AUTHENTIK_INSECURE")
@ -46,7 +46,9 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
msg := websocketMessage{
Instruction: WebsocketInstructionHello,
Args: map[string]interface{}{
"version": pkg.VERSION,
"version": pkg.VERSION,
"buildHash": pkg.BUILD(),
"uuid": ac.instanceUUID.String(),
},
}
err := ws.WriteJSON(msg)
@ -75,7 +77,7 @@ func (ac *APIController) startWSHandler() {
var wsMsg websocketMessage
err := ac.wsConn.ReadJSON(&wsMsg)
if err != nil {
logger.Println("read:", err)
logger.WithError(err).Warning("ws write error, reconnecting")
ac.wsConn.CloseAndReconnect()
continue
}
@ -99,15 +101,28 @@ func (ac *APIController) startWSHealth() {
aliveMsg := websocketMessage{
Instruction: WebsocketInstructionHello,
Args: map[string]interface{}{
"version": pkg.VERSION,
"version": pkg.VERSION,
"buildHash": pkg.BUILD(),
"uuid": ac.instanceUUID.String(),
},
}
err := ac.wsConn.WriteJSON(aliveMsg)
ac.logger.WithField("loop", "ws-health").Trace("hello'd")
if err != nil {
ac.logger.WithField("loop", "ws-health").Println("write:", err)
ac.logger.WithField("loop", "ws-health").WithError(err).Warning("ws write error, reconnecting")
ac.wsConn.CloseAndReconnect()
continue
}
}
}
func (ac *APIController) startIntervalUpdater() {
logger := ac.logger.WithField("loop", "interval-updater")
ticker := time.NewTicker(time.Second * 150)
for ; true; <-ticker.C {
err := ac.Server.Refresh()
if err != nil {
logger.WithError(err).Debug("Failed to update")
}
}
}

View File

@ -20,6 +20,8 @@ func doGlobalSetup(config map[string]interface{}) {
},
})
switch config[ConfigLogLevel].(string) {
case "trace":
log.SetLevel(log.TraceLevel)
case "debug":
log.SetLevel(log.DebugLevel)
case "info":
@ -31,7 +33,7 @@ func doGlobalSetup(config map[string]interface{}) {
default:
log.SetLevel(log.DebugLevel)
}
log.WithField("version", pkg.VERSION).Info("Starting authentik outpost")
log.WithField("buildHash", pkg.BUILD()).WithField("version", pkg.VERSION).Info("Starting authentik outpost")
var dsn string
if config[ConfigErrorReportingEnabled].(bool) {

View File

@ -2,20 +2,22 @@ package ldap
import (
"net"
"strings"
"github.com/nmcclain/ldap"
)
func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
ls.log.WithField("boundDN", bindDN).Info("bind")
ls.log.WithField("bindDN", bindDN).Info("bind")
bindDN = strings.ToLower(bindDN)
for _, instance := range ls.providers {
username, err := instance.getUsername(bindDN)
if err == nil {
return instance.Bind(username, bindPW, conn)
return instance.Bind(username, bindDN, bindPW, conn)
} else {
ls.log.WithError(err).Debug("Username not for instance")
}
}
ls.log.WithField("boundDN", bindDN).WithField("request", "bind").Warning("No provider found for request")
ls.log.WithField("bindDN", bindDN).WithField("request", "bind").Warning("No provider found for request")
return ldap.LDAPResultOperationsError, nil
}

View File

@ -47,7 +47,7 @@ func (pi *ProviderInstance) getUsername(dn string) (string, error) {
return "", errors.New("failed to find cn")
}
func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
func (pi *ProviderInstance) Bind(username string, bindDN, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
jar, err := cookiejar.New(nil)
if err != nil {
pi.log.WithError(err).Warning("Failed to create cookiejar")
@ -67,9 +67,9 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
}
params := url.Values{}
params.Add("goauthentik.io/outpost/ldap", "true")
passed, err := pi.solveFlowChallenge(username, bindPW, client, params.Encode())
passed, err := pi.solveFlowChallenge(username, bindPW, client, params.Encode(), 1)
if err != nil {
pi.log.WithField("boundDN", username).WithError(err).Warning("failed to solve challenge")
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to solve challenge")
return ldap.LDAPResultOperationsError, nil
}
if !passed {
@ -82,25 +82,25 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
}, httptransport.PassThroughAuth)
if err != nil {
if _, denied := err.(*core.CoreApplicationsCheckAccessForbidden); denied {
pi.log.WithField("boundDN", username).Info("Access denied for user")
pi.log.WithField("bindDN", bindDN).Info("Access denied for user")
return ldap.LDAPResultInsufficientAccessRights, nil
}
pi.log.WithField("boundDN", username).WithError(err).Warning("failed to check access")
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to check access")
return ldap.LDAPResultOperationsError, nil
}
pi.log.WithField("boundDN", username).Info("User has access")
pi.log.WithField("bindDN", bindDN).Info("User has access")
// Get user info to store in context
userInfo, err := pi.s.ac.Client.Core.CoreUsersMe(&core.CoreUsersMeParams{
Context: context.Background(),
HTTPClient: client,
}, httptransport.PassThroughAuth)
if err != nil {
pi.log.WithField("boundDN", username).WithError(err).Warning("failed to get user info")
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to get user info")
return ldap.LDAPResultOperationsError, nil
}
pi.boundUsersMutex.Lock()
pi.boundUsers[username] = UserFlags{
UserInfo: userInfo.Payload.User,
pi.boundUsers[bindDN] = UserFlags{
UserInfo: *userInfo.Payload.User,
CanSearch: pi.SearchAccessCheck(userInfo.Payload.User),
}
defer pi.boundUsersMutex.Unlock()
@ -112,7 +112,8 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
func (pi *ProviderInstance) SearchAccessCheck(user *models.User) bool {
for _, group := range user.Groups {
for _, allowedGroup := range pi.searchAllowedGroups {
if &group.Pk == allowedGroup {
pi.log.WithField("userGroup", group.Pk).WithField("allowedGroup", allowedGroup).Trace("Checking search access")
if group.Pk.String() == allowedGroup.String() {
pi.log.WithField("group", group.Name).Info("Allowed access to search")
return true
}
@ -139,7 +140,7 @@ func (pi *ProviderInstance) delayDeleteUserInfo(dn string) {
}()
}
func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, client *http.Client, urlParams string) (bool, error) {
func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, client *http.Client, urlParams string, depth int) (bool, error) {
challenge, err := pi.s.ac.Client.Flows.FlowsExecutorGet(&flows.FlowsExecutorGetParams{
FlowSlug: pi.flowSlug,
Query: urlParams,
@ -169,6 +170,10 @@ func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, c
}
response, err := pi.s.ac.Client.Flows.FlowsExecutorSolve(responseParams, pi.s.ac.Auth)
pi.log.WithField("component", response.Payload.Component).WithField("type", *response.Payload.Type).Debug("Got response")
switch response.Payload.Component {
case "ak-stage-access-denied":
return false, errors.New("got ak-stage-access-denied")
}
if *response.Payload.Type == "redirect" {
return true, nil
}
@ -184,5 +189,8 @@ func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, c
}
}
}
return pi.solveFlowChallenge(bindDN, password, client, urlParams)
if depth >= 10 {
return false, errors.New("exceeded stage recursion depth")
}
return pi.solveFlowChallenge(bindDN, password, client, urlParams, depth+1)
}

View File

@ -29,10 +29,13 @@ func (pi *ProviderInstance) Search(bindDN string, searchReq ldap.SearchRequest,
pi.boundUsersMutex.RLock()
defer pi.boundUsersMutex.RUnlock()
flags, ok := pi.boundUsers[bindDN]
pi.log.WithField("bindDN", bindDN).WithField("ok", ok).Debugf("%+v\n", flags)
if !ok {
pi.log.Debug("User info not cached")
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
}
if !flags.CanSearch {
pi.log.Debug("User can't search")
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
}

View File

@ -31,7 +31,7 @@ type ProviderInstance struct {
}
type UserFlags struct {
UserInfo *models.User
UserInfo models.User
CanSearch bool
}

View File

@ -8,8 +8,8 @@ import (
"github.com/nmcclain/ldap"
)
func (ls *LDAPServer) Search(boundDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
ls.log.WithField("boundDN", boundDN).WithField("baseDN", searchReq.BaseDN).Info("search")
func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
ls.log.WithField("bindDN", bindDN).WithField("baseDN", searchReq.BaseDN).Info("search")
if searchReq.BaseDN == "" {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultSuccess}, nil
}
@ -21,7 +21,7 @@ func (ls *LDAPServer) Search(boundDN string, searchReq ldap.SearchRequest, conn
for _, provider := range ls.providers {
providerBase, _ := goldap.ParseDN(provider.BaseDN)
if providerBase.AncestorOf(bd) {
return provider.Search(boundDN, searchReq, conn)
return provider.Search(bindDN, searchReq, conn)
}
}
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, errors.New("no provider could handle request")

View File

@ -344,7 +344,11 @@ func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request)
}
if _, ok := req.URL.Query()["traefik"]; ok {
host := getHost(req)
http.Redirect(rw, req, fmt.Sprintf("//%s%s", host, p.OAuthStartPath), http.StatusTemporaryRedirect)
proto := req.Header.Get("X-Forwarded-Proto")
if proto != "" {
proto = proto + ":"
}
http.Redirect(rw, req, fmt.Sprintf("%s//%s%s", proto, host, p.OAuthStartPath), http.StatusTemporaryRedirect)
return
}
}

View File

@ -57,7 +57,7 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
for k := range s.Handlers {
hostKeys = append(hostKeys, k)
}
s.logger.WithField("host", host).WithField("known-hosts", strings.Join(hostKeys, ", ")).Debug("Host header does not match any we know of")
s.logger.WithField("host", host).WithField("known-hosts", strings.Join(hostKeys, ",")).Debug("Host header does not match any we know of")
w.WriteHeader(404)
return
}

View File

@ -1,12 +1,20 @@
package proxy
import "net/http"
import (
"net"
"net/http"
)
var xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host")
func getHost(req *http.Request) string {
host := req.Host
if req.Header.Get(xForwardedHost) != "" {
return req.Header.Get(xForwardedHost)
host = req.Header.Get(xForwardedHost)
}
return req.Host
hostOnly, _, err := net.SplitHostPort(host)
if err != nil {
return host
}
return hostOnly
}

View File

@ -1,3 +1,16 @@
package pkg
const VERSION = "2021.5.1-rc1"
import (
"fmt"
"os"
)
const VERSION = "2021.5.1-rc10"
func BUILD() string {
return os.Getenv("GIT_BUILD_HASH")
}
func UserAgent() string {
return fmt.Sprintf("authentik-outpost@%s (%s)", VERSION, BUILD())
}

View File

@ -1,4 +1,6 @@
FROM golang:1.16.4 AS builder
ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
WORKDIR /work

View File

@ -531,11 +531,6 @@ paths:
description: ''
required: false
type: string
- 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.
@ -2532,7 +2527,10 @@ paths:
get:
operationId: crypto_certificatekeypairs_view_certificate
description: Return certificate-key pairs certificate and log access
parameters: []
parameters:
- name: download
in: query
type: boolean
responses:
'200':
description: ''
@ -2560,7 +2558,10 @@ paths:
get:
operationId: crypto_certificatekeypairs_view_private_key
description: Return certificate-key pairs private key and log access
parameters: []
parameters:
- name: download
in: query
type: boolean
responses:
'200':
description: ''
@ -9701,7 +9702,10 @@ paths:
get:
operationId: providers_saml_metadata
description: Return metadata as XML string
parameters: []
parameters:
- name: download
in: query
type: boolean
responses:
'200':
description: ''
@ -15690,6 +15694,7 @@ definitions:
NotificationRule:
required:
- name
- transports
type: object
properties:
pk:
@ -15702,38 +15707,17 @@ definitions:
type: string
minLength: 1
transports:
description: Select which transports should be used to notify the user. If
none are selected, the notification will only be shown in the authentik
UI.
type: array
items:
required:
- name
- mode
type: object
properties:
uuid:
title: Uuid
type: string
format: uuid
readOnly: true
name:
title: Name
type: string
minLength: 1
mode:
title: Mode
type: string
enum:
- webhook
- webhook_slack
- email
webhook_url:
title: Webhook url
type: string
send_once:
title: Send once
description: Only send notification once, for example when sending a
webhook into a chat channel.
type: boolean
readOnly: true
description: Select which transports should be used to notify the user.
If none are selected, the notification will only be shown in the authentik
UI.
type: string
format: uuid
uniqueItems: true
severity:
title: Severity
description: Controls which severity level the created notifications will
@ -15744,57 +15728,14 @@ definitions:
- warning
- alert
group:
required:
- name
type: object
properties:
group_uuid:
title: Group uuid
type: string
format: uuid
readOnly: true
name:
title: Name
type: string
maxLength: 80
minLength: 1
is_superuser:
title: Is superuser
description: Users added to this group will be superusers.
type: boolean
attributes:
title: Attributes
type: object
parent:
required:
- name
- parent
type: object
properties:
group_uuid:
title: Group uuid
type: string
format: uuid
readOnly: true
name:
title: Name
type: string
maxLength: 80
minLength: 1
is_superuser:
title: Is superuser
description: Users added to this group will be superusers.
type: boolean
attributes:
title: Attributes
type: object
parent:
title: Parent
type: string
format: uuid
x-nullable: true
readOnly: true
readOnly: true
title: Group
description: Define which group of users this notification should be sent
and shown to. If left empty, Notification won't ben sent.
type: string
format: uuid
x-nullable: true
group_obj:
$ref: '#/definitions/Group'
NotificationTransport:
required:
- name
@ -16203,7 +16144,7 @@ definitions:
required:
- name
- providers
- _config
- config
type: object
properties:
pk:
@ -16242,8 +16183,8 @@ definitions:
title: Token identifier
type: string
readOnly: true
_config:
title: config
config:
title: Config
type: object
OutpostDefaultConfig:
type: object
@ -17494,6 +17435,11 @@ definitions:
description: Enable support for forwardAuth in traefik and nginx auth_request.
Exclusive with internal_host.
type: boolean
redirect_uris:
title: Redirect uris
type: string
readOnly: true
minLength: 1
SAMLProvider:
required:
- name
@ -18878,237 +18824,7 @@ definitions:
title: Fixed data
type: object
created_by:
required:
- password
- username
- name
type: object
properties:
id:
title: ID
type: integer
readOnly: true
password:
title: Password
type: string
maxLength: 128
minLength: 1
last_login:
title: Last login
type: string
format: date-time
x-nullable: true
username:
title: Username
description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
only.
type: string
pattern: ^[\w.@+-]+$
maxLength: 150
minLength: 1
first_name:
title: First name
type: string
maxLength: 150
last_name:
title: Last name
type: string
maxLength: 150
email:
title: Email address
type: string
format: email
maxLength: 254
is_active:
title: Active
description: Designates whether this user should be treated as active.
Unselect this instead of deleting accounts.
type: boolean
date_joined:
title: Date joined
type: string
format: date-time
uuid:
title: Uuid
type: string
format: uuid
readOnly: true
name:
title: Name
description: User's display name.
type: string
minLength: 1
password_change_date:
title: Password change date
type: string
format: date-time
readOnly: true
attributes:
title: Attributes
type: object
groups:
type: array
items:
required:
- name
type: object
properties:
id:
title: ID
type: integer
readOnly: true
name:
title: Name
type: string
maxLength: 150
minLength: 1
permissions:
type: array
items:
type: integer
uniqueItems: true
readOnly: true
user_permissions:
type: array
items:
required:
- name
- codename
- content_type
type: object
properties:
id:
title: ID
type: integer
readOnly: true
name:
title: Name
type: string
maxLength: 255
minLength: 1
codename:
title: Codename
type: string
maxLength: 100
minLength: 1
content_type:
title: Content type
type: integer
readOnly: true
sources:
type: array
items:
required:
- name
- slug
type: object
properties:
pbm_uuid:
title: Pbm uuid
type: string
format: uuid
readOnly: true
policy_engine_mode:
title: Policy engine mode
type: string
enum:
- all
- any
managed:
title: Managed by authentik
description: Objects which are managed by authentik. These objects
are created and updated automatically. This is flag only indicates
that an object can be overwritten by migrations. You can still
modify the objects via the API, but expect changes to be overwritten
in a later update.
type: string
minLength: 1
x-nullable: true
name:
title: Name
description: Source's display Name.
type: string
minLength: 1
slug:
title: Slug
description: Internal source name, used in URLs.
type: string
format: slug
pattern: ^[-a-zA-Z0-9_]+$
maxLength: 50
minLength: 1
enabled:
title: Enabled
type: boolean
user_matching_mode:
title: User matching mode
description: How the source determines if an existing user should
be authenticated or a new user enrolled.
type: string
enum:
- identifier
- email_link
- email_deny
- username_link
- username_deny
authentication_flow:
title: Authentication flow
description: Flow to use when authenticating existing users.
type: string
format: uuid
x-nullable: true
enrollment_flow:
title: Enrollment flow
description: Flow to use when enrolling new users.
type: string
format: uuid
x-nullable: true
policies:
type: array
items:
type: string
format: uuid
readOnly: true
uniqueItems: true
property_mappings:
type: array
items:
type: string
format: uuid
uniqueItems: true
readOnly: true
ak_groups:
type: array
items:
required:
- name
- parent
type: object
properties:
group_uuid:
title: Group uuid
type: string
format: uuid
readOnly: true
name:
title: Name
type: string
maxLength: 80
minLength: 1
is_superuser:
title: Is superuser
description: Users added to this group will be superusers.
type: boolean
attributes:
title: Attributes
type: object
parent:
title: Parent
type: string
format: uuid
x-nullable: true
readOnly: true
readOnly: true
$ref: '#/definitions/User'
single_use:
title: Single use
description: When enabled, the invitation will be deleted after usage.

View File

@ -81,7 +81,7 @@ http {
location /static/ {
expires 31d;
add_header Cache-Control "public, no-transform";
add_header X-authentik-version "2021.5.1-rc1";
add_header X-authentik-version "2021.5.1-rc10";
add_header Vary X-authentik-version;
}

485
web/package-lock.json generated
View File

@ -24,13 +24,13 @@
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.1",
"@sentry/browser": "^6.3.5",
"@sentry/tracing": "^6.3.5",
"@sentry/browser": "^6.3.6",
"@sentry/tracing": "^6.3.6",
"@types/chart.js": "^2.9.32",
"@types/codemirror": "5.60.0",
"@types/grecaptcha": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^4.22.1",
"@typescript-eslint/parser": "^4.22.1",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"@webcomponents/webcomponentsjs": "^2.5.0",
"authentik-api": "file:api",
"babel-plugin-macros": "^3.1.0",
@ -39,10 +39,10 @@
"chartjs-adapter-moment": "^1.0.0",
"codemirror": "^5.61.0",
"construct-style-sheets-polyfill": "^2.4.16",
"eslint": "^7.25.0",
"eslint": "^7.26.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.2",
"eslint-plugin-lit": "^1.3.0",
"eslint-plugin-lit": "^1.4.0",
"flowchart.js": "^1.15.0",
"lit-element": "^2.5.1",
"lit-html": "^1.4.1",
@ -61,13 +61,12 @@
"typescript": "^4.2.4",
"webcomponent-qr-code": "^1.0.5",
"yaml": "^1.10.2"
},
"devDependencies": {}
}
},
"api": {
"name": "authentik-api",
"version": "1.0.0",
"devDependencies": {
"version": "0.0.1",
"dependencies": {
"typescript": "^3.6"
}
},
@ -75,7 +74,6 @@
"version": "3.9.9",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz",
"integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -1725,9 +1723,9 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
"integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz",
"integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.1.1",
@ -1752,6 +1750,9 @@
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@eslint/eslintrc/node_modules/ignore": {
@ -2337,33 +2338,13 @@
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="
},
"node_modules/@sentry/browser": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.5.tgz",
"integrity": "sha512-fjkhPR5gLCGVWhbWjEoN64hnmTvfTLRCgWmYTc9SiGchWFoFEmLqZyF2uJFyt27+qamLQ9fN58nnv4Ly2yyxqg==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.6.tgz",
"integrity": "sha512-l4323jxuBOArki6Wf+EHes39IEyJ2Zj/CIUaTY7GWh7CntpfHQAfFmZWQw3Ozq+ka1u8lVp25RPhb4Wng3azNA==",
"dependencies": {
"@sentry/core": "6.3.5",
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"tslib": "^1.9.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/browser/node_modules/@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/browser/node_modules/@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"dependencies": {
"@sentry/types": "6.3.5",
"@sentry/core": "6.3.6",
"@sentry/types": "6.3.6",
"@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"engines": {
@ -2376,60 +2357,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/core": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.5.tgz",
"integrity": "sha512-VR2ibDy33mryD0mT6d9fGhKjdNzS2FSwwZPe9GvmNOjkyjly/oV91BKVoYJneCqOeq8fyj2lvkJGKuupdJNDqg==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.6.tgz",
"integrity": "sha512-w6BRizAqh7BaiM9oeKzO6aACXwRijUPacYaVLX/OfhqCSueF9uDxpMRT7+4D/eCeDVqgJYhBJ4Vsu2NSstkk4A==",
"dependencies": {
"@sentry/hub": "6.3.5",
"@sentry/minimal": "6.3.5",
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"tslib": "^1.9.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/core/node_modules/@sentry/hub": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.5.tgz",
"integrity": "sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ==",
"dependencies": {
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"tslib": "^1.9.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/core/node_modules/@sentry/minimal": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.5.tgz",
"integrity": "sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA==",
"dependencies": {
"@sentry/hub": "6.3.5",
"@sentry/types": "6.3.5",
"tslib": "^1.9.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/core/node_modules/@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/core/node_modules/@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"dependencies": {
"@sentry/types": "6.3.5",
"@sentry/hub": "6.3.6",
"@sentry/minimal": "6.3.6",
"@sentry/types": "6.3.6",
"@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"engines": {
@ -2442,12 +2377,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/hub": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.5.tgz",
"integrity": "sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.6.tgz",
"integrity": "sha512-foBZ3ilMnm9Gf9OolrAxYHK8jrA6IF72faDdJ3Al+1H27qcpnBaMdrdEp2/jzwu/dgmwuLmbBaMjEPXaGH/0JQ==",
"dependencies": {
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"@sentry/types": "6.3.6",
"@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"engines": {
@ -2460,12 +2395,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/minimal": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.5.tgz",
"integrity": "sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.6.tgz",
"integrity": "sha512-uM2/dH0a6zfvI5f+vg+/mST+uTBdN6Jgpm585ipH84ckCYQwIIDRg6daqsen4S1sy/xgg1P1YyC3zdEC4G6b1Q==",
"dependencies": {
"@sentry/hub": "6.3.5",
"@sentry/types": "6.3.5",
"@sentry/hub": "6.3.6",
"@sentry/types": "6.3.6",
"tslib": "^1.9.3"
},
"engines": {
@ -2478,14 +2413,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/tracing": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.5.tgz",
"integrity": "sha512-TNKAST1ge2g24BlTfVxNp4gP5t3drbi0OVCh8h8ah+J7UjHSfdiqhd9W2h5qv1GO61gGlpWeN/TyioyQmOxu0Q==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.6.tgz",
"integrity": "sha512-dfyYY2eESJGt5Qbigmfmb2U9ntqbwPhLNAOcjKaVg9WQRV5q2RkHCVctPoYk7TEAvfNeNRXCD8SnuFOZhttt8g==",
"dependencies": {
"@sentry/hub": "6.3.5",
"@sentry/minimal": "6.3.5",
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"@sentry/hub": "6.3.6",
"@sentry/minimal": "6.3.6",
"@sentry/types": "6.3.6",
"@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"engines": {
@ -2498,19 +2433,19 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.6.tgz",
"integrity": "sha512-93cFJdJkWyCfyZeWFARSU11qnoHVOS/R2h5WIsEf+jbQmkqG2C+TXVz/19s6nHVsfDrwpvYpwALPv4/nrxfU7g==",
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.6.tgz",
"integrity": "sha512-HnYlDBf8Dq8MEv7AulH7B6R1D/2LAooVclGdjg48tSrr9g+31kmtj+SAj2WWVHP9+bp29BWaC7i5nkfKrOibWw==",
"dependencies": {
"@sentry/types": "6.3.5",
"@sentry/types": "6.3.6",
"tslib": "^1.9.3"
},
"engines": {
@ -2668,12 +2603,12 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.1.tgz",
"integrity": "sha512-kVTAghWDDhsvQ602tHBc6WmQkdaYbkcTwZu+7l24jtJiYvm9l+/y/b2BZANEezxPDiX5MK2ZecE+9BFi/YJryw==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz",
"integrity": "sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw==",
"dependencies": {
"@typescript-eslint/experimental-utils": "4.22.1",
"@typescript-eslint/scope-manager": "4.22.1",
"@typescript-eslint/experimental-utils": "4.23.0",
"@typescript-eslint/scope-manager": "4.23.0",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15",
@ -2699,14 +2634,14 @@
}
},
"node_modules/@typescript-eslint/experimental-utils": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz",
"integrity": "sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz",
"integrity": "sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA==",
"dependencies": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.22.1",
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/typescript-estree": "4.22.1",
"@typescript-eslint/scope-manager": "4.23.0",
"@typescript-eslint/types": "4.23.0",
"@typescript-eslint/typescript-estree": "4.23.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
},
@ -2722,13 +2657,13 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.1.tgz",
"integrity": "sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.23.0.tgz",
"integrity": "sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug==",
"dependencies": {
"@typescript-eslint/scope-manager": "4.22.1",
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/typescript-estree": "4.22.1",
"@typescript-eslint/scope-manager": "4.23.0",
"@typescript-eslint/types": "4.23.0",
"@typescript-eslint/typescript-estree": "4.23.0",
"debug": "^4.1.1"
},
"engines": {
@ -2748,12 +2683,12 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz",
"integrity": "sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz",
"integrity": "sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w==",
"dependencies": {
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/visitor-keys": "4.22.1"
"@typescript-eslint/types": "4.23.0",
"@typescript-eslint/visitor-keys": "4.23.0"
},
"engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
@ -2764,9 +2699,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.1.tgz",
"integrity": "sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.23.0.tgz",
"integrity": "sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw==",
"engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
},
@ -2776,12 +2711,12 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz",
"integrity": "sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz",
"integrity": "sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw==",
"dependencies": {
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/visitor-keys": "4.22.1",
"@typescript-eslint/types": "4.23.0",
"@typescript-eslint/visitor-keys": "4.23.0",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
@ -2821,11 +2756,11 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz",
"integrity": "sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz",
"integrity": "sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg==",
"dependencies": {
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/types": "4.23.0",
"eslint-visitor-keys": "^2.0.0"
},
"engines": {
@ -2860,7 +2795,10 @@
"node_modules/acorn-jsx": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng=="
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/ajv": {
"version": "6.12.6",
@ -2871,6 +2809,10 @@
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ansi-colors": {
@ -3906,12 +3848,12 @@
}
},
"node_modules/eslint": {
"version": "7.25.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.25.0.tgz",
"integrity": "sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==",
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz",
"integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==",
"dependencies": {
"@babel/code-frame": "7.12.11",
"@eslint/eslintrc": "^0.4.0",
"@eslint/eslintrc": "^0.4.1",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@ -3953,6 +3895,9 @@
},
"engines": {
"node": "^10.12.0 || >=12.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-config-google": {
@ -3972,13 +3917,16 @@
}
},
"node_modules/eslint-plugin-lit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.3.0.tgz",
"integrity": "sha512-fy6Lr5vYI3kvCYaDXA20lwyKAp1keS9UjR5ntj8U2TeV+1yUta3S7xxXe+rABKRPbcNzi1ZvQLE1LmNKc9yr4Q==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.4.0.tgz",
"integrity": "sha512-3PJCC1p4pvDBKtFmg1g2cGzAgJF4IDqhb9NJUh95nYc+QXExa/O/0fILF4WB6X7qdNQKm+gW6nYtSKTyYPHtXw==",
"dependencies": {
"parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1",
"requireindex": "^1.2.0"
},
"peerDependencies": {
"eslint": ">= 5"
}
},
"node_modules/eslint-rule-documentation": {
@ -7355,6 +7303,9 @@
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/supports-color": {
@ -9632,9 +9583,9 @@
}
},
"@eslint/eslintrc": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
"integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz",
"integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==",
"requires": {
"ajv": "^6.12.4",
"debug": "^4.1.1",
@ -10146,30 +10097,16 @@
}
},
"@sentry/browser": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.5.tgz",
"integrity": "sha512-fjkhPR5gLCGVWhbWjEoN64hnmTvfTLRCgWmYTc9SiGchWFoFEmLqZyF2uJFyt27+qamLQ9fN58nnv4Ly2yyxqg==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.6.tgz",
"integrity": "sha512-l4323jxuBOArki6Wf+EHes39IEyJ2Zj/CIUaTY7GWh7CntpfHQAfFmZWQw3Ozq+ka1u8lVp25RPhb4Wng3azNA==",
"requires": {
"@sentry/core": "6.3.5",
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"@sentry/core": "6.3.6",
"@sentry/types": "6.3.6",
"@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"dependencies": {
"@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ=="
},
"@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"requires": {
"@sentry/types": "6.3.5",
"tslib": "^1.9.3"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -10178,51 +10115,17 @@
}
},
"@sentry/core": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.5.tgz",
"integrity": "sha512-VR2ibDy33mryD0mT6d9fGhKjdNzS2FSwwZPe9GvmNOjkyjly/oV91BKVoYJneCqOeq8fyj2lvkJGKuupdJNDqg==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.6.tgz",
"integrity": "sha512-w6BRizAqh7BaiM9oeKzO6aACXwRijUPacYaVLX/OfhqCSueF9uDxpMRT7+4D/eCeDVqgJYhBJ4Vsu2NSstkk4A==",
"requires": {
"@sentry/hub": "6.3.5",
"@sentry/minimal": "6.3.5",
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"@sentry/hub": "6.3.6",
"@sentry/minimal": "6.3.6",
"@sentry/types": "6.3.6",
"@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"dependencies": {
"@sentry/hub": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.5.tgz",
"integrity": "sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ==",
"requires": {
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"tslib": "^1.9.3"
}
},
"@sentry/minimal": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.5.tgz",
"integrity": "sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA==",
"requires": {
"@sentry/hub": "6.3.5",
"@sentry/types": "6.3.5",
"tslib": "^1.9.3"
}
},
"@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ=="
},
"@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"requires": {
"@sentry/types": "6.3.5",
"tslib": "^1.9.3"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -10231,12 +10134,12 @@
}
},
"@sentry/hub": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.5.tgz",
"integrity": "sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.6.tgz",
"integrity": "sha512-foBZ3ilMnm9Gf9OolrAxYHK8jrA6IF72faDdJ3Al+1H27qcpnBaMdrdEp2/jzwu/dgmwuLmbBaMjEPXaGH/0JQ==",
"requires": {
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"@sentry/types": "6.3.6",
"@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"dependencies": {
@ -10248,12 +10151,12 @@
}
},
"@sentry/minimal": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.5.tgz",
"integrity": "sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.6.tgz",
"integrity": "sha512-uM2/dH0a6zfvI5f+vg+/mST+uTBdN6Jgpm585ipH84ckCYQwIIDRg6daqsen4S1sy/xgg1P1YyC3zdEC4G6b1Q==",
"requires": {
"@sentry/hub": "6.3.5",
"@sentry/types": "6.3.5",
"@sentry/hub": "6.3.6",
"@sentry/types": "6.3.6",
"tslib": "^1.9.3"
},
"dependencies": {
@ -10265,14 +10168,14 @@
}
},
"@sentry/tracing": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.5.tgz",
"integrity": "sha512-TNKAST1ge2g24BlTfVxNp4gP5t3drbi0OVCh8h8ah+J7UjHSfdiqhd9W2h5qv1GO61gGlpWeN/TyioyQmOxu0Q==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.6.tgz",
"integrity": "sha512-dfyYY2eESJGt5Qbigmfmb2U9ntqbwPhLNAOcjKaVg9WQRV5q2RkHCVctPoYk7TEAvfNeNRXCD8SnuFOZhttt8g==",
"requires": {
"@sentry/hub": "6.3.5",
"@sentry/minimal": "6.3.5",
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"@sentry/hub": "6.3.6",
"@sentry/minimal": "6.3.6",
"@sentry/types": "6.3.6",
"@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"dependencies": {
@ -10284,16 +10187,16 @@
}
},
"@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ=="
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.6.tgz",
"integrity": "sha512-93cFJdJkWyCfyZeWFARSU11qnoHVOS/R2h5WIsEf+jbQmkqG2C+TXVz/19s6nHVsfDrwpvYpwALPv4/nrxfU7g=="
},
"@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.6.tgz",
"integrity": "sha512-HnYlDBf8Dq8MEv7AulH7B6R1D/2LAooVclGdjg48tSrr9g+31kmtj+SAj2WWVHP9+bp29BWaC7i5nkfKrOibWw==",
"requires": {
"@sentry/types": "6.3.5",
"@sentry/types": "6.3.6",
"tslib": "^1.9.3"
},
"dependencies": {
@ -10450,12 +10353,12 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
},
"@typescript-eslint/eslint-plugin": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.1.tgz",
"integrity": "sha512-kVTAghWDDhsvQ602tHBc6WmQkdaYbkcTwZu+7l24jtJiYvm9l+/y/b2BZANEezxPDiX5MK2ZecE+9BFi/YJryw==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz",
"integrity": "sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw==",
"requires": {
"@typescript-eslint/experimental-utils": "4.22.1",
"@typescript-eslint/scope-manager": "4.22.1",
"@typescript-eslint/experimental-utils": "4.23.0",
"@typescript-eslint/scope-manager": "4.23.0",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15",
@ -10465,50 +10368,50 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz",
"integrity": "sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz",
"integrity": "sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA==",
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.22.1",
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/typescript-estree": "4.22.1",
"@typescript-eslint/scope-manager": "4.23.0",
"@typescript-eslint/types": "4.23.0",
"@typescript-eslint/typescript-estree": "4.23.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
}
},
"@typescript-eslint/parser": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.1.tgz",
"integrity": "sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.23.0.tgz",
"integrity": "sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug==",
"requires": {
"@typescript-eslint/scope-manager": "4.22.1",
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/typescript-estree": "4.22.1",
"@typescript-eslint/scope-manager": "4.23.0",
"@typescript-eslint/types": "4.23.0",
"@typescript-eslint/typescript-estree": "4.23.0",
"debug": "^4.1.1"
}
},
"@typescript-eslint/scope-manager": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz",
"integrity": "sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz",
"integrity": "sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w==",
"requires": {
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/visitor-keys": "4.22.1"
"@typescript-eslint/types": "4.23.0",
"@typescript-eslint/visitor-keys": "4.23.0"
}
},
"@typescript-eslint/types": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.1.tgz",
"integrity": "sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw=="
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.23.0.tgz",
"integrity": "sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw=="
},
"@typescript-eslint/typescript-estree": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz",
"integrity": "sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz",
"integrity": "sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw==",
"requires": {
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/visitor-keys": "4.22.1",
"@typescript-eslint/types": "4.23.0",
"@typescript-eslint/visitor-keys": "4.23.0",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
@ -10532,11 +10435,11 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz",
"integrity": "sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz",
"integrity": "sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg==",
"requires": {
"@typescript-eslint/types": "4.22.1",
"@typescript-eslint/types": "4.23.0",
"eslint-visitor-keys": "^2.0.0"
}
},
@ -10558,7 +10461,8 @@
"acorn-jsx": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng=="
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
"requires": {}
},
"ajv": {
"version": "6.12.6",
@ -10685,8 +10589,7 @@
"typescript": {
"version": "3.9.9",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz",
"integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==",
"dev": true
"integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w=="
}
}
},
@ -11415,12 +11318,12 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"eslint": {
"version": "7.25.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.25.0.tgz",
"integrity": "sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==",
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz",
"integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==",
"requires": {
"@babel/code-frame": "7.12.11",
"@eslint/eslintrc": "^0.4.0",
"@eslint/eslintrc": "^0.4.1",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@ -11535,9 +11438,9 @@
}
},
"eslint-plugin-lit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.3.0.tgz",
"integrity": "sha512-fy6Lr5vYI3kvCYaDXA20lwyKAp1keS9UjR5ntj8U2TeV+1yUta3S7xxXe+rABKRPbcNzi1ZvQLE1LmNKc9yr4Q==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.4.0.tgz",
"integrity": "sha512-3PJCC1p4pvDBKtFmg1g2cGzAgJF4IDqhb9NJUh95nYc+QXExa/O/0fILF4WB6X7qdNQKm+gW6nYtSKTyYPHtXw==",
"requires": {
"parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1",

View File

@ -50,13 +50,13 @@
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.1",
"@sentry/browser": "^6.3.5",
"@sentry/tracing": "^6.3.5",
"@sentry/browser": "^6.3.6",
"@sentry/tracing": "^6.3.6",
"@types/chart.js": "^2.9.32",
"@types/codemirror": "5.60.0",
"@types/grecaptcha": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^4.22.1",
"@typescript-eslint/parser": "^4.22.1",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"@webcomponents/webcomponentsjs": "^2.5.0",
"authentik-api": "file:api",
"babel-plugin-macros": "^3.1.0",
@ -65,10 +65,10 @@
"chartjs-adapter-moment": "^1.0.0",
"codemirror": "^5.61.0",
"construct-style-sheets-polyfill": "^2.4.16",
"eslint": "^7.25.0",
"eslint": "^7.26.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.2",
"eslint-plugin-lit": "^1.3.0",
"eslint-plugin-lit": "^1.4.0",
"flowchart.js": "^1.15.0",
"lit-element": "^2.5.1",
"lit-html": "^1.4.1",

View File

@ -204,6 +204,9 @@ body {
.pf-c-form__field-group-header-title-text {
color: var(--ak-dark-foreground);
}
.pf-c-form__field-group {
border-bottom: 0;
}
/* inputs */
optgroup, option {
color: var(--ak-dark-foreground);

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2021.5.1-rc1";
export const VERSION = "2021.5.1-rc10";
export const PAGE_SIZE = 20;
export const EVENT_REFRESH = "ak-refresh";
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";

View File

@ -5,11 +5,6 @@ import { MessageLevel } from "../messages/Message";
@customElement("ak-action-button")
export class ActionButton extends SpinnerButton {
@property()
url = "";
@property()
method = "POST";
@property({attribute: false})
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -6,6 +6,7 @@ import { ArcElement, BarElement } from "chart.js";
import { TimeScale, LinearScale } from "chart.js";
import "chartjs-adapter-moment";
import { FONT_COLOUR_DARK_MODE, FONT_COLOUR_LIGHT_MODE } from "../../pages/flows/FlowDiagram";
import {EVENT_REFRESH} from "../../constants";
Chart.register(Legend, Tooltip);
Chart.register(LineController, BarController, DoughnutController);
@ -43,6 +44,13 @@ export abstract class AKChart<T> extends LitElement {
this.chart.resize();
}
});
window.addEventListener(EVENT_REFRESH, () => {
this.apiRequest().then((r: T) => {
if (!this.chart) return;
this.chart.data = this.getChartData(r);
this.chart.update();
});
});
const matcher = window.matchMedia("(prefers-color-scheme: light)");
const handler = (ev?: MediaQueryListEvent) => {
if (ev?.matches || matcher.matches) {
@ -56,6 +64,22 @@ export abstract class AKChart<T> extends LitElement {
handler();
}
firstUpdated(): void {
this.apiRequest().then((r) => {
const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas");
if (!canvas) {
console.warn("Failed to get canvas element");
return false;
}
const ctx = canvas.getContext("2d");
if (!ctx) {
console.warn("failed to get 2d context");
return false;
}
this.chart = this.configureChart(r, ctx);
});
}
getChartType(): string {
return "bar";
}
@ -129,23 +153,6 @@ export abstract class AKChart<T> extends LitElement {
return new Chart(ctx, config as ChartConfiguration);
}
firstUpdated(): void {
this.apiRequest().then((r) => {
const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas");
if (!canvas) {
console.warn("Failed to get canvas element");
return false;
}
const ctx = canvas.getContext("2d");
if (!ctx) {
console.warn("failed to get 2d context");
return false;
}
this.chart = this.configureChart(r, ctx);
});
}
render(): TemplateResult {
return html`
<div class="container">

View File

@ -15,6 +15,7 @@ import { MessageLevel } from "../messages/Message";
import { IronFormElement } from "@polymer/iron-form/iron-form";
import { camelToSnake, convertToSlug } from "../../utils";
import { ValidationError } from "authentik-api/src";
import { EVENT_REFRESH } from "../../constants";
export class APIError extends Error {
@ -140,6 +141,12 @@ export class Form<T> extends LitElement {
level: MessageLevel.success,
message: this.getSuccessMessage()
});
this.dispatchEvent(
new CustomEvent(EVENT_REFRESH, {
bubbles: true,
composed: true,
})
);
return r;
}).catch((ex: Response) => {
if (ex.status > 399 && ex.status < 500) {

View File

@ -0,0 +1,36 @@
import { property } from "lit-element";
import { EVENT_REFRESH } from "../../constants";
import { Form } from "./Form";
export abstract class ModelForm<T, PKT extends string | number> extends Form<T> {
abstract loadInstance(pk: PKT): Promise<T>;
@property({attribute: false})
set instancePk(value: PKT) {
this._instancePk = value;
this.loadInstance(value).then(instance => {
this.instance = instance;
});
}
private _instancePk?: PKT;
@property({ attribute: false })
instance?: T = this.defaultInstance;
get defaultInstance(): T | undefined {
return undefined;
}
constructor() {
super();
this.addEventListener(EVENT_REFRESH, () => {
if (!this._instancePk) return;
this.loadInstance(this._instancePk).then(instance => {
this.instance = instance;
});
});
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@ import { t } from "@lingui/macro";
import { CSSResult, customElement, property } from "lit-element";
import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/buttons/Dropdown";
@ -13,18 +12,22 @@ import "../../elements/forms/ModalForm";
import "../../elements/forms/HorizontalFormElement";
import "../../elements/forms/FormGroup";
import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-application-form")
export class ApplicationForm extends Form<Application> {
export class ApplicationForm extends ModelForm<Application, string> {
@property({ attribute: false })
application?: Application;
loadInstance(pk: string): Promise<Application> {
return new CoreApi(DEFAULT_CONFIG).coreApplicationsRead({
slug: pk
});
}
@property({ attribute: false })
provider?: number;
getSuccessMessage(): string {
if (this.application) {
if (this.instance) {
return t`Successfully updated application.`;
} else {
return t`Successfully created application.`;
@ -37,9 +40,9 @@ export class ApplicationForm extends Form<Application> {
send = (data: Application): Promise<Application | void> => {
let writeOp: Promise<Application>;
if (this.application) {
if (this.instance) {
writeOp = new CoreApi(DEFAULT_CONFIG).coreApplicationsUpdate({
slug: this.application.slug,
slug: this.instance.slug,
data: data
});
} else {
@ -72,7 +75,7 @@ export class ApplicationForm extends Form<Application> {
${Array.from(m).map(([group, providers]) => {
return html`<optgroup label=${group}>
${providers.map(p => {
const selected = (this.application?.provider === p.pk) || (this.provider === p.pk);
const selected = (this.instance?.provider === p.pk) || (this.provider === p.pk);
return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>${p.name}</option>`;
})}
</optgroup>`;
@ -86,21 +89,21 @@ export class ApplicationForm extends Form<Application> {
label=${t`Name`}
?required=${true}
name="name">
<input type="text" value="${ifDefined(this.application?.name)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Application's display Name.`}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Slug`}
?required=${true}
name="slug">
<input type="text" value="${ifDefined(this.application?.slug)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Internal application name, used in URLs.`}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Provider`}
name="provider">
<select class="pf-c-form-control">
<option value="" ?selected=${this.application?.provider === undefined}>---------</option>
<option value="" ?selected=${this.instance?.provider === undefined}>---------</option>
${until(new ProvidersApi(DEFAULT_CONFIG).providersAllList({}).then(providers => {
return this.groupProviders(providers.results);
}), html`<option>${t`Loading...`}</option>`)}
@ -142,10 +145,10 @@ export class ApplicationForm extends Form<Application> {
?required=${true}
name="policyEngineMode">
<select class="pf-c-form-control">
<option value=${ApplicationPolicyEngineModeEnum.Any} ?selected=${this.application?.policyEngineMode === ApplicationPolicyEngineModeEnum.Any}>
<option value=${ApplicationPolicyEngineModeEnum.Any} ?selected=${this.instance?.policyEngineMode === ApplicationPolicyEngineModeEnum.Any}>
${t`ANY, any policy must match to grant access.`}
</option>
<option value=${ApplicationPolicyEngineModeEnum.All} ?selected=${this.application?.policyEngineMode === ApplicationPolicyEngineModeEnum.All}>
<option value=${ApplicationPolicyEngineModeEnum.All} ?selected=${this.instance?.policyEngineMode === ApplicationPolicyEngineModeEnum.All}>
${t`ALL, all policies must match to grant access.`}
</option>
</select>
@ -158,23 +161,23 @@ export class ApplicationForm extends Form<Application> {
<ak-form-element-horizontal
label=${t`Launch URL`}
name="metaLaunchUrl">
<input type="text" value="${ifDefined(this.application?.metaLaunchUrl)}" class="pf-c-form-control">
<input type="text" value="${ifDefined(this.instance?.metaLaunchUrl)}" class="pf-c-form-control">
<p class="pf-c-form__helper-text">${t`If left empty, authentik will try to extract the launch URL based on the selected provider.`}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Icon`}
name="metaIcon">
<input type="file" value="${ifDefined(this.application?.metaIcon)}" class="pf-c-form-control">
<input type="file" value="${ifDefined(this.instance?.metaIcon)}" class="pf-c-form-control">
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Description`}
name="metaDescription">
<textarea class="pf-c-form-control">${ifDefined(this.application?.metaDescription)}</textarea>
<textarea class="pf-c-form-control">${ifDefined(this.instance?.metaDescription)}</textarea>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Publisher`}
name="metaPublisher">
<input type="text" value="${ifDefined(this.application?.metaPublisher)}" class="pf-c-form-control">
<input type="text" value="${ifDefined(this.instance?.metaPublisher)}" class="pf-c-form-control">
</ak-form-element-horizontal>
</div>
</ak-form-group>

View File

@ -89,7 +89,7 @@ export class ApplicationListPage extends TablePage<Application> {
<span slot="header">
${t`Update Application`}
</span>
<ak-application-form slot="form" .application=${item}>
<ak-application-form slot="form" .instancePk=${item.slug}>
</ak-application-form>
<button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`}

View File

@ -102,7 +102,7 @@ export class ApplicationViewPage extends LitElement {
<span slot="header">
${t`Update Application`}
</span>
<ak-application-form slot="form" .application=${this.application}>
<ak-application-form slot="form" .instancePk=${this.application.slug}>
</ak-application-form>
<button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`}

View File

@ -1,21 +1,24 @@
import { CertificateKeyPair, CryptoApi } from "authentik-api";
import { t } from "@lingui/macro";
import { customElement, property } from "lit-element";
import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement";
import "../../elements/CodeMirror";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-crypto-certificate-form")
export class CertificateKeyPairForm extends Form<CertificateKeyPair> {
export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string> {
@property({attribute: false})
keyPair?: CertificateKeyPair;
loadInstance(pk: string): Promise<CertificateKeyPair> {
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsRead({
kpUuid: pk,
});
}
getSuccessMessage(): string {
if (this.keyPair) {
if (this.instance) {
return t`Successfully updated certificate-key pair.`;
} else {
return t`Successfully created certificate-key pair.`;
@ -23,9 +26,9 @@ export class CertificateKeyPairForm extends Form<CertificateKeyPair> {
}
send = (data: CertificateKeyPair): Promise<CertificateKeyPair> => {
if (this.keyPair) {
if (this.instance) {
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsPartialUpdate({
kpUuid: this.keyPair.pk || "",
kpUuid: this.instance.pk || "",
data: data
});
} else {
@ -41,21 +44,21 @@ export class CertificateKeyPairForm extends Form<CertificateKeyPair> {
label=${t`Name`}
name="name"
?required=${true}>
<input type="text" value="${ifDefined(this.keyPair?.name)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Certificate`}
name="certificateData"
?writeOnly=${this.keyPair !== undefined}
?writeOnly=${this.instance !== undefined}
?required=${true}>
<textarea class="pf-c-form-control" required>${ifDefined(this.keyPair?.certificateData)}</textarea>
<textarea class="pf-c-form-control" required>${ifDefined(this.instance?.certificateData)}</textarea>
<p class="pf-c-form__helper-text">${t`PEM-encoded Certificate data.`}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
name="keyData"
?writeOnly=${this.keyPair !== undefined}
?writeOnly=${this.instance !== undefined}
label=${t`Private Key`}>
<textarea class="pf-c-form-control" >${ifDefined(this.keyPair?.keyData)}</textarea>
<textarea class="pf-c-form-control" >${ifDefined(this.instance?.keyData)}</textarea>
<p class="pf-c-form__helper-text">${t`Optional Private Key. If this is set, you can use this keypair for encryption.`}</p>
</ak-form-element-horizontal>
</form>`;

View File

@ -70,7 +70,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
<span slot="header">
${t`Update Certificate-Key Pair`}
</span>
<ak-crypto-certificate-form slot="form" .keyPair=${item}>
<ak-crypto-certificate-form slot="form" .instancePk=${item.pk}>
</ak-crypto-certificate-form>
<button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`}
@ -112,6 +112,23 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
<div class="pf-c-description-list__text">${item.certSubject}</div>
</dd>
</div>
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${t`Download`}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<a class="pf-c-button pf-m-secondary" target="_blank"
href="/api/v2beta/crypto/certificatekeypairs/${item.pk}/view_certificate/?download">
${t`Download Certificate`}
</a>
${item.privateKeyAvailable ? html`<a class="pf-c-button pf-m-secondary" target="_blank"
href="/api/v2beta/crypto/certificatekeypairs/${item.pk}/view_private_key/?download">
${t`Download Private key`}
</a>` : html``}
</div>
</dd>
</div>
</dl>
</div>
</td>

View File

@ -1,21 +1,24 @@
import { CoreApi, EventsApi, NotificationRule, NotificationRuleSeverityEnum } from "authentik-api";
import { t } from "@lingui/macro";
import { customElement, property } from "lit-element";
import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement";
import { until } from "lit-html/directives/until";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-event-rule-form")
export class RuleForm extends Form<NotificationRule> {
export class RuleForm extends ModelForm<NotificationRule, string> {
@property({attribute: false})
rule?: NotificationRule;
loadInstance(pk: string): Promise<NotificationRule> {
return new EventsApi(DEFAULT_CONFIG).eventsRulesRead({
pbmUuid: pk,
});
}
getSuccessMessage(): string {
if (this.rule) {
if (this.instance) {
return t`Successfully updated rule.`;
} else {
return t`Successfully created rule.`;
@ -23,9 +26,9 @@ export class RuleForm extends Form<NotificationRule> {
}
send = (data: NotificationRule): Promise<NotificationRule> => {
if (this.rule) {
if (this.instance) {
return new EventsApi(DEFAULT_CONFIG).eventsRulesUpdate({
pbmUuid: this.rule.pk || "",
pbmUuid: this.instance.pk || "",
data: data
});
} else {
@ -37,13 +40,13 @@ export class RuleForm extends Form<NotificationRule> {
renderSeverity(): TemplateResult {
return html`
<option value=${NotificationRuleSeverityEnum.Alert} ?selected=${this.rule?.severity === NotificationRuleSeverityEnum.Alert}>
<option value=${NotificationRuleSeverityEnum.Alert} ?selected=${this.instance?.severity === NotificationRuleSeverityEnum.Alert}>
${t`Alert`}
</option>
<option value=${NotificationRuleSeverityEnum.Warning} ?selected=${this.rule?.severity === NotificationRuleSeverityEnum.Warning}>
<option value=${NotificationRuleSeverityEnum.Warning} ?selected=${this.instance?.severity === NotificationRuleSeverityEnum.Warning}>
${t`Warning`}
</option>
<option value=${NotificationRuleSeverityEnum.Notice} ?selected=${this.rule?.severity === NotificationRuleSeverityEnum.Notice}>
<option value=${NotificationRuleSeverityEnum.Notice} ?selected=${this.instance?.severity === NotificationRuleSeverityEnum.Notice}>
${t`Notice`}
</option>
`;
@ -55,16 +58,16 @@ export class RuleForm extends Form<NotificationRule> {
label=${t`Name`}
?required=${true}
name="name">
<input type="text" value="${ifDefined(this.rule?.name)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Group`}
name="group">
<select class="pf-c-form-control">
<option value="" ?selected=${this.rule?.group === undefined}>---------</option>
<option value="" ?selected=${this.instance?.group === undefined}>---------</option>
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
return groups.results.map(group => {
return html`<option value=${ifDefined(group.pk)} ?selected=${this.rule?.group?.groupUuid === group.pk}>${group.name}</option>`;
return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.group === group.pk}>${group.name}</option>`;
});
}), html`<option>${t`Loading...`}</option>`)}
</select>
@ -76,8 +79,8 @@ export class RuleForm extends Form<NotificationRule> {
<select name="users" class="pf-c-form-control" multiple>
${until(new EventsApi(DEFAULT_CONFIG).eventsTransportsList({}).then(transports => {
return transports.results.map(transport => {
const selected = Array.from(this.rule?.transports || []).some(su => {
return su.uuid == transport.pk;
const selected = Array.from(this.instance?.transports || []).some(su => {
return su == transport.pk;
});
return html`<option value=${ifDefined(transport.pk)} ?selected=${selected}>${transport.name}</option>`;
});

View File

@ -55,7 +55,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
return [
html`${item.name}`,
html`${item.severity}`,
html`${item.group?.name || t`None (rule disabled)`}`,
html`${item.groupObj?.name || t`None (rule disabled)`}`,
html`
<ak-forms-modal>
<span slot="submit">
@ -64,7 +64,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
<span slot="header">
${t`Update Notification Rule`}
</span>
<ak-event-rule-form slot="form" .rule=${item}>
<ak-event-rule-form slot="form" .instancePk=${item.pk}>
</ak-event-rule-form>
<button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`}

View File

@ -3,22 +3,25 @@ import { t } from "@lingui/macro";
import { customElement, property } from "lit-element";
import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement";
import { first } from "../../utils";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-event-transport-form")
export class TransportForm extends Form<NotificationTransport> {
export class TransportForm extends ModelForm<NotificationTransport, string> {
@property({attribute: false})
transport?: NotificationTransport;
loadInstance(pk: string): Promise<NotificationTransport> {
return new EventsApi(DEFAULT_CONFIG).eventsTransportsRead({
uuid: pk,
});
}
@property({type: Boolean})
showWebhook = false;
getSuccessMessage(): string {
if (this.transport) {
if (this.instance) {
return t`Successfully updated transport.`;
} else {
return t`Successfully created transport.`;
@ -26,9 +29,9 @@ export class TransportForm extends Form<NotificationTransport> {
}
send = (data: NotificationTransport): Promise<NotificationTransport> => {
if (this.transport) {
if (this.instance) {
return new EventsApi(DEFAULT_CONFIG).eventsTransportsUpdate({
uuid: this.transport.pk || "",
uuid: this.instance.pk || "",
data: data
});
} else {
@ -40,21 +43,21 @@ export class TransportForm extends Form<NotificationTransport> {
renderTransportModes(): TemplateResult {
return html`
<option value=${NotificationTransportModeEnum.Email} ?selected=${this.transport?.mode === NotificationTransportModeEnum.Email}>
<option value=${NotificationTransportModeEnum.Email} ?selected=${this.instance?.mode === NotificationTransportModeEnum.Email}>
${t`Email`}
</option>
<option value=${NotificationTransportModeEnum.Webhook} ?selected=${this.transport?.mode === NotificationTransportModeEnum.Webhook}>
<option value=${NotificationTransportModeEnum.Webhook} ?selected=${this.instance?.mode === NotificationTransportModeEnum.Webhook}>
${t`Webhook (generic)`}
</option>
<option value=${NotificationTransportModeEnum.WebhookSlack} ?selected=${this.transport?.mode === NotificationTransportModeEnum.WebhookSlack}>
<option value=${NotificationTransportModeEnum.WebhookSlack} ?selected=${this.instance?.mode === NotificationTransportModeEnum.WebhookSlack}>
${t`Webhook (Slack/Discord)`}
</option>
`;
}
firstUpdated(): void {
if (this.transport) {
this.onModeChange(this.transport.mode);
if (this.instance) {
this.onModeChange(this.instance.mode);
}
}
@ -72,7 +75,7 @@ export class TransportForm extends Form<NotificationTransport> {
label=${t`Name`}
?required=${true}
name="name">
<input type="text" value="${ifDefined(this.transport?.name)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Mode`}
@ -89,11 +92,11 @@ export class TransportForm extends Form<NotificationTransport> {
?hidden=${!this.showWebhook}
label=${t`Webhook URL`}
name="webhookUrl">
<input type="text" value="${ifDefined(this.transport?.webhookUrl)}" class="pf-c-form-control">
<input type="text" value="${ifDefined(this.instance?.webhookUrl)}" class="pf-c-form-control">
</ak-form-element-horizontal>
<ak-form-element-horizontal name="sendOnce">
<div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.transport?.sendOnce, false)}>
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.sendOnce, false)}>
<label class="pf-c-check__label">
${t`Send once`}
</label>

View File

@ -68,7 +68,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
<span slot="header">
${t`Update Notification Transport`}
</span>
<ak-event-transport-form slot="form" .transport=${item}>
<ak-event-transport-form slot="form" .instancePk=${item.pk}>
</ak-event-transport-form>
<button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`}

View File

@ -58,7 +58,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
<ak-proxy-form
slot="form"
.args=${{
"stageUUID": item.stage
"instancePk": item.stage
}}
type=${ifDefined(item.stageObj?.component)}>
</ak-proxy-form>
@ -73,7 +73,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
<span slot="header">
${t`Update Stage binding`}
</span>
<ak-stage-binding-form slot="form" .fsb=${item}>
<ak-stage-binding-form slot="form" .instancePk=${item.pk}>
</ak-stage-binding-form>
<button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit Binding`}

View File

@ -1,20 +1,23 @@
import { Flow, FlowDesignationEnum, FlowPolicyEngineModeEnum, FlowsApi } from "authentik-api";
import { t } from "@lingui/macro";
import { customElement, property } from "lit-element";
import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-flow-form")
export class FlowForm extends Form<Flow> {
export class FlowForm extends ModelForm<Flow, string> {
@property({attribute: false})
flow?: Flow;
loadInstance(pk: string): Promise<Flow> {
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesRead({
slug: pk,
});
}
getSuccessMessage(): string {
if (this.flow) {
if (this.instance) {
return t`Successfully updated flow.`;
} else {
return t`Successfully created flow.`;
@ -23,9 +26,9 @@ export class FlowForm extends Form<Flow> {
send = (data: Flow): Promise<void | Flow> => {
let writeOp: Promise<Flow>;
if (this.flow) {
if (this.instance) {
writeOp = new FlowsApi(DEFAULT_CONFIG).flowsInstancesUpdate({
slug: this.flow.slug,
slug: this.instance.slug,
data: data
});
} else {
@ -47,25 +50,25 @@ export class FlowForm extends Form<Flow> {
renderDesignations(): TemplateResult {
return html`
<option value=${FlowDesignationEnum.Authentication} ?selected=${this.flow?.designation === FlowDesignationEnum.Authentication}>
<option value=${FlowDesignationEnum.Authentication} ?selected=${this.instance?.designation === FlowDesignationEnum.Authentication}>
${t`Authentication`}
</option>
<option value=${FlowDesignationEnum.Authorization} ?selected=${this.flow?.designation === FlowDesignationEnum.Authorization}>
<option value=${FlowDesignationEnum.Authorization} ?selected=${this.instance?.designation === FlowDesignationEnum.Authorization}>
${t`Authorization`}
</option>
<option value=${FlowDesignationEnum.Enrollment} ?selected=${this.flow?.designation === FlowDesignationEnum.Enrollment}>
<option value=${FlowDesignationEnum.Enrollment} ?selected=${this.instance?.designation === FlowDesignationEnum.Enrollment}>
${t`Enrollment`}
</option>
<option value=${FlowDesignationEnum.Invalidation} ?selected=${this.flow?.designation === FlowDesignationEnum.Invalidation}>
<option value=${FlowDesignationEnum.Invalidation} ?selected=${this.instance?.designation === FlowDesignationEnum.Invalidation}>
${t`Invalidation`}
</option>
<option value=${FlowDesignationEnum.Recovery} ?selected=${this.flow?.designation === FlowDesignationEnum.Recovery}>
<option value=${FlowDesignationEnum.Recovery} ?selected=${this.instance?.designation === FlowDesignationEnum.Recovery}>
${t`Recovery`}
</option>
<option value=${FlowDesignationEnum.StageConfiguration} ?selected=${this.flow?.designation === FlowDesignationEnum.StageConfiguration}>
<option value=${FlowDesignationEnum.StageConfiguration} ?selected=${this.instance?.designation === FlowDesignationEnum.StageConfiguration}>
${t`Stage Configuration`}
</option>
<option value=${FlowDesignationEnum.Unenrollment} ?selected=${this.flow?.designation === FlowDesignationEnum.Unenrollment}>
<option value=${FlowDesignationEnum.Unenrollment} ?selected=${this.instance?.designation === FlowDesignationEnum.Unenrollment}>
${t`Unenrollment`}
</option>
`;
@ -77,20 +80,20 @@ export class FlowForm extends Form<Flow> {
label=${t`Name`}
?required=${true}
name="name">
<input type="text" value="${ifDefined(this.flow?.name)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Title`}
?required=${true}
name="title">
<input type="text" value="${ifDefined(this.flow?.title)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.title)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Shown as the Title in Flow pages.`}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Slug`}
?required=${true}
name="slug">
<input type="text" value="${ifDefined(this.flow?.slug)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Visible in the URL.`}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
@ -98,10 +101,10 @@ export class FlowForm extends Form<Flow> {
?required=${true}
name="policyEngineMode">
<select class="pf-c-form-control">
<option value=${FlowPolicyEngineModeEnum.Any} ?selected=${this.flow?.policyEngineMode === FlowPolicyEngineModeEnum.Any}>
<option value=${FlowPolicyEngineModeEnum.Any} ?selected=${this.instance?.policyEngineMode === FlowPolicyEngineModeEnum.Any}>
${t`ANY, any policy must match to grant access.`}
</option>
<option value=${FlowPolicyEngineModeEnum.All} ?selected=${this.flow?.policyEngineMode === FlowPolicyEngineModeEnum.All}>
<option value=${FlowPolicyEngineModeEnum.All} ?selected=${this.instance?.policyEngineMode === FlowPolicyEngineModeEnum.All}>
${t`ALL, all policies must match to grant access.`}
</option>
</select>
@ -111,7 +114,7 @@ export class FlowForm extends Form<Flow> {
?required=${true}
name="designation">
<select class="pf-c-form-control">
<option value="" ?selected=${this.flow?.designation === undefined}>---------</option>
<option value="" ?selected=${this.instance?.designation === undefined}>---------</option>
${this.renderDesignations()}
</select>
<p class="pf-c-form__helper-text">${t`Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik.`}</p>
@ -119,7 +122,7 @@ export class FlowForm extends Form<Flow> {
<ak-form-element-horizontal
label=${t`Background`}
name="background">
<input type="file" value="${ifDefined(this.flow?.background)}" class="pf-c-form-control">
<input type="file" value="${ifDefined(this.instance?.background)}" class="pf-c-form-control">
<p class="pf-c-form__helper-text">${t`Background shown during execution.`}</p>
</ak-form-element-horizontal>
</form>`;

View File

@ -68,7 +68,7 @@ export class FlowListPage extends TablePage<Flow> {
<span slot="header">
${t`Update Flow`}
</span>
<ak-flow-form slot="form" .flow=${item}>
<ak-flow-form slot="form" .instancePk=${item.pk}>
</ak-flow-form>
<button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`}

View File

@ -3,23 +3,26 @@ import { t } from "@lingui/macro";
import { customElement, property } from "lit-element";
import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement";
import { first, groupBy } from "../../utils";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-stage-binding-form")
export class StageBindingForm extends Form<FlowStageBinding> {
export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
@property({attribute: false})
fsb?: FlowStageBinding;
loadInstance(pk: string): Promise<FlowStageBinding> {
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsRead({
fsbUuid: pk,
});
}
@property()
targetPk?: string;
getSuccessMessage(): string {
if (this.fsb) {
if (this.instance) {
return t`Successfully updated binding.`;
} else {
return t`Successfully created binding.`;
@ -27,9 +30,9 @@ export class StageBindingForm extends Form<FlowStageBinding> {
}
send = (data: FlowStageBinding): Promise<FlowStageBinding> => {
if (this.fsb) {
if (this.instance) {
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsUpdate({
fsbUuid: this.fsb.pk || "",
fsbUuid: this.instance.pk || "",
data: data
});
} else {
@ -45,7 +48,7 @@ export class StageBindingForm extends Form<FlowStageBinding> {
${groupBy<Stage>(stages, (s => s.verboseName || "")).map(([group, stages]) => {
return html`<optgroup label=${group}>
${stages.map(stage => {
const selected = (this.fsb?.stage === stage.pk);
const selected = (this.instance?.stage === stage.pk);
return html`<option ?selected=${selected} value=${ifDefined(stage.pk)}>${stage.name}</option>`;
})}
</optgroup>`;
@ -54,8 +57,8 @@ export class StageBindingForm extends Form<FlowStageBinding> {
}
getOrder(): Promise<number> {
if (this.fsb) {
return Promise.resolve(this.fsb.order);
if (this.instance) {
return Promise.resolve(this.instance.order);
}
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({
target: this.targetPk || "",
@ -69,9 +72,9 @@ export class StageBindingForm extends Form<FlowStageBinding> {
}
renderTarget(): TemplateResult {
if (this.fsb?.target || this.targetPk) {
if (this.instance?.target || this.targetPk) {
return html`
<input required name="target" type="hidden" value=${ifDefined(this.fsb?.target || this.targetPk)}>
<input required name="target" type="hidden" value=${ifDefined(this.instance?.target || this.targetPk)}>
`;
}
return html`<ak-form-element-horizontal
@ -114,7 +117,7 @@ export class StageBindingForm extends Form<FlowStageBinding> {
</ak-form-element-horizontal>
<ak-form-element-horizontal name="evaluateOnPlan">
<div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.fsb?.evaluateOnPlan, true)}>
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.evaluateOnPlan, true)}>
<label class="pf-c-check__label">
${t`Evaluate on plan`}
</label>
@ -125,7 +128,7 @@ export class StageBindingForm extends Form<FlowStageBinding> {
</ak-form-element-horizontal>
<ak-form-element-horizontal name="reEvaluatePolicies">
<div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.fsb?.reEvaluatePolicies, false)}>
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.reEvaluatePolicies, false)}>
<label class="pf-c-check__label">
${t`Re-evaluate policies`}
</label>
@ -137,10 +140,10 @@ export class StageBindingForm extends Form<FlowStageBinding> {
?required=${true}
name="policyEngineMode">
<select class="pf-c-form-control">
<option value=${FlowStageBindingPolicyEngineModeEnum.Any} ?selected=${this.fsb?.policyEngineMode === FlowStageBindingPolicyEngineModeEnum.Any}>
<option value=${FlowStageBindingPolicyEngineModeEnum.Any} ?selected=${this.instance?.policyEngineMode === FlowStageBindingPolicyEngineModeEnum.Any}>
${t`ANY, any policy must match to include this stage access.`}
</option>
<option value=${FlowStageBindingPolicyEngineModeEnum.All} ?selected=${this.fsb?.policyEngineMode === FlowStageBindingPolicyEngineModeEnum.All}>
<option value=${FlowStageBindingPolicyEngineModeEnum.All} ?selected=${this.instance?.policyEngineMode === FlowStageBindingPolicyEngineModeEnum.All}>
${t`ALL, all policies must match to include this stage access.`}
</option>
</select>

View File

@ -1,9 +1,8 @@
import { CoreApi, Group, User } from "authentik-api";
import { t } from "@lingui/macro";
import { customElement, property } from "lit-element";
import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement";
@ -13,15 +12,19 @@ import "../../elements/chips/Chip";
import "./MemberSelectModal";
import YAML from "yaml";
import { first } from "../../utils";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-group-form")
export class GroupForm extends Form<Group> {
export class GroupForm extends ModelForm<Group, string> {
@property({attribute: false})
group?: Group;
loadInstance(pk: string): Promise<Group> {
return new CoreApi(DEFAULT_CONFIG).coreGroupsRead({
groupUuid: pk
});
}
getSuccessMessage(): string {
if (this.group) {
if (this.instance) {
return t`Successfully updated group.`;
} else {
return t`Successfully created group.`;
@ -29,13 +32,13 @@ export class GroupForm extends Form<Group> {
}
send = (data: Group): Promise<Group> => {
if (this.group?.pk) {
if (this.instance?.pk) {
return new CoreApi(DEFAULT_CONFIG).coreGroupsUpdate({
groupUuid: this.group.pk || "",
groupUuid: this.instance.pk || "",
data: data
});
} else {
data.users = Array.from(this.group?.users || []) as unknown as Set<number>;
data.users = Array.from(this.instance?.users || []) as unknown as Set<number>;
return new CoreApi(DEFAULT_CONFIG).coreGroupsCreate({
data: data
});
@ -48,11 +51,11 @@ export class GroupForm extends Form<Group> {
label=${t`Name`}
?required=${true}
name="name">
<input type="text" value="${ifDefined(this.group?.name)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="isSuperuser">
<div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.group?.isSuperuser, false)}>
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.isSuperuser, false)}>
<label class="pf-c-check__label">
${t`Is superuser`}
</label>
@ -63,10 +66,10 @@ export class GroupForm extends Form<Group> {
label=${t`Parent`}
name="parent">
<select class="pf-c-form-control">
<option value="" ?selected=${this.group?.parent === undefined}>---------</option>
<option value="" ?selected=${this.instance?.parent === undefined}>---------</option>
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
return groups.results.map(group => {
return html`<option value=${ifDefined(group.pk)} ?selected=${this.group?.parent === group.pk}>${group.name}</option>`;
return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.parent === group.pk}>${group.name}</option>`;
});
}), html`<option>${t`Loading...`}</option>`)}
</select>
@ -79,8 +82,8 @@ export class GroupForm extends Form<Group> {
.confirm=${(items: User[]) => {
// Because the model only has the IDs, map the user list to IDs
const ids = items.map(u => u.pk || 0);
if (!this.group) this.group = {} as Group;
this.group.users = new Set(Array.from(this.group?.users || []).concat(ids));
if (!this.instance) this.instance = {} as Group;
this.instance.users = new Set(Array.from(this.instance?.users || []).concat(ids));
this.requestUpdate();
return Promise.resolve();
}}>
@ -94,7 +97,7 @@ export class GroupForm extends Form<Group> {
ordering: "username",
}).then(users => {
return users.results.map(user => {
const selected = Array.from(this.group?.users || []).some(su => {
const selected = Array.from(this.instance?.users || []).some(su => {
return su == user.pk;
});
if (!selected) return;
@ -102,11 +105,11 @@ export class GroupForm extends Form<Group> {
.removable=${true}
value=${ifDefined(user.pk)}
@remove=${() => {
if (!this.group) return;
const users = Array.from(this.group?.users || []);
if (!this.instance) return;
const users = Array.from(this.instance?.users || []);
const idx = users.indexOf(user.pk || 0);
users.splice(idx, 1);
this.group.users = new Set(users);
this.instance.users = new Set(users);
this.requestUpdate();
}}>
${user.username}
@ -122,7 +125,7 @@ export class GroupForm extends Form<Group> {
label=${t`Attributes`}
?required=${true}
name="attributes">
<ak-codemirror mode="yaml" value="${YAML.stringify(first(this.group?.attributes, {}))}">
<ak-codemirror mode="yaml" value="${YAML.stringify(first(this.instance?.attributes, {}))}">
</ak-codemirror>
<p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p>
</ak-form-element-horizontal>

View File

@ -63,7 +63,7 @@ export class GroupListPage extends TablePage<Group> {
<span slot="header">
${t`Update Group`}
</span>
<ak-group-form slot="form" .group=${item}>
<ak-group-form slot="form" .instancePk=${item.pk}>
</ak-group-form>
<button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`}

View File

@ -1,23 +1,26 @@
import { Outpost, OutpostsApi, OutpostTypeEnum, ProvidersApi } from "authentik-api";
import { t } from "@lingui/macro";
import { customElement, property } from "lit-element";
import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement";
import "../../elements/CodeMirror";
import YAML from "yaml";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-outpost-form")
export class OutpostForm extends Form<Outpost> {
export class OutpostForm extends ModelForm<Outpost, string> {
@property({attribute: false})
outpost?: Outpost;
loadInstance(pk: string): Promise<Outpost> {
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesRead({
uuid: pk
});
}
getSuccessMessage(): string {
if (this.outpost) {
if (this.instance) {
return t`Successfully updated outpost.`;
} else {
return t`Successfully created outpost.`;
@ -25,9 +28,9 @@ export class OutpostForm extends Form<Outpost> {
}
send = (data: Outpost): Promise<Outpost> => {
if (this.outpost) {
if (this.instance) {
return new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsUpdate({
uuid: this.outpost.pk || "",
uuid: this.instance.pk || "",
data: data
});
} else {
@ -43,27 +46,27 @@ export class OutpostForm extends Form<Outpost> {
label=${t`Name`}
?required=${true}
name="name">
<input type="text" value="${ifDefined(this.outpost?.name)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Type`}
?required=${true}
name="type">
<select class="pf-c-form-control">
<option value=${OutpostTypeEnum.Proxy} ?selected=${this.outpost?.type === OutpostTypeEnum.Proxy}>${t`Proxy`}</option>
<option value=${OutpostTypeEnum.Ldap} ?selected=${this.outpost?.type === OutpostTypeEnum.Ldap}>${t`LDAP (Technical preview)`}</option>
<option value=${OutpostTypeEnum.Proxy} ?selected=${this.instance?.type === OutpostTypeEnum.Proxy}>${t`Proxy`}</option>
<option value=${OutpostTypeEnum.Ldap} ?selected=${this.instance?.type === OutpostTypeEnum.Ldap}>${t`LDAP (Technical preview)`}</option>
</select>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Service connection`}
name="serviceConnection">
<select class="pf-c-form-control">
<option value="" ?selected=${this.outpost?.serviceConnection === undefined}>---------</option>
<option value="" ?selected=${this.instance?.serviceConnection === undefined}>---------</option>
${until(new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList({
ordering: "pk"
}).then(scs => {
return scs.results.map(sc => {
return html`<option value=${ifDefined(sc.pk)} ?selected=${this.outpost?.serviceConnection === sc.pk}>
return html`<option value=${ifDefined(sc.pk)} ?selected=${this.instance?.serviceConnection === sc.pk}>
${sc.name} (${sc.verboseName})
</option>`;
});
@ -83,7 +86,7 @@ export class OutpostForm extends Form<Outpost> {
ordering: "pk"
}).then(providers => {
return providers.results.map(provider => {
const selected = Array.from(this.outpost?.providers || []).some(sp => {
const selected = Array.from(this.instance?.providers || []).some(sp => {
return sp == provider.pk;
});
return html`<option value=${ifDefined(provider.pk)} ?selected=${selected}>${provider.verboseName} ${provider.name}</option>`;
@ -93,7 +96,7 @@ export class OutpostForm extends Form<Outpost> {
ordering: "pk"
}).then(providers => {
return providers.results.map(provider => {
const selected = Array.from(this.outpost?.providers || []).some(sp => {
const selected = Array.from(this.instance?.providers || []).some(sp => {
return sp == provider.pk;
});
return html`<option value=${ifDefined(provider.pk)} ?selected=${selected}>${provider.verboseName} ${provider.name}</option>`;
@ -102,18 +105,18 @@ export class OutpostForm extends Form<Outpost> {
</select>
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
</ak-form-element-horizontal>
${until(new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsDefaultSettings({}).then(config => {
let fc = config.config;
if (this.outpost) {
fc = this.outpost.config;
}
return html`<ak-form-element-horizontal
label=${t`Configuration`}
name="config">
<ak-codemirror mode="yaml" value="${YAML.stringify(fc)}"></ak-codemirror>
<p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p>
</ak-form-element-horizontal>`;
}))}
<ak-form-element-horizontal
label=${t`Configuration`}
name="config">
<ak-codemirror mode="yaml" value="${until(new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsDefaultSettings({}).then(config => {
let fc = config.config;
if (this.instance) {
fc = this.instance.config;
}
return YAML.stringify(fc);
}))}"></ak-codemirror>
<p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p>
</ak-form-element-horizontal>
</form>`;
}

View File

@ -1,55 +1,69 @@
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { until } from "lit-html/directives/until";
import { OutpostsApi } from "authentik-api";
import { OutpostHealth, OutpostsApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../../api/Config";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import "../../elements/Spinner";
import AKGlobal from "../../authentik.css";
import { PFColor } from "../../elements/Label";
import { EVENT_REFRESH } from "../../constants";
@customElement("ak-outpost-health")
export class OutpostHealth extends LitElement {
export class OutpostHealthElement extends LitElement {
@property()
outpostId?: string;
@property({attribute: false})
outpostHealth: OutpostHealth[] = [];
static get styles(): CSSResult[] {
return [PFBase, AKGlobal];
}
constructor() {
super();
this.addEventListener(EVENT_REFRESH, () => {
this.firstUpdated();
});
}
firstUpdated(): void {
if (!this.outpostId) return;
new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsHealth({
uuid: this.outpostId
}).then(health => {
this.outpostHealth = health;
});
}
render(): TemplateResult {
if (!this.outpostId) {
return html`<ak-spinner></ak-spinner>`;
}
return html`<ul>${until(new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsHealth({
uuid: this.outpostId
}).then((oh) => {
if (oh.length === 0) {
return html`<li>
<ul>
<li role="cell">
<ak-label color=${PFColor.Grey} text=${t`Not available`}></ak-label>
</li>
</ul>
</li>`;
}
return oh.map((h) => {
return html`<li>
<ul>
<li role="cell">
<ak-label color=${PFColor.Green} text=${t`Last seen: ${h.lastSeen?.toLocaleTimeString()}`}></ak-label>
</li>
<li role="cell">
${h.versionOutdated ?
html`<ak-label color=${PFColor.Red}
text=${t`${h.version}, should be ${h.versionShould}`}></ak-label>` :
html`<ak-label color=${PFColor.Green} text=${t`Version: ${h.version || ""}`}></ak-label>`}
</li>
</ul>
</li>`;
});
}), html`<ak-spinner></ak-spinner>`)}</ul>`;
if (this.outpostHealth.length === 0) {
return html`
<ul>
<li role="cell">
<ak-label color=${PFColor.Grey} text=${t`Not available`}></ak-label>
</li>
</ul>`;
}
return html`<ul>${this.outpostHealth.map((h) => {
return html`<li>
<ul>
<li role="cell">
<ak-label color=${PFColor.Green} text=${t`Last seen: ${h.lastSeen?.toLocaleTimeString()}`}></ak-label>
</li>
<li role="cell">
${h.versionOutdated ?
html`<ak-label color=${PFColor.Red}
text=${t`${h.version}, should be ${h.versionShould}`}></ak-label>` :
html`<ak-label color=${PFColor.Green} text=${t`Version: ${h.version || ""}`}></ak-label>`}
</li>
</ul>
</li>`;
})}</ul>`;
}
}

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