Compare commits

...

105 Commits

Author SHA1 Message Date
c1c55a6005 lifecycle: fix permission error with local docker
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-22 20:47:05 +02:00
2d5c45543b release: 2021.5.4 2021-05-22 20:15:23 +02:00
9d476a42d1 web: don't set X-Forwarded-Proto when no request TLS Options are set
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-22 19:46:40 +02:00
2c816e6162 providers/proxy: don't use https to communicate with outpost
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-22 18:56:38 +02:00
dbcb4d46ba web: fix missing flow and policy cache UI
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-22 13:04:13 +02:00
6600da7d98 providers/oauth2: add missing kid header to JWT Tokens
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-21 23:40:00 +02:00
a265dd54cc stages/authenticator_*: fix Permission Error when disabling Authenticator as non-superuser
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-21 21:25:03 +02:00
a603f42cc0 api: add OwnerFilter
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-21 20:46:59 +02:00
d9a788aac8 api: rename auth to authentication, add authorization for rest_framework permission class
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-21 20:14:03 +02:00
7c6185b581 api: fix URL names for admin Authenticator Views
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-21 19:53:40 +02:00
41a1305555 policies: improve debug logging
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-21 19:10:47 +02:00
75f252b530 flows: rename oob to oobe
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-21 19:10:42 +02:00
a9519a4a68 g: set x-forwarded-proto based on upstream TLS Status
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-21 09:41:39 +02:00
bf4cbb25fe release: 2021.5.3 2021-05-20 20:17:39 +02:00
a925418f60 lib: don't send ImproperlyConfigured to sentry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 19:18:35 +02:00
ffd61d0e60 root: fix bumpversion
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 19:16:23 +02:00
71d112bdcf sources/plex: remove default for plex_token
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 19:13:54 +02:00
c58fe18b97 web: remove nginx config, add caching headers to g
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 19:11:55 +02:00
590c7f4c9d outposts: fix error on outpost disconnect
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 18:07:27 +02:00
56f1204c9b outposts: fix update signal not being sent to correct instances
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 15:23:38 +02:00
349a5b2d00 web/admin: fix flow form not loading data
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 01:10:19 +02:00
63e3667e82 web: fix t.reset is not a function
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 01:10:11 +02:00
92f2a82c03 providers/oauth2: fix double login required when prompt=login
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 01:10:08 +02:00
dcf074650e providers/proxy: fix redirect_uris not always being set on save
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-20 01:10:04 +02:00
5a465fbc36 release: 2021.5.2 2021-05-17 19:54:10 +02:00
7cd80a903a build(deps): bump eslint-plugin-lit from 1.4.0 to 1.4.1 in /web (#890)
Bumps [eslint-plugin-lit](https://github.com/43081j/eslint-plugin-lit) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/43081j/eslint-plugin-lit/releases)
- [Commits](https://github.com/43081j/eslint-plugin-lit/compare/v1.4.0...v1.4.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-17 09:12:14 +02:00
dd00351bc7 build(deps): bump rollup from 2.47.0 to 2.48.0 in /web (#889)
Bumps [rollup](https://github.com/rollup/rollup) from 2.47.0 to 2.48.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v2.47.0...v2.48.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-17 09:12:05 +02:00
5fca7d11b8 build(deps): bump boto3 from 1.17.72 to 1.17.73 (#891)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.72 to 1.17.73.
- [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.72...1.17.73)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-17 09:08:53 +02:00
0ff59636f7 build(deps-dev): bump pytest-django from 4.2.0 to 4.3.0 (#892)
Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/pytest-dev/pytest-django/releases)
- [Changelog](https://github.com/pytest-dev/pytest-django/blob/master/docs/changelog.rst)
- [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.2.0...v4.3.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-17 09:08:41 +02:00
e5ebe390d2 ci: fix missing dependencies for scripts.generate_ci_config
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-17 00:08:45 +02:00
b66626f9c4 ci: generate secert_key for CI runs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-16 23:46:23 +02:00
23123c43ee website/docs: improve wording on release notes, point to tag for docker-compose download
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-16 23:08:11 +02:00
8ce918d527 website/docs: Always point to master copy of docker-compose.yml in installation instructions (#888) 2021-05-16 23:02:16 +02:00
45c1a603e7 root: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-16 22:29:28 +02:00
583271d5ed root: only load debug secret key when debug is enabled
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-16 22:25:55 +02:00
176360fdd7 website/docs: fix $auth_cookie not being defined in outpost docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-16 22:18:31 +02:00
8d2a3b67b9 lib: Fix config loading of secrets from files (#887) 2021-05-16 21:10:31 +02:00
d0d3072c50 outposts/ldap: fix AUTHENTIK_INSECURE not being respected for API client during bind
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-16 00:01:16 +02:00
34e2bbc41d Merge branch 'next' 2021-05-15 23:25:17 +02:00
ea2dbb2f33 web/admin: fix error when copying token while none exist
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-15 23:25:06 +02:00
c55f2ad10a root: set additional sentry tags
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-15 19:53:43 +02:00
2cde40aeee website/docs: add release notes for 2021.5.2
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-15 17:49:34 +02:00
a30b32fbbf outposts: fix missing default for OutpostState.for_channel
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-15 17:46:53 +02:00
1745306cc6 outposts: fix error when controller loads from cache but cache has expired
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-15 17:45:33 +02:00
8925787a13 flows: fix error when using cancel flow
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-15 17:42:37 +02:00
968b7ec17a lib: fix parsing of remote IP header when behind multiple reverse proxies
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-15 15:08:53 +02:00
6600d5bf69 providers/oauth2: use user.uid
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-15 14:08:49 +02:00
a4278833d8 providers/proxy: fix ingress not being created with full https
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-15 13:45:41 +02:00
942905b9b1 providers/proxy: fix formatting issue
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 16:24:35 +02:00
81056c3889 LDAP: use username instead of name for user dn (#883) 2021-05-14 12:58:27 +02:00
36b694fc41 website/docs: add example ldapsearch command
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 11:47:38 +02:00
2d9f216658 web/admin: add notice for LDAP Provider's group selection
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 11:44:01 +02:00
8d7bb7da17 providers/proxy: connect ingress to https instead of http
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#882
2021-05-14 11:42:03 +02:00
965db6eaf5 outposts/proxy: fix insecure TLS Skip
closes #882

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 11:38:40 +02:00
9bdd6f23a4 website/docs: add ldap example, use ghcr
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 11:19:09 +02:00
675ad7710c outposts/proxy: fix error redeeming code when using non-standard ports
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 11:13:57 +02:00
9939db13c3 outposts: fix reload notification not working due to wrong ID being saved
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 11:13:04 +02:00
03e134b296 web/admin: fix propertymappings not loading correctly
closes #879

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 10:58:33 +02:00
465750276c core: fix application's slug field not being set to unique
closes #881

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 10:49:42 +02:00
9b13191646 web: fix chunks overwriting each other
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 01:06:29 +02:00
634ea61b50 lifecycle: check if group of docker socket exists
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-14 00:50:20 +02:00
0fcb4936a2 web: output js chunks without hashing
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 23:15:40 +02:00
934e62d5be lifecycle: fix error when worker is not running as root
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 22:55:35 +02:00
c5e9197b19 website/docs: fix release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 21:43:10 +02:00
0b7ebf0e07 release: 2021.5.1 2021-05-13 20:50:31 +02:00
ddca8ef3ca tests/integration: fix outpost tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 20:33:41 +02:00
709581f5a8 root: use ghcr images by default
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 20:15:29 +02:00
72e41c03f5 lifecycle: run worker as root and drop perms later to fix docker permission issues
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 20:11:49 +02:00
40503d06b7 web/admin: improve UI for plex source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 18:12:07 +02:00
1df8790050 stages/authenticator_static: fix error when listing devices
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 18:09:55 +02:00
3c23ad340f web/admin: improve diagram api for flows
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 18:01:40 +02:00
f9f2e00913 core: improve error handling for backups
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 17:56:49 +02:00
8362507bdf outposts: fix GIT_BUILD_HASH not being set correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 17:49:11 +02:00
a2181c3bf0 build(deps): bump actions/create-release from 1.0.0 to 1.1.4 (#876) 2021-05-13 15:40:05 +02:00
a07ded0dae build(deps): bump actions/github-script from 0.2.0 to 4.0.2 (#877) 2021-05-13 15:39:48 +02:00
3b0b9301ee build(deps): bump django from 3.2.2 to 3.2.3 (#878) 2021-05-13 15:39:40 +02:00
919f293fc7 tests/e2e: fix redirect_uri
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 13:09:30 +02:00
c4df2e5a50 Merge branch 'master' into next
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

# Conflicts:
#	Pipfile.lock
2021-05-13 12:47:55 +02:00
4d1500e0f3 outposts/proxy: revert to using request Host for redirect URI
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-13 12:34:53 +02:00
281bd4c69a build(deps): bump @babel/core from 7.14.0 to 7.14.2 in /web (#868)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.0 to 7.14.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.2/packages/babel-core)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-13 11:23:08 +02:00
e4678aa032 build(deps): bump @babel/plugin-transform-runtime in /web (#869)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.13.15 to 7.14.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.2/packages/babel-plugin-transform-runtime)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-13 11:21:52 +02:00
ff1c4d555a build(deps): bump @babel/preset-env from 7.14.1 to 7.14.2 in /web (#870)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.1 to 7.14.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.2/packages/babel-preset-env)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-13 11:20:46 +02:00
4a3e34d40a build(deps): bump @docusaurus/preset-classic in /website (#872)
Bumps [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) from 2.0.0-alpha.75 to 2.0.0-beta.0.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v2.0.0-beta.0/packages/docusaurus-preset-classic)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-13 11:18:39 +02:00
6939898bbe build(deps): bump @babel/plugin-proposal-decorators in /web (#871)
Bumps [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-proposal-decorators) from 7.13.15 to 7.14.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.14.2/packages/babel-plugin-proposal-decorators)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-13 11:18:30 +02:00
549607c5ed build(deps): bump kubernetes from 12.0.1 to 17.17.0 (#874)
Bumps [kubernetes](https://github.com/kubernetes-client/python) from 12.0.1 to 17.17.0.
- [Release notes](https://github.com/kubernetes-client/python/releases)
- [Changelog](https://github.com/kubernetes-client/python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes-client/python/compare/v12.0.1...v17.17.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-13 11:18:20 +02:00
f61acdfbfd build(deps): bump geoip2 from 4.1.0 to 4.2.0 (#873) 2021-05-13 11:15:25 +02:00
e3572bad76 build(deps): bump boto3 from 1.17.71 to 1.17.72 (#875) 2021-05-13 10:36:43 +02:00
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
65ec444e52 build(deps): bump boto3 from 1.17.70 to 1.17.71 (#865)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.70 to 1.17.71.
- [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.70...1.17.71)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-12 10:46:06 +02:00
125 changed files with 6317 additions and 5140 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2021.5.1-rc8
current_version = 2021.5.4
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)
@ -31,8 +31,6 @@ values =
[bumpversion:file:web/src/constants.ts]
[bumpversion:file:web/nginx.conf]
[bumpversion:file:website/docs/outposts/manual-deploy-docker-compose.md]
[bumpversion:file:website/docs/outposts/manual-deploy-kubernetes.md]

View File

@ -36,9 +36,9 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik:2021.5.1-rc8,
beryju/authentik:2021.5.4,
beryju/authentik:latest,
ghcr.io/goauthentik/server:2021.5.1-rc8,
ghcr.io/goauthentik/server:2021.5.4,
ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64
context: .
@ -75,9 +75,9 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-proxy:2021.5.1-rc8,
beryju/authentik-proxy:2021.5.4,
beryju/authentik-proxy:latest,
ghcr.io/goauthentik/proxy:2021.5.1-rc8,
ghcr.io/goauthentik/proxy:2021.5.4,
ghcr.io/goauthentik/proxy:latest
context: outpost/
file: outpost/proxy.Dockerfile
@ -115,9 +115,9 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-ldap:2021.5.1-rc8,
beryju/authentik-ldap:2021.5.4,
beryju/authentik-ldap:latest,
ghcr.io/goauthentik/ldap:2021.5.1-rc8,
ghcr.io/goauthentik/ldap:2021.5.4,
ghcr.io/goauthentik/ldap:latest
context: outpost/
file: outpost/ldap.Dockerfile
@ -139,7 +139,7 @@ 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:
@ -155,5 +155,5 @@ jobs:
SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org
with:
version: authentik@2021.5.1-rc8
version: authentik@2021.5.4
environment: beryjuorg-prod

View File

@ -27,17 +27,17 @@ 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
uses: actions/github-script@v4.0.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
return context.payload.ref.replace(/\/refs\/tags\/version\//, '');
- name: Create Release
id: create_release
uses: actions/create-release@v1.0.0
uses: actions/create-release@v1.1.4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:

View File

@ -48,7 +48,7 @@ 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 git && \
apt-get install -y --no-install-recommends curl ca-certificates gnupg git runit && \
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 && \
@ -58,14 +58,7 @@ RUN apt-get update && \
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
groupadd -g 998 docker_998 && \
groupadd -g 999 docker_999 && \
adduser --system --no-create-home --uid 1000 --group --home /authentik authentik && \
usermod -a -G docker_998 authentik && \
usermod -a -G docker_999 authentik && \
mkdir /backups && \
chown authentik:authentik /backups
@ -77,7 +70,6 @@ COPY ./lifecycle/ /lifecycle
COPY --from=builder /work/authentik /authentik-proxy
USER authentik
STOPSIGNAL SIGINT
ENV TMPDIR /dev/shm/
ENV PYTHONUBUFFERED 1
ENTRYPOINT [ "/lifecycle/bootstrap.sh" ]

View File

@ -36,11 +36,5 @@ gen:
--additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0
cd web/api && npx tsc
local-stack:
export AUTHENTIK_TAG=testing
docker build -t beryju/authentik:testng .
docker-compose up -d
docker-compose run --rm server migrate
run:
go run -v cmd/server/main.go

137
Pipfile.lock generated
View File

@ -56,7 +56,6 @@
"sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a",
"sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"
],
"markers": "python_version >= '3.6'",
"version": "==3.7.4.post0"
},
"aioredis": {
@ -71,7 +70,6 @@
"sha256:03e16e94f2b34c31f8bf1206d8ddd3ccaa4c315f7f6a1879b7b1210d229568c2",
"sha256:493a2ac6788ce270a2f6a765b017299f60c1998f5a8617908ee9be082f7300fb"
],
"markers": "python_version >= '3.6'",
"version": "==5.0.6"
},
"asgiref": {
@ -79,7 +77,6 @@
"sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee",
"sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"
],
"markers": "python_version >= '3.6'",
"version": "==3.3.4"
},
"async-timeout": {
@ -87,7 +84,6 @@
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
"markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1"
},
"attrs": {
@ -95,7 +91,6 @@
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
],
"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": {
@ -103,7 +98,6 @@
"sha256:9195df8af03b0ff29ccd4b7f5abbde957ee90273465942205f9a1bad6c3f07ac",
"sha256:e126c1f583e872fb59e79d36977cfa1f2d0a8a79f90ae31f406faae7664b8e03"
],
"markers": "python_version >= '3.7'",
"version": "==21.3.1"
},
"automat": {
@ -122,26 +116,24 @@
},
"boto3": {
"hashes": [
"sha256:bcb1b76ca5a60586181ad202b19f4c50cb7c22ac68cae20f997abe311e22bf2a",
"sha256:edf9b3b36e08cd575a9458bf59871852335aceb5db2d07bfc8530bae3a97d045"
"sha256:13cfe0e3ae1bdc7baf4272b1814a7e760fbb508b19d6ac3f472a6bbd64baad61",
"sha256:ce08b88a2d7a0ad8edb385f84ea4914296fee6813c66ebf0def956d5278de793"
],
"index": "pypi",
"version": "==1.17.71"
"version": "==1.17.73"
},
"botocore": {
"hashes": [
"sha256:414e1721d381095767db1cf673257fdfec639da3be9405a41d49cc859b817d68",
"sha256:b7afebca1fd6ca1f8af79f377a445d474e3bd2cf88e704169d6713a6362a304f"
"sha256:4b4aa58c61d4b125bc6ec1597924b2749e19de8f2c9a374ac087aa2561e71828",
"sha256:69dc0b6fdc0855f5a4f8b1d29c96b9cec44e71054fea0f968e5904d6ccfd4fd9"
],
"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"
"version": "==1.20.73"
},
"cachetools": {
"hashes": [
"sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001",
"sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"
],
"markers": "python_version ~= '3.5'",
"version": "==4.2.2"
},
"cbor2": {
@ -228,7 +220,6 @@
"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": {
@ -236,7 +227,6 @@
"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": {
@ -310,7 +300,6 @@
"sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f",
"sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393"
],
"markers": "python_version >= '3.6'",
"version": "==3.0.2"
},
"defusedxml": {
@ -323,17 +312,14 @@
},
"django": {
"hashes": [
"sha256:0a1d195ad65c52bf275b8277b3d49680bd1137a5f55039a806f25f6b9752ce3d",
"sha256:18dd3145ddbd04bf189ff79b9954d08fda5171ea7b57bf705789fea766a07d50"
"sha256:13ac78dbfd189532cad8f383a27e58e18b3d33f80009ceb476d7fcbfc5dcebd8",
"sha256:7e0a1393d18c16b503663752a8b6790880c5084412618990ce8a81cc908b4962"
],
"index": "pypi",
"version": "==3.2.2"
"version": "==3.2.3"
},
"django-dbbackup": {
"git": "https://github.com/django-dbbackup/django-dbbackup.git",
"hashes": [
"sha256:bb109735cae98b64ad084e5b461b7aca2d7b39992f10c9ed9435e3ebb6fb76c8"
],
"ref": "9d1909c30a3271c8c9c8450add30d6e0b996e145"
},
"django-filter": {
@ -436,23 +422,21 @@
"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": {
"hashes": [
"sha256:57d8d15de2527e0697bbef44fc16812bba709f03a07ef99297bd56c1df3b1efd",
"sha256:707025542ef076bd8fd80e97138bebdb7812527b2a007d141a27ad98b0370fff"
"sha256:906a1dbf15a179a1af3522970e8420ab15bb3e0afc526942cc179e12146d9c1d",
"sha256:b97b44031fdc463e84eb1316b4f19edd978cb1d78703465fcb1e36dc5a822ba6"
],
"index": "pypi",
"version": "==4.1.0"
"version": "==4.2.0"
},
"google-auth": {
"hashes": [
"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": {
@ -468,7 +452,6 @@
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
],
"markers": "python_version >= '3.6'",
"version": "==0.12.0"
},
"hiredis": {
@ -515,7 +498,6 @@
"sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0",
"sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a"
],
"markers": "python_version >= '3.6'",
"version": "==2.0.0"
},
"httptools": {
@ -564,7 +546,6 @@
"sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417",
"sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"
],
"markers": "python_version >= '3.5'",
"version": "==0.5.1"
},
"itypes": {
@ -579,7 +560,6 @@
"sha256:2f2de5285cf37f33d33ecd4a9080b75c87cd0c1994d5a9c6df17131ea1f049c6",
"sha256:ea8d7dd814ce9df6de6a761ec7f1cac98afe305b8cdc4aaae4e114b8d8ce24c5"
],
"markers": "python_version >= '3.6'",
"version": "==3.0.0"
},
"jmespath": {
@ -587,7 +567,6 @@
"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": {
@ -602,23 +581,19 @@
"sha256:6dc509178ac4269b0e66ab4881f70a2035c33d3a622e20585f965986a5182006",
"sha256:f4965fba0a4718d47d470beeb5d6446e3357a62402b16c510b6a2f251e05ac3c"
],
"markers": "python_version >= '3.6'",
"version": "==5.0.2"
},
"kubernetes": {
"hashes": [
"sha256:23c85d8571df8f56e773f1a413bc081537536dc47e2b5e8dc2e6262edb2c57ca",
"sha256:ec52ea01d52e2ec3da255992f7e859f3a76f2bdb51cf65ba8cd71dfc309d8daa"
"sha256:225a95a0aadbd5b645ab389d941a7980db8cdad2a776fde64d1b43fc3299bde9",
"sha256:c69b318696ba797dcf63eb928a8d4370c52319f4140023c502d7dfdf2080eb79"
],
"index": "pypi",
"version": "==12.0.1"
"version": "==17.17.0"
},
"ldap3": {
"hashes": [
"sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91",
"sha256:4139c91f0eef9782df7b77c8cbc6243086affcb6a8a249b768a9658438e5da59",
"sha256:8c949edbad2be8a03e719ba48bd6779f327ec156929562814b3e84ab56889c8c",
"sha256:afc6fc0d01f02af82cd7bfabd3bbfd5dc96a6ae91e97db0a2dab8a0f1b436056",
"sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57"
],
"index": "pypi",
@ -713,14 +688,12 @@
"sha256:f58b5ba13a5689ca8317b98439fccfbcc673acaaf8241c1869ceea40f5d585bf",
"sha256:fef86115fdad7ae774720d7103aa776144cf9b66673b4afa9bcaa7af990ed07b"
],
"markers": "python_version >= '3.6'",
"version": "==2.0.0"
},
"maxminddb": {
"hashes": [
"sha256:47e86a084dd814fac88c99ea34ba3278a74bc9de5a25f4b815b608798747c7dc"
],
"markers": "python_version >= '3.6'",
"version": "==2.0.3"
},
"msgpack": {
@ -796,7 +769,6 @@
"sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281",
"sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"
],
"markers": "python_version >= '3.6'",
"version": "==5.1.0"
},
"oauthlib": {
@ -804,7 +776,6 @@
"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": {
@ -820,7 +791,6 @@
"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": {
@ -828,7 +798,6 @@
"sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04",
"sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"
],
"markers": "python_full_version >= '3.6.1'",
"version": "==3.0.18"
},
"psycopg2-binary": {
@ -874,37 +843,15 @@
},
"pyasn1": {
"hashes": [
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
"sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
"sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
"sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"
],
"version": "==0.4.8"
},
"pyasn1-modules": {
"hashes": [
"sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
"sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199",
"sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
"sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
"sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4",
"sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
"sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74",
"sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb",
"sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
"sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
"sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
"sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
"sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"
"sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"
],
"version": "==0.2.8"
},
@ -913,7 +860,6 @@
"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": {
@ -957,7 +903,6 @@
"sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316",
"sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29"
],
"markers": "python_version >= '3.5'",
"version": "==2.0.2"
},
"pyjwt": {
@ -980,14 +925,12 @@
"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": {
@ -995,7 +938,6 @@
"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": {
@ -1052,7 +994,6 @@
"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": {
@ -1060,14 +1001,12 @@
"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:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"
"sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"
],
"index": "pypi",
"version": "==1.3.0"
@ -1085,7 +1024,6 @@
"sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28",
"sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22"
],
"markers": "python_version >= '3'",
"version": "==0.17.4"
},
"ruamel.yaml.clib": {
@ -1122,7 +1060,7 @@
"sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2",
"sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f"
],
"markers": "python_version < '3.10' and platform_python_implementation == 'CPython'",
"markers": "platform_python_implementation == 'CPython' and python_version < '3.10'",
"version": "==0.2.2"
},
"s3transfer": {
@ -1153,7 +1091,6 @@
"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": {
@ -1161,7 +1098,6 @@
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
],
"markers": "python_version >= '3.5'",
"version": "==0.4.1"
},
"structlog": {
@ -1217,7 +1153,6 @@
"sha256:7d6f89745680233f1c4db9ddb748df5e88d2a7a37962be174c0fd04c8dba1dc8",
"sha256:c16b55f9a67b2419cfdf8846576e2ec9ba94fe6978a83080c352a80db31c93fb"
],
"markers": "python_version >= '3.6'",
"version": "==21.2.1"
},
"typing-extensions": {
@ -1233,7 +1168,6 @@
"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": {
@ -1278,7 +1212,6 @@
"sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30",
"sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"
],
"markers": "python_version >= '3.6'",
"version": "==5.0.0"
},
"watchgod": {
@ -1308,7 +1241,6 @@
"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": {
@ -1395,7 +1327,6 @@
"sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a",
"sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"
],
"markers": "python_version >= '3.6'",
"version": "==1.6.3"
},
"zope.interface": {
@ -1452,7 +1383,6 @@
"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"
}
},
@ -1469,7 +1399,6 @@
"sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e",
"sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"
],
"markers": "python_version ~= '3.6'",
"version": "==2.5.6"
},
"attrs": {
@ -1477,7 +1406,6 @@
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
],
"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": {
@ -1516,7 +1444,6 @@
"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": {
@ -1524,7 +1451,6 @@
"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": {
@ -1598,16 +1524,14 @@
"sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0",
"sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"
],
"markers": "python_version >= '3.4'",
"version": "==4.0.7"
},
"gitpython": {
"hashes": [
"sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b",
"sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"
"sha256:29fe82050709760081f588dd50ce83504feddbebdc4da6956d02351552b1c135",
"sha256:ee24bdc93dce357630764db659edaf6b8d664d4ff5447ccfeedd2dc5c253f41e"
],
"markers": "python_version >= '3.4'",
"version": "==3.1.14"
"version": "==3.1.17"
},
"idna": {
"hashes": [
@ -1628,7 +1552,6 @@
"sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6",
"sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"
],
"markers": "python_version >= '3.6' and python_version < '4.0'",
"version": "==5.8.0"
},
"lazy-object-proxy": {
@ -1656,7 +1579,6 @@
"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": {
@ -1693,7 +1615,6 @@
"sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd",
"sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"
],
"markers": "python_version >= '2.6'",
"version": "==5.6.0"
},
"pluggy": {
@ -1701,7 +1622,6 @@
"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": {
@ -1709,7 +1629,6 @@
"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": {
@ -1740,7 +1659,6 @@
"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": {
@ -1753,11 +1671,11 @@
},
"pytest-django": {
"hashes": [
"sha256:80f8875226ec4dc0b205f0578072034563879d98d9b1bec143a80b9045716cb0",
"sha256:a51150d8962200250e850c6adcab670779b9c2aa07271471059d1fb92a843fa9"
"sha256:d1c6758a592fb0ef8abaa2fe12dd28858c1dcfc3d466102ffe52aa8934733dca",
"sha256:f96c4556f4e7b15d987dd1dcc1d1526df81d40c1548d31ce840d597ed2be8c46"
],
"index": "pypi",
"version": "==4.2.0"
"version": "==4.3.0"
},
"pyyaml": {
"hashes": [
@ -1845,7 +1763,6 @@
"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": {
@ -1869,7 +1786,6 @@
"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": {
@ -1877,7 +1793,6 @@
"sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182",
"sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"
],
"markers": "python_version >= '3.5'",
"version": "==4.0.0"
},
"stevedore": {
@ -1885,7 +1800,6 @@
"sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee",
"sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"
],
"markers": "python_version >= '3.6'",
"version": "==3.3.0"
},
"toml": {
@ -1893,7 +1807,6 @@
"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"
},
"urllib3": {

View File

@ -1,3 +1,3 @@
"""authentik"""
__version__ = "2021.5.1-rc8"
__version__ = "2021.5.4"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -42,7 +42,7 @@ def token_from_header(raw_header: bytes) -> Optional[Token]:
return tokens.first()
class AuthentikTokenAuthentication(BaseAuthentication):
class TokenAuthentication(BaseAuthentication):
"""Token-based authentication using HTTP Bearer authentication"""
def authenticate(self, request: Request) -> Union[tuple[User, Any], None]:

View File

@ -0,0 +1,35 @@
"""API Authorization"""
from django.db.models import Model
from django.db.models.query import QuerySet
from rest_framework.filters import BaseFilterBackend
from rest_framework.permissions import BasePermission
from rest_framework.request import Request
class OwnerFilter(BaseFilterBackend):
"""Filter objects by their owner"""
owner_key = "user"
def filter_queryset(self, request: Request, queryset: QuerySet, view) -> QuerySet:
return queryset.filter(**{self.owner_key: request.user})
class OwnerPermissions(BasePermission):
"""Authorize requests by an object's owner matching the requesting user"""
owner_key = "user"
def has_permission(self, request: Request, view) -> bool:
"""If the user is authenticated, we allow all requests here. For listing, the
object-level permissions are done by the filter backend"""
return request.user.is_authenticated
def has_object_permission(self, request: Request, view, obj: Model) -> bool:
"""Check if the object's owner matches the currently logged in user"""
if not hasattr(obj, self.owner_key):
return False
owner = getattr(obj, self.owner_key)
if owner != request.user:
return False
return True

View File

@ -5,7 +5,7 @@ from django.test import TestCase
from guardian.shortcuts import get_anonymous_user
from rest_framework.exceptions import AuthenticationFailed
from authentik.api.auth import token_from_header
from authentik.api.authentication import token_from_header
from authentik.core.models import Token, TokenIntents

View File

@ -169,9 +169,19 @@ router.register("propertymappings/scope", ScopeMappingViewSet)
router.register("authenticators/static", StaticDeviceViewSet)
router.register("authenticators/totp", TOTPDeviceViewSet)
router.register("authenticators/webauthn", WebAuthnDeviceViewSet)
router.register("authenticators/admin/static", StaticAdminDeviceViewSet)
router.register("authenticators/admin/totp", TOTPAdminDeviceViewSet)
router.register("authenticators/admin/webauthn", WebAuthnAdminDeviceViewSet)
router.register(
"authenticators/admin/static",
StaticAdminDeviceViewSet,
basename="admin-staticdevice",
)
router.register(
"authenticators/admin/totp", TOTPAdminDeviceViewSet, basename="admin-totpdevice"
)
router.register(
"authenticators/admin/webauthn",
WebAuthnAdminDeviceViewSet,
basename="admin-webauthndevice",
)
router.register("stages/all", StageViewSet)
router.register("stages/authenticator/static", AuthenticatorStaticStageViewSet)

View File

@ -23,6 +23,7 @@ from authentik.core.api.providers import ProviderSerializer
from authentik.core.models import Application
from authentik.events.models import EventAction
from authentik.policies.engine import PolicyEngine
from authentik.stages.user_login.stage import USER_LOGIN_AUTHENTICATED
LOGGER = get_logger()
@ -122,6 +123,7 @@ class ApplicationViewSet(ModelViewSet):
)
def list(self, request: Request) -> Response:
"""Custom list method that checks Policy based access instead of guardian"""
self.request.session.pop(USER_LOGIN_AUTHENTICATED, None)
queryset = self._filter_queryset_for_list(self.get_queryset())
self.paginate_queryset(queryset)

View File

@ -78,7 +78,7 @@ class PropertyMappingViewSet(
filterset_fields = {"managed": ["isnull"]}
ordering = ["name"]
def get_queryset(self):
def get_queryset(self): # pragma: no cover
return PropertyMapping.objects.select_subclasses()
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})

View File

@ -63,7 +63,7 @@ class ProviderViewSet(
"application__name",
]
def get_queryset(self):
def get_queryset(self): # pragma: no cover
return Provider.objects.select_subclasses()
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})

View File

@ -61,7 +61,7 @@ class SourceViewSet(
serializer_class = SourceSerializer
lookup_field = "slug"
def get_queryset(self):
def get_queryset(self): # pragma: no cover
return Source.objects.select_subclasses()
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})

View File

@ -139,7 +139,7 @@ class UserViewSet(ModelViewSet):
search_fields = ["username", "name", "is_active"]
filterset_class = UsersFilter
def get_queryset(self):
def get_queryset(self): # pragma: no cover
return User.objects.all().exclude(pk=get_anonymous_user().pk)
@swagger_auto_schema(responses={200: SessionUserSerializer(many=False)})

View File

@ -4,7 +4,7 @@ from channels.generic.websocket import JsonWebsocketConsumer
from rest_framework.exceptions import AuthenticationFailed
from structlog.stdlib import get_logger
from authentik.api.auth import token_from_header
from authentik.api.authentication import token_from_header
from authentik.core.models import User
LOGGER = get_logger()

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.3 on 2021-05-14 08:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0020_source_user_matching_mode"),
]
operations = [
migrations.AlterField(
model_name="application",
name="slug",
field=models.SlugField(
help_text="Internal application name, used in URLs.", unique=True
),
),
]

View File

@ -207,7 +207,9 @@ class Application(PolicyBindingModel):
add custom fields and other properties"""
name = models.TextField(help_text=_("Application's display Name."))
slug = models.SlugField(help_text=_("Internal application name, used in URLs."))
slug = models.SlugField(
help_text=_("Internal application name, used in URLs."), unique=True
)
provider = models.OneToOneField(
"Provider", null=True, blank=True, default=None, on_delete=models.SET_DEFAULT
)

View File

@ -75,5 +75,6 @@ def backup_database(self: MonitoredTask): # pragma: no cover
Boto3Error,
PermissionError,
CommandConnectorError,
ValueError,
) as exc:
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))

View File

@ -1,12 +1,12 @@
"""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
from authentik.api.authorization import OwnerFilter, OwnerPermissions
from authentik.events.api.event import EventSerializer
from authentik.events.models import Notification
@ -49,12 +49,5 @@ class NotificationViewSet(
"event",
"seen",
]
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
SearchFilter,
]
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()
return Notification.objects.filter(user=user.pk)
permission_classes = [OwnerPermissions]
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]

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

@ -210,6 +210,7 @@ class FlowViewSet(ModelViewSet):
request.user, "authentik_policies.view_policybinding"
)
.filter(target=stage_binding)
.exclude(policy__isnull=True)
.order_by("order")
):
body.append(

View File

@ -65,7 +65,7 @@ class StageViewSet(
search_fields = ["name"]
filterset_fields = ["name"]
def get_queryset(self):
def get_queryset(self): # pragma: no cover
return Stage.objects.select_subclasses()
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})

View File

@ -21,7 +21,7 @@ context["user_backend"] = "django.contrib.auth.backends.ModelBackend"
return True"""
def create_default_oob_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
def create_default_oobe_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from authentik.stages.prompt.models import FieldTypes
User = apps.get_model("authentik_core", "User")
@ -52,20 +52,20 @@ def create_default_oob_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor)
# Create a policy that sets the flow's user
prefill_policy, _ = ExpressionPolicy.objects.using(db_alias).update_or_create(
name="default-oob-prefill-user",
name="default-oobe-prefill-user",
defaults={"expression": PREFILL_POLICY_EXPRESSION},
)
password_usable_policy, _ = ExpressionPolicy.objects.using(
db_alias
).update_or_create(
name="default-oob-password-usable",
name="default-oobe-password-usable",
defaults={"expression": PW_USABLE_POLICY_EXPRESSION},
)
prompt_header, _ = Prompt.objects.using(db_alias).update_or_create(
field_key="oob-header-text",
field_key="oobe-header-text",
defaults={
"label": "oob-header-text",
"label": "oobe-header-text",
"type": FieldTypes.STATIC,
"placeholder": "Welcome to authentik! Please set a password for the default admin user, akadmin.",
"order": 100,
@ -84,7 +84,7 @@ def create_default_oob_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor)
password_second = Prompt.objects.using(db_alias).get(field_key="password_repeat")
prompt_stage, _ = PromptStage.objects.using(db_alias).update_or_create(
name="default-oob-password",
name="default-oobe-password",
)
prompt_stage.fields.set(
[prompt_header, prompt_email, password_first, password_second]
@ -102,7 +102,7 @@ def create_default_oob_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor)
slug="initial-setup",
designation=FlowDesignation.STAGE_CONFIGURATION,
defaults={
"name": "default-oob-setup",
"name": "default-oobe-setup",
"title": "Welcome to authentik!",
},
)
@ -146,5 +146,5 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RunPython(create_default_oob_flow),
migrations.RunPython(create_default_oobe_flow),
]

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:default-invalidation")
return redirect("authentik_flows:default-invalidation")
class ToDefaultFlow(View):

View File

@ -88,10 +88,10 @@ class ConfigLoader:
value = os.getenv(url.netloc, url.query)
if url.scheme == "file":
try:
with open(url.netloc, "r") as _file:
with open(url.path, "r") as _file:
value = _file.read()
except OSError:
self._log("error", f"Failed to read config value from {url.netloc}")
self._log("error", f"Failed to read config value from {url.path}")
value = url.query
return value

View File

@ -42,7 +42,8 @@ outposts:
# Placeholders:
# %(type)s: Outpost type; proxy, ldap, etc
# %(version)s: Current version; 2021.4.1
docker_image_base: "beryju/authentik-%(type)s:%(version)s"
# %(build_hash)s: Build hash if you're running a beta version
docker_image_base: "ghcr.io/goauthentik/%(type)s:%(version)s"
authentik:
avatars: gravatar # gravatar or none

View File

@ -4,10 +4,15 @@ from typing import Optional
from aioredis.errors import ConnectionClosedError, ReplyError
from billiard.exceptions import WorkerLostError
from botocore.client import ClientError
from botocore.exceptions import BotoCoreError
from celery.exceptions import CeleryError
from channels.middleware import BaseMiddleware
from channels_redis.core import ChannelFull
from django.core.exceptions import SuspiciousOperation, ValidationError
from django.core.exceptions import (
ImproperlyConfigured,
SuspiciousOperation,
ValidationError,
)
from django.db import InternalError, OperationalError, ProgrammingError
from django.http.response import Http404
from django_redis.exceptions import ConnectionInterrupted
@ -50,7 +55,8 @@ def before_send(event: dict, hint: dict) -> Optional[dict]:
ConnectionResetError,
OSError,
PermissionError,
# Django DB Errors
# Django Errors
ImproperlyConfigured,
OperationalError,
InternalError,
ProgrammingError,
@ -72,6 +78,7 @@ def before_send(event: dict, hint: dict) -> Optional[dict]:
WorkerLostError,
CeleryError,
# S3 errors
BotoCoreError,
ClientError,
# custom baseclass
SentryIgnoredException,
@ -87,6 +94,6 @@ def before_send(event: dict, hint: dict) -> Optional[dict]:
if isinstance(exc_value, ignored_classes):
return None
if "logger" in event:
if event["logger"] in ["dbbackup"]:
if event["logger"] in ["dbbackup", "botocore"]:
return None
return event

View File

@ -17,7 +17,8 @@ def _get_client_ip_from_meta(meta: dict[str, Any]) -> Optional[str]:
)
for _header in headers:
if _header in meta:
return meta.get(_header).split(", ")[0]
ips: list[str] = meta.get(_header).split(",")
return ips[0].strip()
return None

View File

@ -2,28 +2,6 @@
from django.http import HttpRequest
from django.template.response import TemplateResponse
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView
from guardian.shortcuts import assign_perm
class CreateAssignPermView(CreateView):
"""Assign permissions to object after creation"""
permissions = [
"%s.view_%s",
"%s.change_%s",
"%s.delete_%s",
]
def form_valid(self, form):
response = super().form_valid(form)
for permission in self.permissions:
full_permission = permission % (
self.object._meta.app_label,
self.object._meta.model_name,
)
assign_perm(full_permission, self.request.user, self.object)
return response
def bad_request_message(

View File

@ -18,8 +18,6 @@ class OutpostSerializer(ModelSerializer):
"""Outpost Serializer"""
config = JSONField(validators=[is_dict], source="_config")
# TODO: Remove _config again, this is only here for legacy with older outposts
_config = JSONField(validators=[is_dict], read_only=True)
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
def validate_config(self, config) -> dict:
@ -42,7 +40,6 @@ class OutpostSerializer(ModelSerializer):
"service_connection",
"token_identifier",
"config",
"_config",
]

View File

@ -40,7 +40,7 @@ class WebsocketMessage:
class OutpostConsumer(AuthJsonConsumer):
"""Handler for Outposts that connect over websockets for health checks and live updates"""
outpost: Optional[Outpost] = None
outpost: Outpost
last_uid: Optional[str] = None
@ -64,7 +64,10 @@ class OutpostConsumer(AuthJsonConsumer):
# pylint: disable=unused-argument
def disconnect(self, close_code):
if self.outpost and self.last_uid:
OutpostState.for_channel(self.outpost, self.last_uid).delete()
state = OutpostState.for_instance_uid(self.outpost, self.last_uid)
if self.channel_name in state.channel_ids:
state.channel_ids.remove(self.channel_name)
state.save()
LOGGER.debug(
"removed outpost instance from cache",
outpost=self.outpost,
@ -75,13 +78,13 @@ class OutpostConsumer(AuthJsonConsumer):
msg = from_dict(WebsocketMessage, content)
uid = msg.args.get("uuid", self.channel_name)
self.last_uid = uid
state = OutpostState(
uid=uid,
last_seen=datetime.now(),
_outpost=self.outpost,
)
state = OutpostState.for_instance_uid(self.outpost, uid)
if self.channel_name not in state.channel_ids:
state.channel_ids.append(self.channel_name)
state.last_seen = datetime.now()
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
state.save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)

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
@ -69,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

@ -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,7 +27,7 @@ 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
@ -408,9 +409,11 @@ class OutpostState:
"""Outpost instance state, last_seen and version"""
uid: str
channel_ids: list[str] = field(default_factory=list)
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)
@ -419,6 +422,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
@ -427,21 +432,20 @@ class OutpostState:
keys = cache.keys(f"{outpost.state_cache_prefix}_*")
states = []
for key in keys:
channel = key.replace(f"{outpost.state_cache_prefix}_", "")
states.append(OutpostState.for_channel(outpost, channel))
instance_uid = key.replace(f"{outpost.state_cache_prefix}_", "")
states.append(OutpostState.for_instance_uid(outpost, instance_uid))
return states
@staticmethod
def for_channel(outpost: Outpost, channel: str) -> "OutpostState":
"""Get state for a single channel"""
key = f"{outpost.state_cache_prefix}_{channel}"
default_data = {"uid": channel}
def for_instance_uid(outpost: Outpost, uid: str) -> "OutpostState":
"""Get state for a single instance"""
key = f"{outpost.state_cache_prefix}_{uid}"
default_data = {"uid": uid, "channel_ids": []}
data = cache.get(key, default_data)
if isinstance(data, str):
cache.delete(key)
data = default_data
state = from_dict(OutpostState, data)
state.uid = channel
# pylint: disable=protected-access
state._outpost = outpost
return state

View File

@ -100,6 +100,8 @@ def outpost_controller(
outpost: Outpost = cache.get(CACHE_KEY_OUTPOST_DOWN % outpost_pk)
else:
outpost: Outpost = Outpost.objects.get(pk=outpost_pk)
if not outpost:
return
self.set_uid(slugify(outpost.name))
try:
controller = controller_for_outpost(outpost)
@ -200,8 +202,11 @@ def _outpost_single_update(outpost: Outpost, layer=None):
if not layer: # pragma: no cover
layer = get_channel_layer()
for state in OutpostState.for_outpost(outpost):
LOGGER.debug("sending update", channel=state.uid, outpost=outpost)
async_to_sync(layer.send)(state.uid, {"type": "event.update"})
for channel in state.channel_ids:
LOGGER.debug(
"sending update", channel=channel, instance=state.uid, outpost=outpost
)
async_to_sync(layer.send)(channel, {"type": "event.update"})
@CELERY_APP.task()

View File

@ -91,7 +91,7 @@ class PolicyViewSet(
}
search_fields = ["name"]
def get_queryset(self):
def get_queryset(self): # pragma: no cover
return Policy.objects.select_subclasses().prefetch_related(
"bindings", "promptstage_set"
)

View File

@ -105,16 +105,21 @@ class PolicyEngine:
if cached_policy and self.use_cache:
self.logger.debug(
"P_ENG: Taking result from cache",
policy=binding.policy,
binding=binding,
cache_key=key,
request=self.request,
)
self.__cached_policies.append(cached_policy)
continue
self.logger.debug("P_ENG: Evaluating policy", policy=binding.policy)
self.logger.debug(
"P_ENG: Evaluating policy", binding=binding, request=self.request
)
our_end, task_end = Pipe(False)
task = PolicyProcess(binding, self.request, task_end)
task.daemon = False
self.logger.debug("P_ENG: Starting Process", policy=binding.policy)
self.logger.debug(
"P_ENG: Starting Process", binding=binding, request=self.request
)
if not CURRENT_PROCESS._config.get("daemon"):
task.run()
else:

View File

@ -51,7 +51,12 @@ class PolicyRequest:
LOGGER.warning("failed to get geoip data", exc=exc)
def __str__(self):
return f"<PolicyRequest user={self.user}>"
text = f"<PolicyRequest user={self.user}"
if self.obj:
text += f" obj={self.obj}"
if self.http_request:
text += f" http_request={self.http_request}"
return text + ">"
@dataclass

View File

@ -6,13 +6,11 @@ import time
from dataclasses import asdict, dataclass, field
from datetime import datetime
from hashlib import sha256
from typing import Any, Optional, Type, Union
from typing import Any, Optional, Type
from urllib.parse import urlparse
from uuid import uuid4
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from dacite import from_dict
from django.conf import settings
from django.db import models
from django.http import HttpRequest
from django.utils import dateformat, timezone
@ -239,7 +237,7 @@ class OAuth2Provider(Provider):
token.access_token = token.create_access_token(user, request)
return token
def get_jwt_keys(self) -> Union[RSAPrivateKey, str]:
def get_jwt_key(self) -> str:
"""
Takes a provider and returns the set of keys associated with it.
Returns a list of keys.
@ -256,7 +254,7 @@ class OAuth2Provider(Provider):
self.jwt_alg = JWTAlgorithms.HS256
self.save()
else:
return self.rsa_key.private_key
return self.rsa_key.key_data
if self.jwt_alg == JWTAlgorithms.HS256:
return self.client_secret
@ -300,11 +298,14 @@ class OAuth2Provider(Provider):
def encode(self, payload: dict[str, Any]) -> str:
"""Represent the ID Token as a JSON Web Token (JWT)."""
key = self.get_jwt_keys()
headers = {}
if self.rsa_key:
headers["kid"] = self.rsa_key.kid
key = self.get_jwt_key()
# If the provider does not have an RSA Key assigned, it was switched to Symmetric
self.refresh_from_db()
# pyright: reportGeneralTypeIssues=false
return encode(payload, key, algorithm=self.jwt_alg)
return encode(payload, key, algorithm=self.jwt_alg, headers=headers)
class Meta:
@ -457,7 +458,7 @@ class RefreshToken(ExpiringModel, BaseGrantModel):
See: http://openid.net/specs/openid-connect-core-1_0.html#IDToken"""
sub = ""
if self.provider.sub_mode == SubModes.HASHED_USER_ID:
sub = sha256(f"{user.id}-{settings.SECRET_KEY}".encode("ascii")).hexdigest()
sub = user.uid
elif self.provider.sub_mode == SubModes.USER_EMAIL:
sub = user.email
elif self.provider.sub_mode == SubModes.USER_USERNAME:

View File

@ -4,6 +4,7 @@ from django.urls import reverse
from django.utils.encoding import force_str
from authentik.core.models import Application, User
from authentik.crypto.models import CertificateKeyPair
from authentik.flows.challenge import ChallengeTypes
from authentik.flows.models import Flow
from authentik.providers.oauth2.errors import (
@ -207,6 +208,7 @@ class TestAuthorize(OAuthTestCase):
client_secret=generate_client_secret(),
authorization_flow=flow,
redirect_uris="http://localhost",
rsa_key=CertificateKeyPair.objects.first(),
)
Application.objects.create(name="app", slug="app", provider=provider)
state = generate_client_id()

View File

@ -2,7 +2,11 @@
from django.test import TestCase
from jwt import decode
from authentik.providers.oauth2.models import OAuth2Provider, RefreshToken
from authentik.providers.oauth2.models import (
JWTAlgorithms,
OAuth2Provider,
RefreshToken,
)
class OAuthTestCase(TestCase):
@ -19,9 +23,12 @@ class OAuthTestCase(TestCase):
def validate_jwt(self, token: RefreshToken, provider: OAuth2Provider):
"""Validate that all required fields are set"""
key = provider.client_secret
if provider.jwt_alg == JWTAlgorithms.RS256:
key = provider.rsa_key.public_key
jwt = decode(
token.access_token,
provider.client_secret,
key,
algorithms=[provider.jwt_alg],
audience=provider.client_id,
)

View File

@ -54,6 +54,7 @@ from authentik.stages.consent.stage import (
PLAN_CONTEXT_CONSENT_PERMISSIONS,
ConsentStageView,
)
from authentik.stages.user_login.stage import USER_LOGIN_AUTHENTICATED
LOGGER = get_logger()
@ -437,6 +438,10 @@ class AuthorizationFlowInitView(PolicyAccessView):
if (
PROMPT_LOGIN in self.params.prompt
and SESSION_NEEDS_LOGIN not in self.request.session
# To prevent the user from having to double login when prompt is set to login
# and the user has just signed it. This session variable is set in the UserLoginStage
# and is (quite hackily) removed from the session in applications's API's List method
and USER_LOGIN_AUTHENTICATED not in self.request.session
):
self.request.session[SESSION_NEEDS_LOGIN] = True
return self.handle_no_permission()

View File

@ -53,8 +53,10 @@ class ProxyProviderSerializer(ProviderSerializer):
return instance
def update(self, instance: ProxyProvider, validated_data):
instance = super().update(instance, validated_data)
instance.set_oauth_defaults()
return super().update(instance, validated_data)
instance.save()
return instance
class Meta:

View File

@ -127,7 +127,7 @@ class ProxyProvider(OutpostModel, OAuth2Provider):
"""Ensure all OAuth2-related settings are correct"""
self.client_type = ClientTypes.CONFIDENTIAL
self.jwt_alg = JWTAlgorithms.RS256
self.rsa_key = CertificateKeyPair.objects.first()
self.rsa_key = CertificateKeyPair.objects.exclude(key_data__iexact="").first()
scopes = ScopeMapping.objects.filter(
scope_name__in=[
SCOPE_OPENID,

View File

@ -20,6 +20,7 @@ from time import time
import structlog
from celery.schedules import crontab
from sentry_sdk import init as sentry_init
from sentry_sdk.api import set_tag
from sentry_sdk.integrations.celery import CeleryIntegration
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.redis import RedisIntegration
@ -52,11 +53,9 @@ STATIC_ROOT = BASE_DIR + "/static"
STATICFILES_DIRS = [BASE_DIR + "/web"]
MEDIA_ROOT = BASE_DIR + "/media"
SECRET_KEY = CONFIG.y(
"secret_key", "9$@r!d^1^jrn#fk#1#@ks#9&i$^s#1)_13%$rwjrhd=e8jfi_s"
) # noqa Debug
DEBUG = CONFIG.y_bool("debug")
SECRET_KEY = CONFIG.y("secret_key")
INTERNAL_IPS = ["127.0.0.1"]
ALLOWED_HOSTS = ["*"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
@ -162,7 +161,7 @@ REST_FRAMEWORK = {
"rest_framework.permissions.DjangoObjectPermissions",
),
"DEFAULT_AUTHENTICATION_CLASSES": (
"authentik.api.auth.AuthentikTokenAuthentication",
"authentik.api.authentication.TokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
),
"DEFAULT_RENDERER_CLASSES": [
@ -320,6 +319,7 @@ CELERY_RESULT_BACKEND = (
# Database backup
DBBACKUP_STORAGE = "django.core.files.storage.FileSystemStorage"
DBBACKUP_STORAGE_OPTIONS = {"location": "./backups" if DEBUG else "/backups"}
DBBACKUP_FILENAME_TEMPLATE = "authentik-backup-{datetime}.sql"
if CONFIG.y("postgresql.s3_backup"):
DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
DBBACKUP_STORAGE_OPTIONS = {
@ -353,6 +353,11 @@ if _ERROR_REPORTING:
environment=CONFIG.y("error_reporting.environment", "customer"),
send_default_pii=CONFIG.y_bool("error_reporting.send_pii", False),
)
set_tag("authentik:build_hash", os.environ.get(ENV_GIT_HASH_KEY, "tagged"))
set_tag(
"authentik:env", "kubernetes" if "KUBERNETES_PORT" in os.environ else "compose"
)
set_tag("authentik:component", "backend")
j_print(
"Error reporting is enabled",
env=CONFIG.y("error_reporting.environment", "customer"),

View File

@ -1,9 +1,10 @@
"""OAuth Source Serializer"""
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 ModelViewSet
from rest_framework.viewsets import GenericViewSet
from authentik.api.authorization import OwnerFilter, OwnerPermissions
from authentik.core.api.sources import SourceSerializer
from authentik.sources.oauth.models import UserOAuthSourceConnection
@ -21,20 +22,17 @@ class UserOAuthSourceConnectionSerializer(SourceSerializer):
]
class UserOAuthSourceConnectionViewSet(ModelViewSet):
class UserOAuthSourceConnectionViewSet(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet,
):
"""Source Viewset"""
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()
if user.is_superuser:
return super().get_queryset()
return super().get_queryset().filter(user=user.pk)
permission_classes = [OwnerPermissions]
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.3 on 2021-05-20 17:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_sources_plex", "0002_auto_20210505_1717"),
]
operations = [
migrations.AlterField(
model_name="plexsource",
name="plex_token",
field=models.TextField(help_text="Plex token used to check firends"),
),
]

View File

@ -41,9 +41,7 @@ class PlexSource(Source):
default=True,
help_text=_("Allow friends to authenticate, even if you don't share a server."),
)
plex_token = models.TextField(
default="", help_text=_("Plex token used to check firends")
)
plex_token = models.TextField(help_text=_("Plex token used to check firends"))
@property
def component(self) -> str:

View File

@ -1,13 +1,13 @@
"""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 import mixins
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
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
from authentik.api.authorization import OwnerFilter, OwnerPermissions
from authentik.flows.api.stages import StageSerializer
from authentik.stages.authenticator_static.models import AuthenticatorStaticStage
@ -38,23 +38,22 @@ class StaticDeviceSerializer(ModelSerializer):
depth = 2
class StaticDeviceViewSet(ModelViewSet):
class StaticDeviceViewSet(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet,
):
"""Viewset for static authenticator devices"""
queryset = StaticDevice.objects.none()
queryset = StaticDevice.objects.all()
serializer_class = StaticDeviceSerializer
permission_classes = [OwnerPermissions]
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
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()
return StaticDevice.objects.filter(user=user.pk)
class StaticAdminDeviceViewSet(ReadOnlyModelViewSet):

View File

@ -0,0 +1,20 @@
"""Test Static API"""
from django.urls import reverse
from django_otp.plugins.otp_static.models import StaticDevice
from rest_framework.test import APITestCase
from authentik.core.models import User
class AuthenticatorStaticStage(APITestCase):
"""Test Static API"""
def test_api_delete(self):
"""Test api delete"""
user = User.objects.create(username="foo")
self.client.force_login(user)
dev = StaticDevice.objects.create(user=user)
response = self.client.delete(
reverse("authentik_api:staticdevice-detail", kwargs={"pk": dev.pk})
)
self.assertEqual(response.status_code, 204)

View File

@ -1,12 +1,13 @@
"""AuthenticatorTOTPStage API Views"""
from django_filters.rest_framework import DjangoFilterBackend
from django_filters.rest_framework.backends import DjangoFilterBackend
from django_otp.plugins.otp_totp.models import TOTPDevice
from guardian.utils import get_anonymous_user
from rest_framework import mixins
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
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
from authentik.api.authorization import OwnerFilter, OwnerPermissions
from authentik.flows.api.stages import StageSerializer
from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage
@ -40,23 +41,22 @@ class TOTPDeviceSerializer(ModelSerializer):
depth = 2
class TOTPDeviceViewSet(ModelViewSet):
class TOTPDeviceViewSet(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet,
):
"""Viewset for totp authenticator devices"""
queryset = TOTPDevice.objects.none()
queryset = TOTPDevice.objects.all()
serializer_class = TOTPDeviceSerializer
permission_classes = [OwnerPermissions]
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
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()
return TOTPDevice.objects.filter(user=user.pk)
class TOTPAdminDeviceViewSet(ReadOnlyModelViewSet):

View File

@ -0,0 +1,20 @@
"""Test TOTP API"""
from django.urls import reverse
from django_otp.plugins.otp_totp.models import TOTPDevice
from rest_framework.test import APITestCase
from authentik.core.models import User
class AuthenticatorTOTPStage(APITestCase):
"""Test TOTP API"""
def test_api_delete(self):
"""Test api delete"""
user = User.objects.create(username="foo")
self.client.force_login(user)
dev = TOTPDevice.objects.create(user=user)
response = self.client.delete(
reverse("authentik_api:totpdevice-detail", kwargs={"pk": dev.pk})
)
self.assertEqual(response.status_code, 204)

View File

@ -1,11 +1,12 @@
"""AuthenticateWebAuthnStage API Views"""
from django_filters.rest_framework import DjangoFilterBackend
from guardian.utils import get_anonymous_user
from django_filters.rest_framework.backends import DjangoFilterBackend
from rest_framework import mixins
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
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
from authentik.api.authorization import OwnerFilter, OwnerPermissions
from authentik.flows.api.stages import StageSerializer
from authentik.stages.authenticator_webauthn.models import (
AuthenticateWebAuthnStage,
@ -39,23 +40,22 @@ class WebAuthnDeviceSerializer(ModelSerializer):
depth = 2
class WebAuthnDeviceViewSet(ModelViewSet):
class WebAuthnDeviceViewSet(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet,
):
"""Viewset for WebAuthn authenticator devices"""
queryset = WebAuthnDevice.objects.none()
queryset = WebAuthnDevice.objects.all()
serializer_class = WebAuthnDeviceSerializer
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()
return WebAuthnDevice.objects.filter(user=user.pk)
permission_classes = [OwnerPermissions]
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
class WebAuthnAdminDeviceViewSet(ReadOnlyModelViewSet):

View File

@ -0,0 +1,20 @@
"""Test WebAuthn API"""
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
class AuthenticatorWebAuthnStage(APITestCase):
"""Test WebAuthn API"""
def test_api_delete(self):
"""Test api delete"""
user = User.objects.create(username="foo")
self.client.force_login(user)
dev = WebAuthnDevice.objects.create(user=user)
response = self.client.delete(
reverse("authentik_api:webauthndevice-detail", kwargs={"pk": dev.pk})
)
self.assertEqual(response.status_code, 204)

View File

@ -1,5 +1,5 @@
"""dummy tests"""
from django.test import Client, TestCase
from django.test import TestCase
from django.urls import reverse
from django.utils.encoding import force_str
@ -14,7 +14,6 @@ class TestDummyStage(TestCase):
def setUp(self):
super().setUp()
self.user = User.objects.create(username="unittest", email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create(
name="test-dummy",

View File

@ -12,6 +12,7 @@ from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
LOGGER = get_logger()
DEFAULT_BACKEND = "django.contrib.auth.backends.ModelBackend"
USER_LOGIN_AUTHENTICATED = "user_login_authenticated"
class UserLoginStageView(StageView):
@ -43,5 +44,6 @@ class UserLoginStageView(StageView):
flow_slug=self.executor.flow.slug,
session_duration=self.executor.current_stage.session_duration,
)
self.request.session[USER_LOGIN_AUTHENTICATED] = True
messages.success(self.request, _("Successfully logged in!"))
return self.executor.stage_ok()

View File

@ -43,7 +43,9 @@ stages:
pipenv install --dev
- task: CmdLine@2
inputs:
script: pipenv run pylint authentik tests lifecycle
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run pylint authentik tests lifecycle
- job: black
pool:
vmImage: 'ubuntu-latest'
@ -140,7 +142,9 @@ stages:
pipenv install --dev
- task: CmdLine@2
inputs:
script: pipenv run ./manage.py migrate
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run ./manage.py migrate
- job: migrations_from_previous_release
pool:
vmImage: 'ubuntu-latest'
@ -171,8 +175,9 @@ stages:
- task: CmdLine@2
displayName: Migrate to last tagged release
inputs:
script:
pipenv run ./manage.py migrate
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run python -m lifecycle.migrate
- task: CmdLine@2
displayName: Install current branch
inputs:
@ -184,8 +189,8 @@ stages:
displayName: Migrate to current branch
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run python -m lifecycle.migrate
pipenv run ./manage.py migrate
- job: coverage_unittest
pool:
vmImage: 'ubuntu-latest'
@ -210,6 +215,7 @@ stages:
displayName: Run full test suite
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run make test
- task: CmdLine@2
inputs:
@ -253,6 +259,7 @@ stages:
displayName: Run full test suite
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run make test-integration
- task: CmdLine@2
inputs:
@ -308,6 +315,7 @@ stages:
displayName: Run full test suite
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run make test-e2e
- task: CmdLine@2
condition: always()

View File

@ -21,7 +21,7 @@ services:
networks:
- internal
server:
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc8}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.5.4}
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-rc8}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.5.4}
restart: unless-stopped
command: worker
networks:
@ -64,8 +64,13 @@ services:
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
# AUTHENTIK_ERROR_REPORTING__ENABLED: "true"
# This is optional, and can be removed. If you remove this, the following will happen
# - The permissions for the /backups and /media folders aren't fixed, so make sure they are 1000:1000
# - The docker socket can't be accessed anymore
user: root
volumes:
- ./backups:/backups
- ./media:/media
- /var/run/docker.sock:/var/run/docker.sock
- ./custom-templates:/templates
- geoip:/geoip

View File

@ -1,3 +1,3 @@
package constants
const VERSION = "2021.5.1-rc8"
const VERSION = "2021.5.4"

View File

@ -9,7 +9,18 @@ import (
func (ws *WebServer) configureProxy() {
// Reverse proxy to the application server
u, _ := url.Parse("http://localhost:8000")
rp := httputil.NewSingleHostReverseProxy(u)
director := func(req *http.Request) {
req.URL.Scheme = u.Scheme
req.URL.Host = u.Host
if _, ok := req.Header["User-Agent"]; !ok {
// explicitly disable User-Agent so it's not set to default value
req.Header.Set("User-Agent", "")
}
if req.TLS != nil {
req.Header.Set("X-Forwarded-Proto", "https")
}
}
rp := &httputil.ReverseProxy{Director: director}
rp.ErrorHandler = ws.proxyErrorHandler
rp.ModifyResponse = ws.proxyModifyResponse
ws.m.PathPrefix("/").Handler(rp)

View File

@ -4,16 +4,19 @@ import (
"net/http"
"goauthentik.io/internal/config"
"goauthentik.io/internal/constants"
staticWeb "goauthentik.io/web"
)
func (ws *WebServer) configureStatic() {
statRouter := ws.lh.NewRoute().Subrouter()
if config.G.Debug {
ws.log.Debug("Using local static files")
ws.lh.PathPrefix("/static/dist").Handler(http.StripPrefix("/static/dist", http.FileServer(http.Dir("./web/dist"))))
ws.lh.PathPrefix("/static/authentik").Handler(http.StripPrefix("/static/authentik", http.FileServer(http.Dir("./web/authentik"))))
} else {
ws.log.Debug("Using packaged static files")
statRouter.Use(ws.staticHeaderMiddleware)
ws.log.Debug("Using packaged static files with aggressive caching")
ws.lh.PathPrefix("/static/dist").Handler(http.StripPrefix("/static", http.FileServer(http.FS(staticWeb.StaticDist))))
ws.lh.PathPrefix("/static/authentik").Handler(http.StripPrefix("/static", http.FileServer(http.FS(staticWeb.StaticAuthentik))))
}
@ -41,3 +44,12 @@ func (ws *WebServer) configureStatic() {
// Media files, always local
ws.lh.PathPrefix("/media").Handler(http.StripPrefix("/media", http.FileServer(http.Dir(config.G.Paths.Media))))
}
func (ws *WebServer) staticHeaderMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "\"public, no-transform\"")
w.Header().Set("X-authentik-version", constants.VERSION)
w.Header().Set("Vary", "X-authentik-version")
h.ServeHTTP(w, r)
})
}

View File

@ -1,14 +1,31 @@
#!/bin/bash -e
python -m lifecycle.wait_for_db
printf '{"event": "Bootstrap completed", "level": "info", "logger": "bootstrap", "command": "%s"}\n' "$@" > /dev/stderr
function check_if_root {
if [[ $EUID -ne 0 ]]; then
printf '{"event": "Not running as root, disabling permission fixes", "level": "info", "logger": "bootstrap", "command": "%s"}\n' "$@" > /dev/stderr
$1
return
fi
SOCKET="/var/run/docker.sock"
if [[ -e "$SOCKET" ]]; then
# Get group ID of the docker socket, so we can create a matching group and
# add ourselves to it
DOCKER_GID=$(stat -c '%g' $SOCKET)
getent group $DOCKER_GID || groupadd -f -g $DOCKER_GID docker
usermod -a -G $DOCKER_GID authentik
fi
# Fix permissions of backups and media
chown -R authentik:authentik /media /backups
chpst -u authentik:authentik:docker env HOME=/authentik $1
}
if [[ "$1" == "server" ]]; then
python -m lifecycle.migrate
/authentik-proxy
elif [[ "$1" == "worker" ]]; then
celery -A authentik.root.celery worker --autoscale 3,1 -E -B -s /tmp/celerybeat-schedule -Q authentik,authentik_scheduled,authentik_events
elif [[ "$1" == "migrate" ]]; then
printf "DEPERECATED: database migrations are now executed automatically on startup."
python -m lifecycle.migrate
check_if_root "celery -A authentik.root.celery worker --autoscale 3,1 -E -B -s /tmp/celerybeat-schedule -Q authentik,authentik_scheduled,authentik_events"
elif [[ "$1" == "backup" ]]; then
python -m manage dbbackup --clean
elif [[ "$1" == "restore" ]]; then

View File

@ -113,10 +113,21 @@ stages:
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/outpost-proxy'
command: 'buildAndPush'
command: 'build'
Dockerfile: 'outpost/proxy.Dockerfile'
buildContext: 'outpost/'
tags: "gh-$(branchName)"
tags: |
gh-$(branchName)
gh-$(Build.SourceVersion)
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
- task: Docker@2
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/outpost-proxy'
command: 'push'
tags: |
gh-$(branchName)
gh-$(Build.SourceVersion)
- job: ldap_build_docker
pool:
vmImage: 'ubuntu-latest'
@ -138,7 +149,18 @@ stages:
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/outpost-ldap'
command: 'buildAndPush'
command: 'build'
Dockerfile: 'outpost/ldap.Dockerfile'
buildContext: 'outpost/'
tags: "gh-$(branchName)"
tags: |
gh-$(branchName)
gh-$(Build.SourceVersion)
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
- task: Docker@2
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/outpost-ldap'
command: 'push'
tags: |
gh-$(branchName)
gh-$(Build.SourceVersion)

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,7 +1,6 @@
package ak
import (
"fmt"
"math/rand"
"net/url"
"os"
@ -43,7 +42,7 @@ type APIController struct {
// 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)

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,8 +46,9 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
msg := websocketMessage{
Instruction: WebsocketInstructionHello,
Args: map[string]interface{}{
"version": pkg.VERSION,
"uuid": ac.instanceUUID.String(),
"version": pkg.VERSION,
"buildHash": pkg.BUILD(),
"uuid": ac.instanceUUID.String(),
},
}
err := ws.WriteJSON(msg)
@ -76,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
}
@ -100,14 +101,15 @@ func (ac *APIController) startWSHealth() {
aliveMsg := websocketMessage{
Instruction: WebsocketInstructionHello,
Args: map[string]interface{}{
"version": pkg.VERSION,
"uuid": ac.instanceUUID.String(),
"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
}

View File

@ -33,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) {
@ -52,7 +52,8 @@ func doGlobalSetup(config map[string]interface{}) {
defer sentry.Flush(2 * time.Second)
}
func getTLSTransport() http.RoundTripper {
// GetTLSTransport Get a TLS transport instance, that skips verification if configured via environment variables.
func GetTLSTransport() http.RoundTripper {
value, set := os.LookupEnv("AUTHENTIK_INSECURE")
if !set {
value = "false"

View File

@ -55,14 +55,18 @@ func (ls *LDAPServer) Start() error {
type transport struct {
headers map[string]string
inner http.RoundTripper
}
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
for key, value := range t.headers {
req.Header.Add(key, value)
}
return http.DefaultTransport.RoundTrip(req)
return t.inner.RoundTrip(req)
}
func newTransport(headers map[string]string) *transport {
return &transport{headers}
func newTransport(inner http.RoundTripper, headers map[string]string) *transport {
return &transport{
inner: inner,
headers: headers,
}
}

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

@ -14,6 +14,8 @@ import (
goldap "github.com/go-ldap/ldap/v3"
httptransport "github.com/go-openapi/runtime/client"
"github.com/nmcclain/ldap"
"goauthentik.io/outpost/pkg"
"goauthentik.io/outpost/pkg/ak"
"goauthentik.io/outpost/pkg/client/core"
"goauthentik.io/outpost/pkg/client/flows"
"goauthentik.io/outpost/pkg/models"
@ -47,7 +49,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")
@ -61,15 +63,15 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
// Create new http client that also sets the correct ip
client := &http.Client{
Jar: jar,
Transport: newTransport(map[string]string{
Transport: newTransport(ak.SetUserAgent(ak.GetTLSTransport(), pkg.UserAgent()), map[string]string{
"X-authentik-remote-ip": host,
}),
}
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 +84,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 +114,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 +142,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 +172,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 +191,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")
}
@ -114,7 +117,7 @@ func (pi *ProviderInstance) Search(bindDN string, searchReq ldap.SearchRequest,
attrs = append(attrs, AKAttrsToLDAP(u.Attributes)...)
dn := fmt.Sprintf("cn=%s,%s", *u.Name, pi.UserDN)
dn := fmt.Sprintf("cn=%s,%s", *u.Username, pi.UserDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
}

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

@ -80,19 +80,19 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
ID: "default",
URI: provider.InternalHost,
Path: "/",
InsecureSkipTLSVerify: provider.InternalHostSslValidation,
InsecureSkipTLSVerify: !provider.InternalHostSslValidation,
},
}
}
if provider.Certificate != nil {
pb.log.WithField("provider", provider.ClientID).Debug("Enabling TLS")
pb.log.WithField("provider", provider.Name).Debug("Enabling TLS")
cert, err := pb.s.ak.Client.Crypto.CryptoCertificatekeypairsViewCertificate(&crypto.CryptoCertificatekeypairsViewCertificateParams{
Context: context.Background(),
KpUUID: *provider.Certificate,
}, pb.s.ak.Auth)
if err != nil {
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch certificate")
pb.log.WithField("provider", provider.Name).WithError(err).Warning("Failed to fetch certificate")
return providerOpts
}
key, err := pb.s.ak.Client.Crypto.CryptoCertificatekeypairsViewPrivateKey(&crypto.CryptoCertificatekeypairsViewPrivateKeyParams{
@ -100,17 +100,17 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
KpUUID: *provider.Certificate,
}, pb.s.ak.Auth)
if err != nil {
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch private key")
pb.log.WithField("provider", provider.Name).WithError(err).Warning("Failed to fetch private key")
return providerOpts
}
x509cert, err := tls.X509KeyPair([]byte(cert.Payload.Data), []byte(key.Payload.Data))
if err != nil {
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to parse certificate")
pb.log.WithField("provider", provider.Name).WithError(err).Warning("Failed to parse certificate")
return providerOpts
}
pb.cert = &x509cert
pb.log.WithField("provider", provider.ClientID).Debug("Loaded certificates")
pb.log.WithField("provider", provider.Name).Debug("Loaded certificates")
}
return providerOpts
}

View File

@ -161,7 +161,7 @@ func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
return
}
redirectURI := p.GetRedirectURI(getHost(req))
redirectURI := p.GetRedirectURI(req.Host)
http.Redirect(rw, req, p.provider.GetLoginURL(redirectURI, fmt.Sprintf("%v:%v", nonce, redirect)), http.StatusFound)
}
@ -184,7 +184,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
return
}
session, err := p.redeemCode(req.Context(), getHost(req), req.Form.Get("code"))
session, err := p.redeemCode(req.Context(), req.Host, req.Form.Get("code"))
if err != nil {
p.logger.Errorf("Error redeeming code during OAuth2 callback: %v", err)
p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Internal Error")
@ -207,7 +207,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
}
p.ClearCSRFCookie(rw, req)
if c.Value != nonce {
p.logger.WithField("user", session.Email).WithField("status", "AuthFailure").Info("Invalid authentication via OAuth2: CSRF token mismatch, potential attack")
p.logger.WithField("is", c.Value).WithField("should", nonce).WithField("user", session.Email).WithField("status", "AuthFailure").Info("Invalid authentication via OAuth2: CSRF token mismatch, potential attack")
p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", "CSRF Failed")
return
}

View File

@ -13,12 +13,14 @@ func getTemplates() *template.Template {
<head>
<title>{{.Title}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>* { font-family: sans-serif; }</style>
</head>
<body>
<h2>{{.Title}}</h2>
<p>{{.Message}}</p>
<hr>
<p><a href="{{.ProxyPrefix}}/sign_in">Sign In</a></p>
<p>Powered by <a href="https://goauthentik.io">authentik</a></p>
</body>
</html>{{end}}`)
if err != nil {

View File

@ -1,3 +1,16 @@
package pkg
const VERSION = "2021.5.1-rc8"
import (
"fmt"
"os"
)
const VERSION = "2021.5.4"
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

@ -1,16 +0,0 @@
#!/bin/bash -xe
wget -q -O - https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash
VERSION=3.9.0
wget https://www.python.org/ftp/python/$VERSION/Python-$VERSION.tgz
tar xvzf Python-$VERSION.tgz
cd Python-$VERSION/
./configure --prefix=$HOME/_work/_tool/Python/$VERSION/x64/ --enable-optimizations --with-ensurepip=install
make -j 8
sudo make altinstall
touch $HOME/_work/_tool/Python/$VERSION/x64.complete
ln -s $HOME/_work/_tool/Python/3.9.5/x64 $HOME/_work/_tool/Python/3/x64
ln -s $HOME/_work/_tool/Python/3.9.5/x64 $HOME/_work/_tool/Python/3.9/x64

View File

@ -0,0 +1,8 @@
"""Utility script to generate a config for CI runs"""
from authentik.providers.oauth2.generators import generate_client_id
from yaml import safe_dump
with open("local.env.yml", "w") as _config:
safe_dump({
"secret_key": generate_client_id()
}, _config, default_flow_style=False)

View File

@ -531,6 +531,11 @@ 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.
@ -590,30 +595,6 @@ paths:
$ref: '#/definitions/GenericError'
tags:
- authenticators
post:
operationId: authenticators_static_create
description: Viewset for static authenticator devices
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/StaticDevice'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/StaticDevice'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- authenticators
parameters: []
/authenticators/static/{id}/:
get:
@ -792,30 +773,6 @@ paths:
$ref: '#/definitions/GenericError'
tags:
- authenticators
post:
operationId: authenticators_totp_create
description: Viewset for totp authenticator devices
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/TOTPDevice'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/TOTPDevice'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- authenticators
parameters: []
/authenticators/totp/{id}/:
get:
@ -994,30 +951,6 @@ paths:
$ref: '#/definitions/GenericError'
tags:
- authenticators
post:
operationId: authenticators_webauthn_create
description: Viewset for WebAuthn authenticator devices
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/WebAuthnDevice'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/WebAuthnDevice'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- authenticators
parameters: []
/authenticators/webauthn/{id}/:
get:
@ -10420,30 +10353,6 @@ paths:
$ref: '#/definitions/GenericError'
tags:
- sources
post:
operationId: sources_oauth_user_connections_create
description: Source Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/UserOAuthSourceConnection'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/UserOAuthSourceConnection'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- sources
parameters: []
/sources/oauth_user_connections/{id}/:
get:
@ -15694,6 +15603,7 @@ definitions:
NotificationRule:
required:
- name
- transports
type: object
properties:
pk:
@ -15706,38 +15616,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
@ -15748,57 +15637,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
@ -18103,6 +17949,7 @@ definitions:
required:
- name
- slug
- plex_token
type: object
properties:
pk:

View File

@ -46,7 +46,7 @@ class TestProviderProxy(SeleniumTestCase):
"""Start proxy container based on outpost created"""
client: DockerClient = from_env()
container = client.containers.run(
image=f"beryju/authentik-proxy:{__version__}",
image=f"ghcr.io/goauthentik/proxy:{__version__}",
detach=True,
network_mode="host",
auto_remove=True,
@ -70,7 +70,7 @@ class TestProviderProxy(SeleniumTestCase):
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
internal_host="http://localhost:80",
internal_host="http://localhost",
external_host="http://localhost:4180",
)
# Ensure OAuth2 Params are set
@ -123,7 +123,7 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase):
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
internal_host="http://localhost:80",
internal_host="http://localhost",
external_host="http://localhost:4180",
)
# Ensure OAuth2 Params are set

View File

@ -104,5 +104,5 @@ class OutpostDockerTests(TestCase):
self.assertEqual(compose["version"], "3.5")
self.assertEqual(
compose["services"]["authentik_proxy"]["image"],
f"beryju/authentik-proxy:{__version__}",
f"ghcr.io/goauthentik/proxy:{__version__}",
)

View File

@ -104,5 +104,5 @@ class TestProxyDocker(TestCase):
self.assertEqual(compose["version"], "3.5")
self.assertEqual(
compose["services"]["authentik_proxy"]["image"],
f"beryju/authentik-proxy:{__version__}",
f"ghcr.io/goauthentik/proxy:{__version__}",
)

View File

@ -1,98 +0,0 @@
worker_processes auto;
pid /tmp/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
error_log /dev/stdout;
user www-data;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
log_format json_combined escape=json
'{'
'"timestamp":"$time_local",'
'"host":"$remote_addr",'
'"request_username":"$remote_user",'
'"event":"$request",'
'"status": "$status",'
'"size":"$body_bytes_sent",'
'"runtime":"$request_time",'
'"logger":"nginx",'
'"request_useragent":"$http_user_agent"'
'}';
access_log /dev/null json_combined;
##
# Gzip Settings
##
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
server {
listen 80;
server_name _;
charset utf-8;
root /usr/share/nginx/html;
index index.html;
location / {
access_log /dev/stdout json_combined;
}
location /static/ {
expires 31d;
add_header Cache-Control "public, no-transform";
add_header X-authentik-version "2021.5.1-rc8";
add_header Vary X-authentik-version;
}
location /if/admin {
root /usr/share/nginx/html/static/dist;
try_files $uri /static/dist/if/admin/index.html;
}
location /if/flow {
root /usr/share/nginx/html/static/dist;
try_files $uri /static/dist/if/flow/index.html;
}
}
}

1407
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -35,10 +35,10 @@
]
},
"dependencies": {
"@babel/core": "^7.14.0",
"@babel/plugin-proposal-decorators": "^7.13.15",
"@babel/plugin-transform-runtime": "^7.13.15",
"@babel/preset-env": "^7.14.1",
"@babel/core": "^7.14.2",
"@babel/plugin-proposal-decorators": "^7.14.2",
"@babel/plugin-transform-runtime": "^7.14.2",
"@babel/preset-env": "^7.14.2",
"@babel/preset-typescript": "^7.13.0",
"@fortawesome/fontawesome-free": "^5.15.3",
"@lingui/cli": "^3.8.10",
@ -68,13 +68,13 @@
"eslint": "^7.26.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.2",
"eslint-plugin-lit": "^1.4.0",
"eslint-plugin-lit": "^1.4.1",
"flowchart.js": "^1.15.0",
"lit-element": "^2.5.1",
"lit-html": "^1.4.1",
"moment": "^2.29.1",
"rapidoc": "^9.0.0",
"rollup": "^2.47.0",
"rollup": "^2.48.0",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2",

View File

@ -104,6 +104,7 @@ export default [
dir: "dist",
sourcemap: true,
manualChunks: manualChunks,
chunkFileNames: "admin-[name].js"
},
],
plugins: [
@ -135,6 +136,7 @@ export default [
dir: "dist",
sourcemap: true,
manualChunks: manualChunks,
chunkFileNames: "flow-[name].js"
},
],
plugins: [

View File

@ -6,7 +6,9 @@ import { me } from "./Users";
import { config } from "./Config";
import { Config } from "authentik-api";
export function configureSentry(canDoPpi: boolean = false): Promise<Config> {
export const TAG_SENTRY_COMPONENT = "authentik:component";
export function configureSentry(canDoPpi: boolean = false, tags: { [key: string]: string; } = {}): Promise<Config> {
return config().then((config) => {
if (config.errorReportingEnabled) {
Sentry.init({
@ -51,6 +53,12 @@ export function configureSentry(canDoPpi: boolean = false): Promise<Config> {
return event;
},
});
Sentry.setTags(tags);
if (window.location.pathname.includes("if/")) {
// Get the interface name from URL
const intf = window.location.pathname.replace(/.+if\/(.+)\//, "$1");
Sentry.setTag(TAG_SENTRY_COMPONENT, `web/${intf}`);
}
console.debug("authentik/config: Sentry enabled.");
if (config.errorReportingSendPii && canDoPpi) {
me().then(user => {

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-rc8";
export const VERSION = "2021.5.4";
export const PAGE_SIZE = 20;
export const EVENT_REFRESH = "ak-refresh";
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";

View File

@ -55,7 +55,7 @@ export class ModalButton extends LitElement {
resetForms(): void {
this.querySelectorAll<HTMLFormElement>("[slot=form]").forEach(form => {
form.reset();
form?.reset();
});
}

View File

@ -29,8 +29,8 @@ export class TokenCopyButton extends ActionButton {
this.buttonClass = PRIMARY_CLASS;
}, 1500);
});
}).catch((err: Response) => {
return err.json().then(errResp => {
}).catch((err: Response | undefined) => {
return err?.json().then(errResp => {
throw new Error(errResp["detail"]);
});
});

View File

@ -76,10 +76,7 @@ export class Form<T> extends LitElement {
*/
reset(): void {
const ironForm = this.shadowRoot?.querySelector("iron-form");
if (!ironForm) {
return;
}
ironForm.reset();
ironForm?.reset();
}
/**

View File

@ -23,7 +23,7 @@ export class ModalForm extends ModalButton {
return formPromise.then(() => {
if (this.closeAfterSuccessfulSubmit) {
this.open = false;
form.reset();
form?.reset();
}
this.dispatchEvent(
new CustomEvent(EVENT_REFRESH, {

View File

@ -34,7 +34,7 @@ export abstract class TableModal<T> extends Table<T> {
resetForms(): void {
this.querySelectorAll<HTMLFormElement>("[slot=form]").forEach(form => {
form.reset();
form?.reset();
});
}

View File

@ -10,9 +10,8 @@
<link rel="stylesheet" type="text/css" href="/static/dist/empty-state.css">
<link rel="stylesheet" type="text/css" href="/static/dist/spinner.css">
<link rel="stylesheet" type="text/css" href="/static/dist/authentik.css">
<script>ShadyDOM = { force: !navigator.webdriver };</script>
<script>ShadyDOM = { force: !navigator.webdriver }; window["polymerSkipLoadingFontRoboto"] = true;</script>
<script src="/static/dist/poly.js" type="module"></script>
<script>window["polymerSkipLoadingFontRoboto"] = true;</script>
<script src="/static/dist/FlowInterface.js" type="module"></script>
<title>authentik</title>
</head>

View File

@ -144,7 +144,7 @@ msgstr "Algorithm used to sign the JWT Tokens."
msgid "Allow IDP-initiated logins"
msgstr "Allow IDP-initiated logins"
#: src/pages/sources/plex/PlexSourceForm.ts:150
#: src/pages/sources/plex/PlexSourceForm.ts:103
msgid "Allow friends to authenticate via Plex, even if you don't share any servers"
msgstr "Allow friends to authenticate via Plex, even if you don't share any servers"
@ -152,7 +152,7 @@ msgstr "Allow friends to authenticate via Plex, even if you don't share any serv
msgid "Allow up to N occurrences in the HIBP database."
msgstr "Allow up to N occurrences in the HIBP database."
#: src/pages/policies/PolicyListPage.ts:41
#: src/pages/policies/PolicyListPage.ts:42
msgid "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages."
msgstr "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages."
@ -164,7 +164,7 @@ msgstr "Allowed Redirect URIs"
msgid "Allowed count"
msgstr "Allowed count"
#: src/pages/sources/plex/PlexSourceForm.ts:155
#: src/pages/sources/plex/PlexSourceForm.ts:108
msgid "Allowed servers"
msgstr "Allowed servers"
@ -218,6 +218,22 @@ msgstr "Applications"
msgid "Apps with most usage"
msgstr "Apps with most usage"
#: src/pages/flows/FlowListPage.ts:146
msgid ""
"Are you sure you want to clear the flow cache?\n"
"This will cause all flows to be re-evaluated on their next usage."
msgstr ""
"Are you sure you want to clear the flow cache?\n"
"This will cause all flows to be re-evaluated on their next usage."
#: src/pages/policies/PolicyListPage.ts:165
msgid ""
"Are you sure you want to clear the policy cache?\n"
"This will cause all policies to be re-evaluated on their next usage."
msgstr ""
"Are you sure you want to clear the policy cache?\n"
"This will cause all policies to be re-evaluated on their next usage."
#: src/elements/forms/DeleteForm.ts:69
msgid "Are you sure you want to delete {0} {objName} ?"
msgstr "Are you sure you want to delete {0} {objName} ?"
@ -250,7 +266,7 @@ msgstr "Assertions is empty"
msgid "Assigned to application"
msgstr "Assigned to application"
#: src/pages/policies/PolicyListPage.ts:68
#: src/pages/policies/PolicyListPage.ts:69
msgid "Assigned to {0} objects."
msgstr "Assigned to {0} objects."
@ -282,7 +298,7 @@ msgid "Authentication"
msgstr "Authentication"
#: src/pages/sources/oauth/OAuthSourceForm.ts:231
#: src/pages/sources/plex/PlexSourceForm.ts:185
#: src/pages/sources/plex/PlexSourceForm.ts:190
#: src/pages/sources/saml/SAMLSourceForm.ts:243
msgid "Authentication flow"
msgstr "Authentication flow"
@ -530,6 +546,21 @@ msgstr "Checks if the request's user's password has been changed in the last x d
msgid "Checks the value from the policy request against several rules, mostly used to ensure password strength."
msgstr "Checks the value from the policy request against several rules, mostly used to ensure password strength."
#: src/pages/flows/FlowListPage.ts:143
msgid "Clear Flow cache"
msgstr "Clear Flow cache"
#: src/pages/policies/PolicyListPage.ts:162
msgid "Clear Policy cache"
msgstr "Clear Policy cache"
#: src/pages/flows/FlowListPage.ts:138
#: src/pages/flows/FlowListPage.ts:150
#: src/pages/policies/PolicyListPage.ts:157
#: src/pages/policies/PolicyListPage.ts:169
msgid "Clear cache"
msgstr "Clear cache"
#: src/elements/forms/HorizontalFormElement.ts:82
msgid "Click to change value"
msgstr "Click to change value"
@ -540,7 +571,7 @@ msgstr "Click to copy token"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts:107
#: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts:99
#: src/pages/sources/plex/PlexSourceForm.ts:141
#: src/pages/sources/plex/PlexSourceForm.ts:176
msgid "Client ID"
msgstr "Client ID"
@ -726,8 +757,8 @@ msgstr "Copy download URL"
#: src/pages/flows/BoundStagesList.ts:119
#: src/pages/flows/BoundStagesList.ts:146
#: src/pages/flows/BoundStagesList.ts:167
#: src/pages/flows/FlowListPage.ts:109
#: src/pages/flows/FlowListPage.ts:117
#: src/pages/flows/FlowListPage.ts:110
#: src/pages/flows/FlowListPage.ts:118
#: src/pages/groups/GroupListPage.ts:90
#: src/pages/groups/GroupListPage.ts:98
#: src/pages/outposts/OutpostListPage.ts:101
@ -737,8 +768,8 @@ msgstr "Copy download URL"
#: src/pages/policies/BoundPoliciesList.ts:162
#: src/pages/policies/BoundPoliciesList.ts:189
#: src/pages/policies/BoundPoliciesList.ts:210
#: src/pages/policies/PolicyListPage.ts:124
#: src/pages/policies/PolicyListPage.ts:133
#: src/pages/policies/PolicyListPage.ts:125
#: src/pages/policies/PolicyListPage.ts:134
#: src/pages/property-mappings/PropertyMappingListPage.ts:113
#: src/pages/property-mappings/PropertyMappingListPage.ts:122
#: src/pages/providers/ProviderListPage.ts:108
@ -778,7 +809,7 @@ msgstr "Create Binding"
msgid "Create Certificate-Key Pair"
msgstr "Create Certificate-Key Pair"
#: src/pages/flows/FlowListPage.ts:112
#: src/pages/flows/FlowListPage.ts:113
msgid "Create Flow"
msgstr "Create Flow"
@ -840,7 +871,7 @@ msgstr "Create provider"
#: src/pages/flows/BoundStagesList.ts:149
#: src/pages/outposts/ServiceConnectionListPage.ts:122
#: src/pages/policies/BoundPoliciesList.ts:192
#: src/pages/policies/PolicyListPage.ts:136
#: src/pages/policies/PolicyListPage.ts:137
#: src/pages/property-mappings/PropertyMappingListPage.ts:125
#: src/pages/providers/ProviderListPage.ts:120
#: src/pages/sources/SourcesListPage.ts:126
@ -892,11 +923,11 @@ msgstr "Define how notifications are sent to users, like Email or Webhook."
#: src/pages/crypto/CertificateKeyPairListPage.ts:86
#: src/pages/events/RuleListPage.ts:82
#: src/pages/events/TransportListPage.ts:86
#: src/pages/flows/FlowListPage.ts:86
#: src/pages/flows/FlowListPage.ts:87
#: src/pages/groups/GroupListPage.ts:81
#: src/pages/outposts/OutpostListPage.ts:87
#: src/pages/outposts/ServiceConnectionListPage.ts:101
#: src/pages/policies/PolicyListPage.ts:115
#: src/pages/policies/PolicyListPage.ts:116
#: src/pages/property-mappings/PropertyMappingListPage.ts:104
#: src/pages/providers/ProviderListPage.ts:99
#: src/pages/sources/SourcesListPage.ts:95
@ -967,7 +998,7 @@ msgid "Designates whether this user should be treated as active. Unselect this i
msgstr "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
#: src/pages/flows/FlowForm.ts:119
#: src/pages/flows/FlowListPage.ts:48
#: src/pages/flows/FlowListPage.ts:49
msgid "Designation"
msgstr "Designation"
@ -1049,11 +1080,11 @@ msgstr "Each provider has a different issuer, based on the application slug."
#: src/pages/crypto/CertificateKeyPairListPage.ts:74
#: src/pages/events/RuleListPage.ts:70
#: src/pages/events/TransportListPage.ts:74
#: src/pages/flows/FlowListPage.ts:74
#: src/pages/flows/FlowListPage.ts:75
#: src/pages/groups/GroupListPage.ts:69
#: src/pages/outposts/OutpostListPage.ts:75
#: src/pages/outposts/ServiceConnectionListPage.ts:89
#: src/pages/policies/PolicyListPage.ts:90
#: src/pages/policies/PolicyListPage.ts:91
#: src/pages/property-mappings/PropertyMappingListPage.ts:79
#: src/pages/providers/ProviderListPage.ts:87
#: src/pages/providers/ldap/LDAPProviderViewPage.ts:103
@ -1148,7 +1179,7 @@ msgstr "Enable this if you don't want to use this provider as a proxy, and want
#: src/pages/policies/PolicyBindingForm.ts:199
#: src/pages/sources/ldap/LDAPSourceForm.ts:67
#: src/pages/sources/oauth/OAuthSourceForm.ts:134
#: src/pages/sources/plex/PlexSourceForm.ts:108
#: src/pages/sources/plex/PlexSourceForm.ts:143
#: src/pages/sources/saml/SAMLSourceForm.ts:67
msgid "Enabled"
msgstr "Enabled"
@ -1158,7 +1189,7 @@ msgid "Enrollment"
msgstr "Enrollment"
#: src/pages/sources/oauth/OAuthSourceForm.ts:252
#: src/pages/sources/plex/PlexSourceForm.ts:206
#: src/pages/sources/plex/PlexSourceForm.ts:211
#: src/pages/sources/saml/SAMLSourceForm.ts:264
#: src/pages/stages/identification/IdentificationStageForm.ts:104
msgid "Enrollment flow"
@ -1225,12 +1256,12 @@ msgstr "Events"
msgid "Exception"
msgstr "Exception"
#: src/pages/flows/FlowListPage.ts:98
#: src/pages/flows/FlowViewPage.ts:83
#: src/pages/flows/FlowListPage.ts:99
#: src/pages/flows/FlowViewPage.ts:75
msgid "Execute"
msgstr "Execute"
#: src/pages/flows/FlowViewPage.ts:69
#: src/pages/flows/FlowViewPage.ts:61
msgid "Execute flow"
msgstr "Execute flow"
@ -1277,7 +1308,7 @@ msgstr "Expiry date"
msgid "Explicit Consent"
msgstr "Explicit Consent"
#: src/pages/flows/FlowListPage.ts:101
#: src/pages/flows/FlowListPage.ts:102
msgid "Export"
msgstr "Export"
@ -1316,6 +1347,14 @@ msgstr "Failed attempts before cancel"
msgid "Failed sources"
msgstr "Failed sources"
#: src/pages/flows/FlowListPage.ts:137
msgid "Failed to delete flow cache"
msgstr "Failed to delete flow cache"
#: src/pages/policies/PolicyListPage.ts:156
msgid "Failed to delete policy cache"
msgstr "Failed to delete policy cache"
#: src/elements/forms/DeleteForm.ts:46
msgid "Failed to delete {0}: {1}"
msgstr "Failed to delete {0}: {1}"
@ -1358,7 +1397,7 @@ msgid "Fields a user can identify themselves with. If no fields are selected, th
msgstr "Fields a user can identify themselves with. If no fields are selected, the user will only be able to use sources."
#: src/pages/flows/FlowImportForm.ts:34
#: src/pages/flows/FlowListPage.ts:79
#: src/pages/flows/FlowListPage.ts:80
msgid "Flow"
msgstr "Flow"
@ -1367,19 +1406,19 @@ msgid "Flow Overview"
msgstr "Flow Overview"
#: src/pages/sources/oauth/OAuthSourceForm.ts:227
#: src/pages/sources/plex/PlexSourceForm.ts:181
#: src/pages/sources/plex/PlexSourceForm.ts:186
#: src/pages/sources/saml/SAMLSourceForm.ts:218
msgid "Flow settings"
msgstr "Flow settings"
#: src/pages/sources/oauth/OAuthSourceForm.ts:249
#: src/pages/sources/plex/PlexSourceForm.ts:203
#: src/pages/sources/plex/PlexSourceForm.ts:208
#: src/pages/sources/saml/SAMLSourceForm.ts:261
msgid "Flow to use when authenticating existing users."
msgstr "Flow to use when authenticating existing users."
#: src/pages/sources/oauth/OAuthSourceForm.ts:270
#: src/pages/sources/plex/PlexSourceForm.ts:224
#: src/pages/sources/plex/PlexSourceForm.ts:229
#: src/pages/sources/saml/SAMLSourceForm.ts:282
msgid "Flow to use when enrolling new users."
msgstr "Flow to use when enrolling new users."
@ -1411,12 +1450,12 @@ msgstr "Flow used when authorizing this provider."
#: src/interfaces/AdminInterface.ts:82
#: src/interfaces/AdminInterface.ts:84
#: src/pages/admin-overview/AdminOverviewPage.ts:61
#: src/pages/flows/FlowListPage.ts:28
#: src/pages/flows/FlowListPage.ts:29
#: src/pages/stages/StageListPage.ts:66
msgid "Flows"
msgstr "Flows"
#: src/pages/flows/FlowListPage.ts:31
#: src/pages/flows/FlowListPage.ts:32
msgid "Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them."
msgstr "Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them."
@ -1546,7 +1585,7 @@ msgstr "Hide service-accounts"
#: src/pages/providers/saml/SAMLProviderForm.ts:176
#: src/pages/sources/ldap/LDAPSourceForm.ts:165
#: src/pages/sources/ldap/LDAPSourceForm.ts:191
#: src/pages/sources/plex/PlexSourceForm.ts:168
#: src/pages/sources/plex/PlexSourceForm.ts:121
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts:114
#: src/pages/stages/identification/IdentificationStageForm.ts:83
#: src/pages/stages/password/PasswordStageForm.ts:84
@ -1567,7 +1606,7 @@ msgstr "ID"
msgid "Icon"
msgstr "Icon"
#: src/pages/flows/FlowListPage.ts:46
#: src/pages/flows/FlowListPage.ts:47
#: src/pages/system-tasks/SystemTaskListPage.ts:54
#: src/pages/tokens/TokenListPage.ts:44
#: src/pages/user-settings/tokens/UserTokenForm.ts:49
@ -1600,12 +1639,12 @@ msgstr "If your authentik Instance is using a self-signed certificate, set this
msgid "Impersonate"
msgstr "Impersonate"
#: src/pages/flows/FlowListPage.ts:122
#: src/pages/flows/FlowListPage.ts:130
#: src/pages/flows/FlowListPage.ts:123
#: src/pages/flows/FlowListPage.ts:131
msgid "Import"
msgstr "Import"
#: src/pages/flows/FlowListPage.ts:125
#: src/pages/flows/FlowListPage.ts:126
msgid "Import Flow"
msgstr "Import Flow"
@ -1716,7 +1755,7 @@ msgstr "Last login"
msgid "Last run"
msgstr "Last run"
#: src/pages/outposts/OutpostHealth.ts:54
#: src/pages/outposts/OutpostHealth.ts:53
msgid "Last seen: {0}"
msgstr "Last seen: {0}"
@ -1742,21 +1781,21 @@ msgid "Library"
msgstr "Library"
#: src/pages/sources/oauth/OAuthSourceForm.ts:147
#: src/pages/sources/plex/PlexSourceForm.ts:121
#: src/pages/sources/plex/PlexSourceForm.ts:156
msgid "Link to a user with identical email address. Can have security implications when a source doesn't validate email addresses"
msgstr "Link to a user with identical email address. Can have security implications when a source doesn't validate email addresses"
#: src/pages/sources/oauth/OAuthSourceForm.ts:153
#: src/pages/sources/plex/PlexSourceForm.ts:127
#: src/pages/sources/plex/PlexSourceForm.ts:162
msgid "Link to a user with identical username address. Can have security implications when a username is used with another source."
msgstr "Link to a user with identical username address. Can have security implications when a username is used with another source."
#: src/pages/sources/oauth/OAuthSourceForm.ts:144
#: src/pages/sources/plex/PlexSourceForm.ts:118
#: src/pages/sources/plex/PlexSourceForm.ts:153
msgid "Link users on unique identifier"
msgstr "Link users on unique identifier"
#: src/pages/sources/plex/PlexSourceForm.ts:173
#: src/pages/sources/plex/PlexSourceForm.ts:96
msgid "Load servers"
msgstr "Load servers"
@ -1821,8 +1860,8 @@ msgstr "Loading"
#: src/pages/sources/oauth/OAuthSourceForm.ts:219
#: src/pages/sources/oauth/OAuthSourceForm.ts:247
#: src/pages/sources/oauth/OAuthSourceForm.ts:268
#: src/pages/sources/plex/PlexSourceForm.ts:201
#: src/pages/sources/plex/PlexSourceForm.ts:222
#: src/pages/sources/plex/PlexSourceForm.ts:206
#: src/pages/sources/plex/PlexSourceForm.ts:227
#: src/pages/sources/saml/SAMLSourceForm.ts:124
#: src/pages/sources/saml/SAMLSourceForm.ts:238
#: src/pages/sources/saml/SAMLSourceForm.ts:259
@ -1961,7 +2000,7 @@ msgstr "My Applications"
#: src/pages/events/TransportListPage.ts:46
#: src/pages/flows/BoundStagesList.ts:39
#: src/pages/flows/FlowForm.ts:86
#: src/pages/flows/FlowListPage.ts:47
#: src/pages/flows/FlowListPage.ts:48
#: src/pages/groups/GroupForm.ts:58
#: src/pages/groups/GroupListPage.ts:45
#: src/pages/groups/MemberSelectModal.ts:45
@ -1970,7 +2009,7 @@ msgstr "My Applications"
#: src/pages/outposts/ServiceConnectionDockerForm.ts:51
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts:52
#: src/pages/outposts/ServiceConnectionListPage.ts:53
#: src/pages/policies/PolicyListPage.ts:56
#: src/pages/policies/PolicyListPage.ts:57
#: src/pages/policies/dummy/DummyPolicyForm.ts:54
#: src/pages/policies/event_matcher/EventMatcherPolicyForm.ts:55
#: src/pages/policies/expiry/ExpiryPolicyForm.ts:54
@ -1997,7 +2036,7 @@ msgstr "My Applications"
#: src/pages/sources/ldap/LDAPSourceViewPage.ts:64
#: src/pages/sources/oauth/OAuthSourceForm.ts:108
#: src/pages/sources/oauth/OAuthSourceViewPage.ts:64
#: src/pages/sources/plex/PlexSourceForm.ts:93
#: src/pages/sources/plex/PlexSourceForm.ts:128
#: src/pages/sources/plex/PlexSourceViewPage.ts:63
#: src/pages/sources/saml/SAMLSourceForm.ts:52
#: src/pages/sources/saml/SAMLSourceViewPage.ts:66
@ -2359,15 +2398,15 @@ msgstr "Please enter your password"
#: src/interfaces/AdminInterface.ts:74
#: src/pages/admin-overview/AdminOverviewPage.ts:56
#: src/pages/flows/FlowListPage.ts:50
#: src/pages/policies/PolicyListPage.ts:38
#: src/pages/flows/FlowListPage.ts:51
#: src/pages/policies/PolicyListPage.ts:39
msgid "Policies"
msgstr "Policies"
#: src/pages/policies/PolicyBindingForm.ts:108
#: src/pages/policies/PolicyBindingForm.ts:117
#: src/pages/policies/PolicyBindingForm.ts:148
#: src/pages/policies/PolicyListPage.ts:108
#: src/pages/policies/PolicyListPage.ts:109
msgid "Policy"
msgstr "Policy"
@ -2492,7 +2531,7 @@ msgstr "Protocol Settings"
#: src/pages/providers/proxy/ProxyProviderForm.ts:123
#: src/pages/providers/saml/SAMLProviderForm.ts:77
#: src/pages/sources/oauth/OAuthSourceForm.ts:163
#: src/pages/sources/plex/PlexSourceForm.ts:137
#: src/pages/sources/plex/PlexSourceForm.ts:172
#: src/pages/sources/saml/SAMLSourceForm.ts:74
msgid "Protocol settings"
msgstr "Protocol settings"
@ -2623,7 +2662,7 @@ msgid "Regular expressions for which authentication is not required. Each new li
msgstr "Regular expressions for which authentication is not required. Each new line is interpreted as a new Regular Expression."
#: src/pages/applications/ApplicationViewPage.ts:62
#: src/pages/flows/FlowViewPage.ts:64
#: src/pages/flows/FlowViewPage.ts:56
msgid "Related"
msgstr "Related"
@ -2800,7 +2839,7 @@ msgstr "Select users to add"
msgid "Select which scopes can be used by the client. The client stil has to specify the scope to access the data."
msgstr "Select which scopes can be used by the client. The client stil has to specify the scope to access the data."
#: src/pages/sources/plex/PlexSourceForm.ts:167
#: src/pages/sources/plex/PlexSourceForm.ts:120
msgid "Select which server a user has to be a member of to be allowed to authenticate."
msgstr "Select which server a user has to be a member of to be allowed to authenticate."
@ -2940,7 +2979,7 @@ msgstr "Skip path regex"
#: src/pages/flows/FlowForm.ts:99
#: src/pages/sources/ldap/LDAPSourceForm.ts:58
#: src/pages/sources/oauth/OAuthSourceForm.ts:114
#: src/pages/sources/plex/PlexSourceForm.ts:99
#: src/pages/sources/plex/PlexSourceForm.ts:134
#: src/pages/sources/saml/SAMLSourceForm.ts:58
msgid "Slug"
msgstr "Slug"
@ -3017,7 +3056,7 @@ msgid "Stage-specific settings"
msgstr "Stage-specific settings"
#: src/interfaces/AdminInterface.ts:87
#: src/pages/flows/FlowListPage.ts:49
#: src/pages/flows/FlowListPage.ts:50
#: src/pages/stages/StageListPage.ts:44
#: src/pages/stages/prompt/PromptListPage.ts:50
msgid "Stages"
@ -3081,6 +3120,14 @@ msgstr "Subject-alt name"
msgid "Successful"
msgstr "Successful"
#: src/pages/flows/FlowListPage.ts:136
msgid "Successfully cleared flow cache"
msgstr "Successfully cleared flow cache"
#: src/pages/policies/PolicyListPage.ts:155
msgid "Successfully cleared policy cache"
msgstr "Successfully cleared policy cache"
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts:63
msgid "Successfully copied TOTP Config."
msgstr "Successfully copied TOTP Config."
@ -3405,14 +3452,14 @@ msgid "Template"
msgstr "Template"
#: src/pages/events/TransportListPage.ts:62
#: src/pages/policies/PolicyListPage.ts:95
#: src/pages/policies/PolicyListPage.ts:103
#: src/pages/policies/PolicyListPage.ts:96
#: src/pages/policies/PolicyListPage.ts:104
#: src/pages/property-mappings/PropertyMappingListPage.ts:84
#: src/pages/property-mappings/PropertyMappingListPage.ts:92
msgid "Test"
msgstr "Test"
#: src/pages/policies/PolicyListPage.ts:98
#: src/pages/policies/PolicyListPage.ts:99
msgid "Test Policy"
msgstr "Test Policy"
@ -3429,8 +3476,8 @@ msgid "The URL \"{0}\" was not found."
msgstr "The URL \"{0}\" was not found."
#: src/pages/providers/proxy/ProxyProviderForm.ts:131
msgid "The external URL you'll access the application at"
msgstr "The external URL you'll access the application at"
msgid "The external URL you'll access the application at. Include any non-standard port."
msgstr "The external URL you'll access the application at. Include any non-standard port."
#: src/pages/policies/dummy/DummyPolicyForm.ts:88
msgid "The policy takes a random time to execute. This controls the minimum time it will take."
@ -3564,7 +3611,7 @@ msgstr "Transports"
#: src/pages/flows/BoundStagesList.ts:40
#: src/pages/outposts/OutpostForm.ts:58
#: src/pages/outposts/ServiceConnectionListPage.ts:54
#: src/pages/policies/PolicyListPage.ts:57
#: src/pages/policies/PolicyListPage.ts:58
#: src/pages/property-mappings/PropertyMappingListPage.ts:55
#: src/pages/providers/ProviderListPage.ts:55
#: src/pages/sources/SourcesListPage.ts:53
@ -3640,7 +3687,7 @@ msgstr "Up-to-date!"
#: src/pages/events/TransportListPage.ts:66
#: src/pages/flows/BoundStagesList.ts:53
#: src/pages/flows/BoundStagesList.ts:71
#: src/pages/flows/FlowListPage.ts:66
#: src/pages/flows/FlowListPage.ts:67
#: src/pages/groups/GroupListPage.ts:61
#: src/pages/outposts/OutpostListPage.ts:67
#: src/pages/outposts/ServiceConnectionListPage.ts:76
@ -3648,7 +3695,7 @@ msgstr "Up-to-date!"
#: src/pages/policies/BoundPoliciesList.ts:88
#: src/pages/policies/BoundPoliciesList.ts:103
#: src/pages/policies/BoundPoliciesList.ts:129
#: src/pages/policies/PolicyListPage.ts:77
#: src/pages/policies/PolicyListPage.ts:78
#: src/pages/property-mappings/PropertyMappingListPage.ts:66
#: src/pages/providers/ProviderListPage.ts:74
#: src/pages/providers/ldap/LDAPProviderViewPage.ts:93
@ -3686,7 +3733,7 @@ msgstr "Update Binding"
msgid "Update Certificate-Key Pair"
msgstr "Update Certificate-Key Pair"
#: src/pages/flows/FlowListPage.ts:69
#: src/pages/flows/FlowListPage.ts:70
msgid "Update Flow"
msgstr "Update Flow"
@ -3764,7 +3811,7 @@ msgstr "Update details"
#: src/pages/flows/BoundStagesList.ts:56
#: src/pages/outposts/ServiceConnectionListPage.ts:79
#: src/pages/policies/BoundPoliciesList.ts:71
#: src/pages/policies/PolicyListPage.ts:80
#: src/pages/policies/PolicyListPage.ts:81
#: src/pages/property-mappings/PropertyMappingListPage.ts:69
#: src/pages/providers/ProviderListPage.ts:77
#: src/pages/sources/SourcesListPage.ts:73
@ -3798,12 +3845,12 @@ msgid "Use global settings"
msgstr "Use global settings"
#: src/pages/sources/oauth/OAuthSourceForm.ts:150
#: src/pages/sources/plex/PlexSourceForm.ts:124
#: src/pages/sources/plex/PlexSourceForm.ts:159
msgid "Use the user's email address, but deny enrollment when the email address already exists."
msgstr "Use the user's email address, but deny enrollment when the email address already exists."
#: src/pages/sources/oauth/OAuthSourceForm.ts:156
#: src/pages/sources/plex/PlexSourceForm.ts:130
#: src/pages/sources/plex/PlexSourceForm.ts:165
msgid "Use the user's username, but deny enrollment when the username already exists."
msgstr "Use the user's username, but deny enrollment when the username already exists."
@ -3852,7 +3899,7 @@ msgid "User fields"
msgstr "User fields"
#: src/pages/sources/oauth/OAuthSourceForm.ts:139
#: src/pages/sources/plex/PlexSourceForm.ts:113
#: src/pages/sources/plex/PlexSourceForm.ts:148
msgid "User matching mode"
msgstr "User matching mode"
@ -3913,8 +3960,8 @@ msgid "Users added to this group will be superusers."
msgstr "Users added to this group will be superusers."
#: src/pages/providers/ldap/LDAPProviderForm.ts:86
msgid "Users in the selected group can do search queries."
msgstr "Users in the selected group can do search queries."
msgid "Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed."
msgstr "Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed."
#: src/pages/events/EventInfo.ts:108
msgid "Using flow"
@ -3956,7 +4003,7 @@ msgstr "Verify the user's email address by sending them a one-time-link. Can als
msgid "Version"
msgstr "Version"
#: src/pages/outposts/OutpostHealth.ts:60
#: src/pages/outposts/OutpostHealth.ts:59
msgid "Version: {0}"
msgstr "Version: {0}"
@ -3985,7 +4032,7 @@ msgstr "Wait (min)"
msgid "Warning"
msgstr "Warning"
#: src/pages/policies/PolicyListPage.ts:71
#: src/pages/policies/PolicyListPage.ts:72
msgid "Warning: Policy is not assigned."
msgstr "Warning: Policy is not assigned."
@ -4122,7 +4169,7 @@ msgstr "{0} is available!"
msgid "{0} unread"
msgstr "{0} unread"
#: src/pages/outposts/OutpostHealth.ts:59
#: src/pages/outposts/OutpostHealth.ts:58
msgid "{0}, should be {1}"
msgstr "{0}, should be {1}"

View File

@ -144,7 +144,7 @@ msgstr ""
msgid "Allow IDP-initiated logins"
msgstr ""
#: src/pages/sources/plex/PlexSourceForm.ts:150
#: src/pages/sources/plex/PlexSourceForm.ts:103
msgid "Allow friends to authenticate via Plex, even if you don't share any servers"
msgstr ""
@ -152,7 +152,7 @@ msgstr ""
msgid "Allow up to N occurrences in the HIBP database."
msgstr ""
#: src/pages/policies/PolicyListPage.ts:41
#: src/pages/policies/PolicyListPage.ts:42
msgid "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages."
msgstr ""
@ -164,7 +164,7 @@ msgstr ""
msgid "Allowed count"
msgstr ""
#: src/pages/sources/plex/PlexSourceForm.ts:155
#: src/pages/sources/plex/PlexSourceForm.ts:108
msgid "Allowed servers"
msgstr ""
@ -218,6 +218,18 @@ msgstr ""
msgid "Apps with most usage"
msgstr ""
#: src/pages/flows/FlowListPage.ts:146
msgid ""
"Are you sure you want to clear the flow cache?\n"
"This will cause all flows to be re-evaluated on their next usage."
msgstr ""
#: src/pages/policies/PolicyListPage.ts:165
msgid ""
"Are you sure you want to clear the policy cache?\n"
"This will cause all policies to be re-evaluated on their next usage."
msgstr ""
#: src/elements/forms/DeleteForm.ts:69
msgid "Are you sure you want to delete {0} {objName} ?"
msgstr ""
@ -250,7 +262,7 @@ msgstr ""
msgid "Assigned to application"
msgstr ""
#: src/pages/policies/PolicyListPage.ts:68
#: src/pages/policies/PolicyListPage.ts:69
msgid "Assigned to {0} objects."
msgstr ""
@ -282,7 +294,7 @@ msgid "Authentication"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:231
#: src/pages/sources/plex/PlexSourceForm.ts:185
#: src/pages/sources/plex/PlexSourceForm.ts:190
#: src/pages/sources/saml/SAMLSourceForm.ts:243
msgid "Authentication flow"
msgstr ""
@ -528,6 +540,21 @@ msgstr ""
msgid "Checks the value from the policy request against several rules, mostly used to ensure password strength."
msgstr ""
#: src/pages/flows/FlowListPage.ts:143
msgid "Clear Flow cache"
msgstr ""
#: src/pages/policies/PolicyListPage.ts:162
msgid "Clear Policy cache"
msgstr ""
#: src/pages/flows/FlowListPage.ts:138
#: src/pages/flows/FlowListPage.ts:150
#: src/pages/policies/PolicyListPage.ts:157
#: src/pages/policies/PolicyListPage.ts:169
msgid "Clear cache"
msgstr ""
#: src/elements/forms/HorizontalFormElement.ts:82
msgid "Click to change value"
msgstr ""
@ -538,7 +565,7 @@ msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts:107
#: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts:99
#: src/pages/sources/plex/PlexSourceForm.ts:141
#: src/pages/sources/plex/PlexSourceForm.ts:176
msgid "Client ID"
msgstr ""
@ -724,8 +751,8 @@ msgstr ""
#: src/pages/flows/BoundStagesList.ts:119
#: src/pages/flows/BoundStagesList.ts:146
#: src/pages/flows/BoundStagesList.ts:167
#: src/pages/flows/FlowListPage.ts:109
#: src/pages/flows/FlowListPage.ts:117
#: src/pages/flows/FlowListPage.ts:110
#: src/pages/flows/FlowListPage.ts:118
#: src/pages/groups/GroupListPage.ts:90
#: src/pages/groups/GroupListPage.ts:98
#: src/pages/outposts/OutpostListPage.ts:101
@ -735,8 +762,8 @@ msgstr ""
#: src/pages/policies/BoundPoliciesList.ts:162
#: src/pages/policies/BoundPoliciesList.ts:189
#: src/pages/policies/BoundPoliciesList.ts:210
#: src/pages/policies/PolicyListPage.ts:124
#: src/pages/policies/PolicyListPage.ts:133
#: src/pages/policies/PolicyListPage.ts:125
#: src/pages/policies/PolicyListPage.ts:134
#: src/pages/property-mappings/PropertyMappingListPage.ts:113
#: src/pages/property-mappings/PropertyMappingListPage.ts:122
#: src/pages/providers/ProviderListPage.ts:108
@ -776,7 +803,7 @@ msgstr ""
msgid "Create Certificate-Key Pair"
msgstr ""
#: src/pages/flows/FlowListPage.ts:112
#: src/pages/flows/FlowListPage.ts:113
msgid "Create Flow"
msgstr ""
@ -838,7 +865,7 @@ msgstr ""
#: src/pages/flows/BoundStagesList.ts:149
#: src/pages/outposts/ServiceConnectionListPage.ts:122
#: src/pages/policies/BoundPoliciesList.ts:192
#: src/pages/policies/PolicyListPage.ts:136
#: src/pages/policies/PolicyListPage.ts:137
#: src/pages/property-mappings/PropertyMappingListPage.ts:125
#: src/pages/providers/ProviderListPage.ts:120
#: src/pages/sources/SourcesListPage.ts:126
@ -890,11 +917,11 @@ msgstr ""
#: src/pages/crypto/CertificateKeyPairListPage.ts:86
#: src/pages/events/RuleListPage.ts:82
#: src/pages/events/TransportListPage.ts:86
#: src/pages/flows/FlowListPage.ts:86
#: src/pages/flows/FlowListPage.ts:87
#: src/pages/groups/GroupListPage.ts:81
#: src/pages/outposts/OutpostListPage.ts:87
#: src/pages/outposts/ServiceConnectionListPage.ts:101
#: src/pages/policies/PolicyListPage.ts:115
#: src/pages/policies/PolicyListPage.ts:116
#: src/pages/property-mappings/PropertyMappingListPage.ts:104
#: src/pages/providers/ProviderListPage.ts:99
#: src/pages/sources/SourcesListPage.ts:95
@ -963,7 +990,7 @@ msgid "Designates whether this user should be treated as active. Unselect this i
msgstr ""
#: src/pages/flows/FlowForm.ts:119
#: src/pages/flows/FlowListPage.ts:48
#: src/pages/flows/FlowListPage.ts:49
msgid "Designation"
msgstr ""
@ -1045,11 +1072,11 @@ msgstr ""
#: src/pages/crypto/CertificateKeyPairListPage.ts:74
#: src/pages/events/RuleListPage.ts:70
#: src/pages/events/TransportListPage.ts:74
#: src/pages/flows/FlowListPage.ts:74
#: src/pages/flows/FlowListPage.ts:75
#: src/pages/groups/GroupListPage.ts:69
#: src/pages/outposts/OutpostListPage.ts:75
#: src/pages/outposts/ServiceConnectionListPage.ts:89
#: src/pages/policies/PolicyListPage.ts:90
#: src/pages/policies/PolicyListPage.ts:91
#: src/pages/property-mappings/PropertyMappingListPage.ts:79
#: src/pages/providers/ProviderListPage.ts:87
#: src/pages/providers/ldap/LDAPProviderViewPage.ts:103
@ -1144,7 +1171,7 @@ msgstr ""
#: src/pages/policies/PolicyBindingForm.ts:199
#: src/pages/sources/ldap/LDAPSourceForm.ts:67
#: src/pages/sources/oauth/OAuthSourceForm.ts:134
#: src/pages/sources/plex/PlexSourceForm.ts:108
#: src/pages/sources/plex/PlexSourceForm.ts:143
#: src/pages/sources/saml/SAMLSourceForm.ts:67
msgid "Enabled"
msgstr ""
@ -1154,7 +1181,7 @@ msgid "Enrollment"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:252
#: src/pages/sources/plex/PlexSourceForm.ts:206
#: src/pages/sources/plex/PlexSourceForm.ts:211
#: src/pages/sources/saml/SAMLSourceForm.ts:264
#: src/pages/stages/identification/IdentificationStageForm.ts:104
msgid "Enrollment flow"
@ -1221,12 +1248,12 @@ msgstr ""
msgid "Exception"
msgstr ""
#: src/pages/flows/FlowListPage.ts:98
#: src/pages/flows/FlowViewPage.ts:83
#: src/pages/flows/FlowListPage.ts:99
#: src/pages/flows/FlowViewPage.ts:75
msgid "Execute"
msgstr ""
#: src/pages/flows/FlowViewPage.ts:69
#: src/pages/flows/FlowViewPage.ts:61
msgid "Execute flow"
msgstr ""
@ -1273,7 +1300,7 @@ msgstr ""
msgid "Explicit Consent"
msgstr ""
#: src/pages/flows/FlowListPage.ts:101
#: src/pages/flows/FlowListPage.ts:102
msgid "Export"
msgstr ""
@ -1312,6 +1339,14 @@ msgstr ""
msgid "Failed sources"
msgstr ""
#: src/pages/flows/FlowListPage.ts:137
msgid "Failed to delete flow cache"
msgstr ""
#: src/pages/policies/PolicyListPage.ts:156
msgid "Failed to delete policy cache"
msgstr ""
#: src/elements/forms/DeleteForm.ts:46
msgid "Failed to delete {0}: {1}"
msgstr ""
@ -1354,7 +1389,7 @@ msgid "Fields a user can identify themselves with. If no fields are selected, th
msgstr ""
#: src/pages/flows/FlowImportForm.ts:34
#: src/pages/flows/FlowListPage.ts:79
#: src/pages/flows/FlowListPage.ts:80
msgid "Flow"
msgstr ""
@ -1363,19 +1398,19 @@ msgid "Flow Overview"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:227
#: src/pages/sources/plex/PlexSourceForm.ts:181
#: src/pages/sources/plex/PlexSourceForm.ts:186
#: src/pages/sources/saml/SAMLSourceForm.ts:218
msgid "Flow settings"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:249
#: src/pages/sources/plex/PlexSourceForm.ts:203
#: src/pages/sources/plex/PlexSourceForm.ts:208
#: src/pages/sources/saml/SAMLSourceForm.ts:261
msgid "Flow to use when authenticating existing users."
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:270
#: src/pages/sources/plex/PlexSourceForm.ts:224
#: src/pages/sources/plex/PlexSourceForm.ts:229
#: src/pages/sources/saml/SAMLSourceForm.ts:282
msgid "Flow to use when enrolling new users."
msgstr ""
@ -1407,12 +1442,12 @@ msgstr ""
#: src/interfaces/AdminInterface.ts:82
#: src/interfaces/AdminInterface.ts:84
#: src/pages/admin-overview/AdminOverviewPage.ts:61
#: src/pages/flows/FlowListPage.ts:28
#: src/pages/flows/FlowListPage.ts:29
#: src/pages/stages/StageListPage.ts:66
msgid "Flows"
msgstr ""
#: src/pages/flows/FlowListPage.ts:31
#: src/pages/flows/FlowListPage.ts:32
msgid "Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them."
msgstr ""
@ -1542,7 +1577,7 @@ msgstr ""
#: src/pages/providers/saml/SAMLProviderForm.ts:176
#: src/pages/sources/ldap/LDAPSourceForm.ts:165
#: src/pages/sources/ldap/LDAPSourceForm.ts:191
#: src/pages/sources/plex/PlexSourceForm.ts:168
#: src/pages/sources/plex/PlexSourceForm.ts:121
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts:114
#: src/pages/stages/identification/IdentificationStageForm.ts:83
#: src/pages/stages/password/PasswordStageForm.ts:84
@ -1563,7 +1598,7 @@ msgstr ""
msgid "Icon"
msgstr ""
#: src/pages/flows/FlowListPage.ts:46
#: src/pages/flows/FlowListPage.ts:47
#: src/pages/system-tasks/SystemTaskListPage.ts:54
#: src/pages/tokens/TokenListPage.ts:44
#: src/pages/user-settings/tokens/UserTokenForm.ts:49
@ -1596,12 +1631,12 @@ msgstr ""
msgid "Impersonate"
msgstr ""
#: src/pages/flows/FlowListPage.ts:122
#: src/pages/flows/FlowListPage.ts:130
#: src/pages/flows/FlowListPage.ts:123
#: src/pages/flows/FlowListPage.ts:131
msgid "Import"
msgstr ""
#: src/pages/flows/FlowListPage.ts:125
#: src/pages/flows/FlowListPage.ts:126
msgid "Import Flow"
msgstr ""
@ -1712,7 +1747,7 @@ msgstr ""
msgid "Last run"
msgstr ""
#: src/pages/outposts/OutpostHealth.ts:54
#: src/pages/outposts/OutpostHealth.ts:53
msgid "Last seen: {0}"
msgstr ""
@ -1738,21 +1773,21 @@ msgid "Library"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:147
#: src/pages/sources/plex/PlexSourceForm.ts:121
#: src/pages/sources/plex/PlexSourceForm.ts:156
msgid "Link to a user with identical email address. Can have security implications when a source doesn't validate email addresses"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:153
#: src/pages/sources/plex/PlexSourceForm.ts:127
#: src/pages/sources/plex/PlexSourceForm.ts:162
msgid "Link to a user with identical username address. Can have security implications when a username is used with another source."
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:144
#: src/pages/sources/plex/PlexSourceForm.ts:118
#: src/pages/sources/plex/PlexSourceForm.ts:153
msgid "Link users on unique identifier"
msgstr ""
#: src/pages/sources/plex/PlexSourceForm.ts:173
#: src/pages/sources/plex/PlexSourceForm.ts:96
msgid "Load servers"
msgstr ""
@ -1817,8 +1852,8 @@ msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:219
#: src/pages/sources/oauth/OAuthSourceForm.ts:247
#: src/pages/sources/oauth/OAuthSourceForm.ts:268
#: src/pages/sources/plex/PlexSourceForm.ts:201
#: src/pages/sources/plex/PlexSourceForm.ts:222
#: src/pages/sources/plex/PlexSourceForm.ts:206
#: src/pages/sources/plex/PlexSourceForm.ts:227
#: src/pages/sources/saml/SAMLSourceForm.ts:124
#: src/pages/sources/saml/SAMLSourceForm.ts:238
#: src/pages/sources/saml/SAMLSourceForm.ts:259
@ -1957,7 +1992,7 @@ msgstr ""
#: src/pages/events/TransportListPage.ts:46
#: src/pages/flows/BoundStagesList.ts:39
#: src/pages/flows/FlowForm.ts:86
#: src/pages/flows/FlowListPage.ts:47
#: src/pages/flows/FlowListPage.ts:48
#: src/pages/groups/GroupForm.ts:58
#: src/pages/groups/GroupListPage.ts:45
#: src/pages/groups/MemberSelectModal.ts:45
@ -1966,7 +2001,7 @@ msgstr ""
#: src/pages/outposts/ServiceConnectionDockerForm.ts:51
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts:52
#: src/pages/outposts/ServiceConnectionListPage.ts:53
#: src/pages/policies/PolicyListPage.ts:56
#: src/pages/policies/PolicyListPage.ts:57
#: src/pages/policies/dummy/DummyPolicyForm.ts:54
#: src/pages/policies/event_matcher/EventMatcherPolicyForm.ts:55
#: src/pages/policies/expiry/ExpiryPolicyForm.ts:54
@ -1993,7 +2028,7 @@ msgstr ""
#: src/pages/sources/ldap/LDAPSourceViewPage.ts:64
#: src/pages/sources/oauth/OAuthSourceForm.ts:108
#: src/pages/sources/oauth/OAuthSourceViewPage.ts:64
#: src/pages/sources/plex/PlexSourceForm.ts:93
#: src/pages/sources/plex/PlexSourceForm.ts:128
#: src/pages/sources/plex/PlexSourceViewPage.ts:63
#: src/pages/sources/saml/SAMLSourceForm.ts:52
#: src/pages/sources/saml/SAMLSourceViewPage.ts:66
@ -2355,15 +2390,15 @@ msgstr ""
#: src/interfaces/AdminInterface.ts:74
#: src/pages/admin-overview/AdminOverviewPage.ts:56
#: src/pages/flows/FlowListPage.ts:50
#: src/pages/policies/PolicyListPage.ts:38
#: src/pages/flows/FlowListPage.ts:51
#: src/pages/policies/PolicyListPage.ts:39
msgid "Policies"
msgstr ""
#: src/pages/policies/PolicyBindingForm.ts:108
#: src/pages/policies/PolicyBindingForm.ts:117
#: src/pages/policies/PolicyBindingForm.ts:148
#: src/pages/policies/PolicyListPage.ts:108
#: src/pages/policies/PolicyListPage.ts:109
msgid "Policy"
msgstr ""
@ -2488,7 +2523,7 @@ msgstr ""
#: src/pages/providers/proxy/ProxyProviderForm.ts:123
#: src/pages/providers/saml/SAMLProviderForm.ts:77
#: src/pages/sources/oauth/OAuthSourceForm.ts:163
#: src/pages/sources/plex/PlexSourceForm.ts:137
#: src/pages/sources/plex/PlexSourceForm.ts:172
#: src/pages/sources/saml/SAMLSourceForm.ts:74
msgid "Protocol settings"
msgstr ""
@ -2619,7 +2654,7 @@ msgid "Regular expressions for which authentication is not required. Each new li
msgstr ""
#: src/pages/applications/ApplicationViewPage.ts:62
#: src/pages/flows/FlowViewPage.ts:64
#: src/pages/flows/FlowViewPage.ts:56
msgid "Related"
msgstr ""
@ -2796,7 +2831,7 @@ msgstr ""
msgid "Select which scopes can be used by the client. The client stil has to specify the scope to access the data."
msgstr ""
#: src/pages/sources/plex/PlexSourceForm.ts:167
#: src/pages/sources/plex/PlexSourceForm.ts:120
msgid "Select which server a user has to be a member of to be allowed to authenticate."
msgstr ""
@ -2936,7 +2971,7 @@ msgstr ""
#: src/pages/flows/FlowForm.ts:99
#: src/pages/sources/ldap/LDAPSourceForm.ts:58
#: src/pages/sources/oauth/OAuthSourceForm.ts:114
#: src/pages/sources/plex/PlexSourceForm.ts:99
#: src/pages/sources/plex/PlexSourceForm.ts:134
#: src/pages/sources/saml/SAMLSourceForm.ts:58
msgid "Slug"
msgstr ""
@ -3013,7 +3048,7 @@ msgid "Stage-specific settings"
msgstr ""
#: src/interfaces/AdminInterface.ts:87
#: src/pages/flows/FlowListPage.ts:49
#: src/pages/flows/FlowListPage.ts:50
#: src/pages/stages/StageListPage.ts:44
#: src/pages/stages/prompt/PromptListPage.ts:50
msgid "Stages"
@ -3077,6 +3112,14 @@ msgstr ""
msgid "Successful"
msgstr ""
#: src/pages/flows/FlowListPage.ts:136
msgid "Successfully cleared flow cache"
msgstr ""
#: src/pages/policies/PolicyListPage.ts:155
msgid "Successfully cleared policy cache"
msgstr ""
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts:63
msgid "Successfully copied TOTP Config."
msgstr ""
@ -3401,14 +3444,14 @@ msgid "Template"
msgstr ""
#: src/pages/events/TransportListPage.ts:62
#: src/pages/policies/PolicyListPage.ts:95
#: src/pages/policies/PolicyListPage.ts:103
#: src/pages/policies/PolicyListPage.ts:96
#: src/pages/policies/PolicyListPage.ts:104
#: src/pages/property-mappings/PropertyMappingListPage.ts:84
#: src/pages/property-mappings/PropertyMappingListPage.ts:92
msgid "Test"
msgstr ""
#: src/pages/policies/PolicyListPage.ts:98
#: src/pages/policies/PolicyListPage.ts:99
msgid "Test Policy"
msgstr ""
@ -3425,7 +3468,7 @@ msgid "The URL \"{0}\" was not found."
msgstr ""
#: src/pages/providers/proxy/ProxyProviderForm.ts:131
msgid "The external URL you'll access the application at"
msgid "The external URL you'll access the application at. Include any non-standard port."
msgstr ""
#: src/pages/policies/dummy/DummyPolicyForm.ts:88
@ -3556,7 +3599,7 @@ msgstr ""
#: src/pages/flows/BoundStagesList.ts:40
#: src/pages/outposts/OutpostForm.ts:58
#: src/pages/outposts/ServiceConnectionListPage.ts:54
#: src/pages/policies/PolicyListPage.ts:57
#: src/pages/policies/PolicyListPage.ts:58
#: src/pages/property-mappings/PropertyMappingListPage.ts:55
#: src/pages/providers/ProviderListPage.ts:55
#: src/pages/sources/SourcesListPage.ts:53
@ -3632,7 +3675,7 @@ msgstr ""
#: src/pages/events/TransportListPage.ts:66
#: src/pages/flows/BoundStagesList.ts:53
#: src/pages/flows/BoundStagesList.ts:71
#: src/pages/flows/FlowListPage.ts:66
#: src/pages/flows/FlowListPage.ts:67
#: src/pages/groups/GroupListPage.ts:61
#: src/pages/outposts/OutpostListPage.ts:67
#: src/pages/outposts/ServiceConnectionListPage.ts:76
@ -3640,7 +3683,7 @@ msgstr ""
#: src/pages/policies/BoundPoliciesList.ts:88
#: src/pages/policies/BoundPoliciesList.ts:103
#: src/pages/policies/BoundPoliciesList.ts:129
#: src/pages/policies/PolicyListPage.ts:77
#: src/pages/policies/PolicyListPage.ts:78
#: src/pages/property-mappings/PropertyMappingListPage.ts:66
#: src/pages/providers/ProviderListPage.ts:74
#: src/pages/providers/ldap/LDAPProviderViewPage.ts:93
@ -3678,7 +3721,7 @@ msgstr ""
msgid "Update Certificate-Key Pair"
msgstr ""
#: src/pages/flows/FlowListPage.ts:69
#: src/pages/flows/FlowListPage.ts:70
msgid "Update Flow"
msgstr ""
@ -3756,7 +3799,7 @@ msgstr ""
#: src/pages/flows/BoundStagesList.ts:56
#: src/pages/outposts/ServiceConnectionListPage.ts:79
#: src/pages/policies/BoundPoliciesList.ts:71
#: src/pages/policies/PolicyListPage.ts:80
#: src/pages/policies/PolicyListPage.ts:81
#: src/pages/property-mappings/PropertyMappingListPage.ts:69
#: src/pages/providers/ProviderListPage.ts:77
#: src/pages/sources/SourcesListPage.ts:73
@ -3790,12 +3833,12 @@ msgid "Use global settings"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:150
#: src/pages/sources/plex/PlexSourceForm.ts:124
#: src/pages/sources/plex/PlexSourceForm.ts:159
msgid "Use the user's email address, but deny enrollment when the email address already exists."
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:156
#: src/pages/sources/plex/PlexSourceForm.ts:130
#: src/pages/sources/plex/PlexSourceForm.ts:165
msgid "Use the user's username, but deny enrollment when the username already exists."
msgstr ""
@ -3844,7 +3887,7 @@ msgid "User fields"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts:139
#: src/pages/sources/plex/PlexSourceForm.ts:113
#: src/pages/sources/plex/PlexSourceForm.ts:148
msgid "User matching mode"
msgstr ""
@ -3905,7 +3948,7 @@ msgid "Users added to this group will be superusers."
msgstr ""
#: src/pages/providers/ldap/LDAPProviderForm.ts:86
msgid "Users in the selected group can do search queries."
msgid "Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed."
msgstr ""
#: src/pages/events/EventInfo.ts:108
@ -3948,7 +3991,7 @@ msgstr ""
msgid "Version"
msgstr ""
#: src/pages/outposts/OutpostHealth.ts:60
#: src/pages/outposts/OutpostHealth.ts:59
msgid "Version: {0}"
msgstr ""
@ -3977,7 +4020,7 @@ msgstr ""
msgid "Warning"
msgstr ""
#: src/pages/policies/PolicyListPage.ts:71
#: src/pages/policies/PolicyListPage.ts:72
msgid "Warning: Policy is not assigned."
msgstr ""
@ -4112,7 +4155,7 @@ msgstr ""
msgid "{0} unread"
msgstr ""
#: src/pages/outposts/OutpostHealth.ts:59
#: src/pages/outposts/OutpostHealth.ts:58
msgid "{0}, should be {1}"
msgstr ""

View File

@ -67,7 +67,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
<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.instance?.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>
@ -80,7 +80,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
${until(new EventsApi(DEFAULT_CONFIG).eventsTransportsList({}).then(transports => {
return transports.results.map(transport => {
const selected = Array.from(this.instance?.transports || []).some(su => {
return su.uuid == transport.pk;
return su == transport.pk;
});
return html`<option value=${ifDefined(transport.pk)} ?selected=${selected}>${transport.name}</option>`;
});

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