Compare commits

..

160 Commits

Author SHA1 Message Date
e20bb7d636 release: 2021.4.3 2021-04-20 09:15:07 +02:00
e9abc25b92 website/docs: prepare changelog for 2021.4.3
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 21:08:10 +02:00
dc930c0cdf website/docs: manually set slug so release note URLs don't break
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 21:05:19 +02:00
464a1c0536 api: make 401 messages clearer
closes #755

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 20:46:57 +02:00
837d2f6fab outpost: use tools from docker (#758)
* outpost: replace golang.org/x/lint with golangci-lint

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

* outpost: use swagger generator from docker

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

* outpost: don't use tty for swagger gen

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

* outposts: revert docker-swagger gen

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 20:43:13 +02:00
8f00d73512 website: fix main site not rendering because <BrowserOnly>
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 19:23:26 +02:00
b75feab709 outposts: don't run outpost_controller when no service connection is set
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 19:23:26 +02:00
9c8433ec4d fix(docs/grafana): Fix a silly (#757) 2021-04-19 19:22:23 +02:00
ef080900a4 feat(docs/grafana): Add role mapping info (#756)
* feat(docs/grafana): Add role mapping info

* feat(docs/grafana): More info on role mappings
2021-04-19 19:07:09 +02:00
10b45a8dea api: fix 401 responses which should be 403s
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 19:03:00 +02:00
c43ac1f704 api: mount outposts under outposts/instances to match flows
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 18:51:12 +02:00
14d702450a core: add parameter to output property mapping test formatted
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 18:32:36 +02:00
0a1a2a035e web/admin: fix *Test Forms not having a default for codemirrors
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 18:25:16 +02:00
ace777ebbe website: re-sort releases, add outposts to terminology
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 17:25:26 +02:00
8a6879afa5 core: add superuser_full_list to applications list, shows all applications when superuser
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 16:07:30 +02:00
fdc7f14056 core: fix Tokens being created with incorrect intent by default
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 15:43:16 +02:00
8be80aaf9d api: fix CSRF error when using POST/PATCH/PUT in API Browser
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 15:31:32 +02:00
e476f2dda2 website: bump deps
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 09:28:48 +02:00
5d48cfab14 build(deps): bump @docusaurus/core in /website (#753) 2021-04-19 08:22:35 +02:00
1f22f0e7bb build(deps): bump chart.js from 3.1.0 to 3.1.1 in /web (#751) 2021-04-19 08:22:02 +02:00
ce082ead5e providers/oauth2: add unittests for authorize and token views
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-18 21:05:49 +02:00
dd2cd09637 web/admin: fix undefined being shown when viewing application
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-18 19:04:10 +02:00
828fe07fca website: dynamically load rapidoc to prevent react errors
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-18 17:58:36 +02:00
a074ea70e9 website/docs-dev: add initial translation docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-18 17:31:15 +02:00
84ce2c1df2 website: separate development docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-18 17:25:58 +02:00
8628595590 website: add API Browser
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-18 17:15:19 +02:00
7b8e5c4272 root: auto-migrate on startup, lock database using pg_advisory_lock
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-18 14:47:50 +02:00
caa5dc1d14 web/admin: improve default selection for property-mappings
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-18 14:21:05 +02:00
f328b21e89 providers/oauth2: Set CORS Headers for token endpoint, check Origin header against redirect URLs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-18 14:20:50 +02:00
52abd959eb sources/oauth: save null instead of empty string for sources without configurable URLs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 21:15:06 +02:00
a0cd17a257 docs: add troubleshooting for permission issues
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 20:09:03 +02:00
32c5bf04b8 *: fix linting errors
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 20:08:49 +02:00
766c4873a0 web/admin: add ability to add users to a group whilst creating a group
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 19:56:49 +02:00
240136154b web/admin: fix default for codemirror
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 19:37:13 +02:00
78dd7b0341 web/admin: fix group member table order
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 19:36:55 +02:00
0021a93952 web/admin: fix non-matching provider type being selected when creating an OAuth Source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 19:17:08 +02:00
67240fb9ad *: add model_name to TypeCreate API to pass to forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 19:12:00 +02:00
4add0bbe86 web/admin: fix provider type resetting when changing provider type
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 19:06:56 +02:00
d2dd7d1366 sources/oauth: fix redirect loop for source with non-configurable URLs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 19:06:12 +02:00
476e57daa2 Merge branch 'version-2021.4'
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

# Conflicts:
#	docker-compose.yml
#	website/docs/installation/kubernetes.md
2021-04-17 16:01:35 +02:00
4eb8a0dcd1 docs: prepare changelog for 2021.4.2
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 15:29:55 +02:00
60615c9f3e release: 2021.4.2 2021-04-17 15:26:59 +02:00
b5b8573d87 core: fix propertymapping API returning invalid value for components
closes #746

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:05 +02:00
2e44c1cdfc sources/ldap: improve error handling during sync
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:05 +02:00
31909a4d78 outpost: fix outpost deps
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:05 +02:00
4a444e667a root: base Websocket message storage on Base not fallback
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:05 +02:00
f67b57e369 flows: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:04 +02:00
6be19962d2 outposts: bump go version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:04 +02:00
262a9fa2a0 flows: annotate flows executor 404 error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:04 +02:00
e8ba159756 root: fix setting of EMAIL_USE_TLS and EMAIL_USE_SSL
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:04 +02:00
0b03d66a2f outposts: fix errors when creating multiple outposts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:04 +02:00
7c858c9626 web/admin: fix errors in user profile when non-superuser
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:04 +02:00
71b6839d03 flows: include configure_flow in stages API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:04 +02:00
ada49c077a web/admin: fix error when user doesn't have permissions to read source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:04 +02:00
7880c7fb98 helm: make storage class, size and mode configurable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:03 +02:00
2b48ba4103 sources/oauth: fix resolution of sources' provider type
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:03 +02:00
5e67f68f2b core: improve messaging when creating a recovery link for a user when no recovery flow exists
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:03 +02:00
1992b89154 sources/oauth: fix error when creating an oauth source which has fixed URLs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:01:03 +02:00
9ab2088ab7 helm: turn off monitoring by default
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:00:27 +02:00
a9d0d96418 root: add restart: unless-stopped to compose
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 12:00:27 +02:00
c476503594 web: fix background-color on router outlet on light mode
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 11:59:37 +02:00
de74f3ec1f core: fix propertymapping API returning invalid value for components
closes #746

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 11:50:28 +02:00
ce98255607 sources/ldap: improve error handling during sync
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 11:29:51 +02:00
53b9e5b93f outpost: fix outpost deps
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 11:16:40 +02:00
7aeb390eac docs: add note for minimum values.yaml file for k8s install
closes #745

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-17 11:06:21 +02:00
5df9ad63cf root: base Websocket message storage on Base not fallback
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 23:46:03 +02:00
e4400476a2 flows: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 23:15:06 +02:00
ef3c01ec34 outposts: bump go version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 23:01:10 +02:00
b136d3bc69 flows: annotate flows executor 404 error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 22:56:44 +02:00
c34fcc73dc root: fix setting of EMAIL_USE_TLS and EMAIL_USE_SSL
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 22:44:42 +02:00
11b09c4ebd outposts: fix errors when creating multiple outposts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 22:43:35 +02:00
e32070ddeb web/admin: fix errors in user profile when non-superuser
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 17:24:58 +02:00
33a8cea007 flows: include configure_flow in stages API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 17:19:48 +02:00
d01fd7cdb7 web/admin: fix error when user doesn't have permissions to read source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 17:06:06 +02:00
1770e42cbf sources/oauth: add login with plex support
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 17:05:35 +02:00
2fed739be7 helm: make storage class, size and mode configurable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 12:31:55 +02:00
aa820b2b4d website: fix enrollment for keycloak
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 12:23:46 +02:00
582d2eb5eb sources/oauth: fix resolution of sources' provider type
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 11:29:23 +02:00
c5e2635903 core: improve messaging when creating a recovery link for a user when no recovery flow exists
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 10:09:46 +02:00
cfe0a7a694 sources/oauth: fix error when creating an oauth source which has fixed URLs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 09:49:25 +02:00
c579540473 helm: turn off monitoring by default
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 09:20:17 +02:00
35f2b06611 build(deps): bump boto3 from 1.17.52 to 1.17.53 (#742)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.52 to 1.17.53.
- [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.52...1.17.53)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-16 09:07:17 +02:00
9c4f025d71 build(deps): bump @types/codemirror from 0.0.108 to 0.0.109 in /web (#743)
Bumps [@types/codemirror](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/codemirror) from 0.0.108 to 0.0.109.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/codemirror)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-16 09:07:06 +02:00
d8b8e8a5a3 root: add restart: unless-stopped to compose
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-16 09:03:58 +02:00
ec34c3eb75 website: fix azure ad application proxy on comparison
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-15 20:59:14 +02:00
0554c94c53 docs: add notes for openssl
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-15 17:33:25 +02:00
19a663a645 root: fix healthcheck part in docker-compose
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-15 17:28:38 +02:00
e72881b2a9 root: fix healthcheck part in docker-compose
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-15 17:20:14 +02:00
4452ff171e docs: add Explanation what containers do what
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-15 16:12:55 +02:00
39bdc3a9a9 website: fix enrollment for keycloack
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-15 13:12:50 +02:00
33bb6edf8c web: fix background-color on router outlet on light mode
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-15 11:31:06 +02:00
2eb18ff5e6 root: fix expired discord invite
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-15 10:54:43 +02:00
aeb1b5e8f2 build(deps): bump boto3 from 1.17.51 to 1.17.52 (#736) 2021-04-15 09:16:09 +02:00
bd8447d5a7 release: 2021.4.1 2021-04-14 09:46:16 +02:00
35fad191b8 Merge branch 'master' into version-2021.4 2021-04-14 09:27:11 +02:00
40a6f15cf1 build(deps): bump boto3 from 1.17.50 to 1.17.51 (#734) 2021-04-14 08:27:03 +02:00
420465981b build(deps): bump rollup from 2.45.1 to 2.45.2 in /web (#735) 2021-04-14 08:26:53 +02:00
4f9f936a7f Merge branch 'master' into version-2021.4 2021-04-13 23:16:35 +02:00
85c9fbe763 api: fix linting error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-13 21:49:47 +02:00
3d9874be69 api: fix error when authorization is empty
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-13 21:41:26 +02:00
9742d19729 Merge branch 'master' into version-2021.4 2021-04-13 21:07:20 +02:00
5a25e6d697 api: add legacy support for older outposts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-13 21:06:04 +02:00
7798a046db outpost: fix API calls being made with basic
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-13 20:50:45 +02:00
7a562fe8c0 Merge branch 'master' into version-2021.4 2021-04-13 20:02:25 +02:00
6821679fbc *: add support for bearer authentication on API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-13 20:01:30 +02:00
513d3c1c31 web: add support for PII for sentry, add user feedback dialog
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-13 18:35:26 +02:00
30cb468ec5 website: fix search on docs site
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-13 18:05:19 +02:00
8b66fa55a6 web/elements: center header if no description is shown
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-13 17:46:03 +02:00
55bb9b6643 web/admin: show banner when backend and frontend versions mismatch
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-13 16:52:02 +02:00
1b79fad6cf build(deps): bump @typescript-eslint/eslint-plugin in /web (#732)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.21.0 to 4.22.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.22.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-04-13 10:28:22 +02:00
f9976492e7 build(deps): bump boto3 from 1.17.49 to 1.17.50 (#731)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.49 to 1.17.50.
- [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.49...1.17.50)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-13 09:42:16 +02:00
2fd0e46378 build(deps): bump @typescript-eslint/parser in /web (#733)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.21.0 to 4.22.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.22.0/packages/parser)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-13 09:41:51 +02:00
fd0ad20031 release: 2021.4.1-rc2 2021-04-12 20:03:21 +02:00
13b75c15f0 outpost: download go-swagger from github (#730)
* outpost: download go-swagger from github

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

* outposts: use sudo to download swagger

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-12 20:01:12 +02:00
d329995740 docs: add algolia search
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-12 18:25:52 +02:00
cd1b0c67ea web: fix text colour on initial load when not in dark mode
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-12 12:25:44 +02:00
ab7941922f build(deps): bump @lingui/macro from 3.8.6 to 3.8.9 in /web (#722)
Bumps [@lingui/macro](https://github.com/lingui/js-lingui) from 3.8.6 to 3.8.9.
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v3.8.6...v3.8.9)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-12 12:17:16 +02:00
e057d5fe0a root: fix lockfile
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-12 11:19:11 +02:00
3fb53e8311 build(deps-dev): bump pytest-django from 4.1.0 to 4.2.0 (#721)
Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.1.0 to 4.2.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.1.0...v4.2.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens L <jens@beryju.org>
2021-04-12 10:57:46 +02:00
96b9d931f3 build(deps): bump @lingui/cli from 3.8.6 to 3.8.9 in /web (#725)
Bumps [@lingui/cli](https://github.com/lingui/js-lingui) from 3.8.6 to 3.8.9.
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v3.8.6...v3.8.9)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens L <jens@beryju.org>
2021-04-12 10:56:56 +02:00
a35f77c612 build(deps-dev): bump pylint-django from 2.4.2 to 2.4.3 (#729)
* build(deps-dev): bump pylint-django from 2.4.2 to 2.4.3

Bumps [pylint-django](https://github.com/PyCQA/pylint-django) from 2.4.2 to 2.4.3.
- [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.2...v2.4.3)

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

* root: fix pylint warning

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-12 09:33:56 +02:00
f287745c53 root: remove mapped port from server container
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-12 09:29:06 +02:00
65e09f92cd build(deps): bump boto3 from 1.17.48 to 1.17.49 (#720)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.48 to 1.17.49.
- [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.48...1.17.49)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-12 09:28:23 +02:00
9b6446701e build(deps): bump chart.js from 3.0.2 to 3.1.0 in /web (#724)
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 3.0.2 to 3.1.0.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v3.0.2...v3.1.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-12 09:27:32 +02:00
71f7e23fe4 build(deps): bump eslint from 7.23.0 to 7.24.0 in /web (#723) 2021-04-12 08:31:28 +02:00
59eb89db6c build(deps): bump rollup from 2.44.0 to 2.45.1 in /web (#726) 2021-04-12 08:31:10 +02:00
939b55ce29 build(deps): bump @lingui/core from 3.8.6 to 3.8.9 in /web (#727) 2021-04-12 08:30:59 +02:00
7ba4e63c47 build(deps): bump postcss from 8.2.9 to 8.2.10 in /website (#728) 2021-04-12 08:30:49 +02:00
fae92f6bc8 *: fix JSONField overwriting required
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 23:20:45 +02:00
f9bf491240 stages/invitation: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 23:09:09 +02:00
4f27a97e10 *: add validator to ensure JSON Fields only receive dicts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 23:05:19 +02:00
a0daaabfde web: replace full pf with components for loading animation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 22:02:48 +02:00
ea7ecb50c0 web: disable loading of roboto fonts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:52:01 +02:00
e7626d0716 Revert "release: 2021.4.1-rc1"
This reverts commit 2397cb162a.
2021-04-11 21:04:25 +02:00
e9d29b956d Merge branch 'master' into next 2021-04-11 21:02:59 +02:00
4a4ee98dec docs: fix typo in release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:49 +02:00
0d0baaa2f9 web/admin: fix missing css from ApplicationViewPage
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:49 +02:00
1be1654bf2 web/elements: fix height when using PageHeader with Image
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:48 +02:00
ca51afb7df web: always set css variables
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:37 +02:00
11c8ae8f18 web/admin: remove sidebar box shadow
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:36 +02:00
858fcb8554 web/admin: classify no connected workers as error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:36 +02:00
571772854b web/admin: add tab to show events of a user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:35 +02:00
c91b40fc07 web/elements: use same icon for changelog
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:35 +02:00
a736e708ae web/admin: use less generic slot names for ak-tabs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:24 +02:00
5c133a6c30 web/elements: make provider clickable for user's oauth codes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:23 +02:00
078dfb30f3 web/admin: make username in events log clickable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:22 +02:00
b526250515 web: fix header colour for notification drawer in dark mode
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 21:02:21 +02:00
e52d397cb7 docs: fix typo in release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 20:40:53 +02:00
633029be3f web/admin: fix missing css from ApplicationViewPage
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 19:44:29 +02:00
4147fbb839 web/elements: fix height when using PageHeader with Image
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 19:38:24 +02:00
430e3c576c web: always set css variables
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 19:00:29 +02:00
d6f60ad9ec web/admin: remove sidebar box shadow
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 18:56:34 +02:00
de6f663688 web/admin: classify no connected workers as error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 18:48:24 +02:00
fe17c3aa34 web/admin: add tab to show events of a user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 18:46:26 +02:00
07b2525278 web/elements: use same icon for changelog
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 18:40:48 +02:00
9f758d19ba web/admin: use less generic slot names for ak-tabs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 18:40:01 +02:00
4216577565 web/elements: make provider clickable for user's oauth codes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 18:24:27 +02:00
f3396226e8 web/admin: make username in events log clickable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 17:51:17 +02:00
ae7959ff51 web: fix header colour for notification drawer in dark mode
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 17:41:26 +02:00
b42b7be726 outpost: fix build dependencies
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-11 16:51:50 +02:00
169 changed files with 7456 additions and 6225 deletions

View File

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

View File

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

207
Pipfile.lock generated
View File

@ -116,17 +116,18 @@
},
"boto3": {
"hashes": [
"sha256:e86c15049dc07cb67e8b466795f004f1f23c1acf078d47283cd5e4a692a5aa37"
"sha256:1d26f6e7ae3c940cb07119077ac42485dcf99164350da0ab50d0f5ad345800cd",
"sha256:3bf3305571f3c8b738a53e9e7dcff59137dffe94670046c084a17f9fa4599ff3"
],
"index": "pypi",
"version": "==1.17.48"
"version": "==1.17.53"
},
"botocore": {
"hashes": [
"sha256:99c4f96bffae406f6dec1fa94e0886993e5bcd8657604d950abb43a49121fa7c",
"sha256:af27a8133f5b8f6a55738ccd5efd35689f1822cfd447625a839ef7a991eab64f"
"sha256:d5e70d17b91c9b5867be7d6de0caa7dde9ed789bed62f03ea9b60718dc9350bf",
"sha256:e303500c4e80f6a706602da53daa6f751cfa8f491665c99a24ee732ab6321573"
],
"version": "==1.20.48"
"version": "==1.20.53"
},
"cachetools": {
"hashes": [
@ -436,10 +437,10 @@
},
"google-auth": {
"hashes": [
"sha256:186fe2564634d67fbbb64f3daf8bc8c9cecbb2a7f535ed1a8a71795e50db8d87",
"sha256:70b39558712826e41f65e5f05a8d879361deaf84df8883e5dd0ec3d0da6ab66e"
"sha256:010f011c4e27d3d5eb01106fba6aac39d164842dfcd8709955c4638f5b11ccf8",
"sha256:f30a672a64d91cc2e3137765d088c5deec26416246f7a9e956eaf69a8d7ed49c"
],
"version": "==1.28.1"
"version": "==1.29.0"
},
"gunicorn": {
"hashes": [
@ -1105,10 +1106,10 @@
},
"s3transfer": {
"hashes": [
"sha256:5d48b1fd2232141a9d5fb279709117aaba506cacea7f86f11bc392f06bfa8fc2",
"sha256:c5dadf598762899d8cfaecf68eba649cd25b0ce93b6c954b156aaa3eed160547"
"sha256:35627b86af8ff97e7ac27975fe0a98a312814b46c6333d8a6b889627bcd80994",
"sha256:efa5bd92a897b6a8d5c1383828dca3d52d0790e0756d49740563a3fb6ed03246"
],
"version": "==0.3.6"
"version": "==0.3.7"
},
"sentry-sdk": {
"hashes": [
@ -1373,59 +1374,59 @@
},
"zope.interface": {
"hashes": [
"sha256:02d3535aa18e34ce97c58d241120b7554f7d1cf4f8002fc9675cc7e7745d20e8",
"sha256:0378a42ec284b65706d9ef867600a4a31701a0d6773434e6537cfc744e3343f4",
"sha256:07d289358a8c565ea09e426590dd1179f93cf5ac3dd17d43fcc4fc63c1a9d275",
"sha256:0e6cdbdd94ae94d1433ab51f46a76df0f2cd041747c31baec1c1ffa4e76bd0c1",
"sha256:11354fb8b8bdc5cdd66358ed4f1f0ce739d78ff6d215d33b8f3ae282258c0f11",
"sha256:12588a46ae0a99f172c4524cbbc3bb870f32e0f8405e9fa11a5ef3fa3a808ad7",
"sha256:16caa44a06f6b0b2f7626ced4b193c1ae5d09c1b49c9b4962c93ae8aa2134f55",
"sha256:18c478b89b6505756f007dcf76a67224a23dcf0f365427742ed0c0473099caa4",
"sha256:221b41442cf4428fcda7fc958c9721c916709e2a3a9f584edd70f1493a09a762",
"sha256:26109c50ccbcc10f651f76277cfc05fba8418a907daccc300c9247f24b3158a2",
"sha256:28d8157f8c77662a1e0796a7d3cfa8910289131d4b4dd4e10b2686ab1309b67b",
"sha256:2c51689b7b40c7d9c7e8a678350e73dc647945a13b4e416e7a02bbf0c37bdb01",
"sha256:2ec58e1e1691dde4fbbd97f8610de0f8f1b1a38593653f7d3b8e931b9cd6d67f",
"sha256:416feb6500f7b6fc00d32271f6b8495e67188cb5eb51fc8e289b81fdf465a9cb",
"sha256:520352b18adea5478bbf387e9c77910a914985671fe36bc5ef19fdcb67a854bc",
"sha256:527415b5ca201b4add44026f70278fbc0b942cf0801a26ca5527cb0389b6151e",
"sha256:54243053316b5eec92affe43bbace7c8cd946bc0974a4aa39ff1371df0677b22",
"sha256:61b8454190b9cc87279232b6de28dee0bad040df879064bb2f0e505cda907918",
"sha256:672668729edcba0f2ee522ab177fcad91c81cfce991c24d8767765e2637d3515",
"sha256:67aa26097e194947d29f2b5a123830e03da1519bcce10cac034a51fcdb99c34f",
"sha256:6e7305e42b5f54e5ccf51820de46f0a7c951ba7cb9e3f519e908545b0f5628d0",
"sha256:7234ac6782ca43617de803735949f79b894f0c5d353fbc001d745503c69e6d1d",
"sha256:7426bea25bdf92f00fa52c7b30fcd2a2f71c21cf007178971b1f248b6c2d3145",
"sha256:74b331c5d5efdddf5bbd9e1f7d8cb91a0d6b9c4ba45ca3e9003047a84dca1a3b",
"sha256:79b6db1a18253db86e9bf1e99fa829d60fd3fc7ac04f4451c44e4bdcf6756a42",
"sha256:7d79cd354ae0a033ac7b86a2889c9e8bb0bb48243a6ed27fc5064ce49b842ada",
"sha256:823d1b4a6a028b8327e64865e2c81a8959ae9f4e7c9c8e0eec814f4f9b36b362",
"sha256:8715717a5861932b7fe7f3cbd498c82ff4132763e2fea182cc95e53850394ec1",
"sha256:89a6091f2d07936c8a96ce56f2000ecbef20fb420a94845e7d53913c558a6378",
"sha256:8af4b3116e4a37059bc8c7fe36d4a73d7c1d8802a1d8b6e549f1380d13a40160",
"sha256:8b4b0034e6c7f30133fa64a1cc276f8f1a155ef9529e7eb93a3c1728b40c0f5c",
"sha256:92195df3913c1de80062635bf64cd7bd0d0934a7fa1689b6d287d1cbbd16922c",
"sha256:96c2e68385f3848d58f19b2975a675532abdb65c8fa5f04d94b95b27b6b1ffa7",
"sha256:9c7044dbbf8c58420a9ef4ed6901f5a8b7698d90cd984d7f57a18c78474686f6",
"sha256:a1937efed7e3fe0ee74630e1960df887d8aa83c571e1cf4db9d15b9c181d457d",
"sha256:a38c10423a475a1658e2cb8f52cf84ec20a4c0adff724dd43a6b45183f499bc1",
"sha256:a413c424199bcbab71bf5fa7538246f27177fbd6dd74b2d9c5f34878658807f8",
"sha256:b18a855f8504743e0a2d8b75d008c7720d44e4c76687e13f959e35d9a13eb397",
"sha256:b4d59ab3608538e550a72cea13d3c209dd72b6e19e832688da7884081c01594e",
"sha256:b51d3f1cd87f488455f43046d72003689024b0fa9b2d53635db7523033b19996",
"sha256:c02105deda867d09cdd5088d08708f06d75759df6f83d8f7007b06f422908a30",
"sha256:c7b6032dc4490b0dcaf078f09f5b382dc35493cb7f473840368bf0de3196c2b6",
"sha256:c95b355dba2aaf5177dff943b25ded0529a7feb80021d5fdb114a99f0a1ef508",
"sha256:c980ae87863d76b1ea9a073d6d95554b4135032d34bc541be50c07d4a085821b",
"sha256:d12895cd083e35e9e032eb4b57645b91116f8979527381a8d864d1f6b8cb4a2e",
"sha256:d3cd9bad547a8e5fbe712a1dc1413aff1b917e8d39a2cd1389a6f933b7a21460",
"sha256:e8809b01f27f679e3023b9e2013051e0a3f17abff4228cb5197663afd8a0f2c7",
"sha256:f3c37b0dc1898e305aad4f7a1d75f6da83036588c28a9ce0afc681ff5245a601",
"sha256:f966765f54b536e791541458de84a737a6adba8467190f17a8fe7f85354ba908",
"sha256:fa939c2e2468142c9773443d4038e7c915b0cc1b670d3c9192bdc503f7ea73e9",
"sha256:fcc5c1f95102989d2e116ffc8467963554ce89f30a65a3ea86a4d06849c498d8"
"sha256:08f9636e99a9d5410181ba0729e0408d3d8748026ea938f3b970a0249daa8192",
"sha256:0b465ae0962d49c68aa9733ba92a001b2a0933c317780435f00be7ecb959c702",
"sha256:0cba8477e300d64a11a9789ed40ee8932b59f9ee05f85276dbb4b59acee5dd09",
"sha256:0cee5187b60ed26d56eb2960136288ce91bcf61e2a9405660d271d1f122a69a4",
"sha256:0ea1d73b7c9dcbc5080bb8aaffb776f1c68e807767069b9ccdd06f27a161914a",
"sha256:0f91b5b948686659a8e28b728ff5e74b1be6bf40cb04704453617e5f1e945ef3",
"sha256:15e7d1f7a6ee16572e21e3576d2012b2778cbacf75eb4b7400be37455f5ca8bf",
"sha256:17776ecd3a1fdd2b2cd5373e5ef8b307162f581c693575ec62e7c5399d80794c",
"sha256:194d0bcb1374ac3e1e023961610dc8f2c78a0f5f634d0c737691e215569e640d",
"sha256:1c0e316c9add0db48a5b703833881351444398b04111188069a26a61cfb4df78",
"sha256:205e40ccde0f37496904572035deea747390a8b7dc65146d30b96e2dd1359a83",
"sha256:273f158fabc5ea33cbc936da0ab3d4ba80ede5351babc4f577d768e057651531",
"sha256:2876246527c91e101184f63ccd1d716ec9c46519cc5f3d5375a3351c46467c46",
"sha256:2c98384b254b37ce50eddd55db8d381a5c53b4c10ee66e1e7fe749824f894021",
"sha256:2e5a26f16503be6c826abca904e45f1a44ff275fdb7e9d1b75c10671c26f8b94",
"sha256:334701327f37c47fa628fc8b8d28c7d7730ce7daaf4bda1efb741679c2b087fc",
"sha256:3748fac0d0f6a304e674955ab1365d515993b3a0a865e16a11ec9d86fb307f63",
"sha256:3c02411a3b62668200910090a0dff17c0b25aaa36145082a5a6adf08fa281e54",
"sha256:3dd4952748521205697bc2802e4afac5ed4b02909bb799ba1fe239f77fd4e117",
"sha256:3f24df7124c323fceb53ff6168da70dbfbae1442b4f3da439cd441681f54fe25",
"sha256:469e2407e0fe9880ac690a3666f03eb4c3c444411a5a5fddfdabc5d184a79f05",
"sha256:4de4bc9b6d35c5af65b454d3e9bc98c50eb3960d5a3762c9438df57427134b8e",
"sha256:5208ebd5152e040640518a77827bdfcc73773a15a33d6644015b763b9c9febc1",
"sha256:52de7fc6c21b419078008f697fd4103dbc763288b1406b4562554bd47514c004",
"sha256:5bb3489b4558e49ad2c5118137cfeaf59434f9737fa9c5deefc72d22c23822e2",
"sha256:5dba5f530fec3f0988d83b78cc591b58c0b6eb8431a85edd1569a0539a8a5a0e",
"sha256:5dd9ca406499444f4c8299f803d4a14edf7890ecc595c8b1c7115c2342cadc5f",
"sha256:5f931a1c21dfa7a9c573ec1f50a31135ccce84e32507c54e1ea404894c5eb96f",
"sha256:63b82bb63de7c821428d513607e84c6d97d58afd1fe2eb645030bdc185440120",
"sha256:66c0061c91b3b9cf542131148ef7ecbecb2690d48d1612ec386de9d36766058f",
"sha256:6f0c02cbb9691b7c91d5009108f975f8ffeab5dff8f26d62e21c493060eff2a1",
"sha256:71aace0c42d53abe6fc7f726c5d3b60d90f3c5c055a447950ad6ea9cec2e37d9",
"sha256:7d97a4306898b05404a0dcdc32d9709b7d8832c0c542b861d9a826301719794e",
"sha256:7df1e1c05304f26faa49fa752a8c690126cf98b40b91d54e6e9cc3b7d6ffe8b7",
"sha256:8270252effc60b9642b423189a2fe90eb6b59e87cbee54549db3f5562ff8d1b8",
"sha256:867a5ad16892bf20e6c4ea2aab1971f45645ff3102ad29bd84c86027fa99997b",
"sha256:877473e675fdcc113c138813a5dd440da0769a2d81f4d86614e5d62b69497155",
"sha256:8892f89999ffd992208754851e5a052f6b5db70a1e3f7d54b17c5211e37a98c7",
"sha256:9a9845c4c6bb56e508651f005c4aeb0404e518c6f000d5a1123ab077ab769f5c",
"sha256:a1e6e96217a0f72e2b8629e271e1b280c6fa3fe6e59fa8f6701bec14e3354325",
"sha256:a8156e6a7f5e2a0ff0c5b21d6bcb45145efece1909efcbbbf48c56f8da68221d",
"sha256:a9506a7e80bcf6eacfff7f804c0ad5350c8c95b9010e4356a4b36f5322f09abb",
"sha256:af310ec8335016b5e52cae60cda4a4f2a60a788cbb949a4fbea13d441aa5a09e",
"sha256:b0297b1e05fd128d26cc2460c810d42e205d16d76799526dfa8c8ccd50e74959",
"sha256:bf68f4b2b6683e52bec69273562df15af352e5ed25d1b6641e7efddc5951d1a7",
"sha256:d0c1bc2fa9a7285719e5678584f6b92572a5b639d0e471bb8d4b650a1a910920",
"sha256:d4d9d6c1a455d4babd320203b918ccc7fcbefe308615c521062bc2ba1aa4d26e",
"sha256:db1fa631737dab9fa0b37f3979d8d2631e348c3b4e8325d6873c2541d0ae5a48",
"sha256:dd93ea5c0c7f3e25335ab7d22a507b1dc43976e1345508f845efc573d3d779d8",
"sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4",
"sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263"
],
"version": "==5.3.0"
"version": "==5.4.0"
}
},
"develop": {
@ -1438,10 +1439,10 @@
},
"astroid": {
"hashes": [
"sha256:6b0ed1af831570e500e2437625979eaa3b36011f66ddfc4ce930128610258ca9",
"sha256:cd80bf957c49765dce6d92c43163ff9d2abc43132ce64d4b1b47717c6d2522df"
"sha256:ad63b8552c70939568966811a088ef0bc880f99a24a00834abd0e3681b514f91",
"sha256:bea3f32799fbb8581f58431c12591bc20ce11cbc90ad82e2ea5717d94f2080d5"
],
"version": "==2.5.2"
"version": "==2.5.3"
},
"attrs": {
"hashes": [
@ -1661,11 +1662,11 @@
},
"pylint-django": {
"hashes": [
"sha256:355dddb25ef07dbdb77a818b0860ada722aab654c24da34aab916ec26d6390ba",
"sha256:f8d77f7da47a7019cda5cb669c214f03033208f9e945094661299d2637c0da06"
"sha256:a5a4515209a6237d1d390a4a307d53f53baaf4f058ecf4bb556c775d208f6b0d",
"sha256:dc5ed27bb7662d73444ccd15a0b3964ed6ced6cc2712b85db616102062d2ec35"
],
"index": "pypi",
"version": "==2.4.2"
"version": "==2.4.3"
},
"pylint-plugin-utils": {
"hashes": [
@ -1691,11 +1692,11 @@
},
"pytest-django": {
"hashes": [
"sha256:10e384e6b8912ded92db64c58be8139d9ae23fb8361e5fc139d8e4f8fc601bc2",
"sha256:26f02c16d36fd4c8672390deebe3413678d89f30720c16efb8b2a6bf63b9041f"
"sha256:80f8875226ec4dc0b205f0578072034563879d98d9b1bec143a80b9045716cb0",
"sha256:a51150d8962200250e850c6adcab670779b9c2aa07271471059d1fb92a843fa9"
],
"index": "pypi",
"version": "==4.1.0"
"version": "==4.2.0"
},
"pyyaml": {
"hashes": [
@ -1816,38 +1817,38 @@
},
"typed-ast": {
"hashes": [
"sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1",
"sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d",
"sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6",
"sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd",
"sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37",
"sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151",
"sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07",
"sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440",
"sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70",
"sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496",
"sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea",
"sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400",
"sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc",
"sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606",
"sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc",
"sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581",
"sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412",
"sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a",
"sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2",
"sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787",
"sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f",
"sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937",
"sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64",
"sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487",
"sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b",
"sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41",
"sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a",
"sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3",
"sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166",
"sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"
"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.2"
"version": "==1.4.3"
},
"typing-extensions": {
"hashes": [

View File

@ -4,7 +4,7 @@
---
[![](https://img.shields.io/discord/809154715984199690?label=Discord&style=flat-square)](https://discord.gg/KPnmtNWy)
[![](https://img.shields.io/discord/809154715984199690?label=Discord&style=flat-square)](https://discord.gg/jg33eMhnj6)
[![CI Build status](https://img.shields.io/azure-devops/build/beryjuorg/authentik/1?style=flat-square)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=1)
[![Tests](https://img.shields.io/azure-devops/tests/beryjuorg/authentik/1?compact_message&style=flat-square)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=1)
[![Code Coverage](https://img.shields.io/codecov/c/gh/beryju/authentik?style=flat-square)](https://codecov.io/gh/BeryJu/authentik)

View File

@ -1,3 +1,3 @@
"""authentik"""
__version__ = "2021.4.1-rc1"
__version__ = "2021.4.3"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -6,7 +6,7 @@ from drf_yasg.utils import swagger_auto_schema
from packaging.version import parse
from rest_framework.fields import SerializerMethodField
from rest_framework.mixins import ListModelMixin
from rest_framework.permissions import IsAdminUser
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
@ -50,7 +50,9 @@ class VersionSerializer(PassiveSerializer):
class VersionViewSet(ListModelMixin, GenericViewSet):
"""Get running and latest version."""
permission_classes = [IsAdminUser]
permission_classes = [IsAuthenticated]
pagination_class = None
filter_backends = []
def get_queryset(self): # pragma: no cover
return None

View File

@ -1,61 +1,65 @@
"""API Authentication"""
from base64 import b64decode
from base64 import b64decode, b64encode
from binascii import Error
from typing import Any, Optional, Union
from rest_framework.authentication import BaseAuthentication, get_authorization_header
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.request import Request
from structlog.stdlib import get_logger
from authentik.core.models import Token, TokenIntents, User
LOGGER = get_logger()
X_AUTHENTIK_PREVENT_BASIC_HEADER = "HTTP_X_AUTHENTIK_PREVENT_BASIC"
# pylint: disable=too-many-return-statements
def token_from_header(raw_header: bytes) -> Optional[Token]:
"""raw_header in the Format of `Basic dGVzdDp0ZXN0`"""
"""raw_header in the Format of `Bearer dGVzdDp0ZXN0`"""
auth_credentials = raw_header.decode()
# Accept headers with Type format and without
if " " in auth_credentials:
auth_type, auth_credentials = auth_credentials.split()
if auth_type.lower() != "basic":
LOGGER.debug(
"Unsupported authentication type, denying", type=auth_type.lower()
)
return None
try:
auth_credentials = b64decode(auth_credentials.encode()).decode()
except (UnicodeDecodeError, Error):
if auth_credentials == "":
return None
# Accept credentials with username and without
if ":" in auth_credentials:
_, password = auth_credentials.split(":")
else:
password = auth_credentials
# 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())
raise AuthenticationFailed("Unsupported authentication type")
password = auth_credentials
if auth_type.lower() == "basic":
try:
auth_credentials = b64decode(auth_credentials.encode()).decode()
except (UnicodeDecodeError, Error):
raise AuthenticationFailed("Malformed header")
# Accept credentials with username and without
if ":" in auth_credentials:
_, password = auth_credentials.split(":")
else:
password = auth_credentials
if password == "": # nosec
return None
raise AuthenticationFailed("Malformed header")
tokens = Token.filter_not_expired(key=password, intent=TokenIntents.INTENT_API)
if not tokens.exists():
LOGGER.debug("Token not found")
return None
raise AuthenticationFailed("Token invalid/expired")
return tokens.first()
class AuthentikTokenAuthentication(BaseAuthentication):
"""Token-based authentication using HTTP Basic authentication"""
"""Token-based authentication using HTTP Bearer authentication"""
def authenticate(self, request: Request) -> Union[tuple[User, Any], None]:
"""Token-based authentication using HTTP Basic authentication"""
"""Token-based authentication using HTTP Bearer authentication"""
auth = get_authorization_header(request)
token = token_from_header(auth)
# None is only returned when the header isn't set.
if not token:
return None
return (token.user, None)
def authenticate_header(self, request: Request) -> str:
if X_AUTHENTIK_PREVENT_BASIC_HEADER in request._request.META:
return ""
return 'Basic realm="authentik"'

View File

@ -11,6 +11,29 @@ authentik API Browser
{% endblock %}
{% block body %}
<script>
function getCookie(name) {
let cookieValue = "";
if (document.cookie && document.cookie !== "") {
const cookies = document.cookie.split(";");
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === name + "=") {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
window.addEventListener('DOMContentLoaded', (event) => {
const rapidocEl = document.querySelector('rapi-doc');
rapidocEl.addEventListener('before-try', (e) => {
e.detail.request.headers.append('X-CSRFToken', getCookie("authentik_csrf"));
});
});
</script>
<rapi-doc
spec-url="{{ path }}"
heading-text="authentik"

View File

@ -3,6 +3,7 @@ from base64 import b64encode
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.core.models import Token, TokenIntents
@ -11,7 +12,7 @@ from authentik.core.models import Token, TokenIntents
class TestAPIAuth(TestCase):
"""Test API Authentication"""
def test_valid(self):
def test_valid_basic(self):
"""Test valid token"""
token = Token.objects.create(
intent=TokenIntents.INTENT_API, user=get_anonymous_user()
@ -19,19 +20,30 @@ class TestAPIAuth(TestCase):
auth = b64encode(f":{token.key}".encode()).decode()
self.assertEqual(token_from_header(f"Basic {auth}".encode()), token)
def test_valid_bearer(self):
"""Test valid token"""
token = Token.objects.create(
intent=TokenIntents.INTENT_API, user=get_anonymous_user()
)
self.assertEqual(token_from_header(f"Bearer {token.key}".encode()), token)
def test_invalid_type(self):
"""Test invalid type"""
self.assertIsNone(token_from_header("foo bar".encode()))
with self.assertRaises(AuthenticationFailed):
token_from_header("foo bar".encode())
def test_invalid_decode(self):
"""Test invalid bas64"""
self.assertIsNone(token_from_header("Basic bar".encode()))
with self.assertRaises(AuthenticationFailed):
token_from_header("Basic bar".encode())
def test_invalid_empty_password(self):
"""Test invalid with empty password"""
self.assertIsNone(token_from_header("Basic :".encode()))
with self.assertRaises(AuthenticationFailed):
token_from_header("Basic :".encode())
def test_invalid_no_token(self):
"""Test invalid with no token"""
auth = b64encode(":abc".encode()).decode()
self.assertIsNone(token_from_header(f"Basic :{auth}".encode()))
with self.assertRaises(AuthenticationFailed):
auth = b64encode(":abc".encode()).decode()
self.assertIsNone(token_from_header(f"Basic :{auth}".encode()))

View File

@ -113,6 +113,7 @@ router.register("core/user_consent", UserConsentViewSet)
router.register("core/tokens", TokenViewSet)
router.register("outposts/outposts", OutpostViewSet)
router.register("outposts/instances", OutpostViewSet)
router.register("outposts/service_connections/all", ServiceConnectionViewSet)
router.register("outposts/service_connections/docker", DockerServiceConnectionViewSet)
router.register(

View File

@ -91,6 +91,15 @@ class ApplicationViewSet(ModelViewSet):
applications.append(application)
return applications
@swagger_auto_schema(
manual_parameters=[
openapi.Parameter(
name="superuser_full_list",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
]
)
def list(self, request: Request) -> Response:
"""Custom list method that checks Policy based access instead of guardian"""
queryset = self._filter_queryset_for_list(self.get_queryset())
@ -98,6 +107,13 @@ class ApplicationViewSet(ModelViewSet):
should_cache = request.GET.get("search", "") == ""
superuser_full_list = (
str(request.GET.get("superuser_full_list", "false")).lower() == "true"
)
if superuser_full_list and request.user.is_superuser:
serializer = self.get_serializer(queryset, many=True)
return self.get_paginated_response(serializer.data)
allowed_applications = []
if not should_cache:
allowed_applications = self._get_allowed_applications(queryset)

View File

@ -1,13 +1,17 @@
"""Groups API Viewset"""
from rest_framework.fields import JSONField
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.utils import is_dict
from authentik.core.models import Group
class GroupSerializer(ModelSerializer):
"""Group Serializer"""
attributes = JSONField(validators=[is_dict], required=False)
class Meta:
model = Group

View File

@ -1,6 +1,7 @@
"""PropertyMapping API Views"""
from json import dumps
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from guardian.shortcuts import get_objects_for_user
from rest_framework import mixins
@ -91,7 +92,9 @@ class PropertyMappingViewSet(
{
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass.component,
# pyright: reportGeneralTypeIssues=false
"component": subclass().component,
"model_name": subclass._meta.model_name,
}
)
return Response(TypeCreateSerializer(data, many=True).data)
@ -100,6 +103,13 @@ class PropertyMappingViewSet(
@swagger_auto_schema(
request_body=PolicyTestSerializer(),
responses={200: PropertyMappingTestResultSerializer, 400: "Invalid parameters"},
manual_parameters=[
openapi.Parameter(
name="format_result",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
],
)
@action(detail=True, pagination_class=None, filter_backends=[], methods=["POST"])
# pylint: disable=unused-argument, invalid-name
@ -110,6 +120,8 @@ class PropertyMappingViewSet(
if not test_params.is_valid():
return Response(test_params.errors, status=400)
format_result = str(request.GET.get("format_result", "false")).lower() == "true"
# User permission check, only allow mapping testing for users that are readable
users = get_objects_for_user(request.user, "authentik_core.view_user").filter(
pk=test_params.validated_data["user"].pk
@ -124,7 +136,9 @@ class PropertyMappingViewSet(
self.request,
**test_params.validated_data.get("context", {}),
)
response_data["result"] = dumps(result)
response_data["result"] = dumps(
result, indent=(4 if format_result else None)
)
except Exception as exc: # pylint: disable=broad-except
response_data["result"] = str(exc)
response_data["successful"] = False

View File

@ -78,6 +78,7 @@ class ProviderViewSet(
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
}
)
data.append(
@ -85,6 +86,7 @@ class ProviderViewSet(
"name": _("SAML Provider from Metadata"),
"description": _("Create a SAML Provider by importing its Metadata."),
"component": "ak-provider-saml-import-form",
"model_name": "",
}
)
return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -81,6 +81,7 @@ class SourceViewSet(
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": component,
"model_name": subclass._meta.model_name,
}
)
return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -11,7 +11,7 @@ from rest_framework.viewsets import ModelViewSet
from authentik.api.decorators import permission_required
from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import Token
from authentik.core.models import Token, TokenIntents
from authentik.events.models import Event, EventAction
from authentik.managed.api import ManagedSerializer
@ -64,7 +64,7 @@ class TokenViewSet(ModelViewSet):
ordering = ["expires"]
def perform_create(self, serializer: TokenSerializer):
serializer.save(user=self.request.user)
serializer.save(user=self.request.user, intent=TokenIntents.INTENT_API)
@permission_required("authentik_core.view_token_key")
@swagger_auto_schema(

View File

@ -1,10 +1,11 @@
"""User API Views"""
from django.http.response import Http404
from django.urls import reverse_lazy
from django.utils.http import urlencode
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, SerializerMethodField
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
@ -12,13 +13,14 @@ from rest_framework.viewsets import ModelViewSet
from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h
from authentik.api.decorators import permission_required
from authentik.core.api.utils import LinkSerializer, PassiveSerializer
from authentik.core.api.utils import LinkSerializer, PassiveSerializer, is_dict
from authentik.core.middleware import (
SESSION_IMPERSONATE_ORIGINAL_USER,
SESSION_IMPERSONATE_USER,
)
from authentik.core.models import Token, TokenIntents, User
from authentik.events.models import EventAction
from authentik.flows.models import Flow, FlowDesignation
class UserSerializer(ModelSerializer):
@ -26,6 +28,7 @@ class UserSerializer(ModelSerializer):
is_superuser = BooleanField(read_only=True)
avatar = CharField(read_only=True)
attributes = JSONField(validators=[is_dict], required=False)
class Meta:
@ -120,12 +123,16 @@ class UserViewSet(ModelViewSet):
@permission_required("authentik_core.reset_user_password")
@swagger_auto_schema(
responses={"200": LinkSerializer(many=False)},
responses={"200": LinkSerializer(many=False), "404": "No recovery flow found."},
)
@action(detail=True, pagination_class=None, filter_backends=[])
# pylint: disable=invalid-name, unused-argument
def recovery(self, request: Request, pk: int) -> Response:
"""Create a temporary link that a user can use to recover their accounts"""
# Check that there is a recovery flow, if not return an error
flow = Flow.with_policy(request, designation=FlowDesignation.RECOVERY)
if not flow:
raise Http404
user: User = self.get_object()
token, __ = Token.objects.get_or_create(
identifier=f"{user.uid}-password-reset",

View File

@ -1,7 +1,20 @@
"""API Utilities"""
from typing import Any
from django.db.models import Model
from rest_framework.fields import CharField, IntegerField
from rest_framework.serializers import Serializer, SerializerMethodField
from rest_framework.serializers import (
Serializer,
SerializerMethodField,
ValidationError,
)
def is_dict(value: Any):
"""Ensure a value is a dictionary, useful for JSONFields"""
if isinstance(value, dict):
return
raise ValidationError("Value must be a dictionary.")
class PassiveSerializer(Serializer):
@ -35,6 +48,7 @@ class TypeCreateSerializer(PassiveSerializer):
name = CharField(required=True)
description = CharField(required=True)
component = CharField(required=True)
model_name = CharField(required=True)
class CacheSerializer(PassiveSerializer):

View File

@ -1,6 +1,7 @@
"""Channels base classes"""
from channels.exceptions import DenyConnection
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
@ -22,9 +23,13 @@ class AuthJsonConsumer(JsonWebsocketConsumer):
raw_header = headers[b"authorization"]
token = token_from_header(raw_header)
if not token:
LOGGER.warning("Failed to authenticate")
try:
token = token_from_header(raw_header)
# token is only None when no header was given, in which case we deny too
if not token:
raise DenyConnection()
except AuthenticationFailed as exc:
LOGGER.warning("Failed to authenticate", exc=exc)
raise DenyConnection()
self.user = token.user

View File

@ -10,9 +10,13 @@
<title>{% block title %}{% trans title|default:config.authentik.branding.title %}{% endblock %}</title>
<link rel="icon" type="image/png" href="{% static 'dist/assets/icons/icon.png' %}?v={{ ak_version }}">
<link rel="shortcut icon" type="image/png" href="{% static 'dist/assets/icons/icon.png' %}?v={{ ak_version }}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}?v={{ ak_version }}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly-base.css' %}?v={{ ak_version }}">
<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 }}">
<script src="{% static 'dist/poly.js' %}?v={{ ak_version }}" type="module"></script>
<script>window["polymerSkipLoadingFontRoboto"] = true;</script>
{% block head %}
{% endblock %}
</head>

View File

@ -10,7 +10,7 @@
{% block body %}
<ak-message-container></ak-message-container>
<ak-interface-admin>
<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<section class="ak-initial-load pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<div class="pf-c-empty-state" style="height: 100vh;">
<div class="pf-c-empty-state__content">
<span class="pf-c-spinner pf-m-xl pf-c-empty-state__icon" role="progressbar" aria-valuetext="{% trans 'Loading...' %}">

View File

@ -10,7 +10,7 @@
{% block body %}
<ak-message-container></ak-message-container>
<ak-flow-executor>
<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<section class="ak-initial-load pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<div class="pf-c-empty-state" style="height: 100vh;">
<div class="pf-c-empty-state__content">
<span class="pf-c-spinner pf-m-xl pf-c-empty-state__icon" role="progressbar" aria-valuetext="{% trans 'Loading...' %}">

View File

@ -0,0 +1,15 @@
"""Test API Utils"""
from rest_framework.exceptions import ValidationError
from rest_framework.test import APITestCase
from authentik.core.api.utils import is_dict
class TestAPIUtils(APITestCase):
"""Test API Utils"""
def test_is_dict(self):
"""Test is_dict"""
self.assertIsNone(is_dict({}))
with self.assertRaises(ValidationError):
is_dict("foo")

View File

@ -2,7 +2,7 @@
from django.urls.base import reverse
from rest_framework.test import APITestCase
from authentik.core.models import Token, User
from authentik.core.models import Token, TokenIntents, User
class TestTokenAPI(APITestCase):
@ -19,4 +19,6 @@ class TestTokenAPI(APITestCase):
reverse("authentik_api:token-list"), {"identifier": "test-token"}
)
self.assertEqual(response.status_code, 201)
self.assertEqual(Token.objects.get(identifier="test-token").user, self.user)
token = Token.objects.get(identifier="test-token")
self.assertEqual(token.user, self.user)
self.assertEqual(token.intent, TokenIntents.INTENT_API)

View File

@ -153,10 +153,6 @@ class EventViewSet(ReadOnlyModelViewSet):
data = []
for value, name in EventAction.choices:
data.append(
{
"name": name,
"description": "",
"component": value,
}
{"name": name, "description": "", "component": value, "model_name": ""}
)
return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -4,6 +4,7 @@ from typing import Iterable
from drf_yasg.utils import swagger_auto_schema
from rest_framework import mixins
from rest_framework.decorators import action
from rest_framework.fields import BooleanField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField
@ -19,6 +20,12 @@ from authentik.lib.utils.reflection import all_subclasses
LOGGER = get_logger()
class StageUserSettingSerializer(UserSettingSerializer):
"""User settings but can include a configure flow"""
configure_flow = BooleanField(required=False)
class StageSerializer(ModelSerializer, MetaNameSerializer):
"""Stage Serializer"""
@ -73,12 +80,13 @@ class StageViewSet(
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
}
)
data = sorted(data, key=lambda x: x["name"])
return Response(TypeCreateSerializer(data, many=True).data)
@swagger_auto_schema(responses={200: UserSettingSerializer(many=True)})
@swagger_auto_schema(responses={200: StageUserSettingSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def user_settings(self, request: Request) -> Response:
"""Get all stages the user can configure"""
@ -89,6 +97,10 @@ class StageViewSet(
if not user_settings:
continue
user_settings.initial_data["object_uid"] = str(stage.pk)
if hasattr(stage, "configure_flow"):
user_settings.initial_data["configure_flow"] = bool(
stage.configure_flow
)
if not user_settings.is_valid():
LOGGER.warning(user_settings.errors)
matching_stages.append(user_settings.initial_data)

View File

@ -127,6 +127,7 @@ class FlowExecutorView(APIView):
@swagger_auto_schema(
responses={
200: Challenge(),
404: "No Token found", # This error can be raised by the email stage
},
request_body=no_body,
manual_parameters=[

View File

@ -82,6 +82,7 @@ class ServiceConnectionViewSet(
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
}
)
return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -8,14 +8,14 @@ from rest_framework.serializers import JSONField, ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.utils import PassiveSerializer
from authentik.core.api.utils import PassiveSerializer, is_dict
from authentik.outposts.models import Outpost, default_outpost_config
class OutpostSerializer(ModelSerializer):
"""Outpost Serializer"""
_config = JSONField()
_config = JSONField(validators=[is_dict])
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
class Meta:

View File

@ -356,7 +356,7 @@ class Outpost(models.Model):
intent=TokenIntents.INTENT_API,
description=f"Autogenerated by authentik for Outpost {self.name}",
expiring=False,
managed="goauthentik.io/outpost",
managed=f"goauthentik.io/outpost/{self.token_identifier}",
)
def get_required_objects(self) -> Iterable[models.Model]:

View File

@ -67,6 +67,8 @@ def outpost_controller(self: MonitoredTask, outpost_pk: str):
outpost: Outpost = Outpost.objects.get(pk=outpost_pk)
self.set_uid(slugify(outpost.name))
try:
if not outpost.service_connection:
return
if outpost.type == OutpostType.PROXY:
service_connection = outpost.service_connection
if isinstance(service_connection, DockerServiceConnection):

View File

@ -2,7 +2,7 @@
from rest_framework.fields import BooleanField, CharField, JSONField, ListField
from rest_framework.relations import PrimaryKeyRelatedField
from authentik.core.api.utils import PassiveSerializer
from authentik.core.api.utils import PassiveSerializer, is_dict
from authentik.core.models import User
@ -10,7 +10,7 @@ class PolicyTestSerializer(PassiveSerializer):
"""Test policy execution for a user with context"""
user = PrimaryKeyRelatedField(queryset=User.objects.all())
context = JSONField(required=False)
context = JSONField(required=False, validators=[is_dict])
class PolicyTestResultSerializer(PassiveSerializer):

View File

@ -108,6 +108,7 @@ class PolicyViewSet(
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
}
)
return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -1,13 +1,23 @@
"""Test authorize view"""
from django.test import RequestFactory, TestCase
from django.urls import reverse
from django.utils.encoding import force_str
from authentik.core.models import Application, User
from authentik.flows.challenge import ChallengeTypes
from authentik.flows.models import Flow
from authentik.providers.oauth2.errors import (
AuthorizeError,
ClientIdError,
RedirectUriError,
)
from authentik.providers.oauth2.models import OAuth2Provider
from authentik.providers.oauth2.generators import generate_client_id
from authentik.providers.oauth2.models import (
AuthorizationCode,
GrantTypes,
OAuth2Provider,
RefreshToken,
)
from authentik.providers.oauth2.views.authorize import OAuthAuthorizationParams
@ -32,15 +42,194 @@ class TestViewsAuthorize(TestCase):
)
OAuthAuthorizationParams.from_request(request)
def test_missing_redirect_uri(self):
"""test missing redirect URI"""
def test_request(self):
"""test request param"""
OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=Flow.objects.first(),
redirect_uris="http://local.invalid",
)
with self.assertRaises(AuthorizeError):
request = self.factory.get(
"/",
data={
"response_type": "code",
"client_id": "test",
"redirect_uri": "http://local.invalid",
"request": "foo",
},
)
OAuthAuthorizationParams.from_request(request)
def test_redirect_uri(self):
"""test missing/invalid redirect URI"""
OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=Flow.objects.first(),
redirect_uris="http://local.invalid",
)
with self.assertRaises(RedirectUriError):
request = self.factory.get(
"/", data={"response_type": "code", "client_id": "test"}
)
OAuthAuthorizationParams.from_request(request)
with self.assertRaises(RedirectUriError):
request = self.factory.get(
"/",
data={
"response_type": "code",
"client_id": "test",
"redirect_uri": "http://localhost",
},
)
OAuthAuthorizationParams.from_request(request)
def test_response_type(self):
"""test response_type"""
OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=Flow.objects.first(),
redirect_uris="http://local.invalid",
)
request = self.factory.get(
"/",
data={
"response_type": "code",
"client_id": "test",
"redirect_uri": "http://local.invalid",
},
)
self.assertEqual(
OAuthAuthorizationParams.from_request(request).grant_type,
GrantTypes.AUTHORIZATION_CODE,
)
request = self.factory.get(
"/",
data={
"response_type": "id_token",
"client_id": "test",
"redirect_uri": "http://local.invalid",
"scope": "openid",
"state": "foo",
},
)
self.assertEqual(
OAuthAuthorizationParams.from_request(request).grant_type,
GrantTypes.IMPLICIT,
)
# Implicit without openid scope
with self.assertRaises(AuthorizeError):
request = self.factory.get(
"/",
data={
"response_type": "id_token",
"client_id": "test",
"redirect_uri": "http://local.invalid",
"state": "foo",
},
)
self.assertEqual(
OAuthAuthorizationParams.from_request(request).grant_type,
GrantTypes.IMPLICIT,
)
request = self.factory.get(
"/",
data={
"response_type": "code token",
"client_id": "test",
"redirect_uri": "http://local.invalid",
"scope": "openid",
"state": "foo",
},
)
self.assertEqual(
OAuthAuthorizationParams.from_request(request).grant_type, GrantTypes.HYBRID
)
with self.assertRaises(AuthorizeError):
request = self.factory.get(
"/",
data={
"response_type": "invalid",
"client_id": "test",
"redirect_uri": "http://local.invalid",
},
)
OAuthAuthorizationParams.from_request(request)
def test_full_code(self):
"""Test full authorization"""
flow = Flow.objects.create(slug="empty")
provider = OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=flow,
redirect_uris="http://localhost",
)
Application.objects.create(name="app", slug="app", provider=provider)
state = generate_client_id()
user = User.objects.get(username="akadmin")
self.client.force_login(user)
# Step 1, initiate params and get redirect to flow
self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "code",
"client_id": "test",
"state": state,
"redirect_uri": "http://localhost",
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
code: AuthorizationCode = AuthorizationCode.objects.filter(user=user).first()
self.assertJSONEqual(
force_str(response.content),
{
"type": ChallengeTypes.REDIRECT.value,
"to": f"http://localhost?code={code.code}&state={state}",
},
)
def test_full_implicit(self):
"""Test full authorization"""
flow = Flow.objects.create(slug="empty")
provider = OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=flow,
redirect_uris="http://localhost",
)
Application.objects.create(name="app", slug="app", provider=provider)
state = generate_client_id()
user = User.objects.get(username="akadmin")
self.client.force_login(user)
# Step 1, initiate params and get redirect to flow
self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "id_token",
"client_id": "test",
"state": state,
"scope": "openid",
"redirect_uri": "http://localhost",
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
token: RefreshToken = RefreshToken.objects.filter(user=user).first()
self.assertJSONEqual(
force_str(response.content),
{
"type": ChallengeTypes.REDIRECT.value,
"to": (
f"http://localhost#access_token={token.access_token}"
f"&id_token={provider.encode(token.id_token.to_dict())}&token_type=bearer"
f"&expires_in=600&state={state}"
),
},
)

View File

@ -0,0 +1,171 @@
"""Test token view"""
from base64 import b64encode
from django.test import RequestFactory, TestCase
from django.urls import reverse
from django.utils.encoding import force_str
from authentik.core.models import User
from authentik.flows.models import Flow
from authentik.providers.oauth2.constants import (
GRANT_TYPE_AUTHORIZATION_CODE,
GRANT_TYPE_REFRESH_TOKEN,
)
from authentik.providers.oauth2.generators import (
generate_client_id,
generate_client_secret,
)
from authentik.providers.oauth2.models import (
AuthorizationCode,
OAuth2Provider,
RefreshToken,
)
from authentik.providers.oauth2.views.token import TokenParams
class TestViewsToken(TestCase):
"""Test token view"""
def setUp(self) -> None:
super().setUp()
self.factory = RequestFactory()
def test_request_auth_code(self):
"""test request param"""
provider = OAuth2Provider.objects.create(
name="test",
client_id=generate_client_id(),
client_secret=generate_client_secret(),
authorization_flow=Flow.objects.first(),
redirect_uris="http://local.invalid",
)
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
)
request = self.factory.post(
"/",
data={
"grant_type": GRANT_TYPE_AUTHORIZATION_CODE,
"code": code.code,
"redirect_uri": "http://local.invalid",
},
HTTP_AUTHORIZATION=f"Basic {header}",
)
params = TokenParams.from_request(request)
self.assertEqual(params.provider, provider)
def test_request_refresh_token(self):
"""test request param"""
provider = OAuth2Provider.objects.create(
name="test",
client_id=generate_client_id(),
client_secret=generate_client_secret(),
authorization_flow=Flow.objects.first(),
redirect_uris="http://local.invalid",
)
header = b64encode(
f"{provider.client_id}:{provider.client_secret}".encode()
).decode()
user = User.objects.get(username="akadmin")
token: RefreshToken = RefreshToken.objects.create(
provider=provider,
user=user,
refresh_token=generate_client_id(),
)
request = self.factory.post(
"/",
data={
"grant_type": GRANT_TYPE_REFRESH_TOKEN,
"refresh_token": token.refresh_token,
"redirect_uri": "http://local.invalid",
},
HTTP_AUTHORIZATION=f"Basic {header}",
)
params = TokenParams.from_request(request)
self.assertEqual(params.provider, provider)
def test_auth_code_view(self):
"""test request param"""
provider = OAuth2Provider.objects.create(
name="test",
client_id=generate_client_id(),
client_secret=generate_client_secret(),
authorization_flow=Flow.objects.first(),
redirect_uris="http://local.invalid",
)
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
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
data={
"grant_type": GRANT_TYPE_AUTHORIZATION_CODE,
"code": code.code,
"redirect_uri": "http://local.invalid",
},
HTTP_AUTHORIZATION=f"Basic {header}",
)
new_token: RefreshToken = RefreshToken.objects.filter(user=user).first()
self.assertJSONEqual(
force_str(response.content),
{
"access_token": new_token.access_token,
"refresh_token": new_token.refresh_token,
"token_type": "bearer",
"expires_in": 600,
"id_token": provider.encode(
new_token.id_token.to_dict(),
),
},
)
def test_refresh_token_view(self):
"""test request param"""
provider = OAuth2Provider.objects.create(
name="test",
client_id=generate_client_id(),
client_secret=generate_client_secret(),
authorization_flow=Flow.objects.first(),
redirect_uris="http://local.invalid",
)
header = b64encode(
f"{provider.client_id}:{provider.client_secret}".encode()
).decode()
user = User.objects.get(username="akadmin")
token: RefreshToken = RefreshToken.objects.create(
provider=provider,
user=user,
refresh_token=generate_client_id(),
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
data={
"grant_type": GRANT_TYPE_REFRESH_TOKEN,
"refresh_token": token.refresh_token,
"redirect_uri": "http://local.invalid",
},
HTTP_AUTHORIZATION=f"Basic {header}",
)
new_token: RefreshToken = (
RefreshToken.objects.filter(user=user).exclude(pk=token.pk).first()
)
self.assertJSONEqual(
force_str(response.content),
{
"access_token": new_token.access_token,
"refresh_token": new_token.refresh_token,
"token_type": "bearer",
"expires_in": 600,
"id_token": provider.encode(
new_token.id_token.to_dict(),
),
},
)

View File

@ -3,6 +3,7 @@ import re
from base64 import b64decode
from binascii import Error
from typing import Optional
from urllib.parse import urlparse
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.utils.cache import patch_vary_headers
@ -25,15 +26,34 @@ class TokenResponse(JsonResponse):
self["Pragma"] = "no-cache"
def cors_allow_any(request, response):
"""
Add headers to permit CORS requests from any origin, with or without credentials,
with any headers.
"""
def cors_allow_any(request: HttpRequest, response: HttpResponse, *allowed_origins: str):
"""Add headers to permit CORS requests from any origin, with or without credentials,
with any headers."""
origin = request.META.get("HTTP_ORIGIN")
if not origin:
return response
# OPTIONS requests don't have an authorization header -> hence
# we can't extract the provider this request is for
# so for options requests we allow the calling origin without checking
allowed = request.method == "OPTIONS"
received_origin = urlparse(origin)
for allowed_origin in allowed_origins:
url = urlparse(allowed_origin)
if (
received_origin.scheme == url.scheme
and received_origin.hostname == url.hostname
and received_origin.port == url.port
):
allowed = True
if not allowed:
LOGGER.warning(
"CORS: Origin is not an allowed origin",
requested=origin,
allowed=allowed_origins,
)
return response
# From the CORS spec: The string "*" cannot be used for a resource that supports credentials.
response["Access-Control-Allow-Origin"] = origin
patch_vary_headers(response, ["Origin"])

View File

@ -6,7 +6,7 @@ from urllib.parse import parse_qs, urlencode, urlsplit, urlunsplit
from uuid import uuid4
from django.http import HttpRequest, HttpResponse
from django.http.response import Http404, HttpResponseRedirect
from django.http.response import Http404, HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.utils import timezone
from django.utils.translation import gettext as _
@ -236,6 +236,9 @@ class OAuthFulfillmentStage(StageView):
# pylint: disable=unused-argument
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""final Stage of an OAuth2 Flow"""
if PLAN_CONTEXT_PARAMS not in self.executor.plan.context:
LOGGER.warning("Got to fulfillment stage with no pending context")
return HttpResponseBadRequest()
self.params: OAuthAuthorizationParams = self.executor.plan.context.pop(
PLAN_CONTEXT_PARAMS
)

View File

@ -30,6 +30,8 @@ PLAN_CONTEXT_SCOPES = "scopes"
class ProviderInfoView(View):
"""OpenID-compliant Provider Info"""
provider: OAuth2Provider
def get_info(self, provider: OAuth2Provider) -> dict[str, Any]:
"""Get dictionary for OpenID Connect information"""
scopes = list(
@ -95,19 +97,20 @@ class ProviderInfoView(View):
}
# pylint: disable=unused-argument
def get(
self, request: HttpRequest, application_slug: str, *args, **kwargs
) -> HttpResponse:
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""OpenID-compliant Provider Info"""
return JsonResponse(
self.get_info(self.provider), json_dumps_params={"indent": 2}
)
def dispatch(
self, request: HttpRequest, application_slug: str, *args: Any, **kwargs: Any
) -> HttpResponse:
# Since this view only supports get, we can statically set the CORS headers
application = get_object_or_404(Application, slug=application_slug)
provider: OAuth2Provider = get_object_or_404(
self.provider: OAuth2Provider = get_object_or_404(
OAuth2Provider, pk=application.provider_id
)
return JsonResponse(self.get_info(provider), json_dumps_params={"indent": 2})
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
# Since this view only supports get, we can statically set the CORS headers
response = super().dispatch(request, *args, **kwargs)
cors_allow_any(request, response)
cors_allow_any(request, response, *self.provider.redirect_uris.split("\n"))
return response

View File

@ -198,7 +198,7 @@ class TokenView(View):
response_dict = {
"access_token": refresh_token.access_token,
"refresh_token": refresh_token.refresh_token,
"token_type": "Bearer",
"token_type": "bearer",
"expires_in": timedelta_from_string(
self.params.provider.token_validity
).seconds,

View File

@ -1,7 +1,8 @@
"""authentik OAuth2 OpenID Userinfo views"""
from typing import Any
from typing import Any, Optional
from django.http import HttpRequest, HttpResponse
from django.http.response import HttpResponseBadRequest
from django.utils.translation import gettext_lazy as _
from django.views import View
from structlog.stdlib import get_logger
@ -22,6 +23,8 @@ class UserInfoView(View):
"""Create a dictionary with all the requested claims about the End-User.
See: http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse"""
token: Optional[RefreshToken]
def get_scope_descriptions(self, scopes: list[str]) -> list[dict[str, str]]:
"""Get a list of all Scopes's descriptions"""
scope_descriptions = []
@ -79,16 +82,25 @@ class UserInfoView(View):
final_claims.update(value)
return final_claims
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
self.token = kwargs.get("token", None)
response = super().dispatch(request, *args, **kwargs)
allowed_origins = []
if self.token:
allowed_origins = self.token.provider.redirect_uris.split("\n")
cors_allow_any(self.request, response, *allowed_origins)
return response
def options(self, request: HttpRequest) -> HttpResponse:
return cors_allow_any(self.request, TokenResponse({}))
return TokenResponse({})
def get(self, request: HttpRequest, **kwargs) -> HttpResponse:
"""Handle GET Requests for UserInfo"""
token: RefreshToken = kwargs["token"]
claims = self.get_claims(token)
claims["sub"] = token.id_token.sub
if not self.token:
return HttpResponseBadRequest()
claims = self.get_claims(self.token)
claims["sub"] = self.token.id_token.sub
response = TokenResponse(claims)
cors_allow_any(self.request, response)
return response
def post(self, request: HttpRequest, **kwargs) -> HttpResponse:

View File

@ -21,7 +21,7 @@ class ProxyScopeMappingManager(ObjectManager):
EnsureExists(
ScopeMapping,
"goauthentik.io/providers/proxy/scope-proxy",
name="authentik default OAuth Mapping: proxy outpost",
name="authentik default OAuth Mapping: Proxy outpost",
scope_name=SCOPE_AK_PROXY,
expression=SCOPE_AK_PROXY_EXPRESSION,
),

View File

@ -1,13 +1,12 @@
"""Channels Messages storage"""
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.contrib.messages.storage.base import Message
from django.contrib.messages.storage.fallback import FallbackStorage
from django.contrib.messages.storage.base import BaseStorage, Message
from django.core.cache import cache
from django.http.request import HttpRequest
class ChannelsStorage(FallbackStorage):
class ChannelsStorage(BaseStorage):
"""Send contrib.messages over websocket"""
def __init__(self, request: HttpRequest) -> None:
@ -15,6 +14,9 @@ class ChannelsStorage(FallbackStorage):
super().__init__(request)
self.channel = get_channel_layer()
def _get(self):
return [], True
def _store(self, messages: list[Message], response, *args, **kwargs):
prefix = f"user_{self.request.session.session_key}_messages_"
keys = cache.keys(f"{prefix}*")

View File

@ -143,7 +143,7 @@ SWAGGER_SETTINGS = {
"authentik.api.pagination_schema.PaginationInspector",
],
"SECURITY_DEFINITIONS": {
"token": {"type": "apiKey", "name": "Authorization", "in": "header"}
"Bearer": {"type": "apiKey", "name": "Authorization", "in": "header"}
},
}
@ -254,8 +254,8 @@ EMAIL_HOST = CONFIG.y("email.host")
EMAIL_PORT = int(CONFIG.y("email.port"))
EMAIL_HOST_USER = CONFIG.y("email.username")
EMAIL_HOST_PASSWORD = CONFIG.y("email.password")
EMAIL_USE_TLS = CONFIG.y("email.use_tls")
EMAIL_USE_SSL = CONFIG.y("email.use_ssl")
EMAIL_USE_TLS = CONFIG.y_bool("email.use_tls", True)
EMAIL_USE_SSL = CONFIG.y_bool("email.use_ssl", False)
EMAIL_TIMEOUT = int(CONFIG.y("email.timeout"))
DEFAULT_FROM_EMAIL = CONFIG.y("email.from")
SERVER_EMAIL = DEFAULT_FROM_EMAIL
@ -338,6 +338,7 @@ if CONFIG.y("postgresql.s3_backup"):
# Sentry integration
_ERROR_REPORTING = CONFIG.y_bool("error_reporting.enabled", False)
if not DEBUG and _ERROR_REPORTING:
# pylint: disable=abstract-class-instantiated
sentry_init(
dsn="https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
integrations=[

View File

@ -1,9 +1,11 @@
"""Sync LDAP Users and groups into authentik"""
import ldap3
import ldap3.core.exceptions
from django.core.exceptions import FieldError
from django.db.utils import IntegrityError
from authentik.core.models import Group
from authentik.events.models import Event, EventAction
from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer
@ -47,14 +49,17 @@ class GroupLDAPSynchronizer(BaseLDAPSynchronizer):
"defaults": defaults,
}
)
except IntegrityError as exc:
self._logger.warning("Failed to create group", exc=exc)
self._logger.warning(
(
"To merge new group with existing group, set the group's "
except (IntegrityError, FieldError) as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
message=(
f"Failed to create group: {str(exc)} "
"To merge new group with existing group, set the groups's "
f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'"
)
)
),
source=self._source,
dn=group_dn,
).save()
else:
self._logger.debug("Synced group", group=ak_group.name, created=created)
group_count += 1

View File

@ -3,6 +3,7 @@ from datetime import datetime
import ldap3
import ldap3.core.exceptions
from django.core.exceptions import FieldError
from django.db.utils import IntegrityError
from pytz import UTC
@ -48,7 +49,7 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
"defaults": defaults,
}
)
except IntegrityError as exc:
except (IntegrityError, FieldError) as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
message=(

View File

@ -5,6 +5,7 @@ from rest_framework.decorators import action
from rest_framework.fields import BooleanField, CharField, SerializerMethodField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.sources import SourceSerializer
@ -47,6 +48,20 @@ class OAuthSourceSerializer(SourceSerializer):
"""Get source's type configuration"""
return SourceTypeSerializer(instace.type).data
def validate(self, attrs: dict) -> dict:
provider_type = MANAGER.find_type(attrs.get("provider_type", ""))
for url in [
"authorization_url",
"access_token_url",
"profile_url",
]:
if getattr(provider_type, url, None) is None:
if url not in attrs:
raise ValidationError(
f"{url} is required for provider {provider_type.name}"
)
return attrs
class Meta:
model = OAuthSource
fields = SourceSerializer.Meta.fields + [

View File

@ -9,6 +9,7 @@ from requests.models import Response
from structlog.stdlib import get_logger
from authentik import __version__
from authentik.events.models import Event, EventAction
from authentik.sources.oauth.models import OAuthSource
LOGGER = get_logger()
@ -59,7 +60,16 @@ class BaseOAuthClient:
args.update(additional)
params = urlencode(args)
LOGGER.info("redirect args", **args)
return f"{self.source.authorization_url}?{params}"
base_url = self.source.type.authorization_url
if self.source.authorization_url:
base_url = self.source.authorization_url
if base_url == "":
Event.new(
EventAction.CONFIGURATION_ERROR,
source=self.source,
message="Source has an empty authorization URL.",
).save()
return f"{base_url}?{params}"
def parse_raw_token(self, raw_token: str) -> dict[str, Any]:
"Parse token and secret from raw token response."

View File

@ -28,9 +28,12 @@ class OAuthClient(BaseOAuthClient):
if raw_token is not None and verifier is not None:
token = self.parse_raw_token(raw_token)
try:
access_token_url: str = self.source.type.access_token_url or ""
if self.source.access_token_url:
access_token_url = self.source.access_token_url
response = self.do_request(
"post",
self.source.access_token_url,
access_token_url,
token=token,
headers=self._default_headers,
oauth_verifier=verifier,
@ -48,9 +51,12 @@ class OAuthClient(BaseOAuthClient):
"Fetch the OAuth request token. Only required for OAuth 1.0."
callback = self.request.build_absolute_uri(self.callback)
try:
request_token_url: str = self.source.type.request_token_url or ""
if self.source.request_token_url:
request_token_url = self.source.request_token_url
response = self.do_request(
"post",
self.source.request_token_url,
request_token_url,
headers=self._default_headers,
oauth_callback=callback,
)

View File

@ -56,9 +56,12 @@ class OAuth2Client(BaseOAuthClient):
LOGGER.warning("No code returned by the source")
return None
try:
access_token_url = self.source.type.access_token_url or ""
if self.source.access_token_url:
access_token_url = self.source.access_token_url
response = self.session.request(
"post",
self.source.access_token_url,
access_token_url,
data=args,
headers=self._default_headers,
)

View File

@ -0,0 +1,43 @@
# Generated by Django 3.2 on 2021-04-16 07:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_sources_oauth", "0002_auto_20200520_1108"),
]
operations = [
migrations.AlterField(
model_name="oauthsource",
name="access_token_url",
field=models.CharField(
blank=True,
help_text="URL used by authentik to retrive tokens.",
max_length=255,
verbose_name="Access Token URL",
),
),
migrations.AlterField(
model_name="oauthsource",
name="authorization_url",
field=models.CharField(
blank=True,
help_text="URL the user is redirect to to conest the flow.",
max_length=255,
verbose_name="Authorization URL",
),
),
migrations.AlterField(
model_name="oauthsource",
name="profile_url",
field=models.CharField(
blank=True,
help_text="URL used by authentik to get user information.",
max_length=255,
verbose_name="Profile URL",
),
),
]

View File

@ -0,0 +1,79 @@
# Generated by Django 3.2 on 2021-04-17 19:00
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def update_empty_urls(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
OAuthSource = apps.get_model("authentik_sources_oauth", "oauthsource")
db_alias = schema_editor.connection.alias
for source in OAuthSource.objects.using(db_alias).all():
changed = False
if source.access_token_url == "":
source.access_token_url = None
changed = True
if source.authorization_url == "":
source.authorization_url = None
changed = True
if source.profile_url == "":
source.profile_url = None
changed = True
if source.request_token_url == "":
source.request_token_url = None
changed = True
if changed:
source.save()
class Migration(migrations.Migration):
dependencies = [
("authentik_sources_oauth", "0003_auto_20210416_0726"),
]
operations = [
migrations.AlterField(
model_name="oauthsource",
name="access_token_url",
field=models.CharField(
help_text="URL used by authentik to retrive tokens.",
max_length=255,
null=True,
verbose_name="Access Token URL",
),
),
migrations.AlterField(
model_name="oauthsource",
name="authorization_url",
field=models.CharField(
help_text="URL the user is redirect to to conest the flow.",
max_length=255,
null=True,
verbose_name="Authorization URL",
),
),
migrations.AlterField(
model_name="oauthsource",
name="profile_url",
field=models.CharField(
help_text="URL used by authentik to get user information.",
max_length=255,
null=True,
verbose_name="Profile URL",
),
),
migrations.AlterField(
model_name="oauthsource",
name="request_token_url",
field=models.CharField(
help_text="URL used to request the initial token. This URL is only required for OAuth 1.",
max_length=255,
null=True,
verbose_name="Request Token URL",
),
),
migrations.RunPython(update_empty_urls),
]

View File

@ -19,7 +19,7 @@ class OAuthSource(Source):
provider_type = models.CharField(max_length=255)
request_token_url = models.CharField(
blank=True,
null=True,
max_length=255,
verbose_name=_("Request Token URL"),
help_text=_(
@ -28,16 +28,19 @@ class OAuthSource(Source):
)
authorization_url = models.CharField(
max_length=255,
null=True,
verbose_name=_("Authorization URL"),
help_text=_("URL the user is redirect to to conest the flow."),
)
access_token_url = models.CharField(
max_length=255,
null=True,
verbose_name=_("Access Token URL"),
help_text=_("URL used by authentik to retrive tokens."),
)
profile_url = models.CharField(
max_length=255,
null=True,
verbose_name=_("Profile URL"),
help_text=_("URL used by authentik to get user information."),
)
@ -49,7 +52,7 @@ class OAuthSource(Source):
"""Return the provider instance for this source"""
from authentik.sources.oauth.types.manager import MANAGER
return MANAGER.find_type(self)
return MANAGER.find_type(self.provider_type)
@property
def component(self) -> str:
@ -160,6 +163,16 @@ class OpenIDOAuthSource(OAuthSource):
verbose_name_plural = _("OpenID OAuth Sources")
class PlexOAuthSource(OAuthSource):
"""Login using plex.tv."""
class Meta:
abstract = True
verbose_name = _("Plex OAuth Source")
verbose_name_plural = _("Plex OAuth Sources")
class UserOAuthSourceConnection(UserSourceConnection):
"""Authorized remote OAuth provider."""

View File

@ -9,4 +9,5 @@ AUTHENTIK_SOURCES_OAUTH_TYPES = [
"authentik.sources.oauth.types.twitter",
"authentik.sources.oauth.types.azure_ad",
"authentik.sources.oauth.types.oidc",
"authentik.sources.oauth.types.plex",
]

View File

@ -2,6 +2,7 @@
from django.test import TestCase
from django.urls import reverse
from authentik.sources.oauth.api.source import OAuthSourceSerializer
from authentik.sources.oauth.models import OAuthSource
@ -18,6 +19,31 @@ class TestOAuthSource(TestCase):
consumer_key="",
)
def test_api_validate(self):
"""Test API validation"""
self.assertTrue(
OAuthSourceSerializer(
data={
"name": "foo",
"slug": "bar",
"provider_type": "google",
"consumer_key": "foo",
"consumer_secret": "foo",
}
).is_valid()
)
self.assertFalse(
OAuthSourceSerializer(
data={
"name": "foo",
"slug": "bar",
"provider_type": "openid-connect",
"consumer_key": "foo",
"consumer_secret": "foo",
}
).is_valid()
)
def test_source_redirect(self):
"""test redirect view"""
self.client.get(

View File

@ -1,6 +1,6 @@
"""Source type manager"""
from enum import Enum
from typing import TYPE_CHECKING, Callable, Optional
from typing import Callable, Optional
from structlog.stdlib import get_logger
@ -9,9 +9,6 @@ from authentik.sources.oauth.views.redirect import OAuthRedirect
LOGGER = get_logger()
if TYPE_CHECKING:
from authentik.sources.oauth.models import OAuthSource
class RequestKind(Enum):
"""Enum of OAuth Request types"""
@ -58,24 +55,24 @@ class SourceTypeManager:
"""Get list of tuples of all registered names"""
return [(x.slug, x.name) for x in self.__sources]
def find_type(self, source: "OAuthSource") -> SourceType:
def find_type(self, type_name: str) -> SourceType:
"""Find type based on source"""
found_type = None
for src_type in self.__sources:
if src_type.slug == source.provider_type:
if src_type.slug == type_name:
return src_type
if not found_type:
found_type = SourceType()
LOGGER.warning(
"no matching type found, using default",
wanted=source.provider_type,
have=[x.name for x in self.__sources],
wanted=type_name,
have=[x.slug for x in self.__sources],
)
return found_type
def find(self, source: "OAuthSource", kind: RequestKind) -> Callable:
def find(self, type_name: str, kind: RequestKind) -> Callable:
"""Find fitting Source Type"""
found_type = self.find_type(source)
found_type = self.find_type(type_name)
if kind == RequestKind.CALLBACK:
return found_type.callback_view
if kind == RequestKind.REDIRECT:

View File

@ -0,0 +1,134 @@
"""Plex OAuth Views"""
from typing import Any, Optional
from urllib.parse import urlencode
from django.http.response import Http404
from requests import post
from requests.api import get
from requests.exceptions import RequestException
from structlog.stdlib import get_logger
from authentik import __version__
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
from authentik.sources.oauth.types.manager import MANAGER, SourceType
from authentik.sources.oauth.views.callback import OAuthCallback
from authentik.sources.oauth.views.redirect import OAuthRedirect
LOGGER = get_logger()
SESSION_ID_KEY = "PLEX_ID"
SESSION_CODE_KEY = "PLEX_CODE"
DEFAULT_PAYLOAD = {
"X-Plex-Product": "authentik",
"X-Plex-Version": __version__,
"X-Plex-Device-Vendor": "BeryJu.org",
}
class PlexRedirect(OAuthRedirect):
"""Plex Auth redirect, get a pin then redirect to a URL to claim it"""
headers = {}
def get_pin(self, **data) -> dict:
"""Get plex pin that the user will claim
https://forums.plex.tv/t/authenticating-with-plex/609370"""
return post(
"https://plex.tv/api/v2/pins.json?strong=true",
data=data,
headers=self.headers,
).json()
def get_redirect_url(self, **kwargs) -> str:
slug = kwargs.get("source_slug", "")
self.headers = {"Origin": self.request.build_absolute_uri("/")}
try:
source: OAuthSource = OAuthSource.objects.get(slug=slug)
except OAuthSource.DoesNotExist:
raise Http404(f"Unknown OAuth source '{slug}'.")
else:
payload = DEFAULT_PAYLOAD.copy()
payload["X-Plex-Client-Identifier"] = source.consumer_key
# Get a pin first
pin = self.get_pin(**payload)
LOGGER.debug("Got pin", **pin)
self.request.session[SESSION_ID_KEY] = pin["id"]
self.request.session[SESSION_CODE_KEY] = pin["code"]
qs = {
"clientID": source.consumer_key,
"code": pin["code"],
"forwardUrl": self.request.build_absolute_uri(
self.get_callback_url(source)
),
}
return f"https://app.plex.tv/auth#!?{urlencode(qs)}"
class PlexOAuthClient(OAuth2Client):
"""Retrive the plex token after authentication, then ask the plex API about user info"""
def check_application_state(self) -> bool:
return SESSION_ID_KEY in self.request.session
def get_access_token(self, **request_kwargs) -> Optional[dict[str, Any]]:
payload = dict(DEFAULT_PAYLOAD)
payload["X-Plex-Client-Identifier"] = self.source.consumer_key
payload["Accept"] = "application/json"
response = get(
f"https://plex.tv/api/v2/pins/{self.request.session[SESSION_ID_KEY]}",
headers=payload,
)
response.raise_for_status()
token = response.json()["authToken"]
return {"plex_token": token}
def get_profile_info(self, token: dict[str, str]) -> Optional[dict[str, Any]]:
"Fetch user profile information."
qs = {"X-Plex-Token": token["plex_token"]}
try:
response = self.do_request(
"get", f"https://plex.tv/users/account.json?{urlencode(qs)}"
)
response.raise_for_status()
except RequestException as exc:
LOGGER.warning("Unable to fetch user profile", exc=exc)
return None
else:
return response.json().get("user", {})
class PlexOAuth2Callback(OAuthCallback):
"""Plex OAuth2 Callback"""
client_class = PlexOAuthClient
def get_user_id(
self, source: UserOAuthSourceConnection, info: dict[str, Any]
) -> Optional[str]:
return info.get("uuid")
def get_user_enroll_context(
self,
source: OAuthSource,
access: UserOAuthSourceConnection,
info: dict[str, Any],
) -> dict[str, Any]:
return {
"username": info.get("username"),
"email": info.get("email"),
"name": info.get("title"),
}
@MANAGER.type()
class PlexType(SourceType):
"""Plex Type definition"""
redirect_view = PlexRedirect
callback_view = PlexOAuth2Callback
name = "Plex"
slug = "plex"
authorization_url = ""
access_token_url = "" # nosec
profile_url = ""

View File

@ -209,9 +209,9 @@ class OAuthCallback(OAuthClientMixin, View):
)
return redirect(
reverse(
"authentik_sources_oauth:oauth-client-user",
kwargs={"source_slug": self.source.slug},
"authentik_core:if-admin",
)
+ f"#/user;page-{self.source.slug}"
)
def handle_enroll(

View File

@ -21,6 +21,6 @@ class DispatcherView(View):
if not slug:
raise Http404
source = get_object_or_404(OAuthSource, slug=slug)
view = MANAGER.find(source, kind=RequestKind(self.kind))
view = MANAGER.find(source.provider_type, kind=RequestKind(self.kind))
LOGGER.debug("dispatching OAuth2 request to", view=view, kind=self.kind)
return view.as_view()(*args, **kwargs)

View File

@ -52,7 +52,6 @@ class EmailStageViewSet(ModelViewSet):
queryset = EmailStage.objects.all()
serializer_class = EmailStageSerializer
# TODO: Validate connection settings when use_global_settings is unchecked
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def templates(self, request: Request) -> Response:
@ -64,6 +63,7 @@ class EmailStageViewSet(ModelViewSet):
"name": value,
"description": label,
"component": "",
"model_name": "",
}
)
return Response(TypeCreateSerializer(choices, many=True).data)

View File

@ -1,7 +1,9 @@
"""Invitation Stage API Views"""
from rest_framework.fields import JSONField
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.utils import is_dict
from authentik.flows.api.stages import StageSerializer
from authentik.stages.invitation.models import Invitation, InvitationStage
@ -27,6 +29,8 @@ class InvitationStageViewSet(ModelViewSet):
class InvitationSerializer(ModelSerializer):
"""Invitation Serializer"""
fixed_data = JSONField(validators=[is_dict], required=False)
class Meta:
model = Invitation

View File

@ -142,7 +142,9 @@ class TestInvitationsAPI(APITestCase):
def test_invite_create(self):
"""Test Invitations creation endpoint"""
response = self.client.post(
reverse("authentik_api:invitation-list"), {"identifier": "test-token"}
reverse("authentik_api:invitation-list"),
{"identifier": "test-token", "fixed_data": {}},
format="json",
)
self.assertEqual(response.status_code, 201)
self.assertEqual(Invitation.objects.first().created_by, self.user)

View File

@ -4,6 +4,7 @@ version: '3.2'
services:
postgresql:
image: postgres:12
restart: unless-stopped
volumes:
- database:/var/lib/postgresql/data
networks:
@ -19,7 +20,8 @@ services:
networks:
- internal
server:
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.4.1-rc1}
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.4.3}
restart: unless-stopped
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
@ -33,8 +35,6 @@ services:
- ./media:/media
- ./custom-templates:/templates
- geoip:/geoip
ports:
- 8000
networks:
- internal
labels:
@ -48,7 +48,8 @@ services:
env_file:
- .env
worker:
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.4.1-rc1}
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.4.3}
restart: unless-stopped
command: worker
networks:
- internal
@ -67,7 +68,8 @@ services:
env_file:
- .env
static:
image: ${AUTHENTIK_IMAGE_STATIC:-beryju/authentik-static}:${AUTHENTIK_TAG:-2021.4.1-rc1}
image: ${AUTHENTIK_IMAGE_STATIC:-beryju/authentik-static}:${AUTHENTIK_TAG:-2021.4.3}
restart: unless-stopped
networks:
- internal
labels:
@ -76,13 +78,14 @@ services:
traefik.http.routers.static-router.rule: PathPrefix(`/static`, `/if`, `/media`, `/robots.txt`, `/favicon.ico`)
traefik.http.routers.static-router.tls: 'true'
traefik.http.routers.static-router.service: static-service
traefik.http.services.static-service.loadbalancer.healthcheck.path: /-/health/ready/
traefik.http.services.static-service.loadbalancer.healthcheck.path: /
traefik.http.services.static-service.loadbalancer.healthcheck.interval: 30s
traefik.http.services.static-service.loadbalancer.server.port: '80'
volumes:
- ./media:/usr/share/nginx/html/media
traefik:
image: traefik:2.3
restart: unless-stopped
command:
- "--log.format=json"
- "--api.insecure=true"

View File

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

View File

@ -4,12 +4,12 @@
|-----------------------------------|-------------------------|-------------|
| image.name | beryju/authentik | Image used to run the authentik server and worker |
| image.name_static | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) |
| image.tag | 2021.4.1-rc1 | Image tag |
| image.tag | 2021.4.3 | Image tag |
| image.pullPolicy | IfNotPresent | Image Pull Policy used for all deployments |
| serverReplicas | 1 | Replicas for the Server deployment |
| workerReplicas | 1 | Replicas for the Worker deployment |
| kubernetesIntegration | true | Enable/disable the Kubernetes integration for authentik. This will create a service account for authentik to create and update outposts in authentik |
| config.secretKey | | Secret key used to sign session cookies, generate with `pwgen 50 1` for example. |
| config.secretKey | | Secret key used to sign session cookies, generate with `pwgen 50 1` or `openssl rand -base64 36` for example. |
| config.errorReporting.enabled | false | Enable/disable error reporting |
| config.errorReporting.environment | customer | Environment sent with the error reporting |
| config.errorReporting.sendPii | false | Whether to send Personally-identifiable data with the error reporting |
@ -22,6 +22,11 @@
| config.email.use_ssl | false | Enable SSL |
| config.email.timeout | 10 | SMTP Timeout |
| config.email.from | authentik@localhost | Email address authentik will send from, should have a correct @domain |
| pvc.mode | ReadWriteMany | Mode that the PVCs are created in (uploads and GeoIP, if enabled) |
| pvc.uploadsSize | 5Gi | Size for the uploads PVC |
| pvc.uploadsStorageClass | null | Storage class for the uploads PVC (default: use default storage class) |
| pvc.geoIpSize | 1Gi | Size for the GeoIP PVC |
| pvc.geoIpStorageClass | null | Storage class for the GeoIP PVC (default: use default storage class) |
| geoip.enabled | false | Optionally enable GeoIP |
| geoip.accountId | | GeoIP MaxMind Account ID |
| geoip.licenseKey | | GeoIP MaxMind License key |

View File

@ -10,8 +10,9 @@ metadata:
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
accessModes:
- ReadWriteMany
- {{ .Values.pvc.mode }}
storageClassName: {{ .Values.pvc.geoIpStorageClass }}
resources:
requests:
storage: 1Gi
storage: {{ .Values.pvc.geoIpSize }}
{{- end }}

View File

@ -9,7 +9,8 @@ metadata:
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
accessModes:
- ReadWriteMany
- {{ .Values.pvc.mode }}
storageClassName: {{ .Values.pvc.uploadsStorageClass }}
resources:
requests:
storage: 5Gi
storage: {{ .Values.pvc.uploadsSize }}

View File

@ -43,29 +43,6 @@ spec:
values:
- web
topologyKey: "kubernetes.io/hostname"
initContainers:
- name: authentik-database-migrations
image: "{{ .Values.image.name }}:{{ .Values.image.tag }}"
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
args: [migrate]
envFrom:
- configMapRef:
name: {{ include "authentik.fullname" . }}-config
prefix: AUTHENTIK_
- secretRef:
name: {{ include "authentik.fullname" . }}-secret-key
prefix: AUTHENTIK_
env:
- name: AUTHENTIK_REDIS__PASSWORD
valueFrom:
secretKeyRef:
name: "{{ .Release.Name }}-redis"
key: redis-password
- name: AUTHENTIK_POSTGRESQL__PASSWORD
valueFrom:
secretKeyRef:
name: "{{ .Release.Name }}-postgresql"
key: postgresql-password
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.name }}:{{ .Values.image.tag }}"

View File

@ -5,7 +5,7 @@ image:
name: beryju/authentik
name_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended
tag: 2021.4.1-rc1
tag: 2021.4.3
pullPolicy: IfNotPresent
serverReplicas: 1
@ -15,7 +15,14 @@ workerReplicas: 1
kubernetesIntegration: true
monitoring:
enabled: true
enabled: false
pvc:
mode: ReadWriteMany
uploadsSize: 5Gi
uploadsStorageClass: null
geoIpSize: 1Gi
geoIpStorageClass: null
config:
# Optionally specify fixed secret_key, otherwise generated automatically

View File

@ -2,13 +2,13 @@
python -m lifecycle.wait_for_db
printf '{"event": "Bootstrap completed", "level": "info", "logger": "bootstrap", "command": "%s"}\n' "$@" > /dev/stderr
if [[ "$1" == "server" ]]; then
python -m lifecycle.migrate
gunicorn -c /lifecycle/gunicorn.conf.py authentik.root.asgi:application
elif [[ "$1" == "worker" ]]; then
celery -A authentik.root.celery worker --autoscale 3,1 -E -B -s /tmp/celerybeat-schedule -Q authentik,authentik_scheduled,authentik_events
elif [[ "$1" == "migrate" ]]; then
# Run system migrations first, run normal migrations after
printf "DEPERECATED: database migrations are now executed automatically on startup."
python -m lifecycle.migrate
python -m manage migrate
elif [[ "$1" == "backup" ]]; then
python -m manage dbbackup --clean
elif [[ "$1" == "restore" ]]; then

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python
"""System Migration handler"""
import os
from importlib.util import module_from_spec, spec_from_file_location
from inspect import getmembers, isclass
from pathlib import Path
@ -11,6 +12,7 @@ from structlog.stdlib import get_logger
from authentik.lib.config import CONFIG
LOGGER = get_logger()
ADV_LOCK_UID = 1000
class BaseMigration:
@ -40,18 +42,36 @@ if __name__ == "__main__":
host=CONFIG.y("postgresql.host"),
)
curr = conn.cursor()
# lock an advisory lock to prevent multiple instances from migrating at once
LOGGER.info("waiting to acquire database lock")
curr.execute("SELECT pg_advisory_lock(%s)", (ADV_LOCK_UID,))
try:
for migration in (
Path(__file__).parent.absolute().glob("system_migrations/*.py")
):
spec = spec_from_file_location("lifecycle.system_migrations", migration)
mod = module_from_spec(spec)
# pyright: reportGeneralTypeIssues=false
spec.loader.exec_module(mod)
for migration in Path(__file__).parent.absolute().glob("system_migrations/*.py"):
spec = spec_from_file_location("lifecycle.system_migrations", migration)
mod = module_from_spec(spec)
# pyright: reportGeneralTypeIssues=false
spec.loader.exec_module(mod)
for name, sub in getmembers(mod, isclass):
if name != "Migration":
continue
migration = sub(curr, conn)
if migration.needs_migration():
LOGGER.info("Migration needs to be applied", migration=sub)
migration.run()
LOGGER.info("Migration finished applying", migration=sub)
for name, sub in getmembers(mod, isclass):
if name != "Migration":
continue
migration = sub(curr, conn)
if migration.needs_migration():
LOGGER.info("Migration needs to be applied", migration=sub)
migration.run()
LOGGER.info("Migration finished applying", migration=sub)
LOGGER.info("applying django migrations")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(["", "migrate"])
finally:
curr.execute("SELECT pg_advisory_unlock(%s)", (ADV_LOCK_UID,))

View File

@ -18,15 +18,12 @@ stages:
steps:
- task: GoTool@0
inputs:
version: '1.15'
version: '1.16.3'
- task: CmdLine@2
inputs:
script: |
sudo apt install gnupg ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 379CE192D401AB61
echo "deb https://dl.bintray.com/go-swagger/goswagger-debian ubuntu main" | sudo tee /etc/apt/sources.list.d/goswagger.list
sudo apt update
sudo apt install swagger
sudo wget -O /usr/local/bin/swagger https://github.com/go-swagger/go-swagger/releases/latest/download/swagger_linux_amd64
sudo chmod +x /usr/local/bin/swagger
mkdir -p $(go env GOPATH)
swagger generate client -f ../swagger.yaml -A authentik -t pkg/
workingDirectory: 'outpost/'
@ -43,11 +40,7 @@ stages:
steps:
- task: GoTool@0
inputs:
version: '1.15'
- task: Go@0
inputs:
command: 'get'
arguments: '-u golang.org/x/lint/golint'
version: '1.16.3'
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
@ -56,7 +49,7 @@ stages:
- task: CmdLine@2
inputs:
script: |
$(go list -f {{.Target}} golang.org/x/lint/golint) ./...
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.39.0 golangci-lint run -v --timeout 200s
workingDirectory: 'outpost/'
- stage: proxy_build_go
jobs:
@ -66,7 +59,7 @@ stages:
steps:
- task: GoTool@0
inputs:
version: '1.15'
version: '1.16.3'
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
@ -85,7 +78,7 @@ stages:
steps:
- task: GoTool@0
inputs:
version: '1.15'
version: '1.16.3'
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'

View File

@ -52,13 +52,14 @@ func main() {
ac.Server = proxy.NewServer(ac)
ac.Start()
err = ac.Start()
if err != nil {
log.WithError(err).Panic("Failed to run server")
}
for {
select {
case <-interrupt:
ac.Shutdown()
os.Exit(0)
}
<-interrupt
ac.Shutdown()
os.Exit(0)
}
}

View File

@ -6,13 +6,14 @@ require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/getsentry/sentry-go v0.10.0
github.com/go-openapi/analysis v0.20.1 // indirect
github.com/go-openapi/errors v0.20.0
github.com/go-openapi/runtime v0.19.27
github.com/go-openapi/runtime v0.19.28
github.com/go-openapi/strfmt v0.20.1
github.com/go-openapi/swag v0.19.15
github.com/go-openapi/validate v0.20.2
github.com/go-redis/redis/v7 v7.4.0 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/websocket v1.4.2
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/justinas/alice v1.2.0
@ -20,9 +21,9 @@ require (
github.com/magiconair/properties v1.8.5 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pelletier/go-toml v1.9.0 // indirect
github.com/pkg/errors v0.9.1
github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect
github.com/recws-org/recws v1.3.1
github.com/sirupsen/logrus v1.8.1
github.com/spf13/afero v1.6.0 // indirect
@ -30,9 +31,10 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1 // indirect
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 // indirect
golang.org/x/net v0.0.0-20210331060903-cb1fcc7394e5 // indirect
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d // indirect
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 // indirect
golang.org/x/sys v0.0.0-20210415045647-66c3f260301c // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect

View File

@ -154,8 +154,9 @@ github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9sn
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=
github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk=
github.com/go-openapi/analysis v0.20.0 h1:UN09o0kNhleunxW7LR+KnltD0YrJ8FF03pSqvAN3Vro=
github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og=
github.com/go-openapi/analysis v0.20.1 h1:zdVbw8yoD4SWZeq+cWdGgquaB0W4VrsJvDJHJND/Ktc=
github.com/go-openapi/analysis v0.20.1/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og=
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
@ -195,8 +196,8 @@ github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29g
github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98=
github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
github.com/go-openapi/runtime v0.19.27 h1:4zrQCJoP7rqNCUaApDv1MdPkaa5TuPfO05Lq5WLhX9I=
github.com/go-openapi/runtime v0.19.27/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M=
github.com/go-openapi/runtime v0.19.28 h1:9lYu6axek8LJrVkMVViVirRcpoaCxXX7+sSvmizGVnA=
github.com/go-openapi/runtime v0.19.28/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
@ -300,8 +301,9 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
@ -316,8 +318,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@ -503,8 +506,8 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml v1.9.0 h1:NOd0BRdOKpPf0SxkL3HxSQOG7rNh+4kl6PHcBPFs7Q0=
github.com/pelletier/go-toml v1.9.0/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
@ -518,8 +521,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e h1:BLqxdwZ6j771IpSCRx7s/GJjXHUE00Hmu7/YegCGdzA=
github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ=
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac h1:jWKYCNlX4J5s8M0nHYkh7Y7c9gRVDEb3mq51j5J0F5M=
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -662,8 +665,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc=
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -738,15 +741,16 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210331060903-cb1fcc7394e5 h1:zuP3axpB9rV3xH0EA7n3/gCrNPZm2SRl0l4mVH2BRj4=
golang.org/x/net v0.0.0-20210331060903-cb1fcc7394e5/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d h1:BgJvlyh+UqCUaPlscHJ+PN8GcpfrFdr7NHjd1JL0+Gs=
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 h1:Mj83v+wSRNEar42a/MQgxk9X42TdEmrOl9i+y8WbxLo=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 h1:D7nTwh4J0i+5mW4Zjzn5omvlr6YBcWywE6KOcatyNxY=
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -806,9 +810,9 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/sys v0.0.0-20210415045647-66c3f260301c h1:6L+uOeS3OQt/f4eFHXZcTxeZrGCuz+CLElgEBjbcTA4=
golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -816,8 +820,9 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -957,8 +962,10 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -4,6 +4,7 @@ import (
"fmt"
"math/rand"
"net/url"
"os"
"time"
"github.com/go-openapi/runtime"
@ -39,12 +40,12 @@ type APIController struct {
}
// NewAPIController initialise new API Controller instance from URL and API token
func NewAPIController(pbURL url.URL, token string) *APIController {
transport := httptransport.New(pbURL.Host, client.DefaultBasePath, []string{pbURL.Scheme})
func NewAPIController(akURL url.URL, token string) *APIController {
transport := httptransport.New(akURL.Host, client.DefaultBasePath, []string{akURL.Scheme})
transport.Transport = SetUserAgent(getTLSTransport(), fmt.Sprintf("authentik-proxy@%s", pkg.VERSION))
// create the transport
auth := httptransport.BasicAuth("", token)
auth := httptransport.BearerToken(token)
// create the API client, with the transport
apiClient := client.New(transport, strfmt.Default)
@ -53,10 +54,11 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
// Because we don't know the outpost UUID, we simply do a list and pick the first
// The service account this token belongs to should only have access to a single outpost
outposts, err := apiClient.Outposts.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth)
outposts, err := apiClient.Outposts.OutpostsInstancesList(outposts.NewOutpostsInstancesListParams(), auth)
if err != nil {
log.WithError(err).Panic("Failed to fetch configuration")
log.WithError(err).Error("Failed to fetch configuration")
os.Exit(1)
}
outpost := outposts.Payload.Results[0]
doGlobalSetup(outpost.Config.(map[string]interface{}))
@ -73,7 +75,7 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
lastBundleHash: "",
}
ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset)
ac.initWS(pbURL, outpost.Pk)
ac.initWS(akURL, outpost.Pk)
return ac
}
@ -96,7 +98,10 @@ func (a *APIController) Start() error {
a.startWSHealth()
}()
go func() {
a.Server.Start()
err := a.Server.Start()
if err != nil {
panic(err)
}
}()
return nil
}

View File

@ -2,7 +2,6 @@ package ak
import (
"crypto/tls"
"encoding/base64"
"fmt"
"net/http"
"net/url"
@ -20,7 +19,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
pathTemplate := "%s://%s/ws/outpost/%s/"
scheme := strings.ReplaceAll(pbURL.Scheme, "http", "ws")
authHeader := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("Basic :%s", ac.token)))
authHeader := fmt.Sprintf("Bearer %s", ac.token)
header := http.Header{
"Authorization": []string{authHeader},
@ -65,7 +64,6 @@ func (ac *APIController) Shutdown() {
ac.logger.Println("write close:", err)
return
}
return
}
func (ac *APIController) startWSHandler() {
@ -93,7 +91,8 @@ func (ac *APIController) startWSHandler() {
}
func (ac *APIController) startWSHealth() {
for ; true; <-time.Tick(time.Second * 10) {
ticker := time.NewTicker(time.Second * 10)
for ; true; <-ticker.C {
if !ac.wsConn.IsConnected() {
continue
}

View File

@ -38,7 +38,11 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
return nil
}
providerOpts := &options.Options{}
copier.Copy(&providerOpts, getCommonOptions())
err = copier.Copy(&providerOpts, getCommonOptions())
if err != nil {
log.WithError(err).Warning("Failed to copy options, skipping provider")
return nil
}
providerOpts.ClientID = provider.ClientID
providerOpts.ClientSecret = provider.ClientSecret
@ -62,7 +66,7 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
ID: "default",
URI: *provider.InternalHost,
Path: "/",
InsecureSkipTLSVerify: *&provider.InternalHostSslValidation,
InsecureSkipTLSVerify: provider.InternalHostSslValidation,
},
}
@ -85,7 +89,7 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
return providerOpts
}
x509cert, err := tls.X509KeyPair([]byte(*&cert.Payload.Data), []byte(key.Payload.Data))
x509cert, err := tls.X509KeyPair([]byte(cert.Payload.Data), []byte(key.Payload.Data))
if err != nil {
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to parse certificate")
return providerOpts
@ -135,7 +139,7 @@ func (pb *providerBundle) Build(provider *models.ProxyOutpostConfig) {
os.Exit(1)
}
if *&provider.BasicAuthEnabled {
if provider.BasicAuthEnabled {
oauthproxy.SetBasicAuth = true
oauthproxy.BasicAuthUserAttribute = provider.BasicAuthUserAttribute
oauthproxy.BasicAuthPasswordAttribute = provider.BasicAuthPasswordAttribute

View File

@ -1,3 +1,3 @@
package pkg
const VERSION = "2021.4.1-rc1"
const VERSION = "2021.4.3"

View File

@ -13,12 +13,12 @@ consumes:
produces:
- application/json
securityDefinitions:
token:
Bearer:
type: apiKey
name: Authorization
in: header
security:
- token: []
- Bearer: []
paths:
/admin/apps/:
get:
@ -131,27 +131,7 @@ paths:
get:
operationId: admin_version_list
description: Get running and latest version.
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: Page Index
required: false
type: integer
- name: page_size
in: query
description: Page Size
required: false
type: integer
parameters: []
responses:
'200':
description: ''
@ -1172,6 +1152,9 @@ paths:
description: Page Size
required: false
type: integer
- name: superuser_full_list
in: query
type: boolean
responses:
'200':
description: ''
@ -2257,15 +2240,15 @@ paths:
description: ''
schema:
$ref: '#/definitions/Link'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- core
parameters:
@ -3684,15 +3667,15 @@ paths:
description: ''
schema:
$ref: '#/definitions/Challenge'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- flows
post:
@ -4386,6 +4369,281 @@ paths:
description: A unique integer value identifying this OAuth2 Token.
required: true
type: integer
/outposts/instances/:
get:
operationId: outposts_instances_list
description: Outpost Viewset
parameters:
- name: providers__isnull
in: query
description: ''
required: false
type: string
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: Page Index
required: false
type: integer
- name: page_size
in: query
description: Page Size
required: false
type: integer
responses:
'200':
description: ''
schema:
required:
- results
- pagination
type: object
properties:
pagination:
required:
- next
- previous
- count
- current
- total_pages
- start_index
- end_index
type: object
properties:
next:
type: number
previous:
type: number
count:
type: number
current:
type: number
total_pages:
type: number
start_index:
type: number
end_index:
type: number
results:
type: array
items:
$ref: '#/definitions/Outpost'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- outposts
post:
operationId: outposts_instances_create
description: Outpost Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Outpost'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/Outpost'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- outposts
parameters: []
/outposts/instances/default_settings/:
get:
operationId: outposts_instances_default_settings
description: Global default outpost config
parameters:
- name: providers__isnull
in: query
description: ''
required: false
type: string
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: Page Index
required: false
type: integer
- name: page_size
in: query
description: Page Size
required: false
type: integer
responses:
'200':
description: ''
schema:
$ref: '#/definitions/OutpostDefaultConfig'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- outposts
parameters: []
/outposts/instances/{uuid}/:
get:
operationId: outposts_instances_read
description: Outpost Viewset
parameters: []
responses:
'200':
description: ''
schema:
$ref: '#/definitions/Outpost'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- outposts
put:
operationId: outposts_instances_update
description: Outpost Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Outpost'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/Outpost'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- outposts
patch:
operationId: outposts_instances_partial_update
description: Outpost Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Outpost'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/Outpost'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- outposts
delete:
operationId: outposts_instances_delete
description: Outpost Viewset
parameters: []
responses:
'204':
description: ''
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- outposts
parameters:
- name: uuid
in: path
description: A UUID string identifying this outpost.
required: true
type: string
format: uuid
/outposts/instances/{uuid}/health/:
get:
operationId: outposts_instances_health
description: Get outposts current health
parameters: []
responses:
'200':
description: ''
schema:
type: array
items:
$ref: '#/definitions/OutpostHealth'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- outposts
parameters:
- name: uuid
in: path
description: A UUID string identifying this outpost.
required: true
type: string
format: uuid
/outposts/outposts/:
get:
operationId: outposts_outposts_list
@ -7691,6 +7949,9 @@ paths:
required: true
schema:
$ref: '#/definitions/PolicyTest'
- name: format_result
in: query
type: boolean
responses:
'200':
description: ''
@ -10285,7 +10546,7 @@ paths:
schema:
type: array
items:
$ref: '#/definitions/UserSetting'
$ref: '#/definitions/StageUserSetting'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
@ -14791,6 +15052,7 @@ definitions:
- name
- description
- component
- model_name
type: object
properties:
name:
@ -14805,6 +15067,10 @@ definitions:
title: Component
type: string
minLength: 1
model_name:
title: Model name
type: string
minLength: 1
EventTopPerUser:
required:
- application
@ -16983,9 +17249,6 @@ definitions:
- name
- slug
- provider_type
- authorization_url
- access_token_url
- profile_url
- consumer_key
- consumer_secret
type: object
@ -17052,24 +17315,29 @@ definitions:
for OAuth 1.
type: string
maxLength: 255
minLength: 1
x-nullable: true
authorization_url:
title: Authorization URL
description: URL the user is redirect to to conest the flow.
type: string
maxLength: 255
minLength: 1
x-nullable: true
access_token_url:
title: Access Token URL
description: URL used by authentik to retrive tokens.
type: string
maxLength: 255
minLength: 1
x-nullable: true
profile_url:
title: Profile URL
description: URL used by authentik to get user information.
type: string
maxLength: 255
minLength: 1
x-nullable: true
consumer_key:
title: Consumer key
type: string
@ -17242,6 +17510,28 @@ definitions:
\ log out manually. (Format: hours=1;minutes=2;seconds=3)."
type: string
minLength: 1
StageUserSetting:
required:
- object_uid
- component
- title
type: object
properties:
object_uid:
title: Object uid
type: string
minLength: 1
component:
title: Component
type: string
minLength: 1
title:
title: Title
type: string
minLength: 1
configure_flow:
title: Configure flow
type: boolean
AuthenticatorStaticStage:
required:
- name
@ -17725,7 +18015,6 @@ definitions:
x-nullable: true
fixed_data:
title: Fixed data
description: Optional fixed data to enforce on user enrollment.
type: object
created_by:
required:

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title/><path d="M11.643 0H4.68l7.679 12L4.68 24h6.963l7.677-12-7.677-12"/></svg>

After

Width:  |  Height:  |  Size: 175 B

View File

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

277
web/package-lock.json generated
View File

@ -1336,28 +1336,86 @@
}
},
"@lingui/babel-plugin-extract-messages": {
"version": "3.8.6",
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.8.6.tgz",
"integrity": "sha512-SJoeg6G+vh3AYWDBuwsXitV7YTp9JxUwjPll+TKIsuMITvaOq8+lm1gt9fIhu1FiwmoddmW9wNd9+R231I4TiQ==",
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.8.9.tgz",
"integrity": "sha512-zPpSl89nvUrLyGHfVosZHCP9fylfCfkEMc29wGdjE6f0U+frJ59NRLilWMy7xaE8uz97cD5vkhYaaF1wnavhxA==",
"requires": {
"@babel/generator": "^7.11.6",
"@babel/runtime": "^7.11.2",
"@lingui/conf": "^3.8.6",
"@lingui/conf": "^3.8.9",
"mkdirp": "^1.0.4"
},
"dependencies": {
"@lingui/conf": {
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.8.9.tgz",
"integrity": "sha512-r0RGchwiALjCE6CSOtOKbOqVrNg1EQ78AXjyvbrtJoPWVlChDasWCckXEF0BSnsoZaRP6nQCAI+dsQiGW1deWg==",
"requires": {
"@babel/runtime": "^7.11.2",
"@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2",
"chalk": "^4.1.0",
"cosmiconfig": "^7.0.0",
"jest-validate": "^26.5.2",
"lodash.get": "^4.4.2"
}
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"@lingui/cli": {
"version": "3.8.6",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.8.6.tgz",
"integrity": "sha512-D3r6U6W/CGch8rEd/IFgImhoIlKePtyWLEwmybKnNfXjbtWBVtnVWroHl5IHYkXw9oGbOwxJDERMduR3D6IItw==",
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.8.9.tgz",
"integrity": "sha512-UccLtfwrTjXrZcTxpqA4ggYhuUMbXZtzbUVks8nDVt3emVqU56C3VMvVD8WKXLL8Qmq9cEDXPwIZy7IKRL4mEQ==",
"requires": {
"@babel/generator": "^7.11.6",
"@babel/parser": "^7.11.5",
"@babel/plugin-syntax-jsx": "^7.10.4",
"@babel/runtime": "^7.11.2",
"@babel/types": "^7.11.5",
"@lingui/babel-plugin-extract-messages": "^3.8.6",
"@lingui/conf": "^3.8.6",
"@lingui/babel-plugin-extract-messages": "^3.8.9",
"@lingui/conf": "^3.8.9",
"babel-plugin-macros": "^3.0.1",
"bcp-47": "^1.0.7",
"chalk": "^4.1.0",
@ -1384,6 +1442,19 @@
"ramda": "^0.27.1"
},
"dependencies": {
"@lingui/conf": {
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.8.9.tgz",
"integrity": "sha512-r0RGchwiALjCE6CSOtOKbOqVrNg1EQ78AXjyvbrtJoPWVlChDasWCckXEF0BSnsoZaRP6nQCAI+dsQiGW1deWg==",
"requires": {
"@babel/runtime": "^7.11.2",
"@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2",
"chalk": "^4.1.0",
"cosmiconfig": "^7.0.0",
"jest-validate": "^26.5.2",
"lodash.get": "^4.4.2"
}
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@ -1460,9 +1531,9 @@
}
},
"@lingui/conf": {
"version": "3.8.6",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.8.6.tgz",
"integrity": "sha512-qfTzii2IIt5CBE3vIKYHHY0cH8Nz6gRnIU6DnJhQD76LvdzplAwa7Pt/QwIGJKaV9IvfFZCrNAM34P/hSKl5uQ==",
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.8.9.tgz",
"integrity": "sha512-r0RGchwiALjCE6CSOtOKbOqVrNg1EQ78AXjyvbrtJoPWVlChDasWCckXEF0BSnsoZaRP6nQCAI+dsQiGW1deWg==",
"requires": {
"@babel/runtime": "^7.11.2",
"@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2",
@ -1518,9 +1589,9 @@
}
},
"@lingui/core": {
"version": "3.8.6",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.8.6.tgz",
"integrity": "sha512-zuyu8tVCsctVK4A9mdlr0v1GJ1o8CpA8FE0M6vSKtz8vnPDsaPdNr9YqME6EW++w4PwhnuMyfrGx8iFIJBzp1A==",
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.8.9.tgz",
"integrity": "sha512-QmEfgukR7w/4/4USZT0LGNt7Yq/RgirFl4088wEta0vgroidxaCRgUXr8RXcdFVjTdtG5dc86JTEj4inZECKvg==",
"requires": {
"@babel/runtime": "^7.11.2",
"make-plural": "^6.2.2",
@ -1528,12 +1599,12 @@
}
},
"@lingui/macro": {
"version": "3.8.6",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.8.6.tgz",
"integrity": "sha512-WABB1ufaIB5pSoSArA0vk808OPfFCkYDzOL1foE11WS69/LEJ+1XwEd2hdFmrYppml9pLCEG+obcd2PbZXHatw==",
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.8.9.tgz",
"integrity": "sha512-9LhlbkJ9wOtOLhlaVRLHCRL55S5wOFyyqEhUM+ujUmCskTmMmXzjnRsw5f11nJTK1JJETMT/VlUB5/p7D7Edkw==",
"requires": {
"@babel/runtime": "^7.11.2",
"@lingui/conf": "^3.8.6",
"@lingui/conf": "^3.8.9",
"ramda": "^0.27.1"
}
},
@ -1885,9 +1956,9 @@
}
},
"@types/codemirror": {
"version": "0.0.108",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.108.tgz",
"integrity": "sha512-3FGFcus0P7C2UOGCNUVENqObEb4SFk+S8Dnxq7K6aIsLVs/vDtlangl3PEO0ykaKXyK56swVF6Nho7VsA44uhw==",
"version": "0.0.109",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.109.tgz",
"integrity": "sha512-cSdiHeeLjvGn649lRTNeYrVCDOgDrtP+bDDSFDd1TF+i0jKGPDRozno2NOJ9lTniso+taiv4kiVS8dgM8Jm5lg==",
"requires": {
"@types/tern": "*"
}
@ -2013,12 +2084,12 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
},
"@typescript-eslint/eslint-plugin": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz",
"integrity": "sha512-FPUyCPKZbVGexmbCFI3EQHzCZdy2/5f+jv6k2EDljGdXSRc0cKvbndd2nHZkSLqCNOPk0jB6lGzwIkglXcYVsQ==",
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz",
"integrity": "sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==",
"requires": {
"@typescript-eslint/experimental-utils": "4.21.0",
"@typescript-eslint/scope-manager": "4.21.0",
"@typescript-eslint/experimental-utils": "4.22.0",
"@typescript-eslint/scope-manager": "4.22.0",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15",
@ -2028,50 +2099,102 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.21.0.tgz",
"integrity": "sha512-cEbgosW/tUFvKmkg3cU7LBoZhvUs+ZPVM9alb25XvR0dal4qHL3SiUqHNrzoWSxaXA9gsifrYrS1xdDV6w/gIA==",
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz",
"integrity": "sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==",
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.21.0",
"@typescript-eslint/types": "4.21.0",
"@typescript-eslint/typescript-estree": "4.21.0",
"@typescript-eslint/scope-manager": "4.22.0",
"@typescript-eslint/types": "4.22.0",
"@typescript-eslint/typescript-estree": "4.22.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
}
},
"@typescript-eslint/parser": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.21.0.tgz",
"integrity": "sha512-eyNf7QmE5O/l1smaQgN0Lj2M/1jOuNg2NrBm1dqqQN0sVngTLyw8tdCbih96ixlhbF1oINoN8fDCyEH9SjLeIA==",
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.0.tgz",
"integrity": "sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==",
"requires": {
"@typescript-eslint/scope-manager": "4.21.0",
"@typescript-eslint/types": "4.21.0",
"@typescript-eslint/typescript-estree": "4.21.0",
"@typescript-eslint/scope-manager": "4.22.0",
"@typescript-eslint/types": "4.22.0",
"@typescript-eslint/typescript-estree": "4.22.0",
"debug": "^4.1.1"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz",
"integrity": "sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==",
"requires": {
"@typescript-eslint/types": "4.22.0",
"@typescript-eslint/visitor-keys": "4.22.0"
}
},
"@typescript-eslint/types": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz",
"integrity": "sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA=="
},
"@typescript-eslint/typescript-estree": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz",
"integrity": "sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==",
"requires": {
"@typescript-eslint/types": "4.22.0",
"@typescript-eslint/visitor-keys": "4.22.0",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz",
"integrity": "sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==",
"requires": {
"@typescript-eslint/types": "4.22.0",
"eslint-visitor-keys": "^2.0.0"
}
},
"globby": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz",
"integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==",
"requires": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.1.1",
"ignore": "^5.1.4",
"merge2": "^1.3.0",
"slash": "^3.0.0"
}
}
}
},
"@typescript-eslint/scope-manager": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.21.0.tgz",
"integrity": "sha512-kfOjF0w1Ix7+a5T1knOw00f7uAP9Gx44+OEsNQi0PvvTPLYeXJlsCJ4tYnDj5PQEYfpcgOH5yBlw7K+UEI9Agw==",
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz",
"integrity": "sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==",
"requires": {
"@typescript-eslint/types": "4.21.0",
"@typescript-eslint/visitor-keys": "4.21.0"
"@typescript-eslint/types": "4.22.0",
"@typescript-eslint/visitor-keys": "4.22.0"
}
},
"@typescript-eslint/types": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.21.0.tgz",
"integrity": "sha512-+OQaupjGVVc8iXbt6M1oZMwyKQNehAfLYJJ3SdvnofK2qcjfor9pEM62rVjBknhowTkh+2HF+/KdRAc/wGBN2w=="
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz",
"integrity": "sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA=="
},
"@typescript-eslint/typescript-estree": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.21.0.tgz",
"integrity": "sha512-ZD3M7yLaVGVYLw4nkkoGKumb7Rog7QID9YOWobFDMQKNl+vPxqVIW/uDk+MDeGc+OHcoG2nJ2HphwiPNajKw3w==",
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz",
"integrity": "sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==",
"requires": {
"@typescript-eslint/types": "4.21.0",
"@typescript-eslint/visitor-keys": "4.21.0",
"@typescript-eslint/types": "4.22.0",
"@typescript-eslint/visitor-keys": "4.22.0",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
@ -2095,11 +2218,11 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.21.0.tgz",
"integrity": "sha512-dH22dROWGi5Z6p+Igc8bLVLmwy7vEe8r+8c+raPQU0LxgogPUrRAtRGtvBWmlr9waTu3n+QLt/qrS/hWzk1x5w==",
"version": "4.22.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz",
"integrity": "sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==",
"requires": {
"@typescript-eslint/types": "4.21.0",
"@typescript-eslint/types": "4.22.0",
"eslint-visitor-keys": "^2.0.0"
}
},
@ -2524,9 +2647,9 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
},
"chart.js": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.0.2.tgz",
"integrity": "sha512-DR0GmFSlxcFJp/w//ZmbxSduAkH/AqwxoiZxK97KHnWZf6gvsKWS3160WvNMMHYvzW9OXqGWjPjVh1Qu+xDabg=="
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.1.1.tgz",
"integrity": "sha512-ghNJersc9VD9MECwa5bL8gqvCkndW6RSCicdEHL9lIriNtXwKawlSmwo+u6KNXLYT2+f24GdFPBoynKW3ke4MQ=="
},
"chartjs-adapter-moment": {
"version": "1.0.0",
@ -2798,9 +2921,9 @@
}
},
"date-fns": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.20.0.tgz",
"integrity": "sha512-nmA7y6aDH5+fknfJ0G77HQzUSfTPpq4ifq+c9blP9d+X9zs3kNjxC+t3pcbBMGTp262a6PJB3RVjLlxIgoMI+Q=="
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.20.1.tgz",
"integrity": "sha512-8P5M8Kxbnovd0zfvOs7ipkiVJ3/zZQ0F/nrBW4x5E+I0uAZVZ80h6CKd24fSXQ5TLK5hXMtI4yb2O5rEZdUt2A=="
},
"debug": {
"version": "4.3.1",
@ -2962,9 +3085,9 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"eslint": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz",
"integrity": "sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==",
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.24.0.tgz",
"integrity": "sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==",
"requires": {
"@babel/code-frame": "7.12.11",
"@eslint/eslintrc": "^0.4.0",
@ -3532,9 +3655,9 @@
"integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs="
},
"globals": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz",
"integrity": "sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==",
"version": "13.8.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz",
"integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==",
"requires": {
"type-fest": "^0.20.2"
},
@ -5245,9 +5368,9 @@
}
},
"rollup": {
"version": "2.44.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.44.0.tgz",
"integrity": "sha512-rGSF4pLwvuaH/x4nAS+zP6UNn5YUDWf/TeEU5IoXSZKBbKRNTCI3qMnYXKZgrC0D2KzS2baiOZt1OlqhMu5rnQ==",
"version": "2.45.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.45.2.tgz",
"integrity": "sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ==",
"requires": {
"fsevents": "~2.3.1"
}
@ -5764,9 +5887,9 @@
}
},
"table": {
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/table/-/table-6.0.8.tgz",
"integrity": "sha512-OBAdezyozae8IvjHGXBDHByVkLCcsmffXUSj8LXkNb0SluRd4ug3GFCjk6JynZONIPhOkyr0Nnvbq1rlIspXyQ==",
"version": "6.0.9",
"resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz",
"integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==",
"requires": {
"ajv": "^8.0.1",
"is-boolean-object": "^1.1.0",
@ -5780,9 +5903,9 @@
},
"dependencies": {
"ajv": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.0.1.tgz",
"integrity": "sha512-46ZA4TalFcLLqX1dEU3dhdY38wAtDydJ4e7QQTVekLUTzXkb1LfqU6VOBXC/a9wiv4T094WURqJH6ZitF92Kqw==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.1.0.tgz",
"integrity": "sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==",
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",

View File

@ -41,9 +41,9 @@
"@babel/preset-env": "^7.13.15",
"@babel/preset-typescript": "^7.13.0",
"@fortawesome/fontawesome-free": "^5.15.3",
"@lingui/cli": "^3.8.6",
"@lingui/core": "^3.8.6",
"@lingui/macro": "^3.8.6",
"@lingui/cli": "^3.8.9",
"@lingui/core": "^3.8.9",
"@lingui/macro": "^3.8.9",
"@patternfly/patternfly": "^4.96.2",
"@polymer/iron-form": "^3.0.1",
"@polymer/paper-input": "^3.2.1",
@ -53,18 +53,18 @@
"@sentry/browser": "^6.2.5",
"@sentry/tracing": "^6.2.5",
"@types/chart.js": "^2.9.32",
"@types/codemirror": "0.0.108",
"@types/codemirror": "0.0.109",
"@types/grecaptcha": "^3.0.1",
"@typescript-eslint/eslint-plugin": "^4.21.0",
"@typescript-eslint/parser": "^4.21.0",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"authentik-api": "file:api",
"babel-plugin-macros": "^3.0.1",
"base64-js": "^1.5.1",
"chart.js": "^3.0.2",
"chart.js": "^3.1.1",
"chartjs-adapter-moment": "^1.0.0",
"codemirror": "^5.60.0",
"construct-style-sheets-polyfill": "^2.4.16",
"eslint": "^7.23.0",
"eslint": "^7.24.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-lit": "^1.3.0",
"flowchart.js": "^1.15.0",
@ -72,7 +72,7 @@
"lit-html": "^1.3.0",
"moment": "^2.29.1",
"rapidoc": "^9.0.0",
"rollup": "^2.44.0",
"rollup": "^2.45.2",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2",

View File

@ -15,8 +15,10 @@ const extensions = [
const resources = [
{ src: "node_modules/rapidoc/dist/rapidoc-min.js", dest: "dist/" },
{ src: "node_modules/@patternfly/patternfly/patternfly.min.css", dest: "dist/" },
{ src: "node_modules/@patternfly/patternfly/patternfly.min.css.map", dest: "dist/" },
{ src: "node_modules/@patternfly/patternfly/patternfly-base.css", dest: "dist/" },
{ src: "node_modules/@patternfly/patternfly/components/Page/page.css", dest: "dist/" },
{ src: "node_modules/@patternfly/patternfly/components/EmptyState/empty-state.css", dest: "dist/" },
{ src: "node_modules/@patternfly/patternfly/components/Spinner/spinner.css", dest: "dist/" },
{ src: "src/authentik.css", dest: "dist/" },
{ src: "node_modules/@patternfly/patternfly/assets/*", dest: "dist/assets/" },

View File

@ -1,8 +1,4 @@
import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";
import { VERSION } from "../constants";
import { SentryIgnoredError } from "../common/errors";
import { Config, Configuration, Middleware, ResponseContext, RootApi } from "authentik-api";
import { Configuration, Middleware, ResponseContext } from "authentik-api";
import { getCookie } from "../utils";
import { API_DRAWER_MIDDLEWARE } from "../elements/notifications/APIDrawer";
import { MessageMiddleware } from "../elements/messages/Middleware";
@ -13,13 +9,13 @@ export class LoggingMiddleware implements Middleware {
console.debug(`authentik/api: ${context.response.status} ${context.init.method} ${context.url}`);
return Promise.resolve(context.response);
}
}
export const DEFAULT_CONFIG = new Configuration({
basePath: "/api/v2beta",
headers: {
"X-CSRFToken": getCookie("authentik_csrf"),
"X-Authentik-Prevent-Basic": "true"
},
middleware: [
API_DRAWER_MIDDLEWARE,
@ -27,27 +23,3 @@ export const DEFAULT_CONFIG = new Configuration({
new LoggingMiddleware(),
],
});
export function configureSentry(): Promise<Config> {
return new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => {
if (config.errorReportingEnabled) {
Sentry.init({
dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
release: `authentik@${VERSION}`,
integrations: [
new Integrations.BrowserTracing(),
],
tracesSampleRate: 0.6,
environment: config.errorReportingEnvironment,
beforeSend(event: Sentry.Event, hint: Sentry.EventHint) {
if (hint.originalException instanceof SentryIgnoredError) {
return null;
}
return event;
},
});
console.debug("authentik/config: Sentry enabled.");
}
return config;
});
}

50
web/src/api/Sentry.ts Normal file
View File

@ -0,0 +1,50 @@
import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";
import { VERSION } from "../constants";
import { SentryIgnoredError } from "../common/errors";
import { Config, RootApi } from "authentik-api";
import { me } from "./Users";
import { DEFAULT_CONFIG } from "./Config";
export function configureSentry(): Promise<Config> {
return new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => {
if (config.errorReportingEnabled) {
Sentry.init({
dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
release: `authentik@${VERSION}`,
integrations: [
new Integrations.BrowserTracing({
tracingOrigins: [window.location.host, "localhost"],
}),
],
tracesSampleRate: 0.6,
environment: config.errorReportingEnvironment,
beforeSend(event: Sentry.Event, hint: Sentry.EventHint) {
if (hint.originalException instanceof SentryIgnoredError) {
return null;
}
if (event.exception) {
me().then(user => {
Sentry.showReportDialog({
eventId: event.event_id,
user: {
email: user.user.email,
name: user.user.name,
}
});
});
}
return event;
},
});
console.debug("authentik/config: Sentry enabled.");
if (config.errorReportingSendPii) {
me().then(user => {
Sentry.setUser({ email: user.user.email });
console.debug("authentik/config: Sentry with PII enabled.");
});
}
}
return config;
});
}

View File

@ -1,5 +1,13 @@
:root {
--ak-accent: #fd4b2d;
--ak-dark-foreground: #fafafa;
--ak-dark-foreground-darker: #bebebe;
--ak-dark-foreground-link: #5a5cb9;
--ak-dark-background: #18191a;
--ak-dark-background-darker: #000000;
--ak-dark-background-light: #1c1e21;
--ak-dark-background-lighter: #2b2e33;
}
html {
@ -89,17 +97,12 @@ html > form > input {
body {
background-color: var(--ak-dark-background) !important;
}
.ak-initial-load h1 {
color: var(--ak-dark-foreground);
}
@media (prefers-color-scheme: dark) {
:root {
--ak-dark-foreground: #fafafa;
--ak-dark-foreground-darker: #bebebe;
--ak-dark-foreground-link: #5a5cb9;
--ak-dark-background: #18191a;
--ak-dark-background-darker: #000000;
--ak-dark-background-light: #1c1e21;
--ak-dark-background-lighter: #2b2e33;
--pf-global--Color--100: var(--ak-dark-foreground);
--pf-c-page__main-section--m-light--BackgroundColor: var(--ak-dark-background-darker);
--pf-global--link--Color: var(--ak-dark-foreground-link);
@ -279,6 +282,7 @@ body {
}
.pf-c-notification-drawer__header {
background-color: var(--ak-dark-background-lighter);
color: var(--ak-dark-foreground);
}
/* data list */
.pf-c-data-list__item {

View File

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

View File

@ -39,12 +39,20 @@ export class PageHeader extends LitElement {
:host {
display: flex;
flex-direction: row;
min-height: 114px;
}
button.sidebar-trigger {
background-color: var(--pf-c-page__main-section--m-light--BackgroundColor);
border-radius: 0px;
}
.pf-c-page__main-section {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
img.pf-icon {
max-height: 24px;
}
`];
}

View File

@ -59,7 +59,7 @@ export class ObjectChangelog extends Table<Event> {
${t`On behalf of ${item.user.on_behalf_of.username}`}
</small>` : html``}`,
html`<span>${item.created?.toLocaleString()}</span>`,
html`<span>${item.clientIp}</span>`,
html`<span>${item.clientIp || "-"}</span>`,
];
}
@ -76,7 +76,7 @@ export class ObjectChangelog extends Table<Event> {
}
renderEmpty(): TemplateResult {
return super.renderEmpty(html`<ak-empty-state header=${t`No Events found.`} icon="pf-icon-module">
return super.renderEmpty(html`<ak-empty-state header=${t`No Events found.`}>
<div slot="body">
${t`No matching events could be found.`}
</div>

View File

@ -0,0 +1,77 @@
import { t } from "@lingui/macro";
import { customElement, html, property, TemplateResult } from "lit-element";
import { AKResponse } from "../../api/Client";
import { Table, TableColumn } from "../table/Table";
import { Event, EventsApi } from "authentik-api";
import "../forms/DeleteForm";
import "../Tabs";
import "../buttons/ModalButton";
import "../buttons/SpinnerButton";
import "../buttons/Dropdown";
import "../../pages/events/EventInfo";
import { PAGE_SIZE } from "../../constants";
import { DEFAULT_CONFIG } from "../../api/Config";
import { EventWithContext } from "../../api/Events";
@customElement("ak-events-user")
export class ObjectChangelog extends Table<Event> {
expandable = true;
@property()
order = "-created";
@property()
targetUser!: string;
apiEndpoint(page: number): Promise<AKResponse<Event>> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
page: page,
ordering: this.order,
pageSize: PAGE_SIZE / 2,
username: this.targetUser
});
}
columns(): TableColumn[] {
return [
new TableColumn(t`Action`, "action"),
new TableColumn(t`User`, "enabled"),
new TableColumn(t`Creation Date`, "created"),
new TableColumn(t`Client IP`, "client_ip"),
];
}
row(item: EventWithContext): TemplateResult[] {
return [
html`${item.action}`,
html`<div>${item.user?.username}</div>
${item.user.on_behalf_of ? html`<small>
${t`On behalf of ${item.user.on_behalf_of.username}`}
</small>` : html``}`,
html`<span>${item.created?.toLocaleString()}</span>`,
html`<span>${item.clientIp || "-"}</span>`,
];
}
renderExpanded(item: Event): TemplateResult {
return html`
<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<ak-event-info .event=${item as EventWithContext}></ak-event-info>
</div>
</td>
<td></td>
<td></td>
<td></td>`;
}
renderEmpty(): TemplateResult {
return super.renderEmpty(html`<ak-empty-state header=${t`No Events found.`}>
<div slot="body">
${t`No matching events could be found.`}
</div>
</ak-empty-state>`);
}
}

View File

@ -35,7 +35,9 @@ export class UserOAuthCodeList extends Table<ExpiringBaseGrantModel> {
row(item: ExpiringBaseGrantModel): TemplateResult[] {
return [
html`${item.provider.name}`,
html`<a href="#/core/providers/${item.provider?.pk}">
${item.provider?.name}
</a>`,
html`${item.expires?.toLocaleString()}`,
html`${item.scope.join(", ")}`,
html`

View File

@ -35,7 +35,9 @@ export class UserOAuthRefreshList extends Table<ExpiringBaseGrantModel> {
row(item: ExpiringBaseGrantModel): TemplateResult[] {
return [
html`${item.provider.name}`,
html`<a href="#/core/providers/${item.provider?.pk}">
${item.provider?.name}
</a>`,
html`${item.expires?.toLocaleString()}`,
html`${item.scope.join(", ")}`,
html`

View File

@ -33,7 +33,12 @@ export class RouterOutlet extends LitElement {
css`
:host {
height: 100vh;
background-color: var(--ak-dark-background, var(--pf-c-page--BackgroundColor)) !important;
background-color: var(--pf-global--BackgroundColor--light-300) !important;
}
@media (prefers-color-scheme: dark) {
:host {
background-color: var(--ak-dark-background) !important;
}
}
*:first-child {
height: 100%;

View File

@ -103,6 +103,7 @@ export class Sidebar extends LitElement {
css`
:host {
z-index: 100;
box-shadow: none !important;
}
.pf-c-nav__link.pf-m-current::after,
.pf-c-nav__link.pf-m-current:hover::after,

View File

@ -1,7 +1,7 @@
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFGlobal from "@patternfly/patternfly/patternfly-base.css";
import { configureSentry } from "../../api/Config";
import { configureSentry } from "../../api/Sentry";
import { Config } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";

View File

@ -162,11 +162,11 @@ export class FlowExecutor extends LitElement implements StageHost {
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links">
<li class="pf-c-login__main-footer-links-item">
<a class="pf-c-button pf-m-primary pf-m-block" href="/">
${t`Return`}
</a>
</li>
<li class="pf-c-login__main-footer-links-item">
<a class="pf-c-button pf-m-primary pf-m-block" href="/">
${t`Return`}
</a>
</li>
</ul>
</footer>`
};

View File

@ -2,7 +2,6 @@ import { css, CSSResult, html, LitElement, property, TemplateResult } from "lit-
import { SidebarItem } from "../elements/sidebar/Sidebar";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFSkipToContent from "@patternfly/patternfly/components/SkipToContent/skip-to-content.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
@ -13,7 +12,9 @@ import "../elements/Banner";
import { until } from "lit-html/directives/until";
import { me } from "../api/Users";
import { t } from "@lingui/macro";
import { EVENT_NOTIFICATION_TOGGLE, EVENT_SIDEBAR_TOGGLE } from "../constants";
import { EVENT_NOTIFICATION_TOGGLE, EVENT_SIDEBAR_TOGGLE, VERSION } from "../constants";
import { AdminApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../api/Config";
export abstract class Interface extends LitElement {
@property({type: Boolean})
@ -25,7 +26,7 @@ export abstract class Interface extends LitElement {
abstract get sidebar(): SidebarItem[];
static get styles(): CSSResult[] {
return [PFBase, PFPage, PFSkipToContent, PFButton, PFDrawer, css`
return [PFBase, PFPage, PFButton, PFDrawer, css`
.pf-c-page__main, .pf-c-drawer__content, .pf-c-page__drawer {
z-index: auto !important;
}
@ -48,6 +49,17 @@ export abstract class Interface extends LitElement {
render(): TemplateResult {
return html`
${until(new AdminApi(DEFAULT_CONFIG).adminVersionList().then(version => {
if (version.versionCurrent !== VERSION) {
return html`<ak-banner>
${t`A newer version of the frontend is available.`}
<button @click=${() => { window.location.reload(); }}>
${t`Reload`}
</button>
</ak-banner>`;
}
return html``;
}))}
${until(me().then((u) => {
if (u.original) {
return html`<ak-banner>

View File

@ -5,16 +5,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="icon" type="image/png" href="/static/dist/assets/icons/icon.png">
<link rel="shortcut icon" type="image/png" href="/static/dist/assets/icons/icon.png">
<link rel="stylesheet" type="text/css" href="/static/dist/patternfly.min.css">
<link rel="stylesheet" type="text/css" href="/static/dist/patternfly-base.css">
<link rel="stylesheet" type="text/css" href="/static/dist/page.css">
<link rel="stylesheet" type="text/css" href="/static/dist/empty-state.css">
<link rel="stylesheet" type="text/css" href="/static/dist/spinner.css">
<link rel="stylesheet" type="text/css" href="/static/dist/authentik.css">
<script src="/static/dist/poly.js" type="module"></script>
<script>window["polymerSkipLoadingFontRoboto"] = true;</script>
<script src="/static/dist/AdminInterface.js" type="module"></script>
<title>authentik</title>
</head>
<body>
<ak-message-container></ak-message-container>
<ak-interface-admin>
<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<section class="ak-initial-load pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<div class="pf-c-empty-state" style="height: 100vh;">
<div class="pf-c-empty-state__content">
<span class="pf-c-spinner pf-m-xl pf-c-empty-state__icon" role="progressbar" aria-valuetext="Loading...">

View File

@ -5,16 +5,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="icon" type="image/png" href="/static/dist/assets/icons/icon.png">
<link rel="shortcut icon" type="image/png" href="/static/dist/assets/icons/icon.png">
<link rel="stylesheet" type="text/css" href="/static/dist/patternfly.min.css">
<link rel="stylesheet" type="text/css" href="/static/dist/patternfly-base.css">
<link rel="stylesheet" type="text/css" href="/static/dist/page.css">
<link rel="stylesheet" type="text/css" href="/static/dist/empty-state.css">
<link rel="stylesheet" type="text/css" href="/static/dist/spinner.css">
<link rel="stylesheet" type="text/css" href="/static/dist/authentik.css">
<script src="/static/dist/poly.js" type="module"></script>
<script>window["polymerSkipLoadingFontRoboto"] = true;</script>
<script src="/static/dist/FlowInterface.js" type="module"></script>
<title>authentik</title>
</head>
<body>
<ak-message-container></ak-message-container>
<ak-flow-executor>
<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<section class="ak-initial-load pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<div class="pf-c-empty-state" style="height: 100vh;">
<div class="pf-c-empty-state__content">
<span class="pf-c-spinner pf-m-xl pf-c-empty-state__icon" role="progressbar" aria-valuetext="Loading...">

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