Compare commits

...

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

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

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

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

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

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

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

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:34:09 +02:00
d862028134 build(deps): bump @typescript-eslint/eslint-plugin in /web (#856)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.22.1 to 4.23.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.23.0/packages/eslint-plugin)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:33:59 +02:00
c19d7c37aa build(deps): bump @sentry/tracing from 6.3.5 to 6.3.6 in /web (#853)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.3.5 to 6.3.6.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.3.5...6.3.6)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:30:55 +02:00
6fb3102d25 build(deps): bump @typescript-eslint/parser in /web (#854)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.22.1 to 4.23.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.23.0/packages/parser)

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

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

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

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

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

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

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

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-09 22:55:30 +02:00
07eef2869f build(deps): bump boto3 from 1.17.68 to 1.17.69 (#851)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.68 to 1.17.69.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.17.68...1.17.69)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-09 22:55:19 +02:00
f7fd31cc84 release: 2021.5.1-rc4 2021-05-09 21:43:38 +02:00
465d9c2b93 ci: use local context for docker build
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 21:42:22 +02:00
04aae8f584 sources/oauth: make secret write_only
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 21:40:25 +02:00
bbca90c93a Merge branch 'next' into version-2021.5 2021-05-09 20:57:23 +02:00
dda1d4e0fb core: add more logs to flow_manager
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 20:27:37 +02:00
f072c600cc lifecycle: use URl for redis on startup to prevent errors with no paswords
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 20:13:58 +02:00
65b8a5bb8d outposts/proxy: redirect to protocol based on X-Forwarded-Proto
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 19:12:35 +02:00
92537a6c8d Merge branch 'next' into version-2021.5 2021-05-09 18:46:26 +02:00
72836ecd9d outposts: default to currently running namespace if possible
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 18:44:32 +02:00
251a97c77e Merge branch 'next' into version-2021.5 2021-05-09 18:13:52 +02:00
7f7046f0e4 outposts: lowercase k8s object names
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 18:13:21 +02:00
20e59158c2 root: add github actions to dependabot
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 18:08:06 +02:00
9a9e55ae32 ci: bump qemu action version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 17:53:57 +02:00
481260a5ca ci: bump checkout actions
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 17:51:56 +02:00
436adcce2e website/docs: fix URL for new chart repo
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 17:32:14 +02:00
cd3f02fd3b release: 2021.5.1-rc3 2021-05-09 17:25:48 +02:00
7abfd24150 ci: only build arm64 and arm
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 17:23:19 +02:00
d3feab9463 release: 2021.5.1-rc2 2021-05-09 16:43:36 +02:00
189427609f ci: fix paths for go build
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 16:41:52 +02:00
d76a9c211a ci: fix web api client not being generated for general build
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 16:41:45 +02:00
ef7d9c4d35 ci: fix mixed environment variables
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 16:37:03 +02:00
70c25692eb release: 2021.5.1-rc1 2021-05-09 16:07:50 +02:00
71b31a2812 ci: fix web api client not being generated before docker build
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 16:06:27 +02:00
d4493c0ee9 web/admin: add new base form to handle refresh events
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 12:59:00 +02:00
3208358a03 web: fix font-color of select inputs in dark mode
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 12:44:22 +02:00
a6a8eddf7c providers/proxy: create ingress for forward_auth /akprox path
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 12:40:44 +02:00
8c0a87b710 outposts: improve logging for outpost controller
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 12:34:44 +02:00
2f88c435fb website/docs: update diagram
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 01:30:29 +02:00
5cad59a9f8 providers/proxy: fix being able to set empty internal_host
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 00:07:34 +02:00
5ac6a6910e outposts: check if traefik CRD exists before attempting to delete
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 21:59:13 +02:00
d751a7fc4c lib: add user attribute "goauthentik.io/user/override-ips" to allow overriding of client ips
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 21:42:31 +02:00
f1fd223bc7 outposts/ldap: fix concurrency issues
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 21:21:53 +02:00
e75712fa09 website/docs: add configuration options for postgres and redis
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 17:14:14 +02:00
1b87375661 lib: add default to config from file://
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 17:13:13 +02:00
545a114450 website/docs: add 2021.5 to sidebar
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 16:42:46 +02:00
02b06838e2 root: remove old helm chart
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 16:25:11 +02:00
6868b7722c outposts: delete old outpost deployment when name or namespace is changed
closes #845

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 16:11:38 +02:00
1e303b515b web/flows: update background for new release
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 15:57:42 +02:00
34a9a6a389 ci: run apt update before installing dependencies
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 14:41:40 +02:00
7a1935b4e2 outposts: fix error on k8s when name has spaces
closes #846

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 14:09:21 +02:00
bf60b33d03 website/docs: add diagram for terminology
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-08 13:58:38 +02:00
9bb50fd556 website: update screenshots
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 14:23:43 +02:00
5e7521915a stages/password: fix configure_flow not being set on initial setup
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 14:08:43 +02:00
7b0cda3a6a website/docs: fix tabs not rendering correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 14:08:30 +02:00
db5279f952 web/admin: default to user active to true
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 14:08:17 +02:00
9fc072e4df outposts: fix lint
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 11:56:44 +02:00
55ea9afeec core: fix dark mode on server-side rendered pages
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 11:53:53 +02:00
9485f0b8cc outpost/ldap: make users and groups OU instead of CN
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 11:46:26 +02:00
fabdb6448f ci: fix arguments for sentry release
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 11:00:13 +02:00
e629079352 Merge branch 'master' into next 2021-05-07 10:07:46 +02:00
e6dfa8294e providers/proxy: use name.namespace for middleware service
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 10:07:30 +02:00
e5a5a5c603 outposts: fix k8s controller not handing Disabled() in static deployment
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 09:52:43 +02:00
4d07da5ffa build(deps): bump golang from 1.16.3 to 1.16.4 (#841)
Bumps golang from 1.16.3 to 1.16.4.

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-07 09:44:09 +02:00
5b4f34fd5f build(deps): bump boto3 from 1.17.67 to 1.17.68 (#843)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.67 to 1.17.68.
- [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.67...1.17.68)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-07 09:44:00 +02:00
2e05047151 build(deps): bump sentry-sdk from 1.0.0 to 1.1.0 (#844)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.0.0 to 1.1.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/1.0.0...1.1.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-07 09:43:48 +02:00
459a6ea437 build(deps): bump golang from 1.16.3 to 1.16.4 in /outpost (#842)
Bumps golang from 1.16.3 to 1.16.4.

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-07 09:43:37 +02:00
ea7f9f291f outposts: create traefikmiddleware if forwardAuth is enabled
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-07 00:01:35 +02:00
241d790e69 stages/user_write: if any connection is being sent in the plan context, save it to the user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 22:10:20 +02:00
83e08f12ae core: fix arguments not being passed in FlowManager
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 22:07:48 +02:00
6526659b51 sources/plex: allow auth for owner (when identifier of source plex token matches)
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 21:50:15 +02:00
6c3b7c8d3e events: handle error when notifications are triggered and no users exist
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 20:13:04 +02:00
d51ecc4554 sources/saml: handle internal error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 20:10:56 +02:00
ef63e35ad2 outposts: improve messaging from controller on k8s
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 20:07:29 +02:00
4e9176ed2e outposts: support different port on container vs exposed port
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 19:59:49 +02:00
d1296e9cc7 outposts: fix deployments referencing the wrong secret
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 19:51:14 +02:00
d85e0593f1 core: set attributes on users which are enrolled via source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 19:35:05 +02:00
20c1f15dc0 web/admin: fix color-scheme for charts and flow diagram
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 16:25:29 +02:00
c864f4e312 root: replace images in compose with alpine
closes #840

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 15:01:37 +02:00
202ad1a3ac root: update security md
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 14:43:19 +02:00
979a5f800e web/admin: show callback URL when creating/updating source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 14:40:02 +02:00
c151faeff6 ci: batch runs in azure devops
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 14:02:59 +02:00
b3a3852a54 core: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 13:58:58 +02:00
e401b4e74e web/admin: fix naming of charts on overview page
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 13:51:54 +02:00
9538ad5710 web/admin: show users and groups as chart
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 13:44:53 +02:00
49bf82a0a4 core: add user filter by superuser status
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 13:44:42 +02:00
e6fdec4c8e Merge branch 'master' into next 2021-05-06 13:16:22 +02:00
73b87a5e3d events: fix error in API when specifying max_n
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 13:15:54 +02:00
303b847cdc web/admin: rewrite overview page
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-06 13:15:27 +02:00
0386c0dd7b build(deps): bump lit-element from 2.5.0 to 2.5.1 in /web (#834)
Bumps [lit-element](https://github.com/lit/lit-element) from 2.5.0 to 2.5.1.
- [Release notes](https://github.com/lit/lit-element/releases)
- [Changelog](https://github.com/lit/lit-element/blob/master/CHANGELOG.md)
- [Commits](https://github.com/lit/lit-element/commits)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 11:00:42 +02:00
7f1b9cdeb2 build(deps): bump django from 3.2.1 to 3.2.2 (#839)
Bumps [django](https://github.com/django/django) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.2.1...3.2.2)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 11:00:28 +02:00
252bb04dd3 build(deps): bump boto3 from 1.17.66 to 1.17.67 (#838) 2021-05-06 08:44:21 +02:00
3fbcfb48fb build(deps): bump @types/codemirror from 0.0.109 to 5.60.0 in /web (#833) 2021-05-06 08:43:59 +02:00
69f7198976 build(deps): bump postcss from 8.2.13 to 8.2.14 in /website (#835) 2021-05-06 08:43:29 +02:00
c74c8b2083 build(deps): bump lit-html from 1.4.0 to 1.4.1 in /web (#836) 2021-05-06 08:43:10 +02:00
63d4f598e4 build(deps): bump babel-plugin-macros from 3.0.1 to 3.1.0 in /web (#837) 2021-05-06 08:42:54 +02:00
ded6b6f937 web/admin: refactor chart component to allow setting of general chart data
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 22:15:11 +02:00
225099b1a1 web/admin: fix error when viewing ldap provider
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 21:57:13 +02:00
6b7a32548d web/admin: change icon for aggregate card
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 21:55:11 +02:00
c71d415456 web/admin: load plex servers on load
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 21:15:26 +02:00
c03f0d1d7c ci: fix names for docker images during release
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 21:11:18 +02:00
ac9cac302c outposts: fix outpost state showing last time without version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 20:49:13 +02:00
701c140cfd providers/proxy: fix logic error for ingress lookup
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 20:28:13 +02:00
ca5761652c lifecycle: show errors when initial db check fails
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 20:15:01 +02:00
553872e8dd website: fix layout on mobile
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 20:05:07 +02:00
adc9b67a9c website/docs: move configuration to its own document
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 19:52:07 +02:00
fa2ff5fc2b sources/plex: save user's plex token, add option to allow friends
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 19:37:59 +02:00
d5cab5d580 sources/plex: fix default for client_id
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 19:23:51 +02:00
9e3b5d313b web/admin: rewrite sidebar to use full components, switch to categories
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 19:14:37 +02:00
be8b2bf6f6 providers/proxy: don't create ingress for domains which use forwardAuth, don't create ingress at all if all providers are forward auth
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 17:53:12 +02:00
3f8cd7ff13 website/docs: link correct docker-compose file
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 17:30:44 +02:00
b266a2cdfb outposts: make k8s service type configurable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 15:37:56 +02:00
9a15a66d85 outposts: make k8s object naming configurable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 15:36:27 +02:00
446f104c90 core: add user UID to API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 11:54:28 +02:00
2cad9a3d07 website/docs: add LDAP Outpost docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 11:48:07 +02:00
ee48b8c225 Merge pull request #828 from goauthentik/dependabot/npm_and_yarn/web/typescript-eslint/parser-4.22.1
build(deps): bump @typescript-eslint/parser from 4.22.0 to 4.22.1 in /web
2021-05-05 11:09:08 +02:00
a91649a7d4 build(deps): bump @typescript-eslint/parser in /web
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.22.0 to 4.22.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.22.1/packages/parser)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-05 08:53:54 +00:00
ca89201bd8 Outpost LDAP (#784)
* outposts: initial ldap outpost implementation

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts: add LDAP Binding using flows

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* core: add API to check access to single application by slug

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/ldap: check application access

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* providers/ldap: add LDAP provider

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/ldap: add ability to use multiple providers on the same outpost

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: add UI for LDAP Provider

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/ldap: fix linting

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/ldap: add controllers

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts: fix type not being configurable

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/ldap: use authorization_flow instead of separate field

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/ldap: add dockerfile

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* providers/ldap: fix lint error

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* core: add groups to users

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* providers/ldap: add search_group to limit who can do search requests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/ldap: improve logging,return success for empty DN

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts: allow outposts to have non-object specific permissions

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/ldap: use forked version of ldap library

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/ldap: save user DN to determine who can search

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* */api: fix lookups per user

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: only show plex servers you own

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* lib: add support for file:// protocol in config file

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: hide oauth client secret if not updating

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outpost/ldap: check access based on Group Membership

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* core: show users and groups when user has overall user permissions

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* lib: handle errors when reading config from file://

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: fix package json failing

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* ci: bump node spec to 16x for npm version and lockfile v2

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 10:40:56 +02:00
e3a8fc0746 build(deps): bump @typescript-eslint/eslint-plugin in /web (#829)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.22.0 to 4.22.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.22.1/packages/eslint-plugin)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-05 10:13:10 +02:00
5e3a6b802b build(deps): bump rollup from 2.46.0 to 2.47.0 in /web (#830)
Bumps [rollup](https://github.com/rollup/rollup) from 2.46.0 to 2.47.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.46.0...v2.47.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-05 10:12:59 +02:00
e8d9f992b9 build(deps): bump boto3 from 1.17.65 to 1.17.66 (#831)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.65 to 1.17.66.
- [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.65...1.17.66)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-05 10:12:48 +02:00
260b2c8ca8 ci: bump node spec to 16x for npm version and lockfile v2
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 09:44:15 +02:00
751e77fa9e web: fix package json failing
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 01:24:10 +02:00
86c2a5d69d lib: handle errors when reading config from file://
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 01:03:00 +02:00
1a02049104 core: show users and groups when user has overall user permissions
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 01:02:47 +02:00
32934fcd38 outpost/ldap: check access based on Group Membership
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-05 00:03:19 +02:00
d84d7c26ca Merge branch 'master' into outpost-ldap 2021-05-04 23:34:31 +02:00
2f6e6a3123 core: improve messaging when flow manager denied request
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 23:30:21 +02:00
36b674349a Merge branch 'master' into next 2021-05-04 23:28:04 +02:00
038ef67745 build(deps-dev): bump pytest from 6.2.3 to 6.2.4 (#826)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.3 to 6.2.4.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.2.3...6.2.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-04 23:14:06 +02:00
53831fa354 build(deps): bump boto3 from 1.17.64 to 1.17.65 (#827)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.64 to 1.17.65.
- [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.64...1.17.65)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-04 23:13:56 +02:00
be39673f29 Merge pull request #825 from goauthentik/dependabot/pip/django-3.2.1
build(deps): bump django from 3.2 to 3.2.1
2021-05-04 23:13:46 +02:00
0f8dbfcc9c web/admin: hide oauth client secret if not updating
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 22:58:52 +02:00
ba57bf4fa2 lib: add support for file:// protocol in config file
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 22:53:59 +02:00
b1c9126832 web/admin: only show plex servers you own
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 22:51:52 +02:00
e674f03064 */api: fix lookups per user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 21:58:20 +02:00
08451c15f4 outposts/ldap: save user DN to determine who can search
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 21:49:15 +02:00
99d161e212 Merge branch 'master' into outpost-ldap
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

# Conflicts:
#	authentik/core/api/users.py
#	authentik/policies/event_matcher/migrations/0013_alter_eventmatcherpolicy_app.py
2021-05-04 21:02:20 +02:00
940ccf9ea8 website/docs: fix formatting for release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 20:13:38 +02:00
08cce2ca4e website/docs: update release notes for next
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 18:50:13 +02:00
4acbda2b77 core: improve messaging on flow_manager, authenticate user when they linked their account after not having been authenticateed
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 18:49:27 +02:00
83cfb5f8c2 stages/email: improve error handling
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 18:30:23 +02:00
0d370ef0a9 web/admin: filter out service accounts by default
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 18:23:13 +02:00
a335ca0895 web/admin: use history.replaceState in Tabs to prevent double history entries
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 18:13:20 +02:00
8a666535a8 website/docs: update container explanation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 17:49:21 +02:00
e6431593f7 web/admin: auto-select keypair in oauth provider creation when only 1 keypair exists
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 17:09:04 +02:00
928c2bf0d6 web/admin: add launch button to application view page
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 17:08:46 +02:00
68388e9551 helm: fix typo
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 16:27:05 +02:00
5d26fa0403 gproxy: add sentry integration
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 14:28:48 +02:00
42f9ba8efe gproxy: load default config file for debug and listen statements
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 14:10:34 +02:00
0440ad7c09 web/admin: add missing plex source view
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 12:09:35 +02:00
3ebc531ae2 web/admin/sources: fix userMatchingMode not being configurable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 12:02:16 +02:00
ca3b5fa2a2 Merge pull request #822 from goauthentik/go-proxy
Go proxy
2021-05-04 11:41:17 +02:00
0f0a5b0621 ci: fix API not being generated for server build
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-04 10:58:37 +02:00
51835887ab build(deps): bump @babel/preset-env from 7.14.0 to 7.14.1 in /web (#823)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.0 to 7.14.1.
- [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.1/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-04 10:44:03 +02:00
09bcbcc2ac build(deps): bump boto3 from 1.17.62 to 1.17.64
Bumps [boto3](https://github.com/boto/boto3) from 1.17.62 to 1.17.64.
- [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.62...1.17.64)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-04 10:44:03 +02:00
8a76d6a21b build(deps): bump @babel/preset-env from 7.14.0 to 7.14.1 in /web (#823)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.14.0 to 7.14.1.
- [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.1/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-04 09:51:21 +02:00
48ab436444 Merge pull request #824 from goauthentik/dependabot/pip/boto3-1.17.64
build(deps): bump boto3 from 1.17.62 to 1.17.64
2021-05-04 09:51:10 +02:00
18a53a9e23 build(deps): bump boto3 from 1.17.62 to 1.17.64
Bumps [boto3](https://github.com/boto/boto3) from 1.17.62 to 1.17.64.
- [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.62...1.17.64)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-04 04:35:31 +00:00
6725569ba8 gproxy: listen on tls
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 23:19:22 +02:00
812be495a5 Merge branch 'master' into go-proxy 2021-05-03 22:53:33 +02:00
dbc3df1f63 events: handle error when notification rule doesn't exist during task
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 22:52:39 +02:00
07b001bc2b Merge pull request #814 from goauthentik/plex-auth
sources/plex: rewrite plex source
2021-05-03 22:46:37 +02:00
c012bed379 web: bump CI pipeline to node 14
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 22:13:23 +02:00
d330e9ee7f web/flows: fix rendering for plex login
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 22:08:25 +02:00
be21a5d172 sources/plex: add general tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 21:55:55 +02:00
ea2f623955 tests/e2e: update e2e tests for new source login button
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 21:40:45 +02:00
6fc38436f4 sources/plex: set better defaults on model
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 21:23:13 +02:00
35faf269db sources: rewrite onboarding
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 20:27:52 +02:00
e56c3fc54c Merge branch 'master' into plex-auth 2021-05-03 18:28:53 +02:00
5891fb3ad6 root: fix redis not being set to restart unless-stopped
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 18:28:46 +02:00
1041718e27 sources/saml: fix redirect url dropping non-standard ports
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 14:51:46 +02:00
2507c0eec9 stages/invitation: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 12:44:19 +02:00
5ea9601062 build(deps): bump @sentry/tracing from 6.3.4 to 6.3.5 in /web (#819)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.3.4 to 6.3.5.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.3.4...6.3.5)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-03 12:43:37 +02:00
c0e6a6c614 website/docs: add wip docs for next version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 10:00:14 +02:00
4523550422 stages/invitation: add single_use flag to delete invitation after use
closes #821

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 09:52:38 +02:00
988cf15b71 root: initial go proxy, update compose and helm
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-03 09:39:09 +02:00
6ae660aea4 build(deps): bump boto3 from 1.17.61 to 1.17.62 (#820) 2021-05-03 08:36:53 +02:00
f201ce8059 build(deps): bump chart.js from 3.2.0 to 3.2.1 in /web (#818) 2021-05-03 08:36:27 +02:00
59624ed45c build(deps): bump lit-element from 2.4.0 to 2.5.0 in /web (#817) 2021-05-03 08:36:12 +02:00
3e78baf2d7 build(deps): bump @docusaurus/preset-classic in /website (#816) 2021-05-03 08:36:00 +02:00
08c67b2a2c build(deps): bump @sentry/browser from 6.3.4 to 6.3.5 in /web (#815) 2021-05-03 08:35:39 +02:00
01d29134b9 sources/plex: add API to redeem token
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-02 16:47:20 +02:00
55250e88e5 sources/*: rewrite UILoginButton to return challenge instead
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-02 16:46:27 +02:00
f1b100c8a5 sources/plex: initial plex source implementation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-02 14:43:51 +02:00
19708bc67b core: add additional_data to UILoginButton to pass additional data
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-02 14:43:26 +02:00
40a885aaaa web/admin: add collapse button to sidebar header on mobile viewport
closes #813

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-02 12:48:45 +02:00
c529340d6c *: fix title not being set correctly for server-side rendered views
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-02 12:22:50 +02:00
c317efa14c Merge branch 'master' into outpost-ldap 2021-05-01 00:26:55 +02:00
379fcf9c1f sources/saml: fix error ValueError while decoding XML
closes #812

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-01 00:18:57 +02:00
e10a7b48b7 sources/saml: fix Redirect bindings when SSO Url already has query params
related to #812

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-30 23:44:04 +02:00
3e666de91d outposts: fix formatting of image name
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-30 16:52:28 +02:00
333758d91f crypto: handle encrypted private keys
closes #811

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-30 15:25:42 +02:00
50678a9e2e build(deps): bump @sentry/tracing from 6.3.3 to 6.3.4 in /web (#809)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.3.3 to 6.3.4.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.3.3...6.3.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-30 10:36:58 +02:00
eb8f52b870 stages/identification: fix tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-30 10:15:27 +02:00
3ee90712b2 build(deps): bump @types/grecaptcha from 3.0.1 to 3.0.2 in /web (#807)
Bumps [@types/grecaptcha](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/grecaptcha) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/grecaptcha)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-30 09:37:48 +02:00
e4eadf8080 build(deps): bump rollup from 2.45.2 to 2.46.0 in /web (#806)
Bumps [rollup](https://github.com/rollup/rollup) from 2.45.2 to 2.46.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.45.2...v2.46.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-30 09:37:30 +02:00
26ebaf16fc build(deps): bump @babel/preset-env from 7.13.15 to 7.14.0 in /web (#808)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.13.15 to 7.14.0.
- [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.0/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-04-30 09:37:21 +02:00
d0ed372af0 build(deps): bump @sentry/browser from 6.3.3 to 6.3.4 in /web (#805)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.3.3 to 6.3.4.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.3.3...6.3.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-30 09:37:09 +02:00
cc8b2d7dfe build(deps): bump @babel/core from 7.13.16 to 7.14.0 in /web (#804)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.16 to 7.14.0.
- [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.0/packages/babel-core)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-30 09:36:59 +02:00
61a212371f build(deps): bump boto3 from 1.17.60 to 1.17.61 (#810)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.60 to 1.17.61.
- [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.60...1.17.61)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-30 09:36:40 +02:00
9ce49c2089 stages/identification: fix unused import
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-30 09:34:33 +02:00
34c45900c2 stages/identification: allow selection of no user fields to only allow login via sources
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-30 01:07:37 +02:00
bf7d110af3 Merge branch 'version-2021.4'
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

# Conflicts:
#	.github/workflows/release.yml
#	helm/README.md
#	helm/values.yaml
#	website/docs/installation/kubernetes.md
2021-04-29 23:50:52 +02:00
e7b498e8b4 outposts/ldap: use forked version of ldap library
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-29 21:06:30 +02:00
b55cb2b40c Merge branch 'master' into outpost-ldap 2021-04-29 20:13:47 +02:00
25c001f2cd outposts: allow better configuration of outpost image name
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-29 20:07:53 +02:00
2a409215d3 outpost: forwardAuth mode (#790) 2021-04-29 18:17:10 +02:00
ad8ee83697 root: use upstream sentry action for release
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-29 16:14:03 +02:00
1efd09fcd5 website/docs: update release notes for 2021.4.5
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-29 15:26:42 +02:00
35f0e6b88d lib: don't send 404 errors to sentry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-29 15:25:54 +02:00
bb2c4423b0 core: fix text color of error pages not being white
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-29 15:17:10 +02:00
ad9f29566b build(deps): bump @sentry/tracing from 6.3.1 to 6.3.3 in /web (#798)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.3.1 to 6.3.3.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.3.1...6.3.3)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-29 10:30:50 +02:00
e76bb6bc13 build(deps): bump pyjwt from 2.0.1 to 2.1.0 (#801) 2021-04-29 07:31:40 +02:00
a68642779d build(deps): bump lit-html from 1.3.0 to 1.4.0 in /web (#797) 2021-04-29 07:31:04 +02:00
3c04fcaa9f build(deps): bump django-otp from 1.0.3 to 1.0.4 (#800) 2021-04-29 07:30:54 +02:00
5955d28073 build(deps): bump boto3 from 1.17.59 to 1.17.60 (#799) 2021-04-29 07:30:23 +02:00
a6fb6161d7 build(deps): bump @sentry/browser from 6.3.1 to 6.3.3 in /web (#796) 2021-04-29 07:30:06 +02:00
6b0e0610c6 website/docs: add release notes for 2021.4.5
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-28 22:51:22 +02:00
d7631e8af0 stages/invitation: accept token from prompt_data
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-28 22:39:06 +02:00
6e625f7400 stages/invitation: fix token not being loaded correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-28 22:13:54 +02:00
f54ead2b45 web: fix text-colour for form help text
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-28 21:55:01 +02:00
c4e4e17f93 providers/oauth2: add access_code_validity (#795)
closes #794

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-28 21:03:43 +02:00
43c87f87c3 root: use docker action during release
closes #738

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-28 21:03:13 +02:00
4da0c81f44 root: use docker action to login
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-28 20:58:11 +02:00
9b70aaa717 outposts: only kill docker container if its running
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-28 18:32:16 +02:00
5769eb277c website/admin: fix sessionDuration not updated on UserLoginStageForm
closes #793

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-28 09:16:04 +02:00
26f60b3e85 build(deps): bump @docusaurus/preset-classic in /website (#791)
Bumps [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) from 2.0.0-alpha.73 to 2.0.0-alpha.74.
- [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-alpha.74/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-04-28 09:10:57 +02:00
7d8ed06539 build(deps): bump boto3 from 1.17.58 to 1.17.59 (#792)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.58 to 1.17.59.
- [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.58...1.17.59)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-28 09:09:35 +02:00
4d858c64e0 Merge branch 'master' into outpost-ldap 2021-04-27 17:08:26 +02:00
6f0792ccfe api: remove legacy basic auth for 2021.3 outposts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 17:06:47 +02:00
04f06e00ff api: add tests for permission_required decorator
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 17:04:38 +02:00
776c3128b8 flows: add tests for stage type, component and ui_user_settings
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 16:52:50 +02:00
e9e0992dce root: add middleware to properly report websocket connection to sentry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 16:21:44 +02:00
69af788b0f web: ignore network errors for sentry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 15:54:57 +02:00
ceace0282b web/admin: don't show docker certs as required
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 15:43:40 +02:00
ccef7b4233 *: make logger not use .error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 15:43:26 +02:00
cad6c42fdd lib: add more tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 15:43:11 +02:00
d2abe6d455 stages/email: catch ValueError when global email settings are invalid
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 15:20:09 +02:00
68d120b3b4 sources/oauth: add tests for google type
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 15:19:54 +02:00
48c0c0baca */api: simplify lookups for per-user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 14:53:01 +02:00
7b29a1e485 stages/user_login: add tests for explicit session length
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 14:52:42 +02:00
fe28d216fe providers/oauth2: always test JWT keys in tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-27 14:07:04 +02:00
e36fb6641e Merge branch 'master' into outpost-ldap 2021-04-27 09:27:11 +02:00
972471ce79 build(deps): bump boto3 from 1.17.57 to 1.17.58 (#788)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.57 to 1.17.58.
- [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.57...1.17.58)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-27 09:18:13 +02:00
38edd76949 build(deps): bump postcss from 8.2.12 to 8.2.13 in /website (#787)
Bumps [postcss](https://github.com/postcss/postcss) from 8.2.12 to 8.2.13.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.2.12...8.2.13)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-27 09:17:46 +02:00
cd07c12c1b build(deps-dev): bump pylint from 2.8.1 to 2.8.2 (#789)
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.8.1 to 2.8.2.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/master/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/pylint-2.8.1...v2.8.2)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-27 09:17:21 +02:00
3ce8b836dc outposts: allow outposts to have non-object specific permissions
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 23:28:26 +02:00
d27dfcc1e3 outposts/ldap: improve logging,return success for empty DN
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 23:25:31 +02:00
1d5958a78f providers/ldap: add search_group to limit who can do search requests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 23:25:03 +02:00
b6e0a1d8f4 website: remove slot from api browser
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 19:51:47 +02:00
2a122845d9 core: add groups to users
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 19:51:24 +02:00
21c7787eed web/flows: fix redirect loop when sentry is enabled
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 17:45:09 +02:00
fae4d34131 Merge branch 'master' into outpost-ldap 2021-04-26 17:11:50 +02:00
7ff7bfeb58 core: fix incorrect styling for bse_full template
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 16:44:13 +02:00
983604265b Merge branch 'master' into outpost-ldap 2021-04-26 15:55:52 +02:00
f8d6daa928 root: unlock pylint again
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 15:55:36 +02:00
6fc26aca72 build(deps-dev): bump pylint-django from 2.4.3 to 2.4.4 (#786)
Bumps [pylint-django](https://github.com/PyCQA/pylint-django) from 2.4.3 to 2.4.4.
- [Release notes](https://github.com/PyCQA/pylint-django/releases)
- [Changelog](https://github.com/PyCQA/pylint-django/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/PyCQA/pylint-django/compare/v2.4.3...v2.4.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-26 15:51:00 +02:00
29da7dd8d6 providers/ldap: fix lint error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 15:49:19 +02:00
91ca90f700 outposts/ldap: add dockerfile
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 15:35:56 +02:00
b3c8ffb96c outposts/ldap: use authorization_flow instead of separate field
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 15:09:41 +02:00
b35d9ae8b0 outposts: fix type not being configurable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 14:45:14 +02:00
302b047f1a outposts/ldap: add controllers
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 14:26:31 +02:00
dcd80c6d63 outposts/ldap: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 12:24:46 +02:00
d741ed430a web/admin: add UI for LDAP Provider
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 12:12:02 +02:00
8436738b0f root: fix transifex link
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 11:56:10 +02:00
5b150657f5 outposts/ldap: add ability to use multiple providers on the same outpost
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 11:53:06 +02:00
f89479caf3 providers/ldap: add LDAP provider
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 11:52:42 +02:00
2f3bf5efe7 outposts/ldap: check application access
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 10:46:53 +02:00
5fb07acf54 core: add API to check access to single application by slug
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 10:08:35 +02:00
99d0d4e8de Merge branch 'master' into outpost-ldap 2021-04-26 09:25:26 +02:00
afc5dc5543 root: lock pylint to < 2.8
https://github.com/PyCQA/pylint-django/issues/323
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 09:24:18 +02:00
9341787fe7 providers/oauth2: replace deprecated jwkest with pyjwt
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-26 00:02:13 +02:00
6c9b3ebd2b outposts: add LDAP Binding using flows
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-25 22:07:12 +02:00
a525d6c3a9 Merge branch 'master' into outpost-ldap 2021-04-25 20:46:02 +02:00
b59b9314e4 web/flows/identification: fix phrasing account recovery
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-25 18:50:19 +02:00
7687b744cc build(deps): bump @sentry/browser from 6.3.0 to 6.3.1 in /web (#780)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.3.0 to 6.3.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.3.0...6.3.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 18:14:42 +02:00
9fb41b8d10 build(deps): bump boto3 from 1.17.54 to 1.17.57 (#783)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.54 to 1.17.57.
- [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.54...1.17.57)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 18:12:34 +02:00
51ffdcb5cb build(deps): bump @sentry/tracing from 6.3.0 to 6.3.1 in /web (#779)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.3.0 to 6.3.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.3.0...6.3.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 18:12:12 +02:00
4d6cd4c57d build(deps): bump @patternfly/patternfly from 4.96.2 to 4.102.2 in /web (#778)
Bumps [@patternfly/patternfly](https://github.com/patternfly/patternfly) from 4.96.2 to 4.102.2.
- [Release notes](https://github.com/patternfly/patternfly/releases)
- [Changelog](https://github.com/patternfly/patternfly/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/patternfly/patternfly/compare/prerelease-v4.96.2...prerelease-v4.102.2)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 18:11:59 +02:00
41c5f01422 build(deps): bump postcss from 8.2.10 to 8.2.12 in /website (#777)
Bumps [postcss](https://github.com/postcss/postcss) from 8.2.10 to 8.2.12.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.2.10...8.2.12)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 18:11:43 +02:00
e567cd5580 build(deps): bump chart.js from 3.1.1 to 3.2.0 in /web (#776)
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v3.1.1...v3.2.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 18:11:30 +02:00
5f81909bab build(deps): bump eslint from 7.24.0 to 7.25.0 in /web (#775)
Bumps [eslint](https://github.com/eslint/eslint) from 7.24.0 to 7.25.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v7.24.0...v7.25.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-25 18:11:21 +02:00
d03b43605e root: fix dependabot 2021-04-25 17:12:48 +02:00
ea187d4e81 root: remove dependabot
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-25 17:12:28 +02:00
502ac51fa7 web: don't enable ShadyDOM on selenium
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-25 01:29:01 +02:00
4bc6fd28d4 web/flows: include ShadyDOM, always enable ShadyDOM for flow interface
improve compatibility with password managers and iOS

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-24 23:32:46 +02:00
820c9e7d06 Merge branch 'master' into outpost-ldap 2021-04-24 22:22:01 +02:00
e5a8714e6a Merge branch 'version-2021.4' 2021-04-24 21:44:40 +02:00
d56d6ea3a9 website/docs: add release notes for 2021.4.4
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-24 21:11:11 +02:00
4f5e1fb86b outposts: initial ldap outpost implementation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 00:30:27 +02:00
418 changed files with 37784 additions and 16268 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2021.4.5
current_version = 2021.5.4
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)
@ -19,26 +19,18 @@ values =
[bumpversion:file:website/docs/installation/docker-compose.md]
[bumpversion:file:website/docs/installation/kubernetes.md]
[bumpversion:file:docker-compose.yml]
[bumpversion:file:helm/values.yaml]
[bumpversion:file:helm/README.md]
[bumpversion:file:helm/Chart.yaml]
[bumpversion:file:.github/workflows/release.yml]
[bumpversion:file:authentik/__init__.py]
[bumpversion:file:internal/constants/constants.go]
[bumpversion:file:outpost/pkg/version.go]
[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

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

View File

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

View File

@ -10,7 +10,10 @@ jobs:
name: Create Release from Tag
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: prepare ts api client
run: |
docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/api --additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0
- name: Pre-release test
run: |
sudo apt-get install -y pwgen
@ -24,26 +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"
- name: Install Helm
run: |
apt update && apt install -y curl
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
- name: Helm package
run: |
helm dependency update helm/
helm package helm/
mv authentik-*.tgz authentik-chart.tgz
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:
@ -51,13 +45,3 @@ jobs:
release_name: Release ${{ steps.get_version.outputs.result }}
draft: true
prerelease: false
- name: Upload packaged Helm Chart
id: upload-release-asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./authentik-chart.tgz
asset_name: authentik-chart.tgz
asset_content_type: application/gzip

2
.gitignore vendored
View File

@ -202,3 +202,5 @@ selenium_screenshots/
backups/
media/
*mmdb
.idea/

View File

@ -1,3 +1,4 @@
# Stage 1: Lock python dependencies
FROM python:3.9-slim-buster as locker
COPY ./Pipfile /app/
@ -9,6 +10,34 @@ RUN pip install pipenv && \
pipenv lock -r > requirements.txt && \
pipenv lock -rd > requirements-dev.txt
# Stage 2: Build webui
FROM node as npm-builder
COPY ./web /static/
ENV NODE_ENV=production
RUN cd /static && npm i --production=false && npm run build
# Stage 3: Build go proxy
FROM golang:1.16.4 AS builder
WORKDIR /work
COPY --from=npm-builder /static/robots.txt /work/web/robots.txt
COPY --from=npm-builder /static/security.txt /work/web/security.txt
COPY --from=npm-builder /static/dist/ /work/web/dist/
COPY --from=npm-builder /static/authentik/ /work/web/authentik/
# RUN ls /work/web/static/authentik/ && exit 1
COPY ./cmd /work/cmd
COPY ./web/static.go /work/web/static.go
COPY ./internal /work/internal
COPY ./go.mod /work/go.mod
COPY ./go.sum /work/go.sum
RUN go build -o /work/authentik ./cmd/server/main.go
# Stage 4: Run
FROM python:3.9-slim-buster
WORKDIR /
@ -19,23 +48,17 @@ ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
RUN apt-get update && \
apt-get install -y --no-install-recommends curl ca-certificates gnupg && \
apt-get install -y --no-install-recommends curl ca-certificates gnupg git 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 && \
apt-get install -y --no-install-recommends postgresql-client-12 postgresql-client-11 build-essential libxmlsec1-dev pkg-config libmaxminddb0 && \
apt-get clean && \
apt-get install -y --no-install-recommends libpq-dev postgresql-client build-essential libxmlsec1-dev pkg-config libmaxminddb0 && \
pip install -r /requirements.txt --no-cache-dir && \
apt-get remove --purge -y build-essential && \
apt-get remove --purge -y build-essential git && \
apt-get autoremove --purge -y && \
# 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 && \
apt-get clean && \
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \
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
@ -44,9 +67,9 @@ COPY ./pyproject.toml /
COPY ./xml /xml
COPY ./manage.py /
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

@ -1,4 +1,7 @@
all: lint-fix lint coverage gen
.SHELLFLAGS += -x -e
PWD = $(shell pwd)
all: lint-fix lint test gen
test-integration:
k3d cluster create || exit 0
@ -8,7 +11,7 @@ test-integration:
test-e2e:
coverage run manage.py test --failfast -v 3 tests/e2e
coverage:
test:
coverage run manage.py test -v 3 authentik
coverage html
coverage report
@ -22,16 +25,16 @@ lint:
bandit -r authentik tests lifecycle -x node_modules
pylint authentik tests lifecycle
gen: coverage
gen:
./manage.py generate_swagger -o swagger.yaml -f yaml
docker run \
--rm -v ${PWD}:/local \
openapitools/openapi-generator-cli generate \
-i /local/swagger.yaml \
-g typescript-fetch \
-o /local/web/api \
--additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0
cd web/api && npx tsc
local-stack:
export AUTHENTIK_TAG=testing
docker build -t beryju/authentik:testng .
docker-compose up -d
docker-compose run --rm server migrate
build-static:
docker-compose -f scripts/ci.docker-compose.yml up -d
docker build -t beryju/authentik-static -f static.Dockerfile --network=scripts_default .
docker-compose -f scripts/ci.docker-compose.yml down -v
run:
go run -v cmd/server/main.go

View File

@ -11,7 +11,7 @@ channels-redis = "*"
dacite = "*"
defusedxml = "*"
django = "*"
django-dbbackup = "*"
django-dbbackup = { git = 'https://github.com/django-dbbackup/django-dbbackup.git', ref = '9d1909c30a3271c8c9c8450add30d6e0b996e145' }
django-filter = "*"
django-guardian = "*"
django-model-utils = "*"
@ -32,7 +32,7 @@ lxml = ">=4.6.3"
packaging = "*"
psycopg2-binary = "*"
pycryptodome = "*"
pyjwkest = "*"
pyjwt = "*"
pyyaml = "*"
requests-oauthlib = "*"
sentry-sdk = "*"
@ -50,7 +50,7 @@ python_version = "3.9"
[dev-packages]
bandit = "*"
black = "==20.8b1"
black = "==21.5b1"
bump2version = "*"
colorama = "*"
coverage = "*"
@ -59,3 +59,4 @@ pylint-django = "*"
pytest = "*"
pytest-django = "*"
selenium = "*"
requests-mock = "*"

447
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "a9d504f00ee8820017f26a4fda2938de456cb72b4bc2f8735fc8c6a6c615d46a"
"sha256": "8a32708c1c04f8da03c817df973de28c37c97ee773f571ce0b3f3f834e1b7094"
},
"pipfile-spec": 6,
"requires": {
@ -88,10 +88,10 @@
},
"attrs": {
"hashes": [
"sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
"sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
],
"version": "==20.3.0"
"version": "==21.2.0"
},
"autobahn": {
"hashes": [
@ -116,25 +116,25 @@
},
"boto3": {
"hashes": [
"sha256:1e55df93aa47a84e2a12a639c7f145e16e6e9ef959542d69d5526d50d2e92692",
"sha256:eab42daaaf68cdad5b112d31dcb0684162098f6558ba7b64156be44f993525fa"
"sha256:13cfe0e3ae1bdc7baf4272b1814a7e760fbb508b19d6ac3f472a6bbd64baad61",
"sha256:ce08b88a2d7a0ad8edb385f84ea4914296fee6813c66ebf0def956d5278de793"
],
"index": "pypi",
"version": "==1.17.54"
"version": "==1.17.73"
},
"botocore": {
"hashes": [
"sha256:20a864fc6570ba11d52532c72c3ccabab5c71a9b4a9418601a313d56f1d2ce5b",
"sha256:37ec76ea2df8609540ba6cb0fe360ae1c589d2e1ee91eb642fd767823f3fcedd"
"sha256:4b4aa58c61d4b125bc6ec1597924b2749e19de8f2c9a374ac087aa2561e71828",
"sha256:69dc0b6fdc0855f5a4f8b1d29c96b9cec44e71054fea0f968e5904d6ccfd4fd9"
],
"version": "==1.20.54"
"version": "==1.20.73"
},
"cachetools": {
"hashes": [
"sha256:1d9d5f567be80f7c07d765e21b814326d78c61eb0c3a637dffc0e5d1796cb2e2",
"sha256:f469e29e7aa4cff64d8de4aad95ce76de8ea1125a16c68e0d93f65c3c3dc92e9"
"sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001",
"sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"
],
"version": "==4.2.1"
"version": "==4.2.2"
},
"cbor2": {
"hashes": [
@ -312,18 +312,15 @@
},
"django": {
"hashes": [
"sha256:0604e84c4fb698a5e53e5857b5aea945b2f19a18f25f10b8748dbdf935788927",
"sha256:21f0f9643722675976004eb683c55d33c05486f94506672df3d6a141546f389d"
"sha256:13ac78dbfd189532cad8f383a27e58e18b3d33f80009ceb476d7fcbfc5dcebd8",
"sha256:7e0a1393d18c16b503663752a8b6790880c5084412618990ce8a81cc908b4962"
],
"index": "pypi",
"version": "==3.2"
"version": "==3.2.3"
},
"django-dbbackup": {
"hashes": [
"sha256:bb109735cae98b64ad084e5b461b7aca2d7b39992f10c9ed9435e3ebb6fb76c8"
],
"index": "pypi",
"version": "==3.3.0"
"git": "https://github.com/django-dbbackup/django-dbbackup.git",
"ref": "9d1909c30a3271c8c9c8450add30d6e0b996e145"
},
"django-filter": {
"hashes": [
@ -351,11 +348,11 @@
},
"django-otp": {
"hashes": [
"sha256:381a15e65293b8b06d47b7d6b306e0b7af2e104137ac92f6c566d3b9b90b6244",
"sha256:f4ab096b424c33ffe69453620356e1b7517f30dfb9ba13bfeaa1d1f20faddc13"
"sha256:75a815747a0542cc5442e3a6396dfd272c49a0866bee2149ac57ecc36ddd3961",
"sha256:cc657a0e7266cda6ab42f861bdc3840ed24f7e441bc7f249916174dd1a6375a0"
],
"index": "pypi",
"version": "==1.0.3"
"version": "==1.0.5"
},
"django-prometheus": {
"hashes": [
@ -429,21 +426,22 @@
},
"geoip2": {
"hashes": [
"sha256:57d8d15de2527e0697bbef44fc16812bba709f03a07ef99297bd56c1df3b1efd",
"sha256:707025542ef076bd8fd80e97138bebdb7812527b2a007d141a27ad98b0370fff"
"sha256:906a1dbf15a179a1af3522970e8420ab15bb3e0afc526942cc179e12146d9c1d",
"sha256:b97b44031fdc463e84eb1316b4f19edd978cb1d78703465fcb1e36dc5a822ba6"
],
"index": "pypi",
"version": "==4.1.0"
"version": "==4.2.0"
},
"google-auth": {
"hashes": [
"sha256:010f011c4e27d3d5eb01106fba6aac39d164842dfcd8709955c4638f5b11ccf8",
"sha256:f30a672a64d91cc2e3137765d088c5deec26416246f7a9e956eaf69a8d7ed49c"
"sha256:588bdb03a41ecb4978472b847881e5518b5d9ec6153d3d679aa127a55e13b39f",
"sha256:9ad25fba07f46a628ad4d0ca09f38dcb262830df2ac95b217f9b0129c9e42206"
],
"version": "==1.29.0"
"version": "==1.30.0"
},
"gunicorn": {
"hashes": [
"sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e",
"sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"
],
"index": "pypi",
@ -504,20 +502,23 @@
},
"httptools": {
"hashes": [
"sha256:0a4b1b2012b28e68306575ad14ad5e9120b34fccd02a81eb08838d7e3bbb48be",
"sha256:3592e854424ec94bd17dc3e0c96a64e459ec4147e6d53c0a42d0ebcef9cb9c5d",
"sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce",
"sha256:56b6393c6ac7abe632f2294da53f30d279130a92e8ae39d8d14ee2e1b05ad1f2",
"sha256:86c6acd66765a934e8730bf0e9dfaac6fdcf2a4334212bd4a0a1c78f16475ca6",
"sha256:96da81e1992be8ac2fd5597bf0283d832287e20cb3cfde8996d2b00356d4e17f",
"sha256:96eb359252aeed57ea5c7b3d79839aaa0382c9d3149f7d24dd7172b1bcecb009",
"sha256:a2719e1d7a84bb131c4f1e0cb79705034b48de6ae486eb5297a139d6a3296dce",
"sha256:ac0aa11e99454b6a66989aa2d44bca41d4e0f968e395a0a8f164b401fefe359a",
"sha256:bc3114b9edbca5a1eb7ae7db698c669eb53eb8afbbebdde116c174925260849c",
"sha256:fa3cd71e31436911a44620473e873a256851e1f53dee56669dae403ba41756a4",
"sha256:fea04e126014169384dee76a153d4573d90d0cbd1d12185da089f73c78390437"
"sha256:07659649fe6b3948b6490825f89abe5eb1cec79ebfaaa0b4bf30f3f33f3c2ba8",
"sha256:08b79e09114e6ab5c3dbf560bba2cb2257ea38cdaeaf99b7cb80d8f92622fcd9",
"sha256:1e35aa179b67086cc600a984924a88589b90793c9c1b260152ca4908786e09df",
"sha256:31629e1f1b89959f8c0927bad12184dc07977dcf71e24f4772934aa490aa199b",
"sha256:851026bd63ec0af7e7592890d97d15c92b62d9e17094353f19a52c8e2b33710a",
"sha256:8fcca4b7efe353b13a24017211334c57d055a6e132c7adffed13a10d28efca57",
"sha256:9abd788465aa46a0f288bd3a99e53edd184177d6379e2098fd6097bb359ad9d6",
"sha256:aebdf0bd7bf7c90ae6b3be458692bf6e9e5b610b501f9f74c7979015a51db4c4",
"sha256:bda99a5723e7eab355ce57435c70853fc137a65aebf2f1cd4d15d96e2956da7b",
"sha256:c1c63d860749841024951b0a78e4dec6f543d23751ef061d6ab60064c7b8b524",
"sha256:c4111a0a8a00eff1e495d43ea5230aaf64968a48ddba8ea2d5f982efae827404",
"sha256:dce59ee45dd6ee6c434346a5ac527c44014326f560866b4b2f414a692ee1aca8",
"sha256:f759717ca1b2ef498c67ba4169c2b33eecf943a89f5329abcff8b89d153eb500",
"sha256:fb7199b8fb0c50a22e77260bb59017e0c075fa80cb03bb2c8692de76e7bb7fe7",
"sha256:fbf7ecd31c39728f251b1c095fd27c84e4d21f60a1d079a0333472ff3ae59d34"
],
"version": "==0.1.1"
"version": "==0.1.2"
},
"hyperlink": {
"hashes": [
@ -556,10 +557,10 @@
},
"jinja2": {
"hashes": [
"sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
"sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
"sha256:2f2de5285cf37f33d33ecd4a9080b75c87cd0c1994d5a9c6df17131ea1f049c6",
"sha256:ea8d7dd814ce9df6de6a761ec7f1cac98afe305b8cdc4aaae4e114b8d8ce24c5"
],
"version": "==2.11.3"
"version": "==3.0.0"
},
"jmespath": {
"hashes": [
@ -584,11 +585,11 @@
},
"kubernetes": {
"hashes": [
"sha256:23c85d8571df8f56e773f1a413bc081537536dc47e2b5e8dc2e6262edb2c57ca",
"sha256:ec52ea01d52e2ec3da255992f7e859f3a76f2bdb51cf65ba8cd71dfc309d8daa"
"sha256:225a95a0aadbd5b645ab389d941a7980db8cdad2a776fde64d1b43fc3299bde9",
"sha256:c69b318696ba797dcf63eb928a8d4370c52319f4140023c502d7dfdf2080eb79"
],
"index": "pypi",
"version": "==12.0.1"
"version": "==17.17.0"
},
"ldap3": {
"hashes": [
@ -603,18 +604,24 @@
"sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d",
"sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3",
"sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2",
"sha256:1b38116b6e628118dea5b2186ee6820ab138dbb1e24a13e478490c7db2f326ae",
"sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f",
"sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927",
"sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3",
"sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7",
"sha256:3082c518be8e97324390614dacd041bb1358c882d77108ca1957ba47738d9d59",
"sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f",
"sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade",
"sha256:36108c73739985979bf302006527cf8a20515ce444ba916281d1c43938b8bb96",
"sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468",
"sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b",
"sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4",
"sha256:4c61b3a0db43a1607d6264166b230438f85bfed02e8cff20c22e564d0faff354",
"sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83",
"sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04",
"sha256:5c8c163396cc0df3fd151b927e74f6e4acd67160d6c33304e805b84293351d16",
"sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791",
"sha256:6f12e1427285008fd32a6025e38e977d44d6382cf28e7201ed10d6c1698d2a9a",
"sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51",
"sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1",
"sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a",
@ -627,10 +634,14 @@
"sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa",
"sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106",
"sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d",
"sha256:c47ff7e0a36d4efac9fd692cfa33fbd0636674c102e9e8d9b26e1b93a94e7617",
"sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4",
"sha256:cdaf11d2bd275bf391b5308f86731e5194a21af45fbaaaf1d9e8147b9160ea92",
"sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0",
"sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4",
"sha256:d916d31fd85b2f78c76400d625076d9124de3e4bda8b016d25a050cc7d603f24",
"sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2",
"sha256:e1cbd3f19a61e27e011e02f9600837b921ac661f0c40560eefb366e4e4fb275e",
"sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0",
"sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654",
"sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2",
@ -642,60 +653,42 @@
},
"markupsafe": {
"hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
"sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f",
"sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014",
"sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85",
"sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850",
"sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1",
"sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
"sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5",
"sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c",
"sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
"sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
"sha256:007dc055dbce5b1104876acee177dbfd18757e19d562cd440182e1f492e96b95",
"sha256:031bf79a27d1c42f69c276d6221172417b47cb4b31cdc73d362a9bf5a1889b9f",
"sha256:161d575fa49395860b75da5135162481768b11208490d5a2143ae6785123e77d",
"sha256:24bbc3507fb6dfff663af7900a631f2aca90d5a445f272db5fc84999fa5718bc",
"sha256:2efaeb1baff547063bad2b2893a8f5e9c459c4624e1a96644bbba08910ae34e0",
"sha256:32200f562daaab472921a11cbb63780f1654552ae49518196fc361ed8e12e901",
"sha256:3261fae28155e5c8634dd7710635fe540a05b58f160cef7713c7700cb9980e66",
"sha256:3b54a9c68995ef4164567e2cd1a5e16db5dac30b2a50c39c82db8d4afaf14f63",
"sha256:3c352ff634e289061711608f5e474ec38dbaa21e3e168820d53d5f4015e5b91b",
"sha256:3fb47f97f1d338b943126e90b79cad50d4fcfa0b80637b5a9f468941dbbd9ce5",
"sha256:441ce2a8c17683d97e06447fcbccbdb057cbf587c78eb75ae43ea7858042fe2c",
"sha256:45535241baa0fc0ba2a43961a1ac7562ca3257f46c4c3e9c0de38b722be41bd1",
"sha256:4aca81a687975b35e3e80bcf9aa93fe10cd57fac37bf18b2314c186095f57e05",
"sha256:4cc563836f13c57f1473bc02d1e01fc37bab70ad4ee6be297d58c1d66bc819bf",
"sha256:4fae0677f712ee090721d8b17f412f1cbceefbf0dc180fe91bab3232f38b4527",
"sha256:58bc9fce3e1557d463ef5cee05391a05745fd95ed660f23c1742c711712c0abb",
"sha256:664832fb88b8162268928df233f4b12a144a0c78b01d38b81bdcf0fc96668ecb",
"sha256:70820a1c96311e02449591cbdf5cd1c6a34d5194d5b55094ab725364375c9eb2",
"sha256:79b2ae94fa991be023832e6bcc00f41dbc8e5fe9d997a02db965831402551730",
"sha256:83cf0228b2f694dcdba1374d5312f2277269d798e65f40344964f642935feac1",
"sha256:87de598edfa2230ff274c4de7fcf24c73ffd96208c8e1912d5d0fee459767d75",
"sha256:8f806bfd0f218477d7c46a11d3e52dc7f5fdfaa981b18202b7dc84bbc287463b",
"sha256:90053234a6479738fd40d155268af631c7fca33365f964f2208867da1349294b",
"sha256:a00dce2d96587651ef4fa192c17e039e8cfab63087c67e7d263a5533c7dad715",
"sha256:a08cd07d3c3c17cd33d9e66ea9dee8f8fc1c48e2d11bd88fd2dc515a602c709b",
"sha256:a19d39b02a24d3082856a5b06490b714a9d4179321225bbf22809ff1e1887cc8",
"sha256:d00a669e4a5bec3ee6dbeeeedd82a405ced19f8aeefb109a012ea88a45afff96",
"sha256:dab0c685f21f4a6c95bfc2afd1e7eae0033b403dd3d8c1b6d13a652ada75b348",
"sha256:df561f65049ed3556e5b52541669310e88713fdae2934845ec3606f283337958",
"sha256:e4570d16f88c7f3032ed909dc9e905a17da14a1c4cfd92608e3fda4cb1208bbd",
"sha256:e77e4b983e2441aff0c0d07ee711110c106b625f440292dfe02a2f60c8218bd6",
"sha256:e79212d09fc0e224d20b43ad44bb0a0a3416d1e04cf6b45fed265114a5d43d20",
"sha256:f58b5ba13a5689ca8317b98439fccfbcc673acaaf8241c1869ceea40f5d585bf",
"sha256:fef86115fdad7ae774720d7103aa776144cf9b66673b4afa9bcaa7af990ed07b"
],
"version": "==1.1.1"
"version": "==2.0.0"
},
"maxminddb": {
"hashes": [
@ -905,41 +898,6 @@
"index": "pypi",
"version": "==3.10.1"
},
"pycryptodomex": {
"hashes": [
"sha256:00a584ee52bf5e27d540129ca9bf7c4a7e7447f24ff4a220faa1304ad0c09bcd",
"sha256:04265a7a84ae002001249bd1de2823bcf46832bd4b58f6965567cb8a07cf4f00",
"sha256:0bd35af6a18b724c689e56f2dbbdd8e409288be71952d271ba3d9614b31d188c",
"sha256:20c45a30f3389148f94edb77f3b216c677a277942f62a2b81a1cc0b6b2dde7fc",
"sha256:2959304d1ce31ab303d9fb5db2b294814278b35154d9b30bf7facc52d6088d0a",
"sha256:36dab7f506948056ceba2d57c1ade74e898401960de697cefc02f3519bd26c1b",
"sha256:37ec1b407ec032c7a0c1fdd2da12813f560bad38ae61ad9c7ce3c0573b3e5e30",
"sha256:3b8eb85b3cc7f083d87978c264d10ff9de3b4bfc46f1c6fdc2792e7d7ebc87bb",
"sha256:3dfce70c4e425607ae87b8eae67c9c7dbba59a33b62d70f79417aef0bc5c735b",
"sha256:418f51c61eab52d9920f4ef468d22c89dab1be5ac796f71cf3802f6a6e667df0",
"sha256:4195604f75cdc1db9bccdb9e44d783add3c817319c30aaff011670c9ed167690",
"sha256:4344ab16faf6c2d9df2b6772995623698fb2d5f114dace4ab2ff335550cf71d5",
"sha256:541cd3e3e252fb19a7b48f420b798b53483302b7fe4d9954c947605d0a263d62",
"sha256:564063e3782474c92cbb333effd06e6eb718471783c6e67f28c63f0fc3ac7b23",
"sha256:72f44b5be46faef2a1bf2a85902511b31f4dd7b01ce0c3978e92edb2cc812a82",
"sha256:8a98e02cbf8f624add45deff444539bf26345b479fc04fa0937b23cd84078d91",
"sha256:940db96449d7b2ebb2c7bf190be1514f3d67914bd37e54e8d30a182bd375a1a9",
"sha256:961333e7ee896651f02d4692242aa36b787b8e8e0baa2256717b2b9d55ae0a3c",
"sha256:9f713ffb4e27b5575bd917c70bbc3f7b348241a351015dbbc514c01b7061ff7e",
"sha256:a6584ae58001d17bb4dc0faa8a426919c2c028ef4d90ceb4191802ca6edb8204",
"sha256:c2b680987f418858e89dbb4f09c8c919ece62811780a27051ace72b2f69fb1be",
"sha256:d8fae5ba3d34c868ae43614e0bd6fb61114b2687ac3255798791ce075d95aece",
"sha256:dbd2c361db939a4252589baa94da4404d45e3fc70da1a31e541644cdf354336e",
"sha256:e090a8609e2095aa86978559b140cf8968af99ee54b8791b29ff804838f29f10",
"sha256:e4a1245e7b846e88ba63e7543483bda61b9acbaee61eadbead5a1ce479d94740",
"sha256:ec9901d19cadb80d9235ee41cc58983f18660314a0eb3fc7b11b0522ac3b6c4a",
"sha256:f2abeb4c4ce7584912f4d637b2c57f23720d35dd2892bfeb1b2c84b6fb7a8c88",
"sha256:f3bb267df679f70a9f40f17d62d22fe12e8b75e490f41807e7560de4d3e6bf9f",
"sha256:f933ecf4cb736c7af60a6a533db2bf569717f2318b265f92907acff1db43bc34",
"sha256:fc9c55dc1ed57db76595f2d19a479fc1c3a1be2c9da8de798a93d286c5f65f38"
],
"version": "==3.10.1"
},
"pyhamcrest": {
"hashes": [
"sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316",
@ -947,12 +905,13 @@
],
"version": "==2.0.2"
},
"pyjwkest": {
"pyjwt": {
"hashes": [
"sha256:5560fd5ba08655f29ff6ad1df1e15dc05abc9d976fcbcec8d2b5167f49b70222"
"sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1",
"sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130"
],
"index": "pypi",
"version": "==1.4.2"
"version": "==2.1.0"
},
"pyopenssl": {
"hashes": [
@ -983,10 +942,10 @@
},
"python-dotenv": {
"hashes": [
"sha256:471b782da0af10da1a80341e8438fca5fadeba2881c54360d5fd8d03d03a4f4a",
"sha256:49782a97c9d641e8a09ae1d9af0856cc587c8d2474919342d5104d85be9890b2"
"sha256:00aa34e92d992e9f8383730816359647f358f4a3be1ba45e5a5cefd27ee91544",
"sha256:b1ae5e9643d5ed987fc57cc2583021e38db531946518130777734f9589b3141f"
],
"version": "==0.17.0"
"version": "==0.17.1"
},
"pytz": {
"hashes": [
@ -1106,33 +1065,33 @@
},
"s3transfer": {
"hashes": [
"sha256:af1af6384bd7fb8208b06480f9be73d0295d965c4c073a5c95ea5b6661dccc18",
"sha256:f3dfd791cad2799403e3c8051810a7ca6ee1d2e630e5d2a8f9649d892bdb3db6"
"sha256:9b3752887a2880690ce628bc263d6d13a3864083aeacff4890c1c9839a5eb0bc",
"sha256:cb022f4b16551edebbb31a377d3f09600dbada7363d8c5db7976e7f47732e1b2"
],
"version": "==0.4.0"
"version": "==0.4.2"
},
"sentry-sdk": {
"hashes": [
"sha256:71de00c9711926816f750bc0f57ef2abbcb1bfbdf5378c601df7ec978f44857a",
"sha256:9221e985f425913204989d0e0e1cbb719e8b7fa10540f1bc509f660c06a34e66"
"sha256:c1227d38dca315ba35182373f129c3e2722e8ed999e52584e6aca7d287870739",
"sha256:c7d380a21281e15be3d9f67a3c4fbb4f800c481d88ff8d8931f39486dd7b4ada"
],
"index": "pypi",
"version": "==1.0.0"
"version": "==1.1.0"
},
"service-identity": {
"hashes": [
"sha256:001c0707759cb3de7e49c078a7c0c9cd12594161d3bf06b9c254fdcb1a60dc36",
"sha256:0858a54aabc5b459d1aafa8a518ed2081a285087f349fe3e55197989232e2e2d"
"sha256:6e6c6086ca271dc11b033d17c3a8bea9f24ebff920c587da090afc9519419d34",
"sha256:f0b0caac3d40627c3c04d7a51b6e06721857a0e10a8775f2d1d7e72901b3a7db"
],
"index": "pypi",
"version": "==18.1.0"
"version": "==21.1.0"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"version": "==1.15.0"
"version": "==1.16.0"
},
"sqlparse": {
"hashes": [
@ -1198,11 +1157,11 @@
},
"typing-extensions": {
"hashes": [
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
],
"version": "==3.7.4.3"
"version": "==3.10.0.0"
},
"uritemplate": {
"hashes": [
@ -1279,10 +1238,10 @@
},
"websocket-client": {
"hashes": [
"sha256:44b5df8f08c74c3d82d28100fdc81f4536809ce98a17f0757557813275fbb663",
"sha256:63509b41d158ae5b7f67eb4ad20fecbb4eee99434e73e140354dc3ff8e09716f"
"sha256:2e50d26ca593f70aba7b13a489435ef88b8fc3b5c5643c1ce8808ff9b40f0b32",
"sha256:d376bd60eace9d437ab6d7ee16f4ab4e821c9dae591e1b783c58ebd8aaf80c5c"
],
"version": "==0.58.0"
"version": "==0.59.0"
},
"websockets": {
"hashes": [
@ -1313,22 +1272,20 @@
},
"xmlsec": {
"hashes": [
"sha256:252f79ed4482d6eefcca62c3bfc99b8d95c07abd846262d854a207ec4d67fac5",
"sha256:31884dc97cc34cf1681a0f239f613969e61f9a01f4c2d2a62e53d68216fe42d6",
"sha256:32a669dfe447bccecdb4ef79221c0452ce6dad919f3a75daf512792141a54dac",
"sha256:3d13d7b6cb921dbc4d60d00ad00081a038df73a1e69f5bcc3695deb1bf2093b0",
"sha256:5e2f263a21fd146859911479ec35e40a57f519e650f56c775f91367d2a1b6e15",
"sha256:61076be98da4c7cf842a78aa3f129a5039f2ba4992e02480eefe78028d317698",
"sha256:69d7f965d6b74b3266f7baa99a0377d9c76acbf26c615b4ee8d2cbe17bf85528",
"sha256:6d8bb24c3a4db398011f394e29b58cd34c9c26d76b772c5d418d8579df127234",
"sha256:6d9d46d1f6b4985023469a1e334cb35c7c8fc6bd9d8b65ca52b923a7a6869c2a",
"sha256:8a7ffdc4f7f760253aa4dd8d2037358eb33915ca1dcf1c2422b19fcf0ab68506",
"sha256:927fc5755bb93dc09275bd5d818811e016290c194012d63f8e6f86b7ece3e468",
"sha256:dcaa084c3700f775eba09d81a1432444f82d9ad6270320c56c1a733d71cceb3a",
"sha256:f59698cc0366395ca79b48b080674973541aae290670c57d88f05d939a4c00da"
"sha256:17d2e66d4e3e601d210eed936b53c3eb44cddaef62f60b5c6ad5c18e948d926c",
"sha256:2bc1b871b49d6580779805a4a1c2d835e834a2fa614fe40cf71931d11a8279cf",
"sha256:52eded125c0d1ab72125105ef061370c6b06ab9bd37e29a61bc2f8a61205bae4",
"sha256:72af9a5a747a5fe6e425d2be10daa43d18307dbe03498df3820fc3cd93daa148",
"sha256:806855d505da24aeb77758a6f373b1473e5ed63bdbe346af90cc6d2b053e4716",
"sha256:8746dd992aaec06ed8ff1615f4a8e2a32258e8af38f9a9f8acf3ee1fb34a5da6",
"sha256:9d52b2b15d42292725e4f9d8a5b040e39cba0a9cd58059ac951e7310d6340bb9",
"sha256:b380f3ebc042f71afab057632481d06e06f1ba4f90047d91ca92612a7d3d487b",
"sha256:be0f475edd8e9c98f57449c97839f6a81946e79e4cccb81e4b5196a2cc40e044",
"sha256:bf3c62d154f2222caf56d897ddfd53fd0aef560d5a2202447d90e015301a0a10",
"sha256:fe6a5f05aba3ff47e105a308482b68f8b0fd80656eb1456a9c1e4de47d2c580f"
],
"index": "pypi",
"version": "==1.3.9"
"version": "==1.3.10"
},
"yarl": {
"hashes": [
@ -1439,17 +1396,17 @@
},
"astroid": {
"hashes": [
"sha256:ad63b8552c70939568966811a088ef0bc880f99a24a00834abd0e3681b514f91",
"sha256:bea3f32799fbb8581f58431c12591bc20ce11cbc90ad82e2ea5717d94f2080d5"
"sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e",
"sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"
],
"version": "==2.5.3"
"version": "==2.5.6"
},
"attrs": {
"hashes": [
"sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
"sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
],
"version": "==20.3.0"
"version": "==21.2.0"
},
"bandit": {
"hashes": [
@ -1461,10 +1418,11 @@
},
"black": {
"hashes": [
"sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"
"sha256:23695358dbcb3deafe7f0a3ad89feee5999a46be5fec21f4f1d108be0bcdb3b1",
"sha256:8a60071a0043876a4ae96e6c69bd3a127dad2c1ca7c8083573eb82f92705d008"
],
"index": "pypi",
"version": "==20.8b1"
"version": "==21.5b1"
},
"bump2version": {
"hashes": [
@ -1474,6 +1432,20 @@
"index": "pypi",
"version": "==1.0.1"
},
"certifi": {
"hashes": [
"sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
"sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
],
"version": "==2020.12.5"
},
"chardet": {
"hashes": [
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
],
"version": "==4.0.0"
},
"click": {
"hashes": [
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
@ -1556,10 +1528,17 @@
},
"gitpython": {
"hashes": [
"sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b",
"sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"
"sha256:29fe82050709760081f588dd50ce83504feddbebdc4da6956d02351552b1c135",
"sha256:ee24bdc93dce357630764db659edaf6b8d664d4ff5447ccfeedd2dc5c253f41e"
],
"version": "==3.1.14"
"version": "==3.1.17"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
"version": "==2.10"
},
"iniconfig": {
"hashes": [
@ -1633,10 +1612,10 @@
},
"pbr": {
"hashes": [
"sha256:5fad80b613c402d5b7df7bd84812548b2a61e9977387a80a5fc5c396492b13c9",
"sha256:b236cde0ac9a6aedd5e3c34517b423cd4fd97ef723849da6b0d2231142d89c00"
"sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd",
"sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"
],
"version": "==5.5.1"
"version": "==5.6.0"
},
"pluggy": {
"hashes": [
@ -1654,19 +1633,19 @@
},
"pylint": {
"hashes": [
"sha256:209d712ec870a0182df034ae19f347e725c1e615b2269519ab58a35b3fcbbe7a",
"sha256:bd38914c7731cdc518634a8d3c5585951302b6e2b6de60fbb3f7a0220e21eeee"
"sha256:586d8fa9b1891f4b725f587ef267abe2a1bad89d6b184520c7f07a253dd6e217",
"sha256:f7e2072654a6b6afdf5e2fb38147d3e2d2d43c89f648637baab63e026481279b"
],
"index": "pypi",
"version": "==2.7.4"
"version": "==2.8.2"
},
"pylint-django": {
"hashes": [
"sha256:a5a4515209a6237d1d390a4a307d53f53baaf4f058ecf4bb556c775d208f6b0d",
"sha256:dc5ed27bb7662d73444ccd15a0b3964ed6ced6cc2712b85db616102062d2ec35"
"sha256:aff49d9602a39c027b4ed7521a041438893205918f405800063b7ff692b7371b",
"sha256:f63f717169b0c2e4e19c28f1c32c28290647330184fcb7427805ae9b6994f3fc"
],
"index": "pypi",
"version": "==2.4.3"
"version": "==2.4.4"
},
"pylint-plugin-utils": {
"hashes": [
@ -1684,19 +1663,19 @@
},
"pytest": {
"hashes": [
"sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634",
"sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"
"sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b",
"sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"
],
"index": "pypi",
"version": "==6.2.3"
"version": "==6.2.4"
},
"pytest-django": {
"hashes": [
"sha256:80f8875226ec4dc0b205f0578072034563879d98d9b1bec143a80b9045716cb0",
"sha256:a51150d8962200250e850c6adcab670779b9c2aa07271471059d1fb92a843fa9"
"sha256:d1c6758a592fb0ef8abaa2fe12dd28858c1dcfc3d466102ffe52aa8934733dca",
"sha256:f96c4556f4e7b15d987dd1dcc1d1526df81d40c1548d31ce840d597ed2be8c46"
],
"index": "pypi",
"version": "==4.2.0"
"version": "==4.3.0"
},
"pyyaml": {
"hashes": [
@ -1779,6 +1758,21 @@
],
"version": "==2021.4.4"
},
"requests": {
"hashes": [
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
],
"version": "==2.25.1"
},
"requests-mock": {
"hashes": [
"sha256:33296f228d8c5df11a7988b741325422480baddfdf5dd9318fd0eb40c3ed8595",
"sha256:5c8ef0254c14a84744be146e9799dc13ebc4f6186058112d9aeed96b131b58e2"
],
"index": "pypi",
"version": "==1.9.2"
},
"selenium": {
"hashes": [
"sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c",
@ -1789,10 +1783,10 @@
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"version": "==1.15.0"
"version": "==1.16.0"
},
"smmap": {
"hashes": [
@ -1815,49 +1809,6 @@
],
"version": "==0.10.2"
},
"typed-ast": {
"hashes": [
"sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace",
"sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff",
"sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266",
"sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528",
"sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6",
"sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808",
"sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4",
"sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363",
"sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341",
"sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04",
"sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41",
"sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e",
"sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3",
"sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899",
"sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805",
"sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c",
"sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c",
"sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39",
"sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a",
"sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3",
"sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7",
"sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f",
"sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075",
"sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0",
"sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40",
"sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428",
"sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927",
"sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3",
"sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f",
"sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"
],
"version": "==1.4.3"
},
"typing-extensions": {
"hashes": [
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
],
"version": "==3.7.4.3"
},
"urllib3": {
"extras": [
"secure"

View File

@ -11,6 +11,7 @@
![Docker pulls](https://img.shields.io/docker/pulls/beryju/authentik.svg?style=flat-square)
![Latest version](https://img.shields.io/docker/v/beryju/authentik?sort=semver&style=flat-square)
![LGTM Grade](https://img.shields.io/lgtm/grade/python/github/goauthentik/authentik?style=flat-square)
[Transifex](https://www.transifex.com/beryjuorg/authentik/)
## What is authentik?

View File

@ -4,8 +4,8 @@
| Version | Supported |
| ---------- | ------------------ |
| 2021.3.x | :white_check_mark: |
| 2021.4.x | :white_check_mark: |
| 2021.5.x | :white_check_mark: |
## Reporting a Vulnerability

View File

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

View File

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

View File

@ -1,5 +1,5 @@
"""API Authentication"""
from base64 import b64decode, b64encode
from base64 import b64decode
from binascii import Error
from typing import Any, Optional, Union
@ -19,14 +19,6 @@ def token_from_header(raw_header: bytes) -> Optional[Token]:
auth_credentials = raw_header.decode()
if auth_credentials == "":
return None
# Legacy, accept basic auth thats fully encoded (2021.3 outposts)
if " " not in auth_credentials:
try:
plain = b64decode(auth_credentials.encode()).decode()
auth_type, body = plain.split()
auth_credentials = f"{auth_type} {b64encode(body.encode()).decode()}"
except (UnicodeDecodeError, Error):
raise AuthenticationFailed("Malformed header")
auth_type, auth_credentials = auth_credentials.split()
if auth_type.lower() not in ["basic", "bearer"]:
LOGGER.debug("Unsupported authentication type, denying", type=auth_type.lower())
@ -50,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]:
@ -62,4 +54,4 @@ class AuthentikTokenAuthentication(BaseAuthentication):
if not token:
return None
return (token.user, None)
return (token.user, None) # pragma: no cover

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

@ -3,7 +3,7 @@
{% load static %}
{% block title %}
authentik API Browser
API Browser - {{ config.authentik.branding.title }}
{% endblock %}
{% block head %}

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

@ -0,0 +1,16 @@
"""Test config API"""
from json import loads
from django.urls import reverse
from rest_framework.test import APITestCase
class TestConfig(APITestCase):
"""Test config API"""
def test_config(self):
"""Test YAML generation"""
response = self.client.get(
reverse("authentik_api:configs-list"),
)
self.assertTrue(loads(response.content.decode()))

View File

@ -0,0 +1,33 @@
"""test decorators api"""
from django.urls import reverse
from guardian.shortcuts import assign_perm
from rest_framework.test import APITestCase
from authentik.core.models import Application, User
class TestAPIDecorators(APITestCase):
"""test decorators api"""
def setUp(self) -> None:
super().setUp()
self.user = User.objects.create(username="test-user")
def test_obj_perm_denied(self):
"""Test object perm denied"""
self.client.force_login(self.user)
app = Application.objects.create(name="denied", slug="denied")
response = self.client.get(
reverse("authentik_api:application-metrics", kwargs={"slug": app.slug})
)
self.assertEqual(response.status_code, 403)
def test_other_perm_denied(self):
"""Test other perm denied"""
self.client.force_login(self.user)
app = Application.objects.create(name="denied", slug="denied")
assign_perm("authentik_core.view_application", self.user, app)
response = self.client.get(
reverse("authentik_api:application-metrics", kwargs={"slug": app.slug})
)
self.assertEqual(response.status_code, 403)

View File

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

View File

@ -47,6 +47,7 @@ from authentik.policies.reputation.api import (
ReputationPolicyViewSet,
UserReputationViewSet,
)
from authentik.providers.ldap.api import LDAPOutpostConfigViewSet, LDAPProviderViewSet
from authentik.providers.oauth2.api.provider import OAuth2ProviderViewSet
from authentik.providers.oauth2.api.scope import ScopeMappingViewSet
from authentik.providers.oauth2.api.tokens import (
@ -63,6 +64,7 @@ from authentik.sources.oauth.api.source import OAuthSourceViewSet
from authentik.sources.oauth.api.source_connection import (
UserOAuthSourceConnectionViewSet,
)
from authentik.sources.plex.api import PlexSourceViewSet
from authentik.sources.saml.api import SAMLSourceViewSet
from authentik.stages.authenticator_static.api import (
AuthenticatorStaticStageViewSet,
@ -120,6 +122,7 @@ router.register(
"outposts/service_connections/kubernetes", KubernetesServiceConnectionViewSet
)
router.register("outposts/proxy", ProxyOutpostConfigViewSet)
router.register("outposts/ldap", LDAPOutpostConfigViewSet)
router.register("flows/instances", FlowViewSet)
router.register("flows/bindings", FlowStageBindingViewSet)
@ -136,6 +139,7 @@ router.register("sources/oauth_user_connections", UserOAuthSourceConnectionViewS
router.register("sources/ldap", LDAPSourceViewSet)
router.register("sources/saml", SAMLSourceViewSet)
router.register("sources/oauth", OAuthSourceViewSet)
router.register("sources/plex", PlexSourceViewSet)
router.register("policies/all", PolicyViewSet)
router.register("policies/bindings", PolicyBindingViewSet)
@ -149,6 +153,7 @@ router.register("policies/reputation/ips", IPReputationViewSet)
router.register("policies/reputation", ReputationPolicyViewSet)
router.register("providers/all", ProviderViewSet)
router.register("providers/ldap", LDAPProviderViewSet)
router.register("providers/proxy", ProxyProviderViewSet)
router.register("providers/oauth2", OAuth2ProviderViewSet)
router.register("providers/saml", SAMLProviderViewSet)
@ -164,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

@ -4,6 +4,7 @@ from typing import Optional
from django.core.cache import cache
from django.db.models import QuerySet
from django.http.response import HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from drf_yasg import openapi
from drf_yasg.utils import no_body, swagger_auto_schema
from rest_framework.decorators import action
@ -22,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()
@ -91,6 +93,25 @@ class ApplicationViewSet(ModelViewSet):
applications.append(application)
return applications
@swagger_auto_schema(
responses={
204: "Access granted",
403: "Access denied",
}
)
@action(detail=True, methods=["GET"])
# pylint: disable=unused-argument
def check_access(self, request: Request, slug: str) -> Response:
"""Check access to a single application by slug"""
# Don't use self.get_object as that checks for view_application permission
# which the user might not have, even if they have access
application = get_object_or_404(Application, slug=slug)
engine = PolicyEngine(application, self.request.user, self.request)
engine.build()
if engine.passing:
return Response(status=204)
return Response(status=403)
@swagger_auto_schema(
manual_parameters=[
openapi.Parameter(
@ -102,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

@ -1,7 +1,9 @@
"""Groups API Viewset"""
from django.db.models.query import QuerySet
from rest_framework.fields import JSONField
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from rest_framework_guardian.filters import ObjectPermissionsFilter
from authentik.core.api.utils import is_dict
from authentik.core.models import Group
@ -26,3 +28,16 @@ class GroupViewSet(ModelViewSet):
search_fields = ["name", "is_superuser"]
filterset_fields = ["name", "is_superuser"]
ordering = ["name"]
def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet:
"""Custom filter_queryset method which ignores guardian, but still supports sorting"""
for backend in list(self.filter_backends):
if backend == ObjectPermissionsFilter:
continue
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
def filter_queryset(self, queryset):
if self.request.user.has_perm("authentik_core.view_group"):
return self._filter_queryset_for_list(queryset)
return super().filter_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

@ -45,6 +45,7 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer):
"verbose_name",
"verbose_name_plural",
"policy_engine_mode",
"user_matching_mode",
]
@ -60,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

@ -1,18 +1,30 @@
"""User API Views"""
from json import loads
from django.db.models.query import QuerySet
from django.http.response import Http404
from django.urls import reverse_lazy
from django.utils.http import urlencode
from django_filters.filters import BooleanFilter, CharFilter
from django_filters.filterset import FilterSet
from drf_yasg.utils import swagger_auto_schema, swagger_serializer_method
from guardian.utils import get_anonymous_user
from rest_framework.decorators import action
from rest_framework.fields import CharField, JSONField, SerializerMethodField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import BooleanField, ModelSerializer
from rest_framework.serializers import (
BooleanField,
ListSerializer,
ModelSerializer,
ValidationError,
)
from rest_framework.viewsets import ModelViewSet
from rest_framework_guardian.filters import ObjectPermissionsFilter
from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h
from authentik.api.decorators import permission_required
from authentik.core.api.groups import GroupSerializer
from authentik.core.api.utils import LinkSerializer, PassiveSerializer, is_dict
from authentik.core.middleware import (
SESSION_IMPERSONATE_ORIGINAL_USER,
@ -29,6 +41,8 @@ class UserSerializer(ModelSerializer):
is_superuser = BooleanField(read_only=True)
avatar = CharField(read_only=True)
attributes = JSONField(validators=[is_dict], required=False)
groups = ListSerializer(child=GroupSerializer(), read_only=True, source="ak_groups")
uid = CharField(read_only=True)
class Meta:
@ -40,9 +54,11 @@ class UserSerializer(ModelSerializer):
"is_active",
"last_login",
"is_superuser",
"groups",
"email",
"avatar",
"attributes",
"uid",
]
@ -84,15 +100,46 @@ class UserMetricsSerializer(PassiveSerializer):
)
class UsersFilter(FilterSet):
"""Filter for users"""
attributes = CharFilter(
field_name="attributes",
lookup_expr="",
label="Attributes",
method="filter_attributes",
)
is_superuser = BooleanFilter(field_name="ak_groups", lookup_expr="is_superuser")
# pylint: disable=unused-argument
def filter_attributes(self, queryset, name, value):
"""Filter attributes by query args"""
try:
value = loads(value)
except ValueError:
raise ValidationError(detail="filter: failed to parse JSON")
if not isinstance(value, dict):
raise ValidationError(detail="filter: value must be key:value mapping")
qs = {}
for key, _value in value.items():
qs[f"attributes__{key}"] = _value
return queryset.filter(**qs)
class Meta:
model = User
fields = ["username", "name", "is_active", "is_superuser", "attributes"]
class UserViewSet(ModelViewSet):
"""User Viewset"""
queryset = User.objects.none()
serializer_class = UserSerializer
search_fields = ["username", "name", "is_active"]
filterset_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)})
@ -144,3 +191,16 @@ class UserViewSet(ModelViewSet):
reverse_lazy("authentik_flows:default-recovery") + f"?{querystring}"
)
return Response({"link": link})
def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet:
"""Custom filter_queryset method which ignores guardian, but still supports sorting"""
for backend in list(self.filter_backends):
if backend == ObjectPermissionsFilter:
continue
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
def filter_queryset(self, queryset):
if self.request.user.has_perm("authentik_core.view_group"):
return self._filter_queryset_for_list(queryset)
return super().filter_queryset(queryset)

View File

@ -20,10 +20,12 @@ def is_dict(value: Any):
class PassiveSerializer(Serializer):
"""Base serializer class which doesn't implement create/update methods"""
def create(self, validated_data: dict) -> Model:
def create(self, validated_data: dict) -> Model: # pragma: no cover
return Model()
def update(self, instance: Model, validated_data: dict) -> Model:
def update(
self, instance: Model, validated_data: dict
) -> Model: # pragma: no cover
return Model()

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,40 @@
# Generated by Django 3.2 on 2021-05-03 17:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0019_source_managed"),
]
operations = [
migrations.AddField(
model_name="source",
name="user_matching_mode",
field=models.TextField(
choices=[
("identifier", "Use the source-specific identifier"),
(
"email_link",
"Link to a user with identical email address. Can have security implications when a source doesn't validate email addresses.",
),
(
"email_deny",
"Use the user's email address, but deny enrollment when the email address already exists.",
),
(
"username_link",
"Link to a user with identical username address. Can have security implications when a username is used with another source.",
),
(
"username_deny",
"Use the user's username, but deny enrollment when the username already exists.",
),
],
default="identifier",
help_text="How the source determines if an existing user should be authenticated or a new user enrolled.",
),
),
]

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

@ -34,6 +34,7 @@ from authentik.policies.models import PolicyBindingModel
LOGGER = get_logger()
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
USER_ATTRIBUTE_SA = "goauthentik.io/user/service-account"
USER_ATTRIBUTE_SOURCES = "goauthentik.io/user/sources"
GRAVATAR_URL = "https://secure.gravatar.com"
DEFAULT_AVATAR = static("dist/assets/images/user_default.png")
@ -206,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
)
@ -240,6 +243,30 @@ class Application(PolicyBindingModel):
verbose_name_plural = _("Applications")
class SourceUserMatchingModes(models.TextChoices):
"""Different modes a source can handle new/returning users"""
IDENTIFIER = "identifier", _("Use the source-specific identifier")
EMAIL_LINK = "email_link", _(
(
"Link to a user with identical email address. Can have security implications "
"when a source doesn't validate email addresses."
)
)
EMAIL_DENY = "email_deny", _(
"Use the user's email address, but deny enrollment when the email address already exists."
)
USERNAME_LINK = "username_link", _(
(
"Link to a user with identical username address. Can have security implications "
"when a username is used with another source."
)
)
USERNAME_DENY = "username_deny", _(
"Use the user's username, but deny enrollment when the username already exists."
)
class Source(ManagedModel, SerializerModel, PolicyBindingModel):
"""Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server"""
@ -272,6 +299,17 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
related_name="source_enrollment",
)
user_matching_mode = models.TextField(
choices=SourceUserMatchingModes.choices,
default=SourceUserMatchingModes.IDENTIFIER,
help_text=_(
(
"How the source determines if an existing user should be authenticated or "
"a new user enrolled."
)
),
)
objects = InheritanceManager()
@property
@ -301,6 +339,8 @@ class UserSourceConnection(CreatedUpdatedModel):
user = models.ForeignKey(User, on_delete=models.CASCADE)
source = models.ForeignKey(Source, on_delete=models.CASCADE)
objects = InheritanceManager()
class Meta:
unique_together = (("user", "source"),)

View File

View File

@ -0,0 +1,286 @@
"""Source decision helper"""
from enum import Enum
from typing import Any, Optional, Type
from django.contrib import messages
from django.db import IntegrityError
from django.db.models.query_utils import Q
from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import gettext as _
from structlog.stdlib import get_logger
from authentik.core.models import (
Source,
SourceUserMatchingModes,
User,
UserSourceConnection,
)
from authentik.core.sources.stage import (
PLAN_CONTEXT_SOURCES_CONNECTION,
PostUserEnrollmentStage,
)
from authentik.events.models import Event, EventAction
from authentik.flows.models import Flow, Stage, in_memory_stage
from authentik.flows.planner import (
PLAN_CONTEXT_PENDING_USER,
PLAN_CONTEXT_REDIRECT,
PLAN_CONTEXT_SOURCE,
PLAN_CONTEXT_SSO,
FlowPlanner,
)
from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs
from authentik.policies.utils import delete_none_keys
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
class Action(Enum):
"""Actions that can be decided based on the request
and source settings"""
LINK = "link"
AUTH = "auth"
ENROLL = "enroll"
DENY = "deny"
class SourceFlowManager:
"""Help sources decide what they should do after authorization. Based on source settings and
previous connections, authenticate the user, enroll a new user, link to an existing user
or deny the request."""
source: Source
request: HttpRequest
identifier: str
connection_type: Type[UserSourceConnection] = UserSourceConnection
def __init__(
self,
source: Source,
request: HttpRequest,
identifier: str,
enroll_info: dict[str, Any],
) -> None:
self.source = source
self.request = request
self.identifier = identifier
self.enroll_info = enroll_info
self._logger = get_logger().bind(source=source, identifier=identifier)
# pylint: disable=too-many-return-statements
def get_action(self, **kwargs) -> tuple[Action, Optional[UserSourceConnection]]:
"""decide which action should be taken"""
new_connection = self.connection_type(
source=self.source, identifier=self.identifier
)
# When request is authenticated, always link
if self.request.user.is_authenticated:
new_connection.user = self.request.user
new_connection = self.update_connection(new_connection, **kwargs)
new_connection.save()
return Action.LINK, new_connection
existing_connections = self.connection_type.objects.filter(
source=self.source, identifier=self.identifier
)
if existing_connections.exists():
connection = existing_connections.first()
return Action.AUTH, self.update_connection(connection, **kwargs)
# No connection exists, but we match on identifier, so enroll
if self.source.user_matching_mode == SourceUserMatchingModes.IDENTIFIER:
# We don't save the connection here cause it doesn't have a user assigned yet
return Action.ENROLL, self.update_connection(new_connection, **kwargs)
# Check for existing users with matching attributes
query = Q()
# Either query existing user based on email or username
if self.source.user_matching_mode in [
SourceUserMatchingModes.EMAIL_LINK,
SourceUserMatchingModes.EMAIL_DENY,
]:
if not self.enroll_info.get("email", None):
self._logger.warning("Refusing to use none email", source=self.source)
return Action.DENY, None
query = Q(email__exact=self.enroll_info.get("email", None))
if self.source.user_matching_mode in [
SourceUserMatchingModes.USERNAME_LINK,
SourceUserMatchingModes.USERNAME_DENY,
]:
if not self.enroll_info.get("username", None):
self._logger.warning(
"Refusing to use none username", source=self.source
)
return Action.DENY, None
query = Q(username__exact=self.enroll_info.get("username", None))
self._logger.debug("trying to link with existing user", query=query)
matching_users = User.objects.filter(query)
# No matching users, always enroll
if not matching_users.exists():
self._logger.debug("no matching users found, enrolling")
return Action.ENROLL, self.update_connection(new_connection, **kwargs)
user = matching_users.first()
if self.source.user_matching_mode in [
SourceUserMatchingModes.EMAIL_LINK,
SourceUserMatchingModes.USERNAME_LINK,
]:
new_connection.user = user
new_connection = self.update_connection(new_connection, **kwargs)
new_connection.save()
return Action.LINK, new_connection
if self.source.user_matching_mode in [
SourceUserMatchingModes.EMAIL_DENY,
SourceUserMatchingModes.USERNAME_DENY,
]:
self._logger.info("denying source because user exists", user=user)
return Action.DENY, None
# Should never get here as default enroll case is returned above.
return Action.DENY, None
def update_connection(
self, connection: UserSourceConnection, **kwargs
) -> UserSourceConnection:
"""Optionally make changes to the connection after it is looked up/created."""
return connection
def get_flow(self, **kwargs) -> HttpResponse:
"""Get the flow response based on user_matching_mode"""
try:
action, connection = self.get_action(**kwargs)
except IntegrityError as exc:
self._logger.warning("failed to get action", exc=exc)
return redirect("/")
self._logger.debug("get_action() says", action=action, connection=connection)
if connection:
if action == Action.LINK:
self._logger.debug("Linking existing user")
return self.handle_existing_user_link(connection)
if action == Action.AUTH:
self._logger.debug("Handling auth user")
return self.handle_auth_user(connection)
if action == Action.ENROLL:
self._logger.debug("Handling enrollment of new user")
return self.handle_enroll(connection)
# Default case, assume deny
messages.error(
self.request,
_(
(
"Request to authenticate with %(source)s has been denied. Please authenticate "
"with the source you've previously signed up with."
)
% {"source": self.source.name}
),
)
return redirect("/")
# pylint: disable=unused-argument
def get_stages_to_append(self, flow: Flow) -> list[Stage]:
"""Hook to override stages which are appended to the flow"""
if flow.slug == self.source.enrollment_flow.slug:
return [
in_memory_stage(PostUserEnrollmentStage),
]
return []
def _handle_login_flow(self, flow: Flow, **kwargs) -> HttpResponse:
"""Prepare Authentication Plan, redirect user FlowExecutor"""
# Ensure redirect is carried through when user was trying to
# authorize application
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
NEXT_ARG_NAME, "authentik_core:if-admin"
)
kwargs.update(
{
# Since we authenticate the user by their token, they have no backend set
PLAN_CONTEXT_AUTHENTICATION_BACKEND: "django.contrib.auth.backends.ModelBackend",
PLAN_CONTEXT_SSO: True,
PLAN_CONTEXT_SOURCE: self.source,
PLAN_CONTEXT_REDIRECT: final_redirect,
}
)
if not flow:
return HttpResponseBadRequest()
# We run the Flow planner here so we can pass the Pending user in the context
planner = FlowPlanner(flow)
plan = planner.plan(self.request, kwargs)
for stage in self.get_stages_to_append(flow):
plan.append(stage)
self.request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
self.request.GET,
flow_slug=flow.slug,
)
# pylint: disable=unused-argument
def handle_auth_user(
self,
connection: UserSourceConnection,
) -> HttpResponse:
"""Login user and redirect."""
messages.success(
self.request,
_(
"Successfully authenticated with %(source)s!"
% {"source": self.source.name}
),
)
flow_kwargs = {PLAN_CONTEXT_PENDING_USER: connection.user}
return self._handle_login_flow(self.source.authentication_flow, **flow_kwargs)
def handle_existing_user_link(
self,
connection: UserSourceConnection,
) -> HttpResponse:
"""Handler when the user was already authenticated and linked an external source
to their account."""
# Connection has already been saved
Event.new(
EventAction.SOURCE_LINKED,
message="Linked Source",
source=self.source,
).from_http(self.request)
messages.success(
self.request,
_("Successfully linked %(source)s!" % {"source": self.source.name}),
)
# When request isn't authenticated we jump straight to auth
if not self.request.user.is_authenticated:
return self.handle_auth_user(connection)
return redirect(
reverse(
"authentik_core:if-admin",
)
+ f"#/user;page-{self.source.slug}"
)
def handle_enroll(
self,
connection: UserSourceConnection,
) -> HttpResponse:
"""User was not authenticated and previous request was not authenticated."""
messages.success(
self.request,
_(
"Successfully authenticated with %(source)s!"
% {"source": self.source.name}
),
)
# We run the Flow planner here so we can pass the Pending user in the context
if not self.source.enrollment_flow:
self._logger.warning("source has no enrollment flow")
return HttpResponseBadRequest()
return self._handle_login_flow(
self.source.enrollment_flow,
**{
PLAN_CONTEXT_PROMPT: delete_none_keys(self.enroll_info),
PLAN_CONTEXT_SOURCES_CONNECTION: connection,
},
)

View File

@ -1,32 +1,30 @@
"""OAuth Stages"""
"""Source flow manager stages"""
from django.http import HttpRequest, HttpResponse
from authentik.core.models import User
from authentik.core.models import User, UserSourceConnection
from authentik.events.models import Event, EventAction
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import StageView
from authentik.sources.oauth.models import UserOAuthSourceConnection
PLAN_CONTEXT_SOURCES_OAUTH_ACCESS = "sources_oauth_access"
PLAN_CONTEXT_SOURCES_CONNECTION = "goauthentik.io/sources/connection"
class PostUserEnrollmentStage(StageView):
"""Dynamically injected stage which saves the OAuth Connection after
"""Dynamically injected stage which saves the Connection after
the user has been enrolled."""
# pylint: disable=unused-argument
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""Stage used after the user has been enrolled"""
access: UserOAuthSourceConnection = self.executor.plan.context[
PLAN_CONTEXT_SOURCES_OAUTH_ACCESS
connection: UserSourceConnection = self.executor.plan.context[
PLAN_CONTEXT_SOURCES_CONNECTION
]
user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
access.user = user
access.save()
UserOAuthSourceConnection.objects.filter(pk=access.pk).update(user=user)
connection.user = user
connection.save()
Event.new(
EventAction.SOURCE_LINKED,
message="Linked OAuth Source",
source=access.source,
message="Linked Source",
source=connection.source,
).from_http(self.request)
return self.executor.stage_ok()

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

@ -14,9 +14,9 @@
<link rel="stylesheet" type="text/css" href="{% static 'dist/page.css' %}?v={{ ak_version }}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/empty-state.css' %}?v={{ ak_version }}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/spinner.css' %}?v={{ ak_version }}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}?v={{ ak_version }}">
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}?v={{ ak_version }}">
<script src="{% static 'dist/poly.js' %}?v={{ ak_version }}" type="module"></script>
<script>window["polymerSkipLoadingFontRoboto"] = true;</script>
{% block head %}

View File

@ -3,6 +3,10 @@
{% load static %}
{% load i18n %}
{% block head_before %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}?v={{ ak_version }}">
{% endblock %}
{% block body %}
<div class="pf-c-background-image">
<svg xmlns="http://www.w3.org/2000/svg" class="pf-c-background-image__filter" width="0" height="0">

View File

@ -0,0 +1,125 @@
"""Test Applications API"""
from django.urls import reverse
from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import Application, User
from authentik.policies.dummy.models import DummyPolicy
from authentik.policies.models import PolicyBinding
class TestApplicationsAPI(APITestCase):
"""Test applications API"""
def setUp(self) -> None:
self.user = User.objects.get(username="akadmin")
self.allowed = Application.objects.create(name="allowed", slug="allowed")
self.denied = Application.objects.create(name="denied", slug="denied")
PolicyBinding.objects.create(
target=self.denied,
policy=DummyPolicy.objects.create(
name="deny", result=False, wait_min=1, wait_max=2
),
order=0,
)
def test_check_access(self):
"""Test check_access operation"""
self.client.force_login(self.user)
response = self.client.get(
reverse(
"authentik_api:application-check-access",
kwargs={"slug": self.allowed.slug},
)
)
self.assertEqual(response.status_code, 204)
response = self.client.get(
reverse(
"authentik_api:application-check-access",
kwargs={"slug": self.denied.slug},
)
)
self.assertEqual(response.status_code, 403)
def test_list(self):
"""Test list operation without superuser_full_list"""
self.client.force_login(self.user)
response = self.client.get(reverse("authentik_api:application-list"))
self.assertJSONEqual(
force_str(response.content),
{
"pagination": {
"next": 0,
"previous": 0,
"count": 2,
"current": 1,
"total_pages": 1,
"start_index": 1,
"end_index": 2,
},
"results": [
{
"pk": str(self.allowed.pk),
"name": "allowed",
"slug": "allowed",
"provider": None,
"provider_obj": None,
"launch_url": None,
"meta_launch_url": "",
"meta_icon": None,
"meta_description": "",
"meta_publisher": "",
"policy_engine_mode": "any",
},
],
},
)
def test_list_superuser_full_list(self):
"""Test list operation with superuser_full_list"""
self.client.force_login(self.user)
response = self.client.get(
reverse("authentik_api:application-list") + "?superuser_full_list=true"
)
self.assertJSONEqual(
force_str(response.content),
{
"pagination": {
"next": 0,
"previous": 0,
"count": 2,
"current": 1,
"total_pages": 1,
"start_index": 1,
"end_index": 2,
},
"results": [
{
"pk": str(self.allowed.pk),
"name": "allowed",
"slug": "allowed",
"provider": None,
"provider_obj": None,
"launch_url": None,
"meta_launch_url": "",
"meta_icon": None,
"meta_description": "",
"meta_publisher": "",
"policy_engine_mode": "any",
},
{
"launch_url": None,
"meta_description": "",
"meta_icon": None,
"meta_launch_url": "",
"meta_publisher": "",
"name": "denied",
"pk": str(self.denied.pk),
"policy_engine_mode": "any",
"provider": None,
"provider_obj": None,
"slug": "denied",
},
],
},
)

View File

@ -1,11 +1,14 @@
"""authentik core models tests"""
from time import sleep
from typing import Callable, Type
from django.test import TestCase
from django.utils.timezone import now
from guardian.shortcuts import get_anonymous_user
from authentik.core.models import Token
from authentik.core.models import Provider, Source, Token
from authentik.flows.models import Stage
from authentik.lib.utils.reflection import all_subclasses
class TestModels(TestCase):
@ -18,9 +21,46 @@ class TestModels(TestCase):
self.assertTrue(token.is_expired)
def test_token_expire_no_expire(self):
"""Test token expiring with "expiring" set """
"""Test token expiring with "expiring" set"""
token = Token.objects.create(
expires=now(), user=get_anonymous_user(), expiring=False
)
sleep(0.5)
self.assertFalse(token.is_expired)
def source_tester_factory(test_model: Type[Stage]) -> Callable:
"""Test source"""
def tester(self: TestModels):
model_class = None
if test_model._meta.abstract:
model_class = test_model.__bases__[0]()
else:
model_class = test_model()
model_class.slug = "test"
self.assertIsNotNone(model_class.component)
_ = model_class.ui_login_button
_ = model_class.ui_user_settings
return tester
def provider_tester_factory(test_model: Type[Stage]) -> Callable:
"""Test provider"""
def tester(self: TestModels):
model_class = None
if test_model._meta.abstract:
model_class = test_model.__bases__[0]()
else:
model_class = test_model()
self.assertIsNotNone(model_class.component)
return tester
for model in all_subclasses(Source):
setattr(TestModels, f"test_model_{model.__name__}", source_tester_factory(model))
for model in all_subclasses(Provider):
setattr(TestModels, f"test_model_{model.__name__}", provider_tester_factory(model))

View File

@ -2,9 +2,10 @@
from dataclasses import dataclass
from typing import Optional
from rest_framework.fields import CharField
from rest_framework.fields import CharField, DictField
from authentik.core.api.utils import PassiveSerializer
from authentik.flows.challenge import Challenge
@dataclass
@ -14,8 +15,8 @@ class UILoginButton:
# Name, ran through i18n
name: str
# URL Which Button points to
url: str
# Challenge which is presented to the user when they click the button
challenge: Challenge
# Icon URL, used as-is
icon_url: Optional[str] = None
@ -25,7 +26,7 @@ class UILoginButtonSerializer(PassiveSerializer):
"""Serializer for Login buttons of sources"""
name = CharField()
url = CharField()
challenge = DictField()
icon_url = CharField(required=False, allow_null=True)

View File

@ -3,7 +3,9 @@ import django_filters
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import load_pem_x509_certificate
from django.http.response import HttpResponse
from django.utils.translation import gettext_lazy as _
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.fields import (
@ -39,7 +41,7 @@ class CertificateKeyPairSerializer(ModelSerializer):
"""Show if this keypair has a private key configured or not"""
return instance.key_data != "" and instance.key_data is not None
def validate_certificate_data(self, value):
def validate_certificate_data(self, value: str) -> str:
"""Verify that input is a valid PEM x509 Certificate"""
try:
load_pem_x509_certificate(value.encode("utf-8"), default_backend())
@ -47,7 +49,7 @@ class CertificateKeyPairSerializer(ModelSerializer):
raise ValidationError("Unable to load certificate.")
return value
def validate_key_data(self, value):
def validate_key_data(self, value: str) -> str:
"""Verify that input is a valid PEM RSA Key"""
# Since this field is optional, data can be empty.
if value != "":
@ -57,8 +59,10 @@ class CertificateKeyPairSerializer(ModelSerializer):
password=None,
backend=default_backend(),
)
except ValueError:
raise ValidationError("Unable to load private key.")
except (ValueError, TypeError):
raise ValidationError(
"Unable to load private key (possibly encrypted?)."
)
return value
class Meta:
@ -143,7 +147,16 @@ class CertificateKeyPairViewSet(ModelViewSet):
serializer = self.get_serializer(instance)
return Response(serializer.data)
@swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)})
@swagger_auto_schema(
manual_parameters=[
openapi.Parameter(
name="download",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
],
responses={200: CertificateDataSerializer(many=False)},
)
@action(detail=True, pagination_class=None, filter_backends=[])
# pylint: disable=invalid-name, unused-argument
def view_certificate(self, request: Request, pk: str) -> Response:
@ -154,11 +167,29 @@ class CertificateKeyPairViewSet(ModelViewSet):
secret=certificate,
type="certificate",
).from_http(request)
if "download" in request._request.GET:
# Mime type from https://pki-tutorial.readthedocs.io/en/latest/mime.html
response = HttpResponse(
certificate.certificate_data, content_type="application/x-pem-file"
)
response[
"Content-Disposition"
] = f'attachment; filename="{certificate.name}_certificate.pem"'
return response
return Response(
CertificateDataSerializer({"data": certificate.certificate_data}).data
)
@swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)})
@swagger_auto_schema(
manual_parameters=[
openapi.Parameter(
name="download",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
],
responses={200: CertificateDataSerializer(many=False)},
)
@action(detail=True, pagination_class=None, filter_backends=[])
# pylint: disable=invalid-name, unused-argument
def view_private_key(self, request: Request, pk: str) -> Response:
@ -169,4 +200,13 @@ class CertificateKeyPairViewSet(ModelViewSet):
secret=certificate,
type="private_key",
).from_http(request)
if "download" in request._request.GET:
# Mime type from https://pki-tutorial.readthedocs.io/en/latest/mime.html
response = HttpResponse(
certificate.key_data, content_type="application/x-pem-file"
)
response[
"Content-Disposition"
] = f'attachment; filename="{certificate.name}_private_key.pem"'
return response
return Response(CertificateDataSerializer({"data": certificate.key_data}).data)

View File

@ -33,7 +33,7 @@ class CertificateBuilder:
def save(self) -> Optional[CertificateKeyPair]:
"""Save generated certificate as model"""
if not self.__certificate:
return None
raise ValueError("Certificated hasn't been built yet")
return CertificateKeyPair.objects.create(
name=self.common_name,
certificate_data=self.certificate,

View File

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

View File

@ -8,10 +8,10 @@ from rest_framework.decorators import action
from rest_framework.fields import CharField, DictField, IntegerField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, Serializer
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ReadOnlyModelViewSet
from authentik.core.api.utils import TypeCreateSerializer
from authentik.core.api.utils import PassiveSerializer, TypeCreateSerializer
from authentik.events.models import Event, EventAction
@ -38,31 +38,19 @@ class EventSerializer(ModelSerializer):
]
class EventTopPerUserParams(Serializer):
class EventTopPerUserParams(PassiveSerializer):
"""Query params for top_per_user"""
top_n = IntegerField(default=15)
def create(self, request: Request) -> Response:
raise NotImplementedError
def update(self, request: Request) -> Response:
raise NotImplementedError
class EventTopPerUserSerializer(Serializer):
class EventTopPerUserSerializer(PassiveSerializer):
"""Response object of Event's top_per_user"""
application = DictField()
counted_events = IntegerField()
unique_users = IntegerField()
def create(self, request: Request) -> Response:
raise NotImplementedError
def update(self, request: Request) -> Response:
raise NotImplementedError
class EventsFilter(django_filters.FilterSet):
"""Filter for events"""
@ -132,7 +120,7 @@ class EventViewSet(ReadOnlyModelViewSet):
def top_per_user(self, request: Request):
"""Get the top_n events grouped by user count"""
filtered_action = request.query_params.get("action", EventAction.LOGIN)
top_n = request.query_params.get("top_n", 15)
top_n = int(request.query_params.get("top_n", "15"))
return Response(
get_objects_for_user(request.user, "authentik_events.view_event")
.filter(action=filtered_action)

View File

@ -1,9 +1,12 @@
"""Notification API Views"""
from django_filters.rest_framework import DjangoFilterBackend
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
@ -46,8 +49,5 @@ class NotificationViewSet(
"event",
"seen",
]
def get_queryset(self):
if not self.request:
return super().get_queryset()
return Notification.objects.filter(user=self.request.user)
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

@ -1,6 +1,6 @@
"""Event notification tasks"""
from guardian.shortcuts import get_anonymous_user
from structlog import get_logger
from structlog.stdlib import get_logger
from authentik.core.models import User
from authentik.events.models import (
@ -35,7 +35,10 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
LOGGER.warning("event doesn't exist yet or anymore", event_uuid=event_uuid)
return
event: Event = events.first()
trigger: NotificationRule = NotificationRule.objects.get(name=trigger_name)
triggers: NotificationRule = NotificationRule.objects.filter(name=trigger_name)
if not triggers.exists():
return
trigger = triggers.first()
if "policy_uuid" in event.context:
policy_uuid = event.context["policy_uuid"]
@ -58,7 +61,13 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
return
LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger)
user = User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user()
try:
user = (
User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user()
)
except User.DoesNotExist:
LOGGER.warning("e(trigger): failed to get user", trigger=trigger)
return
policy_engine = PolicyEngine(trigger, user)
policy_engine.mode = PolicyEngineMode.MODE_ANY
policy_engine.empty_result = False

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

@ -0,0 +1,32 @@
"""base model tests"""
from typing import Callable, Type
from django.test import TestCase
from authentik.flows.models import Stage
from authentik.flows.stage import StageView
from authentik.lib.utils.reflection import all_subclasses
class TestModels(TestCase):
"""Generic model properties tests"""
def model_tester_factory(test_model: Type[Stage]) -> Callable:
"""Test a form"""
def tester(self: TestModels):
model_class = None
if test_model._meta.abstract:
model_class = test_model.__bases__[0]()
else:
model_class = test_model()
self.assertTrue(issubclass(model_class.type, StageView))
self.assertIsNotNone(test_model.component)
_ = test_model.ui_user_settings
return tester
for model in all_subclasses(Stage):
setattr(TestModels, f"test_model_{model.__name__}", model_tester_factory(model))

View File

@ -13,7 +13,7 @@ from django.db.models.query_utils import Q
from django.db.utils import IntegrityError
from rest_framework.exceptions import ValidationError
from rest_framework.serializers import BaseSerializer, Serializer
from structlog import BoundLogger, get_logger
from structlog.stdlib import BoundLogger, get_logger
from authentik.flows.models import Flow, FlowStageBinding, Stage
from authentik.flows.transfer.common import (

View File

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

View File

@ -86,6 +86,13 @@ class ConfigLoader:
url = urlparse(value)
if url.scheme == "env":
value = os.getenv(url.netloc, url.query)
if url.scheme == "file":
try:
with open(url.path, "r") as _file:
value = _file.read()
except OSError:
self._log("error", f"Failed to read config value from {url.path}")
value = url.query
return value
def update_from_file(self, path: str):
@ -163,6 +170,7 @@ class ConfigLoader:
# Walk each component of the path
path_parts = path.split(sep)
for comp in path_parts[:-1]:
# pyright: reportGeneralTypeIssues=false
if comp not in root:
root[comp] = {}
root = root.get(comp)

View File

@ -3,8 +3,13 @@ postgresql:
host: localhost
name: authentik
user: authentik
port: 5432
password: 'env://POSTGRES_PASSWORD'
web:
listen: 0.0.0.0:9000
listen_tls: 0.0.0.0:9443
redis:
host: localhost
password: ''
@ -34,7 +39,11 @@ email:
from: authentik@localhost
outposts:
docker_image_base: "beryju/authentik" # this is prepended to -proxy:version
# Placeholders:
# %(type)s: Outpost type; proxy, ldap, etc
# %(version)s: Current version; 2021.4.1
# %(build_hash)s: Build hash if you're running a beta version
docker_image_base: "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

@ -0,0 +1,16 @@
"""Test Reflection utils"""
from datetime import datetime
from django.test import TestCase
from authentik.lib.utils.reflection import path_to_class
class TestReflectionUtils(TestCase):
"""Test Reflection-utils"""
def test_path_to_class(self):
"""Test path_to_class"""
self.assertIsNone(path_to_class(None))
self.assertEqual(path_to_class("datetime.datetime"), datetime)

View File

@ -3,6 +3,9 @@ from typing import Any, Optional
from django.http import HttpRequest
OUTPOST_REMOTE_IP_HEADER = "HTTP_X_AUTHENTIK_REMOTE_IP"
USER_ATTRIBUTE_CAN_OVERRIDE_IP = "goauthentik.io/user/override-ips"
def _get_client_ip_from_meta(meta: dict[str, Any]) -> Optional[str]:
"""Attempt to get the client's IP by checking common HTTP Headers.
@ -14,13 +17,32 @@ 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
def _get_outpost_override_ip(request: HttpRequest) -> Optional[str]:
"""Get the actual remote IP when set by an outpost. Only
allowed when the request is authenticated, by a user with USER_ATTRIBUTE_CAN_OVERRIDE_IP set
to outpost"""
if not hasattr(request, "user"):
return None
if not request.user.is_authenticated:
return None
if OUTPOST_REMOTE_IP_HEADER not in request.META:
return None
if request.user.attributes.get(USER_ATTRIBUTE_CAN_OVERRIDE_IP, False):
return None
return request.META[OUTPOST_REMOTE_IP_HEADER]
def get_client_ip(request: Optional[HttpRequest]) -> Optional[str]:
"""Attempt to get the client's IP by checking common HTTP Headers.
Returns none if no IP Could be found"""
if request:
override = _get_outpost_override_ip(request)
if override:
return override
return _get_client_ip_from_meta(request.META)
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

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

View File

@ -40,7 +40,9 @@ 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
def connect(self):
super().connect()
@ -52,9 +54,7 @@ class OutpostConsumer(AuthJsonConsumer):
raise DenyConnection()
self.accept()
self.outpost = outpost.first()
OutpostState(
uid=self.channel_name, last_seen=datetime.now(), _outpost=self.outpost
).save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)
self.last_uid = self.channel_name
LOGGER.debug(
"added outpost instace to cache",
outpost=self.outpost,
@ -63,23 +63,28 @@ class OutpostConsumer(AuthJsonConsumer):
# pylint: disable=unused-argument
def disconnect(self, close_code):
if self.outpost:
OutpostState.for_channel(self.outpost, self.channel_name).delete()
if self.outpost and self.last_uid:
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,
channel_name=self.channel_name,
instance_uuid=self.last_uid,
)
def receive_json(self, content: Data):
msg = from_dict(WebsocketMessage, content)
state = OutpostState(
uid=self.channel_name,
last_seen=datetime.now(),
_outpost=self.outpost,
)
uid = msg.args.get("uuid", self.channel_name)
self.last_uid = uid
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,9 +1,13 @@
"""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 ENV_GIT_HASH_KEY, __version__
from authentik.lib.config import CONFIG
from authentik.lib.sentry import SentryIgnoredException
from authentik.outposts.models import Outpost, OutpostServiceConnection
@ -21,6 +25,7 @@ class DeploymentPort:
port: int
name: str
protocol: str
inner_port: Optional[int] = None
class BaseController:
@ -52,6 +57,21 @@ class BaseController:
"""Handler to delete everything we've created"""
raise NotImplementedError
def down_with_logs(self) -> list[str]:
"""Call .down() but capture all log output and return it."""
with capture_logs() as logs:
self.down()
return [x["event"] for x in logs]
def get_static_deployment(self) -> str:
"""Return a static deployment configuration"""
raise NotImplementedError
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__,
"build_hash": environ.get(ENV_GIT_HASH_KEY, ""),
}

View File

@ -8,7 +8,6 @@ from docker.models.containers import Container
from yaml import safe_dump
from authentik import __version__
from authentik.lib.config import CONFIG
from authentik.outposts.controllers.base import BaseController, ControllerException
from authentik.outposts.models import (
DockerServiceConnection,
@ -60,15 +59,14 @@ class DockerController(BaseController):
return self.client.containers.get(container_name), False
except NotFound:
self.logger.info("Container does not exist, creating")
image_prefix = CONFIG.y("outposts.docker_image_base")
image_name = f"{image_prefix}-{self.outpost.type}:{__version__}"
image_name = self.get_container_image()
self.client.images.pull(image_name)
container_args = {
"image": image_name,
"name": f"authentik-proxy-{self.outpost.uuid.hex}",
"detach": True,
"ports": {
f"{port.port}/{port.protocol.lower()}": port.port
f"{port.port}/{port.protocol.lower()}": port.inner_port or port.port
for port in self.deployment_ports
},
"environment": self._get_env(),
@ -143,15 +141,15 @@ class DockerController(BaseController):
def get_static_deployment(self) -> str:
"""Generate docker-compose yaml for proxy, version 3.5"""
ports = [
f"{port.port}:{port.port}/{port.protocol.lower()}"
f"{port.port}:{port.inner_port or port.port}/{port.protocol.lower()}"
for port in self.deployment_ports
]
image_prefix = CONFIG.y("outposts.docker_image_base")
image_name = self.get_container_image()
compose = {
"version": "3.5",
"services": {
f"authentik_{self.outpost.type}": {
"image": f"{image_prefix}-{self.outpost.type}:{__version__}",
"image": image_name,
"ports": ports,
"environment": {
"AUTHENTIK_HOST": self.outpost.config.authentik_host,

View File

@ -1,6 +1,7 @@
"""Base Kubernetes Reconciler"""
from typing import TYPE_CHECKING, Generic, TypeVar
from django.utils.text import slugify
from kubernetes.client import V1ObjectMeta
from kubernetes.client.models.v1_deployment import V1Deployment
from kubernetes.client.models.v1_pod import V1Pod
@ -37,16 +38,30 @@ class KubernetesObjectReconciler(Generic[T]):
def __init__(self, controller: "KubernetesController"):
self.controller = controller
self.namespace = controller.outpost.config.kubernetes_namespace
self.logger = get_logger()
self.logger = get_logger().bind(type=self.__class__.__name__)
@property
def noop(self) -> bool:
"""Return true if this object should not be created/updated/deleted in this cluster"""
return False
@property
def name(self) -> str:
"""Get the name of the object this reconciler manages"""
raise NotImplementedError
return (
self.controller.outpost.config.object_naming_template
% {
"name": slugify(self.controller.outpost.name),
"uuid": self.controller.outpost.uuid.hex,
}
).lower()
def up(self):
"""Create object if it doesn't exist, update if needed or recreate if needed."""
current = None
if self.noop:
self.logger.debug("Object is noop")
return
reference = self.get_reference_object()
try:
try:
@ -58,7 +73,6 @@ class KubernetesObjectReconciler(Generic[T]):
self.logger.debug("Other unhandled error", exc=exc)
raise exc
else:
self.logger.debug("Got current, running reconcile")
self.reconcile(current, reference)
except NeedsRecreate:
self.logger.debug("Recreate requested")
@ -67,16 +81,19 @@ class KubernetesObjectReconciler(Generic[T]):
self.delete(current)
else:
self.logger.debug("No old found, creating")
self.logger.debug("Created")
self.logger.debug("Creating")
self.create(reference)
except NeedsUpdate:
self.logger.debug("Updating")
self.update(current, reference)
else:
self.logger.debug("Nothing to do...")
self.logger.debug("Object is up-to-date.")
def down(self):
"""Delete object if found"""
if self.noop:
self.logger.debug("Object is noop")
return
try:
current = self.retrieve()
self.delete(current)
@ -120,7 +137,7 @@ class KubernetesObjectReconciler(Generic[T]):
namespace=self.namespace,
labels={
"app.kubernetes.io/name": f"authentik-{self.controller.outpost.type.lower()}",
"app.kubernetes.io/instance": self.controller.outpost.name,
"app.kubernetes.io/instance": slugify(self.controller.outpost.name),
"app.kubernetes.io/version": __version__,
"app.kubernetes.io/managed-by": "goauthentik.io",
"goauthentik.io/outpost-uuid": self.controller.outpost.uuid.hex,

View File

@ -16,8 +16,6 @@ from kubernetes.client import (
V1SecretKeySelector,
)
from authentik import __version__
from authentik.lib.config import CONFIG
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import (
KubernetesObjectReconciler,
@ -39,10 +37,6 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
self.api = AppsV1Api(controller.client)
self.outpost = self.controller.outpost
@property
def name(self) -> str:
return f"authentik-outpost-{self.controller.outpost.uuid.hex}"
def reconcile(self, current: V1Deployment, reference: V1Deployment):
super().reconcile(current, reference)
if current.spec.replicas != reference.spec.replicas:
@ -68,14 +62,13 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
for port in self.controller.deployment_ports:
container_ports.append(
V1ContainerPort(
container_port=port.port,
container_port=port.inner_port or port.port,
name=port.name,
protocol=port.protocol.upper(),
)
)
meta = self.get_object_meta(name=self.name)
secret_name = f"authentik-outpost-{self.controller.outpost.uuid.hex}-api"
image_prefix = CONFIG.y("outposts.docker_image_base")
image_name = self.controller.get_container_image()
return V1Deployment(
metadata=meta,
spec=V1DeploymentSpec(
@ -87,14 +80,14 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
containers=[
V1Container(
name=str(self.outpost.type),
image=f"{image_prefix}-{self.outpost.type}:{__version__}",
image=image_name,
ports=container_ports,
env=[
V1EnvVar(
name="AUTHENTIK_HOST",
value_from=V1EnvVarSource(
secret_key_ref=V1SecretKeySelector(
name=secret_name,
name=self.name,
key="authentik_host",
)
),
@ -103,7 +96,7 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
name="AUTHENTIK_TOKEN",
value_from=V1EnvVarSource(
secret_key_ref=V1SecretKeySelector(
name=secret_name,
name=self.name,
key="token",
)
),
@ -112,7 +105,7 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
name="AUTHENTIK_INSECURE",
value_from=V1EnvVarSource(
secret_key_ref=V1SecretKeySelector(
name=secret_name,
name=self.name,
key="authentik_host_insecure",
)
),

View File

@ -26,10 +26,6 @@ class SecretReconciler(KubernetesObjectReconciler[V1Secret]):
super().__init__(controller)
self.api = CoreV1Api(controller.client)
@property
def name(self) -> str:
return f"authentik-outpost-{self.controller.outpost.uuid.hex}-api"
def reconcile(self, current: V1Secret, reference: V1Secret):
super().reconcile(current, reference)
for key in reference.data.keys():

View File

@ -21,10 +21,6 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
super().__init__(controller)
self.api = CoreV1Api(controller.client)
@property
def name(self) -> str:
return f"authentik-outpost-{self.controller.outpost.uuid.hex}"
def reconcile(self, current: V1Service, reference: V1Service):
super().reconcile(current, reference)
if len(current.spec.ports) != len(reference.spec.ports):
@ -43,13 +39,17 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
name=port.name,
port=port.port,
protocol=port.protocol.upper(),
target_port=port.port,
target_port=port.inner_port or port.port,
)
)
selector_labels = DeploymentReconciler(self.controller).get_pod_meta()
return V1Service(
metadata=meta,
spec=V1ServiceSpec(ports=ports, selector=selector_labels, type="ClusterIP"),
spec=V1ServiceSpec(
ports=ports,
selector=selector_labels,
type=self.controller.outpost.config.kubernetes_service_type,
),
)
def create(self, reference: V1Service):

View File

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

View File

@ -2,10 +2,9 @@
from io import StringIO
from typing import Type
from kubernetes.client import OpenApiException
from kubernetes.client.api_client import ApiClient
from kubernetes.client.exceptions import ApiException
from structlog.testing import capture_logs
from urllib3.exceptions import HTTPError
from yaml import dump_all
from authentik.outposts.controllers.base import BaseController, ControllerException
@ -43,34 +42,55 @@ class KubernetesController(BaseController):
reconciler = self.reconcilers[reconcile_key](self)
reconciler.up()
except (OpenApiException, HTTPError) as exc:
raise ControllerException from exc
except ApiException as exc:
raise ControllerException(str(exc)) from exc
def up_with_logs(self) -> list[str]:
try:
all_logs = []
for reconcile_key in self.reconcile_order:
if reconcile_key in self.outpost.config.kubernetes_disabled_components:
all_logs += [f"{reconcile_key.title()}: Disabled"]
continue
with capture_logs() as logs:
reconciler = self.reconcilers[reconcile_key](self)
reconciler.up()
all_logs += [f"{reconcile_key.title()}: {x['event']}" for x in logs]
return all_logs
except (OpenApiException, HTTPError) as exc:
raise ControllerException from exc
except ApiException as exc:
raise ControllerException(str(exc)) from exc
def down(self):
try:
for reconcile_key in self.reconcile_order:
reconciler = self.reconcilers[reconcile_key](self)
self.logger.debug("Tearing down object", name=reconcile_key)
reconciler.down()
except OpenApiException as exc:
raise ControllerException from exc
except ApiException as exc:
raise ControllerException(str(exc)) from exc
def down_with_logs(self) -> list[str]:
try:
all_logs = []
for reconcile_key in self.reconcile_order:
if reconcile_key in self.outpost.config.kubernetes_disabled_components:
all_logs += [f"{reconcile_key.title()}: Disabled"]
continue
with capture_logs() as logs:
reconciler = self.reconcilers[reconcile_key](self)
reconciler.down()
all_logs += [f"{reconcile_key.title()}: {x['event']}" for x in logs]
return all_logs
except ApiException as exc:
raise ControllerException(str(exc)) from exc
def get_static_deployment(self) -> str:
documents = []
for reconcile_key in self.reconcile_order:
reconciler = self.reconcilers[reconcile_key](self)
if reconciler.noop:
continue
documents.append(reconciler.get_reference_object().to_dict())
with StringIO() as _str:

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2 on 2021-04-26 09:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_outposts", "0015_auto_20201224_1206"),
]
operations = [
migrations.AlterField(
model_name="outpost",
name="type",
field=models.TextField(
choices=[("proxy", "Proxy"), ("ldap", "Ldap")], default="proxy"
),
),
]

View File

@ -1,10 +1,12 @@
"""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
from dacite import from_dict
from django.contrib.auth.models import Permission
from django.core.cache import cache
from django.db import models, transaction
from django.db.models.base import Model
@ -25,12 +27,14 @@ from packaging.version import LegacyVersion, Version, parse
from structlog.stdlib import get_logger
from urllib3.exceptions import HTTPError
from authentik import __version__
from authentik import ENV_GIT_HASH_KEY, __version__
from authentik.core.models import USER_ATTRIBUTE_SA, Provider, Token, TokenIntents, User
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.config import CONFIG
from authentik.lib.models import InheritanceForeignKey
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.http import USER_ATTRIBUTE_CAN_OVERRIDE_IP
from authentik.outposts.controllers.k8s.utils import get_namespace
from authentik.outposts.docker_tls import DockerInlineTLS
OUR_VERSION = parse(__version__)
@ -39,7 +43,7 @@ LOGGER = get_logger()
class ServiceConnectionInvalid(SentryIgnoredException):
""""Exception raised when a Service Connection has invalid parameters"""
"""Exception raised when a Service Connection has invalid parameters"""
@dataclass
@ -55,16 +59,19 @@ class OutpostConfig:
"error_reporting.environment", "customer"
)
object_naming_template: str = field(default="ak-outpost-%(name)s")
kubernetes_replicas: int = field(default=1)
kubernetes_namespace: str = field(default="default")
kubernetes_namespace: str = field(default_factory=get_namespace)
kubernetes_ingress_annotations: dict[str, str] = field(default_factory=dict)
kubernetes_ingress_secret_name: str = field(default="authentik-outpost")
kubernetes_ingress_secret_name: str = field(default="authentik-outpost-tls")
kubernetes_service_type: str = field(default="ClusterIP")
kubernetes_disabled_components: list[str] = field(default_factory=list)
class OutpostModel(Model):
"""Base model for providers that need more objects than just themselves"""
def get_required_objects(self) -> Iterable[models.Model]:
def get_required_objects(self) -> Iterable[Union[models.Model, str]]:
"""Return a list of all required objects"""
return [self]
@ -77,6 +84,7 @@ class OutpostType(models.TextChoices):
"""Outpost types, currently only the reverse proxy is available"""
PROXY = "proxy"
LDAP = "ldap"
def default_outpost_config(host: Optional[str] = None):
@ -326,6 +334,7 @@ class Outpost(models.Model):
if not users.exists():
user: User = User.objects.create(username=self.user_identifier)
user.attributes[USER_ATTRIBUTE_SA] = True
user.attributes[USER_ATTRIBUTE_CAN_OVERRIDE_IP] = True
user.set_unusable_password()
user.save()
else:
@ -334,9 +343,29 @@ class Outpost(models.Model):
# the ones the user needs
with transaction.atomic():
UserObjectPermission.objects.filter(user=user).delete()
for model in self.get_required_objects():
code_name = f"{model._meta.app_label}.view_{model._meta.model_name}"
assign_perm(code_name, user, model)
user.user_permissions.clear()
for model_or_perm in self.get_required_objects():
if isinstance(model_or_perm, models.Model):
model_or_perm: models.Model
code_name = (
f"{model_or_perm._meta.app_label}."
f"view_{model_or_perm._meta.model_name}"
)
assign_perm(code_name, user, model_or_perm)
else:
app_label, perm = model_or_perm.split(".")
permission = Permission.objects.filter(
codename=perm,
content_type__app_label=app_label,
)
if not permission.exists():
LOGGER.warning("permission doesn't exist", perm=model_or_perm)
continue
user.user_permissions.add(permission.first())
LOGGER.debug(
"Updated service account's permissions",
perms=UserObjectPermission.objects.filter(user=user),
)
return user
@property
@ -359,9 +388,9 @@ class Outpost(models.Model):
managed=f"goauthentik.io/outpost/{self.token_identifier}",
)
def get_required_objects(self) -> Iterable[models.Model]:
def get_required_objects(self) -> Iterable[Union[models.Model, str]]:
"""Get an iterator of all objects the user needs read access to"""
objects = [self]
objects: list[Union[models.Model, str]] = [self]
for provider in (
Provider.objects.filter(outpost=self).select_related().select_subclasses()
):
@ -380,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)
@ -391,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
@ -399,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

@ -1,7 +1,7 @@
"""authentik outpost signals"""
from django.conf import settings
from django.core.cache import cache
from django.db.models import Model
from django.db.models.signals import post_save, pre_delete
from django.db.models.signals import post_save, pre_delete, pre_save
from django.dispatch import receiver
from structlog.stdlib import get_logger
@ -9,7 +9,11 @@ from authentik.core.models import Provider
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.utils.reflection import class_to_path
from authentik.outposts.models import Outpost, OutpostServiceConnection
from authentik.outposts.tasks import outpost_post_save, outpost_pre_delete
from authentik.outposts.tasks import (
CACHE_KEY_OUTPOST_DOWN,
outpost_controller,
outpost_post_save,
)
LOGGER = get_logger()
UPDATE_TRIGGERING_MODELS = (
@ -20,6 +24,28 @@ UPDATE_TRIGGERING_MODELS = (
)
@receiver(pre_save, sender=Outpost)
# pylint: disable=unused-argument
def pre_save_outpost(sender, instance: Outpost, **_):
"""Pre-save checks for an outpost, if the name or config.kubernetes_namespace changes,
we call down and then wait for the up after save"""
old_instances = Outpost.objects.filter(pk=instance.pk)
if not old_instances.exists():
return
old_instance = old_instances.first()
dirty = False
# Name changes the deployment name, need to recreate
dirty += old_instance.name != instance.name
# namespace requires re-create
dirty += (
old_instance.config.kubernetes_namespace != instance.config.kubernetes_namespace
)
if bool(dirty):
LOGGER.info("Outpost needs re-deployment due to changes", instance=instance)
cache.set(CACHE_KEY_OUTPOST_DOWN % instance.pk.hex, old_instance)
outpost_controller.delay(instance.pk.hex, action="down", from_cache=True)
@receiver(post_save)
# pylint: disable=unused-argument
def post_save_update(sender, instance: Model, **_):
@ -41,15 +67,5 @@ def post_save_update(sender, instance: Model, **_):
def pre_delete_cleanup(sender, instance: Outpost, **_):
"""Ensure that Outpost's user is deleted (which will delete the token through cascade)"""
instance.user.delete()
# To ensure that deployment is cleaned up *consistently* we call the controller, and wait
# for it to finish. We don't want to call it in this thread, as we don't have the Outpost
# Service connection here
try:
outpost_pre_delete.delay(instance.pk.hex).get()
except RuntimeError:
# In e2e/integration tests, this might run inside a thread/process and
# trigger the celery `Never call result.get() within a task` detection
if settings.TEST:
pass
else:
raise
cache.set(CACHE_KEY_OUTPOST_DOWN % instance.pk.hex, instance)
outpost_controller.delay(instance.pk.hex, action="down", from_cache=True)

View File

@ -3,7 +3,7 @@ from os import R_OK, access
from os.path import expanduser
from pathlib import Path
from socket import gethostname
from typing import Any
from typing import Any, Optional
from urllib.parse import urlparse
import yaml
@ -19,7 +19,7 @@ from structlog.stdlib import get_logger
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.lib.utils.reflection import path_to_class
from authentik.outposts.controllers.base import ControllerException
from authentik.outposts.controllers.base import BaseController, ControllerException
from authentik.outposts.models import (
DockerServiceConnection,
KubernetesServiceConnection,
@ -29,18 +29,32 @@ from authentik.outposts.models import (
OutpostState,
OutpostType,
)
from authentik.providers.ldap.controllers.docker import LDAPDockerController
from authentik.providers.ldap.controllers.kubernetes import LDAPKubernetesController
from authentik.providers.proxy.controllers.docker import ProxyDockerController
from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesController
from authentik.root.celery import CELERY_APP
LOGGER = get_logger()
CACHE_KEY_OUTPOST_DOWN = "outpost_teardown_%s"
@CELERY_APP.task()
def outpost_controller_all():
"""Launch Controller for all Outposts which support it"""
for outpost in Outpost.objects.exclude(service_connection=None):
outpost_controller.delay(outpost.pk.hex)
def controller_for_outpost(outpost: Outpost) -> Optional[BaseController]:
"""Get a controller for the outpost, when a service connection is defined"""
if not outpost.service_connection:
return None
service_connection = outpost.service_connection
if outpost.type == OutpostType.PROXY:
if isinstance(service_connection, DockerServiceConnection):
return ProxyDockerController(outpost, service_connection)
if isinstance(service_connection, KubernetesServiceConnection):
return ProxyKubernetesController(outpost, service_connection)
if outpost.type == OutpostType.LDAP:
if isinstance(service_connection, DockerServiceConnection):
return LDAPDockerController(outpost, service_connection)
if isinstance(service_connection, KubernetesServiceConnection):
return LDAPKubernetesController(outpost, service_connection)
return None
@CELERY_APP.task()
@ -69,23 +83,31 @@ def outpost_service_connection_monitor(self: MonitoredTask):
)
@CELERY_APP.task()
def outpost_controller_all():
"""Launch Controller for all Outposts which support it"""
for outpost in Outpost.objects.exclude(service_connection=None):
outpost_controller.delay(outpost.pk.hex, "up", from_cache=False)
@CELERY_APP.task(bind=True, base=MonitoredTask)
def outpost_controller(self: MonitoredTask, outpost_pk: str):
"""Create/update/monitor the deployment of an Outpost"""
def outpost_controller(
self: MonitoredTask, outpost_pk: str, action: str = "up", from_cache: bool = False
):
"""Create/update/monitor/delete the deployment of an Outpost"""
logs = []
outpost: Outpost = Outpost.objects.get(pk=outpost_pk)
if from_cache:
outpost: Outpost = cache.get(CACHE_KEY_OUTPOST_DOWN % outpost_pk)
else:
outpost: Outpost = Outpost.objects.get(pk=outpost_pk)
if not outpost:
return
self.set_uid(slugify(outpost.name))
try:
if not outpost.service_connection:
controller = controller_for_outpost(outpost)
if not controller:
return
if outpost.type == OutpostType.PROXY:
service_connection = outpost.service_connection
if isinstance(service_connection, DockerServiceConnection):
logs = ProxyDockerController(outpost, service_connection).up_with_logs()
if isinstance(service_connection, KubernetesServiceConnection):
logs = ProxyKubernetesController(
outpost, service_connection
).up_with_logs()
logs = getattr(controller, f"{action}_with_logs")()
LOGGER.debug("---------------Outpost Controller logs starting----------------")
for log in logs:
LOGGER.debug(log)
@ -96,18 +118,6 @@ def outpost_controller(self: MonitoredTask, outpost_pk: str):
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, logs))
@CELERY_APP.task()
def outpost_pre_delete(outpost_pk: str):
"""Delete outpost objects before deleting the DB Object"""
outpost = Outpost.objects.get(pk=outpost_pk)
if outpost.type == OutpostType.PROXY:
service_connection = outpost.service_connection
if isinstance(service_connection, DockerServiceConnection):
ProxyDockerController(outpost, service_connection).down()
if isinstance(service_connection, KubernetesServiceConnection):
ProxyKubernetesController(outpost, service_connection).down()
@CELERY_APP.task(bind=True, base=MonitoredTask)
def outpost_token_ensurer(self: MonitoredTask):
"""Periodically ensure that all Outposts have valid Service Accounts
@ -192,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

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

View File

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

@ -0,0 +1,84 @@
# Generated by Django 3.2 on 2021-05-02 17:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_policies_event_matcher", "0012_auto_20210323_1339"),
]
operations = [
migrations.AlterField(
model_name="eventmatcherpolicy",
name="app",
field=models.TextField(
blank=True,
choices=[
("authentik.admin", "authentik Admin"),
("authentik.api", "authentik API"),
("authentik.events", "authentik Events"),
("authentik.crypto", "authentik Crypto"),
("authentik.flows", "authentik Flows"),
("authentik.outposts", "authentik Outpost"),
("authentik.lib", "authentik lib"),
("authentik.policies", "authentik Policies"),
("authentik.policies.dummy", "authentik Policies.Dummy"),
(
"authentik.policies.event_matcher",
"authentik Policies.Event Matcher",
),
("authentik.policies.expiry", "authentik Policies.Expiry"),
("authentik.policies.expression", "authentik Policies.Expression"),
("authentik.policies.hibp", "authentik Policies.HaveIBeenPwned"),
("authentik.policies.password", "authentik Policies.Password"),
("authentik.policies.reputation", "authentik Policies.Reputation"),
("authentik.providers.proxy", "authentik Providers.Proxy"),
("authentik.providers.oauth2", "authentik Providers.OAuth2"),
("authentik.providers.saml", "authentik Providers.SAML"),
("authentik.recovery", "authentik Recovery"),
("authentik.sources.ldap", "authentik Sources.LDAP"),
("authentik.sources.oauth", "authentik Sources.OAuth"),
("authentik.sources.plex", "authentik Sources.Plex"),
("authentik.sources.saml", "authentik Sources.SAML"),
(
"authentik.stages.authenticator_static",
"authentik Stages.Authenticator.Static",
),
(
"authentik.stages.authenticator_totp",
"authentik Stages.Authenticator.TOTP",
),
(
"authentik.stages.authenticator_validate",
"authentik Stages.Authenticator.Validate",
),
(
"authentik.stages.authenticator_webauthn",
"authentik Stages.Authenticator.WebAuthn",
),
("authentik.stages.captcha", "authentik Stages.Captcha"),
("authentik.stages.consent", "authentik Stages.Consent"),
("authentik.stages.deny", "authentik Stages.Deny"),
("authentik.stages.dummy", "authentik Stages.Dummy"),
("authentik.stages.email", "authentik Stages.Email"),
(
"authentik.stages.identification",
"authentik Stages.Identification",
),
("authentik.stages.invitation", "authentik Stages.User Invitation"),
("authentik.stages.password", "authentik Stages.Password"),
("authentik.stages.prompt", "authentik Stages.Prompt"),
("authentik.stages.user_delete", "authentik Stages.User Delete"),
("authentik.stages.user_login", "authentik Stages.User Login"),
("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.core", "authentik Core"),
("authentik.managed", "authentik Managed"),
],
default="",
help_text="Match events created by selected application. When left empty, all applications are matched.",
),
),
]

View File

@ -0,0 +1,85 @@
# Generated by Django 3.2.1 on 2021-05-05 17:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_policies_event_matcher", "0013_alter_eventmatcherpolicy_app"),
]
operations = [
migrations.AlterField(
model_name="eventmatcherpolicy",
name="app",
field=models.TextField(
blank=True,
choices=[
("authentik.admin", "authentik Admin"),
("authentik.api", "authentik API"),
("authentik.events", "authentik Events"),
("authentik.crypto", "authentik Crypto"),
("authentik.flows", "authentik Flows"),
("authentik.outposts", "authentik Outpost"),
("authentik.lib", "authentik lib"),
("authentik.policies", "authentik Policies"),
("authentik.policies.dummy", "authentik Policies.Dummy"),
(
"authentik.policies.event_matcher",
"authentik Policies.Event Matcher",
),
("authentik.policies.expiry", "authentik Policies.Expiry"),
("authentik.policies.expression", "authentik Policies.Expression"),
("authentik.policies.hibp", "authentik Policies.HaveIBeenPwned"),
("authentik.policies.password", "authentik Policies.Password"),
("authentik.policies.reputation", "authentik Policies.Reputation"),
("authentik.providers.proxy", "authentik Providers.Proxy"),
("authentik.providers.ldap", "authentik Providers.LDAP"),
("authentik.providers.oauth2", "authentik Providers.OAuth2"),
("authentik.providers.saml", "authentik Providers.SAML"),
("authentik.recovery", "authentik Recovery"),
("authentik.sources.ldap", "authentik Sources.LDAP"),
("authentik.sources.oauth", "authentik Sources.OAuth"),
("authentik.sources.plex", "authentik Sources.Plex"),
("authentik.sources.saml", "authentik Sources.SAML"),
(
"authentik.stages.authenticator_static",
"authentik Stages.Authenticator.Static",
),
(
"authentik.stages.authenticator_totp",
"authentik Stages.Authenticator.TOTP",
),
(
"authentik.stages.authenticator_validate",
"authentik Stages.Authenticator.Validate",
),
(
"authentik.stages.authenticator_webauthn",
"authentik Stages.Authenticator.WebAuthn",
),
("authentik.stages.captcha", "authentik Stages.Captcha"),
("authentik.stages.consent", "authentik Stages.Consent"),
("authentik.stages.deny", "authentik Stages.Deny"),
("authentik.stages.dummy", "authentik Stages.Dummy"),
("authentik.stages.email", "authentik Stages.Email"),
(
"authentik.stages.identification",
"authentik Stages.Identification",
),
("authentik.stages.invitation", "authentik Stages.User Invitation"),
("authentik.stages.password", "authentik Stages.Password"),
("authentik.stages.prompt", "authentik Stages.Prompt"),
("authentik.stages.user_delete", "authentik Stages.User Delete"),
("authentik.stages.user_login", "authentik Stages.User Login"),
("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.core", "authentik Core"),
("authentik.managed", "authentik Managed"),
],
default="",
help_text="Match events created by selected application. When left empty, all applications are matched.",
),
),
]

View File

@ -4,7 +4,7 @@
{% load i18n %}
{% block title %}
{% trans 'Permission denied - authentik' %}
{% trans 'Permission denied' %} - {{ config.authentik.branding.title }}
{% endblock %}
{% block card_title %}

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

View File

@ -0,0 +1,54 @@
"""LDAPProvider API Views"""
from rest_framework.fields import CharField
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
from authentik.core.api.providers import ProviderSerializer
from authentik.providers.ldap.models import LDAPProvider
class LDAPProviderSerializer(ProviderSerializer):
"""LDAPProvider Serializer"""
class Meta:
model = LDAPProvider
fields = ProviderSerializer.Meta.fields + [
"base_dn",
"search_group",
]
class LDAPProviderViewSet(ModelViewSet):
"""LDAPProvider Viewset"""
queryset = LDAPProvider.objects.all()
serializer_class = LDAPProviderSerializer
ordering = ["name"]
class LDAPOutpostConfigSerializer(ModelSerializer):
"""LDAPProvider Serializer"""
application_slug = CharField(source="application.slug")
bind_flow_slug = CharField(source="authorization_flow.slug")
class Meta:
model = LDAPProvider
fields = [
"pk",
"name",
"base_dn",
"bind_flow_slug",
"application_slug",
"search_group",
]
class LDAPOutpostConfigViewSet(ReadOnlyModelViewSet):
"""LDAPProvider Viewset"""
queryset = LDAPProvider.objects.filter(application__isnull=False)
serializer_class = LDAPOutpostConfigSerializer
ordering = ["name"]

View File

@ -0,0 +1,10 @@
"""authentik ldap provider app config"""
from django.apps import AppConfig
class AuthentikProviderLDAPConfig(AppConfig):
"""authentik ldap provider app config"""
name = "authentik.providers.ldap"
label = "authentik_providers_ldap"
verbose_name = "authentik Providers.LDAP"

View File

@ -0,0 +1,14 @@
"""LDAP Provider Docker Contoller"""
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.docker import DockerController
from authentik.outposts.models import DockerServiceConnection, Outpost
class LDAPDockerController(DockerController):
"""LDAP Provider Docker Contoller"""
def __init__(self, outpost: Outpost, connection: DockerServiceConnection):
super().__init__(outpost, connection)
self.deployment_ports = [
DeploymentPort(389, "ldap", "tcp", 3389),
]

View File

@ -0,0 +1,14 @@
"""LDAP Provider Kubernetes Contoller"""
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.kubernetes import KubernetesController
from authentik.outposts.models import KubernetesServiceConnection, Outpost
class LDAPKubernetesController(KubernetesController):
"""LDAP Provider Kubernetes Contoller"""
def __init__(self, outpost: Outpost, connection: KubernetesServiceConnection):
super().__init__(outpost, connection)
self.deployment_ports = [
DeploymentPort(389, "ldap", "tcp", 3389),
]

View File

@ -0,0 +1,44 @@
# Generated by Django 3.2 on 2021-04-26 12:45
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("authentik_core", "0019_source_managed"),
]
operations = [
migrations.CreateModel(
name="LDAPProvider",
fields=[
(
"provider_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_core.provider",
),
),
(
"base_dn",
models.TextField(
default="DC=ldap,DC=goauthentik,DC=io",
help_text="DN under which objects are accessible.",
),
),
],
options={
"verbose_name": "LDAP Provider",
"verbose_name_plural": "LDAP Providers",
},
bases=("authentik_core.provider", models.Model),
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 3.2 on 2021-04-26 19:57
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0019_source_managed"),
("authentik_providers_ldap", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="ldapprovider",
name="search_group",
field=models.ForeignKey(
default=None,
help_text="Users in this group can do search queries. If not set, every user can execute search queries.",
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
to="authentik_core.group",
),
),
]

View File

@ -0,0 +1,55 @@
"""LDAP Provider"""
from typing import Iterable, Optional, Type, Union
from django.db import models
from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from authentik.core.models import Group, Provider
from authentik.outposts.models import OutpostModel
class LDAPProvider(OutpostModel, Provider):
"""Allow applications to authenticate against authentik's users using LDAP."""
base_dn = models.TextField(
default="DC=ldap,DC=goauthentik,DC=io",
help_text=_("DN under which objects are accessible."),
)
search_group = models.ForeignKey(
Group,
null=True,
default=None,
on_delete=models.SET_DEFAULT,
help_text=_(
"Users in this group can do search queries. "
"If not set, every user can execute search queries."
),
)
@property
def launch_url(self) -> Optional[str]:
"""LDAP never has a launch URL"""
return None
@property
def component(self) -> str:
return "ak-provider-ldap-form"
@property
def serializer(self) -> Type[Serializer]:
from authentik.providers.ldap.api import LDAPProviderSerializer
return LDAPProviderSerializer
def __str__(self):
return f"LDAP Provider {self.name}"
def get_required_objects(self) -> Iterable[Union[models.Model, str]]:
return [self, "authentik_core.view_user", "authentik_core.view_group"]
class Meta:
verbose_name = _("LDAP Provider")
verbose_name_plural = _("LDAP Providers")

View File

@ -38,6 +38,7 @@ class OAuth2ProviderSerializer(ProviderSerializer):
"client_type",
"client_id",
"client_secret",
"access_code_validity",
"token_validity",
"include_claims_in_id_token",
"jwt_alg",

View File

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

View File

@ -1,11 +1,11 @@
"""authentik auth oauth provider app config"""
"""authentik oauth provider app config"""
from importlib import import_module
from django.apps import AppConfig
class AuthentikProviderOAuth2Config(AppConfig):
"""authentik auth oauth provider app config"""
"""authentik oauth provider app config"""
name = "authentik.providers.oauth2"
label = "authentik_providers_oauth2"

View File

@ -0,0 +1,24 @@
# Generated by Django 3.2 on 2021-04-28 18:17
from django.db import migrations, models
import authentik.lib.utils.time
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_oauth2", "0011_managed"),
]
operations = [
migrations.AddField(
model_name="oauth2provider",
name="access_code_validity",
field=models.TextField(
default="minutes=1",
help_text="Access codes not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).",
validators=[authentik.lib.utils.time.timedelta_string_validator],
),
),
]

View File

@ -11,13 +11,11 @@ from urllib.parse import urlparse
from uuid import uuid4
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
from django.utils.translation import gettext_lazy as _
from jwkest.jwk import Key, RSAKey, SYMKey, import_rsa_key
from jwkest.jws import JWS
from jwt import encode
from rest_framework.serializers import Serializer
from authentik.core.models import ExpiringModel, PropertyMapping, Provider, User
@ -175,6 +173,16 @@ class OAuth2Provider(Provider):
),
)
access_code_validity = models.TextField(
default="minutes=1",
validators=[timedelta_string_validator],
help_text=_(
(
"Access codes not valid on or after current time + this value "
"(Format: hours=1;minutes=2;seconds=3)."
)
),
)
token_validity = models.TextField(
default="minutes=10",
validators=[timedelta_string_validator],
@ -229,7 +237,7 @@ class OAuth2Provider(Provider):
token.access_token = token.create_access_token(user, request)
return token
def get_jwt_keys(self) -> list[Key]:
def get_jwt_key(self) -> str:
"""
Takes a provider and returns the set of keys associated with it.
Returns a list of keys.
@ -246,17 +254,10 @@ class OAuth2Provider(Provider):
self.jwt_alg = JWTAlgorithms.HS256
self.save()
else:
# Because the JWT Library uses python cryptodome,
# we can't directly pass the RSAPublicKey
# object, but have to load it ourselves
key = import_rsa_key(self.rsa_key.key_data)
keys = [RSAKey(key=key, kid=self.rsa_key.kid)]
if not keys:
raise Exception("You must add at least one RSA Key.")
return keys
return self.rsa_key.key_data
if self.jwt_alg == JWTAlgorithms.HS256:
return [SYMKey(key=self.client_secret, alg=self.jwt_alg)]
return self.client_secret
raise Exception("Unsupported key algorithm.")
@ -297,11 +298,14 @@ class OAuth2Provider(Provider):
def encode(self, payload: dict[str, Any]) -> str:
"""Represent the ID Token as a JSON Web Token (JWT)."""
keys = 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()
jws = JWS(payload, alg=self.jwt_alg)
return jws.sign_compact(keys)
# pyright: reportGeneralTypeIssues=false
return encode(payload, key, algorithm=self.jwt_alg, headers=headers)
class Meta:
@ -454,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

@ -14,7 +14,7 @@
{% endblock %}
{% block title %}
{% trans 'End session' %}
{% trans 'End session' %} - {{ config.authentik.branding.title }}
{% endblock %}
{% block card_title %}

View File

@ -1,9 +1,10 @@
"""Test authorize view"""
from django.test import RequestFactory, TestCase
from django.test import RequestFactory
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 (
@ -11,17 +12,21 @@ from authentik.providers.oauth2.errors import (
ClientIdError,
RedirectUriError,
)
from authentik.providers.oauth2.generators import generate_client_id
from authentik.providers.oauth2.generators import (
generate_client_id,
generate_client_secret,
)
from authentik.providers.oauth2.models import (
AuthorizationCode,
GrantTypes,
OAuth2Provider,
RefreshToken,
)
from authentik.providers.oauth2.tests.utils import OAuthTestCase
from authentik.providers.oauth2.views.authorize import OAuthAuthorizationParams
class TestViewsAuthorize(TestCase):
class TestAuthorize(OAuthTestCase):
"""Test authorize view"""
def setUp(self) -> None:
@ -200,8 +205,10 @@ class TestViewsAuthorize(TestCase):
provider = OAuth2Provider.objects.create(
name="test",
client_id="test",
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()
@ -233,3 +240,4 @@ class TestViewsAuthorize(TestCase):
),
},
)
self.validate_jwt(token, provider)

View File

@ -1,11 +1,11 @@
"""Test token view"""
from base64 import b64encode
from django.test import RequestFactory, TestCase
from django.test import RequestFactory
from django.urls import reverse
from django.utils.encoding import force_str
from authentik.core.models import User
from authentik.core.models import Application, User
from authentik.flows.models import Flow
from authentik.providers.oauth2.constants import (
GRANT_TYPE_AUTHORIZATION_CODE,
@ -20,15 +20,17 @@ from authentik.providers.oauth2.models import (
OAuth2Provider,
RefreshToken,
)
from authentik.providers.oauth2.tests.utils import OAuthTestCase
from authentik.providers.oauth2.views.token import TokenParams
class TestViewsToken(TestCase):
class TestToken(OAuthTestCase):
"""Test token view"""
def setUp(self) -> None:
super().setUp()
self.factory = RequestFactory()
self.app = Application.objects.create(name="test", slug="test")
def test_request_auth_code(self):
"""test request param"""
@ -97,12 +99,15 @@ class TestViewsToken(TestCase):
authorization_flow=Flow.objects.first(),
redirect_uris="http://local.invalid",
)
# Needs to be assigned to an application for iss to be set
self.app.provider = provider
self.app.save()
header = b64encode(
f"{provider.client_id}:{provider.client_secret}".encode()
).decode()
user = User.objects.get(username="akadmin")
code = AuthorizationCode.objects.create(
code="foobar", provider=provider, user=user
code="foobar", provider=provider, user=user, is_open_id=True
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
@ -126,6 +131,7 @@ class TestViewsToken(TestCase):
),
},
)
self.validate_jwt(new_token, provider)
def test_refresh_token_view(self):
"""test request param"""
@ -136,6 +142,9 @@ class TestViewsToken(TestCase):
authorization_flow=Flow.objects.first(),
redirect_uris="http://local.invalid",
)
# Needs to be assigned to an application for iss to be set
self.app.provider = provider
self.app.save()
header = b64encode(
f"{provider.client_id}:{provider.client_secret}".encode()
).decode()
@ -174,6 +183,7 @@ class TestViewsToken(TestCase):
),
},
)
self.validate_jwt(new_token, provider)
def test_refresh_token_view_invalid_origin(self):
"""test request param"""

View File

@ -0,0 +1,38 @@
"""OAuth test helpers"""
from django.test import TestCase
from jwt import decode
from authentik.providers.oauth2.models import (
JWTAlgorithms,
OAuth2Provider,
RefreshToken,
)
class OAuthTestCase(TestCase):
"""OAuth test helpers"""
required_jwt_keys = [
"exp",
"iat",
"auth_time",
"acr",
"sub",
"iss",
]
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,
key,
algorithms=[provider.jwt_alg],
audience=provider.client_id,
)
id_token = token.id_token.to_dict()
for key in self.required_jwt_keys:
self.assertIsNotNone(jwt[key], f"Key {key} is missing in access_token")
self.assertIsNotNone(id_token[key], f"Key {key} is missing in id_token")

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