Compare commits

..

117 Commits

Author SHA1 Message Date
5f58a4566c release: 2021.4.4 2021-04-24 21:03:29 +02:00
d616bdd5d6 providers/oauth2: add proper support for non-http schemes as redirect URIs
closes #772

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-23 16:34:52 +02:00
5112ef9331 web/admin: fix error when updating identification stage
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-23 14:27:23 +02:00
7a49377caf outpost: check for X-Forwarded-Host to switch context
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-23 14:07:44 +02:00
5b3941a425 outposts: always update bundles and swap maps
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-23 10:08:19 +02:00
c1ab5c5556 web: fix title not being loaded from config
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#770
2021-04-22 23:50:37 +02:00
3282b34431 providers/oauth2: fix TokenView not having CORS headers set even with proper Origin
and added tests. closes #771

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 23:48:28 +02:00
392d9bb10b providers/oauth2: fix misleading name of cors_allow_any
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#771
2021-04-22 23:29:49 +02:00
82f6c515ea root: fix readme links to az pipelines
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 22:32:38 +02:00
d67d5f73c5 website/docs: fix config options with double-underscores not showing correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 22:31:24 +02:00
799d186510 web/flows: fix Sentry not being loaded correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 20:48:22 +02:00
3983b7fbe4 lib: don't send SuspiciousOperation to sentry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 20:17:00 +02:00
d75284a587 flows: fix errors which occur during flow execution being sent to sentry malformed
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 20:14:37 +02:00
71e4936dc3 web/admin: fix error when me() returns 403
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 19:52:01 +02:00
9d3b6f7a4d web: only report http errors for 500 and above
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 19:51:32 +02:00
003df44a34 web/admin: adjust phrasing of cards on overview page
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 14:07:30 +02:00
a7598c6ee5 *: fix more URLs for github org
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 11:06:56 +02:00
0891e43040 web/admin: fix invalid group member count
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 10:36:10 +02:00
1f49aea48d web/admin: fix mismatched required tags
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-22 10:33:36 +02:00
499b52df6a root: update urls to github org
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-21 22:46:48 +02:00
b8a566f4a0 outposts: move local connection check to task, run every 60 minutes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-21 11:34:48 +02:00
aa0e8edb8b *: make tasks run every 60 minutes not :00 every hour
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-21 11:26:17 +02:00
0e35bb18c7 web/admin: fix display for user supseruser status
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-21 11:18:55 +02:00
4a06ebf4f9 build(deps): bump @sentry/browser from 6.2.5 to 6.3.0 in /web (#766)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.2.5 to 6.3.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.2.5...6.3.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-21 11:13:37 +02:00
11584af425 website/docs: add note for nextcloud Reverse proxy and extension
closes #750

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-21 10:22:43 +02:00
a31da9e1d3 build(deps): bump @babel/core from 7.13.15 to 7.13.16 in /web (#764)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.13.15 to 7.13.16.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.16/packages/babel-core)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-21 10:18:45 +02:00
8d6d49834b build(deps): bump codemirror from 5.60.0 to 5.61.0 in /web (#765)
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.60.0 to 5.61.0.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.60.0...5.61.0)

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

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-21 10:18:18 +02:00
7346ccf2b7 web/admin: add description for fields in proxy provider form
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-21 10:18:00 +02:00
57072dd6ce stages/identification: fix query logic for user lookup
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-21 10:09:38 +02:00
fec098a823 web/admin: only allow policies to be bound to sources as users/groups cannot be checked
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 23:30:37 +02:00
73950b72e5 web/admin: improve phrasing for Policy bindings
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 23:16:17 +02:00
b40afb9b7d stages/identification: ignore inactive users
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 21:45:14 +02:00
1f783dfc01 stages/user_login: add default backend
closes #763

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 20:53:07 +02:00
7ccf8bcdc8 web/admin: only pre-select items when creating a new object
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 20:32:47 +02:00
76131e40ec tests/e2e: monkey patch OAuth1 test instead of setting URLs manually
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 20:03:20 +02:00
5955394c1d web: send response info when response is thrown
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 17:32:38 +02:00
a8998a6356 sources/oauth: handle error in auzre_ad when ID Can't be extracted
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 17:27:52 +02:00
dc75d7b7f0 sources/oauth: fix error whilst fetching user profile when source uses fixed URLs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 17:25:59 +02:00
34a191f216 web/admin: fix link to providers on overview page
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 16:35:21 +02:00
299931985e web: fix mis-matched package-lock file
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 11:27:56 +02:00
b946fbf9e7 Merge branch 'version-2021.4' 2021-04-20 09:21:26 +02:00
e20bb7d636 release: 2021.4.3 2021-04-20 09:15:07 +02:00
5db3409efc web: bump lingui
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-20 09:13:42 +02:00
649db054a6 build(deps): bump boto3 from 1.17.53 to 1.17.54 (#762) 2021-04-20 08:26:10 +02:00
15d5b91642 root: fix developer link in readme
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-04-19 22:05:58 +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
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
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
164 changed files with 7249 additions and 6354 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2021.4.2
current_version = 2021.4.4
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.2
-t beryju/authentik:2021.4.4
-t beryju/authentik:latest
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik:2021.4.2
run: docker push beryju/authentik:2021.4.4
- 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.2 \
-t beryju/authentik-proxy:2021.4.4 \
-t beryju/authentik-proxy:latest \
-f proxy.Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik-proxy:2021.4.2
run: docker push beryju/authentik-proxy:2021.4.4
- 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.2 \
-t beryju/authentik-static:2021.4.4 \
-t beryju/authentik-static:latest \
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik-static:2021.4.2
run: docker push beryju/authentik-static:2021.4.4
- 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.2
tagName: 2021.4.4
environment: beryjuorg-prod

128
Pipfile.lock generated
View File

@ -116,18 +116,18 @@
},
"boto3": {
"hashes": [
"sha256:73bcd04f6f919e7f8acc27c9d83dab5aee22225fe624c028b2e1c7feaf771098",
"sha256:c45e7d3aef8965ae1b42c9855c31ded19fbb38cfad0a34cc37dc880ded3672c2"
"sha256:1e55df93aa47a84e2a12a639c7f145e16e6e9ef959542d69d5526d50d2e92692",
"sha256:eab42daaaf68cdad5b112d31dcb0684162098f6558ba7b64156be44f993525fa"
],
"index": "pypi",
"version": "==1.17.51"
"version": "==1.17.54"
},
"botocore": {
"hashes": [
"sha256:ae45ea7451513373666b7571064c173d649e61fd3e8f413f0e1f1f9db26b3513",
"sha256:c853d6c2321e2f2328282c7d49d7b1a06201826ba0e7049c6975ab5f22927ea8"
"sha256:20a864fc6570ba11d52532c72c3ccabab5c71a9b4a9418601a313d56f1d2ce5b",
"sha256:37ec76ea2df8609540ba6cb0fe360ae1c589d2e1ee91eb642fd767823f3fcedd"
],
"version": "==1.20.51"
"version": "==1.20.54"
},
"cachetools": {
"hashes": [
@ -437,10 +437,10 @@
},
"google-auth": {
"hashes": [
"sha256:186fe2564634d67fbbb64f3daf8bc8c9cecbb2a7f535ed1a8a71795e50db8d87",
"sha256:70b39558712826e41f65e5f05a8d879361deaf84df8883e5dd0ec3d0da6ab66e"
"sha256:010f011c4e27d3d5eb01106fba6aac39d164842dfcd8709955c4638f5b11ccf8",
"sha256:f30a672a64d91cc2e3137765d088c5deec26416246f7a9e956eaf69a8d7ed49c"
],
"version": "==1.28.1"
"version": "==1.29.0"
},
"gunicorn": {
"hashes": [
@ -1106,10 +1106,10 @@
},
"s3transfer": {
"hashes": [
"sha256:35627b86af8ff97e7ac27975fe0a98a312814b46c6333d8a6b889627bcd80994",
"sha256:efa5bd92a897b6a8d5c1383828dca3d52d0790e0756d49740563a3fb6ed03246"
"sha256:af1af6384bd7fb8208b06480f9be73d0295d965c4c073a5c95ea5b6661dccc18",
"sha256:f3dfd791cad2799403e3c8051810a7ca6ee1d2e630e5d2a8f9649d892bdb3db6"
],
"version": "==0.3.7"
"version": "==0.4.0"
},
"sentry-sdk": {
"hashes": [
@ -1374,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": {

View File

@ -4,13 +4,13 @@
---
[![](https://img.shields.io/discord/809154715984199690?label=Discord&style=flat-square)](https://discord.gg/KPnmtNWy)
[![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)
[![](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/6?style=flat-square)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=6)
[![Tests](https://img.shields.io/azure-devops/tests/beryjuorg/authentik/6?compact_message&style=flat-square)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=6)
[![Code Coverage](https://img.shields.io/codecov/c/gh/goauthentik/authentik?style=flat-square)](https://codecov.io/gh/goauthentik/authentik)
![Docker pulls](https://img.shields.io/docker/pulls/beryju/authentik.svg?style=flat-square)
![Latest version](https://img.shields.io/docker/v/beryju/authentik?sort=semver&style=flat-square)
![LGTM Grade](https://img.shields.io/lgtm/grade/python/github/BeryJu/authentik?style=flat-square)
![LGTM Grade](https://img.shields.io/lgtm/grade/python/github/goauthentik/authentik?style=flat-square)
## What is authentik?
@ -31,7 +31,7 @@ Light | Dark
## Development
See [Development Documentation](https://goauthentik.io/docs/development/local-dev-environment)
See [Development Documentation](https://goauthentik.io/developer-docs/)
## Security

View File

@ -1,3 +1,3 @@
"""authentik"""
__version__ = "2021.4.2"
__version__ = "2021.4.4"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -4,7 +4,7 @@ from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = {
"admin_latest_version": {
"task": "authentik.admin.tasks.update_latest_version",
"schedule": crontab(minute=0), # Run every hour
"schedule": crontab(minute="*/60"), # Run every hour
"options": {"queue": "authentik_scheduled"},
}
}

View File

@ -23,7 +23,9 @@ URL_FINDER = URLValidator.regex.pattern[1:]
def update_latest_version(self: MonitoredTask):
"""Update latest version info"""
try:
response = get("https://api.github.com/repos/beryju/authentik/releases/latest")
response = get(
"https://api.github.com/repos/goauthentik/authentik/releases/latest"
)
response.raise_for_status()
data = response.json()
tag_name = data.get("tag_name")

View File

@ -4,6 +4,7 @@ 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
@ -14,7 +15,7 @@ LOGGER = get_logger()
# 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()
if auth_credentials == "":
return None
@ -25,28 +26,27 @@ def token_from_header(raw_header: bytes) -> Optional[Token]:
auth_type, body = plain.split()
auth_credentials = f"{auth_type} {b64encode(body.encode()).decode()}"
except (UnicodeDecodeError, Error):
return None
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())
return None
raise AuthenticationFailed("Unsupported authentication type")
password = auth_credentials
if auth_type.lower() == "basic":
try:
auth_credentials = b64decode(auth_credentials.encode()).decode()
except (UnicodeDecodeError, Error):
return None
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()
@ -58,10 +58,8 @@ class AuthentikTokenAuthentication(BaseAuthentication):
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:
return "Bearer"

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
@ -28,17 +29,21 @@ class TestAPIAuth(TestCase):
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(
@ -195,7 +196,8 @@ info = openapi.Info(
default_version="v2beta",
contact=openapi.Contact(email="hello@beryju.org"),
license=openapi.License(
name="GNU GPLv3", url="https://github.com/BeryJu/authentik/blob/master/LICENSE"
name="GNU GPLv3",
url="https://github.com/goauthentik/authentik/blob/master/LICENSE",
),
)
SchemaView = get_schema_view(info, public=True, permission_classes=(AllowAny,))

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,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
@ -93,6 +94,7 @@ class PropertyMappingViewSet(
"description": subclass.__doc__,
# pyright: reportGeneralTypeIssues=false
"component": subclass().component,
"model_name": subclass._meta.model_name,
}
)
return Response(TypeCreateSerializer(data, many=True).data)
@ -101,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
@ -111,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
@ -125,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

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

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

@ -80,6 +80,7 @@ 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"])

View File

@ -14,6 +14,7 @@ from drf_yasg import openapi
from drf_yasg.utils import no_body, swagger_auto_schema
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from sentry_sdk import capture_exception
from structlog.stdlib import BoundLogger, get_logger
from authentik.core.models import USER_ATTRIBUTE_DEBUG
@ -152,7 +153,8 @@ class FlowExecutorView(APIView):
stage_response = self.current_stage_view.get(request, *args, **kwargs)
return to_stage_response(request, stage_response)
except Exception as exc: # pylint: disable=broad-except
self._logger.exception(exc)
capture_exception(exc)
self._logger.warning(exc)
return to_stage_response(request, FlowErrorResponse(request, exc))
@swagger_auto_schema(
@ -180,7 +182,8 @@ class FlowExecutorView(APIView):
stage_response = self.current_stage_view.post(request, *args, **kwargs)
return to_stage_response(request, stage_response)
except Exception as exc: # pylint: disable=broad-except
self._logger.exception(exc)
capture_exception(exc)
self._logger.warning(exc)
return to_stage_response(request, FlowErrorResponse(request, exc))
def _initiate_plan(self) -> FlowPlan:

View File

@ -6,7 +6,7 @@ from billiard.exceptions import WorkerLostError
from botocore.client import ClientError
from celery.exceptions import CeleryError
from channels_redis.core import ChannelFull
from django.core.exceptions import DisallowedHost, ValidationError
from django.core.exceptions import SuspiciousOperation, ValidationError
from django.db import InternalError, OperationalError, ProgrammingError
from django_redis.exceptions import ConnectionInterrupted
from docker.errors import DockerException
@ -36,7 +36,7 @@ def before_send(event: dict, hint: dict) -> Optional[dict]:
OperationalError,
InternalError,
ProgrammingError,
DisallowedHost,
SuspiciousOperation,
ValidationError,
# Redis errors
RedisConnectionError,

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

@ -1,17 +1,8 @@
"""authentik outposts app config"""
from importlib import import_module
from os import R_OK, access
from os.path import expanduser
from pathlib import Path
from socket import gethostname
from urllib.parse import urlparse
import yaml
from django.apps import AppConfig
from django.db import ProgrammingError
from docker.constants import DEFAULT_UNIX_SOCKET
from kubernetes.config.incluster_config import SERVICE_TOKEN_FILENAME
from kubernetes.config.kube_config import KUBE_CONFIG_DEFAULT_LOCATION
from structlog.stdlib import get_logger
LOGGER = get_logger()
@ -27,49 +18,8 @@ class AuthentikOutpostConfig(AppConfig):
def ready(self):
import_module("authentik.outposts.signals")
try:
AuthentikOutpostConfig.init_local_connection()
from authentik.outposts.tasks import outpost_local_connection
outpost_local_connection.delay()
except ProgrammingError:
pass
@staticmethod
def init_local_connection():
"""Check if local kubernetes or docker connections should be created"""
from authentik.outposts.models import (
DockerServiceConnection,
KubernetesServiceConnection,
)
# Explicitly check against token filename, as thats
# only present when the integration is enabled
if Path(SERVICE_TOKEN_FILENAME).exists():
LOGGER.debug("Detected in-cluster Kubernetes Config")
if not KubernetesServiceConnection.objects.filter(local=True).exists():
LOGGER.debug("Created Service Connection for in-cluster")
KubernetesServiceConnection.objects.create(
name="Local Kubernetes Cluster", local=True, kubeconfig={}
)
# For development, check for the existence of a kubeconfig file
kubeconfig_path = expanduser(KUBE_CONFIG_DEFAULT_LOCATION)
if Path(kubeconfig_path).exists():
LOGGER.debug("Detected kubeconfig")
kubeconfig_local_name = f"k8s-{gethostname()}"
if not KubernetesServiceConnection.objects.filter(
name=kubeconfig_local_name
).exists():
LOGGER.debug("Creating kubeconfig Service Connection")
with open(kubeconfig_path, "r") as _kubeconfig:
KubernetesServiceConnection.objects.create(
name=kubeconfig_local_name,
kubeconfig=yaml.safe_load(_kubeconfig),
)
unix_socket_path = urlparse(DEFAULT_UNIX_SOCKET).path
socket = Path(unix_socket_path)
if socket.exists() and access(socket, R_OK):
LOGGER.debug("Detected local docker socket")
if len(DockerServiceConnection.objects.filter(local=True)) == 0:
LOGGER.debug("Created Service Connection for docker")
DockerServiceConnection.objects.create(
name="Local Docker connection",
local=True,
url=unix_socket_path,
)

View File

@ -9,7 +9,7 @@ CELERY_BEAT_SCHEDULE = {
},
"outposts_service_connection_check": {
"task": "authentik.outposts.tasks.outpost_service_connection_monitor",
"schedule": crontab(minute=0, hour="*"),
"schedule": crontab(minute="*/60"),
"options": {"queue": "authentik_scheduled"},
},
"outpost_token_ensurer": {
@ -17,4 +17,9 @@ CELERY_BEAT_SCHEDULE = {
"schedule": crontab(minute="*/5"),
"options": {"queue": "authentik_scheduled"},
},
"outpost_local_connection": {
"task": "authentik.outposts.tasks.outpost_local_connection",
"schedule": crontab(minute="*/60"),
"options": {"queue": "authentik_scheduled"},
},
}

View File

@ -1,11 +1,20 @@
"""outpost tasks"""
from os import R_OK, access
from os.path import expanduser
from pathlib import Path
from socket import gethostname
from typing import Any
from urllib.parse import urlparse
import yaml
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.core.cache import cache
from django.db.models.base import Model
from django.utils.text import slugify
from docker.constants import DEFAULT_UNIX_SOCKET
from kubernetes.config.incluster_config import SERVICE_TOKEN_FILENAME
from kubernetes.config.kube_config import KUBE_CONFIG_DEFAULT_LOCATION
from structlog.stdlib import get_logger
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
@ -67,6 +76,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):
@ -183,3 +194,42 @@ def _outpost_single_update(outpost: Outpost, layer=None):
for state in OutpostState.for_outpost(outpost):
LOGGER.debug("sending update", channel=state.uid, outpost=outpost)
async_to_sync(layer.send)(state.uid, {"type": "event.update"})
@CELERY_APP.task()
def outpost_local_connection():
"""Checks the local environment and create Service connections."""
# Explicitly check against token filename, as thats
# only present when the integration is enabled
if Path(SERVICE_TOKEN_FILENAME).exists():
LOGGER.debug("Detected in-cluster Kubernetes Config")
if not KubernetesServiceConnection.objects.filter(local=True).exists():
LOGGER.debug("Created Service Connection for in-cluster")
KubernetesServiceConnection.objects.create(
name="Local Kubernetes Cluster", local=True, kubeconfig={}
)
# For development, check for the existence of a kubeconfig file
kubeconfig_path = expanduser(KUBE_CONFIG_DEFAULT_LOCATION)
if Path(kubeconfig_path).exists():
LOGGER.debug("Detected kubeconfig")
kubeconfig_local_name = f"k8s-{gethostname()}"
if not KubernetesServiceConnection.objects.filter(
name=kubeconfig_local_name
).exists():
LOGGER.debug("Creating kubeconfig Service Connection")
with open(kubeconfig_path, "r") as _kubeconfig:
KubernetesServiceConnection.objects.create(
name=kubeconfig_local_name,
kubeconfig=yaml.safe_load(_kubeconfig),
)
unix_socket_path = urlparse(DEFAULT_UNIX_SOCKET).path
socket = Path(unix_socket_path)
if socket.exists() and access(socket, R_OK):
LOGGER.debug("Detected local docker socket")
if len(DockerServiceConnection.objects.filter(local=True)) == 0:
LOGGER.debug("Created Service Connection for docker")
DockerServiceConnection.objects.create(
name="Local Docker connection",
local=True,
url=unix_socket_path,
)

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="foo://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": "foo://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"foo://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,222 @@
"""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}",
HTTP_ORIGIN="http://local.invalid",
)
new_token: RefreshToken = (
RefreshToken.objects.filter(user=user).exclude(pk=token.pk).first()
)
self.assertEqual(response["Access-Control-Allow-Credentials"], "true")
self.assertEqual(
response["Access-Control-Allow-Origin"], "http://local.invalid"
)
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_invalid_origin(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}",
HTTP_ORIGIN="http://another.invalid",
)
new_token: RefreshToken = (
RefreshToken.objects.filter(user=user).exclude(pk=token.pk).first()
)
self.assertNotIn("Access-Control-Allow-Credentials", response)
self.assertNotIn("Access-Control-Allow-Origin", response)
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

@ -2,9 +2,11 @@
import re
from base64 import b64decode
from binascii import Error
from typing import Optional
from typing import Any, Optional
from urllib.parse import urlparse
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.http.response import HttpResponseRedirect
from django.utils.cache import patch_vary_headers
from structlog.stdlib import get_logger
@ -25,15 +27,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(request: HttpRequest, response: HttpResponse, *allowed_origins: str):
"""Add headers to permit CORS requests from allowed_origins, 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"])
@ -141,3 +162,18 @@ def protected_resource_view(scopes: list[str]):
return view_wrapper
return wrapper
class HttpResponseRedirectScheme(HttpResponseRedirect):
"""HTTP Response to redirect, can be to a non-http scheme"""
def __init__(
self,
redirect_to: str,
*args: Any,
allowed_schemes: Optional[list[str]] = None,
**kwargs: Any,
) -> None:
self.allowed_schemes = allowed_schemes or ["http", "https", "ftp"]
# pyright: reportGeneralTypeIssues=false
super().__init__(redirect_to, *args, **kwargs)

View File

@ -2,12 +2,12 @@
from dataclasses import dataclass, field
from datetime import timedelta
from typing import Optional
from urllib.parse import parse_qs, urlencode, urlsplit, urlunsplit
from urllib.parse import parse_qs, urlencode, urlparse, urlsplit, urlunsplit
from uuid import uuid4
from django.http import HttpRequest, HttpResponse
from django.http.response import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.http.response import Http404, HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.translation import gettext as _
from structlog.stdlib import get_logger
@ -46,6 +46,7 @@ from authentik.providers.oauth2.models import (
OAuth2Provider,
ResponseTypes,
)
from authentik.providers.oauth2.utils import HttpResponseRedirectScheme
from authentik.providers.oauth2.views.userinfo import UserInfoView
from authentik.stages.consent.models import ConsentMode, ConsentStage
from authentik.stages.consent.stage import (
@ -233,9 +234,17 @@ class OAuthFulfillmentStage(StageView):
params: OAuthAuthorizationParams
provider: OAuth2Provider
def redirect(self, uri: str) -> HttpResponse:
"""Redirect using HttpResponseRedirectScheme, compatible with non-http schemes"""
parsed = urlparse(uri)
return HttpResponseRedirectScheme(uri, allowed_schemes=[parsed.scheme])
# 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
)
@ -258,7 +267,7 @@ class OAuthFulfillmentStage(StageView):
flow=self.executor.plan.flow_pk,
scopes=", ".join(self.params.scope),
).from_http(self.request)
return redirect(self.create_response_uri())
return self.redirect(self.create_response_uri())
except (ClientIdError, RedirectUriError) as error:
error.to_event(application=application).from_http(request)
self.executor.stage_invalid()
@ -267,7 +276,7 @@ class OAuthFulfillmentStage(StageView):
except AuthorizeError as error:
error.to_event(application=application).from_http(request)
self.executor.stage_invalid()
return redirect(error.create_uri())
return self.redirect(error.create_uri())
def create_response_uri(self) -> str:
"""Create a final Response URI the user is redirected to."""
@ -301,7 +310,7 @@ class OAuthFulfillmentStage(StageView):
return urlunsplit(uri)
raise OAuth2Error()
except OAuth2Error as error:
LOGGER.exception("Error when trying to create response uri", error=error)
LOGGER.warning("Error when trying to create response uri", error=error)
raise AuthorizeError(
self.params.redirect_uri,
"server_error",

View File

@ -19,7 +19,7 @@ from authentik.providers.oauth2.models import (
ResponseTypes,
ScopeMapping,
)
from authentik.providers.oauth2.utils import cors_allow_any
from authentik.providers.oauth2.utils import cors_allow
LOGGER = get_logger()
@ -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(request, response, *self.provider.redirect_uris.split("\n"))
return response

View File

@ -19,7 +19,11 @@ from authentik.providers.oauth2.models import (
OAuth2Provider,
RefreshToken,
)
from authentik.providers.oauth2.utils import TokenResponse, extract_client_auth
from authentik.providers.oauth2.utils import (
TokenResponse,
cors_allow,
extract_client_auth,
)
LOGGER = get_logger()
@ -154,7 +158,18 @@ class TokenParams:
class TokenView(View):
"""Generate tokens for clients"""
params: TokenParams
params: Optional[TokenParams] = None
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
response = super().dispatch(request, *args, **kwargs)
allowed_origins = []
if self.params:
allowed_origins = self.params.provider.redirect_uris.split("\n")
cors_allow(self.request, response, *allowed_origins)
return response
def options(self, request: HttpRequest) -> HttpResponse:
return TokenResponse({})
def post(self, request: HttpRequest) -> HttpResponse:
"""Generate tokens for clients"""
@ -198,7 +213,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
@ -13,7 +14,7 @@ from authentik.providers.oauth2.constants import (
SCOPE_GITHUB_USER_READ,
)
from authentik.providers.oauth2.models import RefreshToken, ScopeMapping
from authentik.providers.oauth2.utils import TokenResponse, cors_allow_any
from authentik.providers.oauth2.utils import TokenResponse, cors_allow
LOGGER = 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(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

@ -337,7 +337,7 @@ if CONFIG.y("postgresql.s3_backup"):
# Sentry integration
_ERROR_REPORTING = CONFIG.y_bool("error_reporting.enabled", False)
if not DEBUG and _ERROR_REPORTING:
if _ERROR_REPORTING:
# pylint: disable=abstract-class-instantiated
sentry_init(
dsn="https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",

View File

@ -8,7 +8,7 @@ AUTHENTICATION_BACKENDS = [
CELERY_BEAT_SCHEDULE = {
"sources_ldap_sync": {
"task": "authentik.sources.ldap.tasks.ldap_sync_all",
"schedule": crontab(minute=0), # Run every hour
"schedule": crontab(minute="*/60"), # Run every hour
"options": {"queue": "authentik_scheduled"},
}
}

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()
@ -39,8 +40,11 @@ class BaseOAuthClient:
def get_profile_info(self, token: dict[str, str]) -> Optional[dict[str, Any]]:
"Fetch user profile information."
profile_url = self.source.type.profile_url or ""
if self.source.type.urls_customizable and self.source.profile_url:
profile_url = self.source.profile_url
try:
response = self.do_request("get", self.source.profile_url, token=token)
response = self.do_request("get", profile_url, token=token)
response.raise_for_status()
except RequestException as exc:
LOGGER.warning("Unable to fetch user profile", exc=exc)
@ -59,7 +63,16 @@ class BaseOAuthClient:
args.update(additional)
params = urlencode(args)
LOGGER.info("redirect args", **args)
return f"{self.source.authorization_url}?{params}"
authorization_url = self.source.type.authorization_url or ""
if self.source.type.urls_customizable and self.source.authorization_url:
authorization_url = self.source.authorization_url
if authorization_url == "":
Event.new(
EventAction.CONFIGURATION_ERROR,
source=self.source,
message="Source has an empty authorization URL.",
).save()
return f"{authorization_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 = self.source.type.access_token_url or ""
if self.source.type.urls_customizable and 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 = self.source.type.request_token_url or ""
if self.source.type.urls_customizable and 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.type.urls_customizable and 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,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,19 +28,19 @@ class OAuthSource(Source):
)
authorization_url = models.CharField(
max_length=255,
blank=True,
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,
blank=True,
null=True,
verbose_name=_("Access Token URL"),
help_text=_("URL used by authentik to retrive tokens."),
)
profile_url = models.CharField(
max_length=255,
blank=True,
null=True,
verbose_name=_("Profile URL"),
help_text=_("URL used by authentik to get user information."),
)
@ -163,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

@ -1,5 +1,5 @@
"""AzureAD OAuth2 Views"""
from typing import Any
from typing import Any, Optional
from uuid import UUID
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
@ -10,8 +10,11 @@ from authentik.sources.oauth.views.callback import OAuthCallback
class AzureADOAuthCallback(OAuthCallback):
"""AzureAD OAuth2 Callback"""
def get_user_id(self, source: OAuthSource, info: dict[str, Any]) -> str:
return str(UUID(info.get("objectId")).int)
def get_user_id(self, source: OAuthSource, info: dict[str, Any]) -> Optional[str]:
try:
return str(UUID(info.get("objectId")).int)
except TypeError:
return None
def get_user_enroll_context(
self,

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

@ -2,12 +2,15 @@
from typing import Optional, Type
from django.http.request import HttpRequest
from structlog.stdlib import get_logger
from authentik.sources.oauth.clients.base import BaseOAuthClient
from authentik.sources.oauth.clients.oauth1 import OAuthClient
from authentik.sources.oauth.clients.oauth2 import OAuth2Client
from authentik.sources.oauth.models import OAuthSource
LOGGER = get_logger()
# pylint: disable=too-few-public-methods
class OAuthClientMixin:
@ -22,6 +25,9 @@ class OAuthClientMixin:
if self.client_class is not None:
# pylint: disable=not-callable
return self.client_class(source, self.request, **kwargs)
if source.request_token_url:
return OAuthClient(source, self.request, **kwargs)
return OAuth2Client(source, self.request, **kwargs)
if source.type.request_token_url or source.request_token_url:
client = OAuthClient(source, self.request, **kwargs)
else:
client = OAuth2Client(source, self.request, **kwargs)
LOGGER.debug("Using client for oauth request", client=client)
return client

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

@ -39,13 +39,13 @@ from authentik.sources.saml.processors.constants import (
from authentik.sources.saml.processors.request import SESSION_REQUEST_ID
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
from authentik.stages.user_login.stage import DEFAULT_BACKEND
LOGGER = get_logger()
if TYPE_CHECKING:
from xml.etree.ElementTree import Element # nosec
CACHE_SEEN_REQUEST_ID = "authentik_saml_seen_ids_%s"
DEFAULT_BACKEND = "django.contrib.auth.backends.ModelBackend"
class ResponseProcessor:

View File

@ -63,6 +63,7 @@ class EmailStageViewSet(ModelViewSet):
"name": value,
"description": label,
"component": "",
"model_name": "",
}
)
return Response(TypeCreateSerializer(choices, many=True).data)

View File

@ -1,5 +1,6 @@
"""Identification stage logic"""
from dataclasses import asdict
from time import sleep
from typing import Optional
from django.db.models import Q
@ -46,6 +47,7 @@ class IdentificationChallengeResponse(ChallengeResponse):
"""Validate that user exists"""
pre_user = self.stage.get_user(value)
if not pre_user:
sleep(0.150)
LOGGER.debug("invalid_login", identifier=value)
raise ValidationError("Failed to authenticate.")
self.pre_user = pre_user
@ -68,7 +70,7 @@ class IdentificationStageView(ChallengeStageView):
else:
model_field += "__exact"
query |= Q(**{model_field: uid_value})
users = User.objects.filter(query)
users = User.objects.filter(query, is_active=True)
if users.exists():
LOGGER.debug("Found user", user=users.first(), query=query)
return users.first()

View File

@ -11,6 +11,7 @@ from authentik.lib.utils.time import timedelta_from_string
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
LOGGER = get_logger()
DEFAULT_BACKEND = "django.contrib.auth.backends.ModelBackend"
class UserLoginStageView(StageView):
@ -23,12 +24,9 @@ class UserLoginStageView(StageView):
messages.error(request, message)
LOGGER.debug(message)
return self.executor.stage_invalid()
if PLAN_CONTEXT_AUTHENTICATION_BACKEND not in self.executor.plan.context:
message = _("Pending user has no backend.")
messages.error(request, message)
LOGGER.debug(message)
return self.executor.stage_invalid()
backend = self.executor.plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND]
backend = self.executor.plan.context.get(
PLAN_CONTEXT_AUTHENTICATION_BACKEND, DEFAULT_BACKEND
)
login(
self.request,
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER],

View File

@ -12,7 +12,6 @@ from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.tests.test_views import TO_STAGE_RESPONSE_MOCK
from authentik.flows.views import SESSION_KEY_PLAN
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.user_login.models import UserLoginStage
@ -38,9 +37,6 @@ class TestUserLoginStage(TestCase):
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
)
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
plan.context[
PLAN_CONTEXT_AUTHENTICATION_BACKEND
] = "django.contrib.auth.backends.ModelBackend"
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session.save()
@ -82,32 +78,3 @@ class TestUserLoginStage(TestCase):
"type": ChallengeTypes.NATIVE.value,
},
)
@patch(
"authentik.flows.views.to_stage_response",
TO_STAGE_RESPONSE_MOCK,
)
def test_without_backend(self):
"""Test a plan with pending user, without backend, resulting in a denied"""
plan = FlowPlan(
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
)
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session.save()
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_str(response.content),
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value,
},
)

View File

@ -369,8 +369,6 @@ stages:
coverage-unittest/unittest.xml
mergeTestResults: true
- task: CmdLine@2
env:
CODECOV_TOKEN: $(CODECOV_TOKEN)
inputs:
script: bash <(curl -s https://codecov.io/bash)
- stage: Build

View File

@ -20,7 +20,7 @@ services:
networks:
- internal
server:
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.4.2}
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.4.4}
restart: unless-stopped
command: server
environment:
@ -48,7 +48,7 @@ services:
env_file:
- .env
worker:
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.4.2}
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.4.4}
restart: unless-stopped
command: worker
networks:
@ -68,7 +68,7 @@ services:
env_file:
- .env
static:
image: ${AUTHENTIK_IMAGE_STATIC:-beryju/authentik-static}:${AUTHENTIK_TAG:-2021.4.2}
image: ${AUTHENTIK_IMAGE_STATIC:-beryju/authentik-static}:${AUTHENTIK_TAG:-2021.4.4}
restart: unless-stopped
networks:
- internal

View File

@ -3,9 +3,9 @@ description: authentik is an open-source Identity Provider focused on flexibilit
name: authentik
home: https://goauthentik.io
sources:
- https://github.com/BeryJu/authentik
version: "2021.4.2"
icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg
- https://github.com/goauthentik/authentik
version: "2021.4.4"
icon: https://raw.githubusercontent.com/goauthentik/authentik/master/web/icons/icon.svg
dependencies:
- name: postgresql
version: 9.4.1

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.2 | Image tag |
| image.tag | 2021.4.4 | 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 |

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.2
tag: 2021.4.4
pullPolicy: IfNotPresent
serverReplicas: 1

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

@ -1,6 +1,6 @@
# authentik outpost
[![CI Build status](https://img.shields.io/azure-devops/build/beryjuorg/authentik/3?style=flat-square)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=3)
[![CI Build status](https://img.shields.io/azure-devops/build/beryjuorg/authentik/3?style=flat-square)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=8)
![Docker pulls (proxy)](https://img.shields.io/docker/pulls/beryju/authentik-proxy.svg?style=flat-square)
Reverse Proxy based on [oauth2_proxy](https://github.com/oauth2-proxy/oauth2-proxy), completely managed and monitored by authentik.

View File

@ -41,10 +41,6 @@ stages:
- task: GoTool@0
inputs:
version: '1.16.3'
- task: Go@0
inputs:
command: 'get'
arguments: '-u golang.org/x/lint/golint'
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
@ -53,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:

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

@ -8,12 +8,12 @@ require (
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/go-swagger/go-swagger v0.27.0 // 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
@ -23,6 +23,7 @@ require (
github.com/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc
github.com/pelletier/go-toml v1.9.0 // indirect
github.com/pkg/errors v0.9.1
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,10 +31,11 @@ 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/net v0.0.0-20210410081132-afb366fc7cd1 // indirect
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // 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
google.golang.org/grpc v1.31.0 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
)

View File

@ -102,7 +102,6 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -124,8 +123,6 @@ github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHj
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@ -157,7 +154,6 @@ 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=
@ -171,8 +167,6 @@ github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpX
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.20.0 h1:Sxpo9PjEHDzhs3FbnGNonvDgWcMW2U7wGTcDDSFSceM=
github.com/go-openapi/errors v0.20.0/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
@ -202,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=
@ -252,9 +246,6 @@ github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRf
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-swagger/go-swagger v0.27.0 h1:K7+nkBuf4oS1jTBrdvWqYFpqD69V5CN8HamZzCDDhAI=
github.com/go-swagger/go-swagger v0.27.0/go.mod h1:WodZVysInJilkW7e6IRw+dZGp5yW6rlMFZ4cb+THl9A=
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
@ -310,8 +301,6 @@ 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=
@ -329,8 +318,8 @@ 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=
@ -352,8 +341,6 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
@ -392,8 +379,6 @@ github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@ -521,8 +506,6 @@ 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=
@ -538,8 +521,6 @@ 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=
@ -559,7 +540,6 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
@ -586,7 +566,6 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@ -595,7 +574,6 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -613,8 +591,6 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@ -649,7 +625,6 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yuin/gopher-lua v0.0.0-20191213034115-f46add6fdb5c/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
@ -690,8 +665,6 @@ 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=
@ -715,7 +688,6 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
@ -725,8 +697,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -767,23 +737,18 @@ golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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-20210226172049-e18ecbb05110/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-20210410081132-afb366fc7cd1 h1:4qWs8cYYH6PoEFy4dfhDFgoMGkwAcETd+MmPdCPMzUc=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
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=
@ -795,7 +760,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -846,13 +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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/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/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/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=
@ -860,7 +820,6 @@ 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=
@ -920,8 +879,6 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1005,7 +962,6 @@ 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=

View File

@ -4,6 +4,7 @@ import (
"fmt"
"math/rand"
"net/url"
"os"
"time"
"github.com/go-openapi/runtime"
@ -30,8 +31,7 @@ type APIController struct {
Server Outpost
lastBundleHash string
logger *log.Entry
logger *log.Entry
reloadOffset time.Duration
@ -39,8 +39,8 @@ 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
@ -53,10 +53,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{}))
@ -69,18 +70,12 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
logger: log,
reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
lastBundleHash: "",
}
ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset)
ac.initWS(pbURL, outpost.Pk)
ac.initWS(akURL, outpost.Pk)
return ac
}
func (a *APIController) GetLastBundleHash() string {
return a.lastBundleHash
}
// Start Starts all handlers, non-blocking
func (a *APIController) Start() error {
err := a.Server.Refresh()
@ -96,7 +91,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

@ -1,10 +1,6 @@
package ak
import (
"crypto/sha512"
"encoding/hex"
"encoding/json"
"goauthentik.io/outpost/pkg/client/outposts"
"goauthentik.io/outpost/pkg/models"
)
@ -15,16 +11,5 @@ func (a *APIController) Update() ([]*models.ProxyOutpostConfig, error) {
a.logger.WithError(err).Error("Failed to fetch providers")
return nil, err
}
// Check provider hash to see if anything is changed
hasher := sha512.New()
out, err := json.Marshal(providers.Payload.Results)
if err != nil {
return nil, nil
}
hash := hex.EncodeToString(hasher.Sum(out))
if hash == a.lastBundleHash {
return nil, nil
}
a.lastBundleHash = hash
return providers.Payload.Results, nil
}

View File

@ -15,9 +15,9 @@ import (
"goauthentik.io/outpost/pkg"
)
func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
pathTemplate := "%s://%s/ws/outpost/%s/"
scheme := strings.ReplaceAll(pbURL.Scheme, "http", "ws")
scheme := strings.ReplaceAll(akURL.Scheme, "http", "ws")
authHeader := fmt.Sprintf("Bearer %s", ac.token)
@ -37,7 +37,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
InsecureSkipVerify: strings.ToLower(value) == "true",
},
}
ws.Dial(fmt.Sprintf(pathTemplate, scheme, pbURL.Host, outpostUUID.String()), header)
ws.Dial(fmt.Sprintf(pathTemplate, scheme, akURL.Host, outpostUUID.String()), header)
ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithField("outpost", outpostUUID.String()).Debug("connecting to authentik")
@ -64,7 +64,6 @@ func (ac *APIController) Shutdown() {
ac.logger.Println("write close:", err)
return
}
return
}
func (ac *APIController) startWSHandler() {
@ -92,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

@ -107,7 +107,7 @@ func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
duration := float64(time.Since(t)) / float64(time.Millisecond)
h.logger.WithFields(log.Fields{
"host": req.RemoteAddr,
"vhost": req.Host,
"vhost": getHost(req),
"request_protocol": req.Proto,
"runtime": fmt.Sprintf("%0.3f", duration),
"method": req.Method,

View File

@ -161,7 +161,7 @@ func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
return
}
redirectURI := p.GetRedirectURI(req.Host)
redirectURI := p.GetRedirectURI(getHost(req))
http.Redirect(rw, req, p.provider.GetLoginURL(redirectURI, fmt.Sprintf("%v:%v", nonce, redirect)), http.StatusFound)
}
@ -184,7 +184,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
return
}
session, err := p.redeemCode(req.Context(), req.Host, req.Form.Get("code"))
session, err := p.redeemCode(req.Context(), getHost(req), req.Form.Get("code"))
if err != nil {
p.logger.Errorf("Error redeeming code during OAuth2 callback: %v", err)
p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Internal Error")

View File

@ -42,7 +42,8 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(204)
return
}
handler, ok := s.Handlers[r.Host]
host := getHost(r)
handler, ok := s.Handlers[host]
if !ok {
// If we only have one handler, host name switching doesn't matter
if len(s.Handlers) == 1 {
@ -56,7 +57,7 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
for k := range s.Handlers {
hostKeys = append(hostKeys, k)
}
s.logger.WithField("host", r.Host).WithField("known-hosts", strings.Join(hostKeys, ", ")).Debug("Host header does not match any we know of")
s.logger.WithField("host", host).WithField("known-hosts", strings.Join(hostKeys, ", ")).Debug("Host header does not match any we know of")
w.WriteHeader(404)
return
}

View File

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

View File

@ -1,3 +1,3 @@
package pkg
const VERSION = "2021.4.2"
const VERSION = "2021.4.4"

View File

@ -5,7 +5,7 @@ info:
email: hello@beryju.org
license:
name: GNU GPLv3
url: https://github.com/BeryJu/authentik/blob/master/LICENSE
url: https://github.com/goauthentik/authentik/blob/master/LICENSE
version: v2beta
basePath: /api/v2beta
consumes:
@ -1152,6 +1152,9 @@ paths:
description: Page Size
required: false
type: integer
- name: superuser_full_list
in: query
type: boolean
responses:
'200':
description: ''
@ -4366,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
@ -7671,6 +7949,9 @@ paths:
required: true
schema:
$ref: '#/definitions/PolicyTest'
- name: format_result
in: query
type: boolean
responses:
'200':
description: ''
@ -14771,6 +15052,7 @@ definitions:
- name
- description
- component
- model_name
type: object
properties:
name:
@ -14785,6 +15067,10 @@ definitions:
title: Component
type: string
minLength: 1
model_name:
title: Model name
type: string
minLength: 1
EventTopPerUser:
required:
- application
@ -17029,21 +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

View File

@ -13,13 +13,13 @@ from selenium.webdriver.common.by import By
from authentik import __version__
from authentik.core.models import Application
from authentik.flows.models import Flow
from authentik.outposts.apps import AuthentikOutpostConfig
from authentik.outposts.models import (
DockerServiceConnection,
Outpost,
OutpostConfig,
OutpostType,
)
from authentik.outposts.tasks import outpost_local_connection
from authentik.providers.proxy.models import ProxyProvider
from tests.e2e.utils import SeleniumTestCase, apply_migration, object_manager, retry
@ -117,7 +117,7 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase):
@object_manager
def test_proxy_connectivity(self):
"""Test proxy connectivity over websocket"""
AuthentikOutpostConfig.init_local_connection()
outpost_local_connection()
proxy: ProxyProvider = ProxyProvider.objects.create(
name="proxy_provider",
authorization_flow=Flow.objects.get(

View File

@ -4,6 +4,7 @@ from sys import platform
from time import sleep
from typing import Any, Optional
from unittest.case import skipUnless
from unittest.mock import Mock, patch
from django.test import override_settings
from docker.models.containers import Container
@ -22,12 +23,31 @@ from authentik.providers.oauth2.generators import (
generate_client_secret,
)
from authentik.sources.oauth.models import OAuthSource
from authentik.sources.oauth.types.manager import SourceType
from authentik.sources.oauth.types.twitter import TwitterOAuthCallback
from tests.e2e.utils import SeleniumTestCase, apply_migration, object_manager, retry
CONFIG_PATH = "/tmp/dex.yml" # nosec
LOGGER = get_logger()
class OAUth1Type(SourceType):
"""Twitter Type definition"""
callback_view = TwitterOAuthCallback
name = "Twitter"
slug = "twitter"
request_token_url = "http://localhost:5000/oauth/request_token" # nosec
access_token_url = "http://localhost:5000/oauth/access_token" # nosec
authorization_url = "http://localhost:5000/oauth/authorize"
profile_url = "http://localhost:5000/api/me"
urls_customizable = False
SOURCE_TYPE_MOCK = Mock(return_value=OAUth1Type())
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestSourceOAuth2(SeleniumTestCase):
"""test OAuth Source flow"""
@ -291,10 +311,6 @@ class TestSourceOAuth1(SeleniumTestCase):
authentication_flow=authentication_flow,
enrollment_flow=enrollment_flow,
provider_type="twitter",
request_token_url="http://localhost:5000/oauth/request_token",
access_token_url="http://localhost:5000/oauth/access_token",
authorization_url="http://localhost:5000/oauth/authorize",
profile_url="http://localhost:5000/api/me",
consumer_key=self.client_id,
consumer_secret=self.client_secret,
)
@ -304,6 +320,10 @@ class TestSourceOAuth1(SeleniumTestCase):
@apply_migration("authentik_flows", "0008_default_flows")
@apply_migration("authentik_flows", "0009_source_flows")
@apply_migration("authentik_crypto", "0002_create_self_signed_kp")
@patch(
"authentik.sources.oauth.types.manager.SourceTypeManager.find_type",
SOURCE_TYPE_MOCK,
)
@object_manager
def test_oauth_enroll(self):
"""test OAuth Source With With OIDC"""

View File

@ -12,9 +12,9 @@ from docker.types.healthcheck import Healthcheck
from authentik import __version__
from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow
from authentik.outposts.apps import AuthentikOutpostConfig
from authentik.outposts.controllers.docker import DockerController
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType
from authentik.outposts.tasks import outpost_local_connection
from authentik.providers.proxy.models import ProxyProvider
@ -53,7 +53,7 @@ class OutpostDockerTests(TestCase):
self.ssl_folder = mkdtemp()
self.container = self._start_container(self.ssl_folder)
# Ensure that local connection have been created
AuthentikOutpostConfig.init_local_connection()
outpost_local_connection()
self.provider: ProxyProvider = ProxyProvider.objects.create(
name="test",
internal_host="http://localhost",

View File

@ -3,11 +3,11 @@ from django.test import TestCase
from authentik.flows.models import Flow
from authentik.lib.config import CONFIG
from authentik.outposts.apps import AuthentikOutpostConfig
from authentik.outposts.controllers.k8s.base import NeedsUpdate
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
from authentik.outposts.controllers.kubernetes import KubernetesController
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
from authentik.outposts.tasks import outpost_local_connection
from authentik.providers.proxy.models import ProxyProvider
@ -17,7 +17,7 @@ class OutpostKubernetesTests(TestCase):
def setUp(self):
super().setUp()
# Ensure that local connection have been created
AuthentikOutpostConfig.init_local_connection()
outpost_local_connection()
self.provider: ProxyProvider = ProxyProvider.objects.create(
name="test",
internal_host="http://localhost",

View File

@ -12,8 +12,8 @@ from docker.types.healthcheck import Healthcheck
from authentik import __version__
from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow
from authentik.outposts.apps import AuthentikOutpostConfig
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType
from authentik.outposts.tasks import outpost_local_connection
from authentik.providers.proxy.controllers.docker import DockerController
from authentik.providers.proxy.models import ProxyProvider
@ -53,7 +53,7 @@ class TestProxyDocker(TestCase):
self.ssl_folder = mkdtemp()
self.container = self._start_container(self.ssl_folder)
# Ensure that local connection have been created
AuthentikOutpostConfig.init_local_connection()
outpost_local_connection()
self.provider: ProxyProvider = ProxyProvider.objects.create(
name="test",
internal_host="http://localhost",

View File

@ -3,8 +3,8 @@ import yaml
from django.test import TestCase
from authentik.flows.models import Flow
from authentik.outposts.apps import AuthentikOutpostConfig
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
from authentik.outposts.tasks import outpost_local_connection
from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesController
from authentik.providers.proxy.models import ProxyProvider
@ -14,7 +14,7 @@ class TestProxyKubernetes(TestCase):
def setUp(self):
# Ensure that local connection have been created
AuthentikOutpostConfig.init_local_connection()
outpost_local_connection()
def test_kubernetes_controller_static(self):
"""Test Kubernetes Controller"""

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.2";
add_header X-authentik-version "2021.4.4";
add_header Vary X-authentik-version;
}

394
web/package-lock.json generated
View File

@ -26,19 +26,19 @@
"integrity": "sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ=="
},
"@babel/core": {
"version": "7.13.15",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.15.tgz",
"integrity": "sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ==",
"version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.16.tgz",
"integrity": "sha512-sXHpixBiWWFti0AV2Zq7avpTasr6sIAu7Y396c608541qAU2ui4a193m0KSQmfPSKFZLnQ3cvlKDOm3XkuXm3Q==",
"requires": {
"@babel/code-frame": "^7.12.13",
"@babel/generator": "^7.13.9",
"@babel/helper-compilation-targets": "^7.13.13",
"@babel/generator": "^7.13.16",
"@babel/helper-compilation-targets": "^7.13.16",
"@babel/helper-module-transforms": "^7.13.14",
"@babel/helpers": "^7.13.10",
"@babel/parser": "^7.13.15",
"@babel/helpers": "^7.13.16",
"@babel/parser": "^7.13.16",
"@babel/template": "^7.12.13",
"@babel/traverse": "^7.13.15",
"@babel/types": "^7.13.14",
"@babel/types": "^7.13.16",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@ -55,6 +55,32 @@
"@babel/highlight": "^7.12.13"
}
},
"@babel/compat-data": {
"version": "7.13.15",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz",
"integrity": "sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA=="
},
"@babel/generator": {
"version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.16.tgz",
"integrity": "sha512-grBBR75UnKOcUWMp8WoDxNsWCFl//XCK6HWTrBQKTr5SV9f5g0pNOjdyzi/DTBv12S9GnYPInIXQBTky7OXEMg==",
"requires": {
"@babel/types": "^7.13.16",
"jsesc": "^2.5.1",
"source-map": "^0.5.0"
}
},
"@babel/helper-compilation-targets": {
"version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz",
"integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==",
"requires": {
"@babel/compat-data": "^7.13.15",
"@babel/helper-validator-option": "^7.12.17",
"browserslist": "^4.14.5",
"semver": "^6.3.0"
}
},
"@babel/helper-validator-identifier": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
@ -71,25 +97,34 @@
}
},
"@babel/parser": {
"version": "7.13.15",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz",
"integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ=="
"version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.16.tgz",
"integrity": "sha512-6bAg36mCwuqLO0hbR+z7PHuqWiCeP7Dzg73OpQwsAB1Eb8HnGEz5xYBzCfbu+YjoaJsJs+qheDxVAuqbt3ILEw=="
},
"@babel/traverse": {
"version": "7.13.15",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz",
"integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==",
"version": "7.13.17",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.17.tgz",
"integrity": "sha512-BMnZn0R+X6ayqm3C3To7o1j7Q020gWdqdyP50KEoVqaCO2c/Im7sYZSmVgvefp8TTMQ+9CtwuBp0Z1CZ8V3Pvg==",
"requires": {
"@babel/code-frame": "^7.12.13",
"@babel/generator": "^7.13.9",
"@babel/generator": "^7.13.16",
"@babel/helper-function-name": "^7.12.13",
"@babel/helper-split-export-declaration": "^7.12.13",
"@babel/parser": "^7.13.15",
"@babel/types": "^7.13.14",
"@babel/parser": "^7.13.16",
"@babel/types": "^7.13.17",
"debug": "^4.1.0",
"globals": "^11.1.0"
}
},
"@babel/types": {
"version": "7.13.17",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.17.tgz",
"integrity": "sha512-RawydLgxbOPDlTLJNtoIypwdmAy//uQIzlKt2+iBiJaRlVuI6QLUxVAyWGNfOzp8Yu4L4lLIacoCyTNtpb4wiA==",
"requires": {
"@babel/helper-validator-identifier": "^7.12.11",
"to-fast-properties": "^2.0.0"
}
},
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@ -355,13 +390,87 @@
}
},
"@babel/helpers": {
"version": "7.13.10",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz",
"integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==",
"version": "7.13.17",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.17.tgz",
"integrity": "sha512-Eal4Gce4kGijo1/TGJdqp3WuhllaMLSrW6XcL0ulyUAQOuxHcCafZE8KHg9857gcTehsm/v7RcOx2+jp0Ryjsg==",
"requires": {
"@babel/template": "^7.12.13",
"@babel/traverse": "^7.13.0",
"@babel/types": "^7.13.0"
"@babel/traverse": "^7.13.17",
"@babel/types": "^7.13.17"
},
"dependencies": {
"@babel/code-frame": {
"version": "7.12.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
"integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
"requires": {
"@babel/highlight": "^7.12.13"
}
},
"@babel/generator": {
"version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.16.tgz",
"integrity": "sha512-grBBR75UnKOcUWMp8WoDxNsWCFl//XCK6HWTrBQKTr5SV9f5g0pNOjdyzi/DTBv12S9GnYPInIXQBTky7OXEMg==",
"requires": {
"@babel/types": "^7.13.16",
"jsesc": "^2.5.1",
"source-map": "^0.5.0"
}
},
"@babel/helper-validator-identifier": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
"integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw=="
},
"@babel/highlight": {
"version": "7.13.10",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz",
"integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==",
"requires": {
"@babel/helper-validator-identifier": "^7.12.11",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
}
},
"@babel/parser": {
"version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.16.tgz",
"integrity": "sha512-6bAg36mCwuqLO0hbR+z7PHuqWiCeP7Dzg73OpQwsAB1Eb8HnGEz5xYBzCfbu+YjoaJsJs+qheDxVAuqbt3ILEw=="
},
"@babel/traverse": {
"version": "7.13.17",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.17.tgz",
"integrity": "sha512-BMnZn0R+X6ayqm3C3To7o1j7Q020gWdqdyP50KEoVqaCO2c/Im7sYZSmVgvefp8TTMQ+9CtwuBp0Z1CZ8V3Pvg==",
"requires": {
"@babel/code-frame": "^7.12.13",
"@babel/generator": "^7.13.16",
"@babel/helper-function-name": "^7.12.13",
"@babel/helper-split-export-declaration": "^7.12.13",
"@babel/parser": "^7.13.16",
"@babel/types": "^7.13.17",
"debug": "^4.1.0",
"globals": "^11.1.0"
}
},
"@babel/types": {
"version": "7.13.17",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.17.tgz",
"integrity": "sha512-RawydLgxbOPDlTLJNtoIypwdmAy//uQIzlKt2+iBiJaRlVuI6QLUxVAyWGNfOzp8Yu4L4lLIacoCyTNtpb4wiA==",
"requires": {
"@babel/helper-validator-identifier": "^7.12.11",
"to-fast-properties": "^2.0.0"
}
},
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
"@babel/highlight": {
@ -1336,86 +1445,28 @@
}
},
"@lingui/babel-plugin-extract-messages": {
"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==",
"version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.8.10.tgz",
"integrity": "sha512-16EnNRb1HXNjdDLMY3xS7jh0wKA00x21LC1CIKRAki80u92jvkSMOJYk+lD6yhdrcl0dH5OMAbdluAm1+rpEPw==",
"requires": {
"@babel/generator": "^7.11.6",
"@babel/runtime": "^7.11.2",
"@lingui/conf": "^3.8.9",
"@lingui/conf": "^3.8.10",
"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.9",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.8.9.tgz",
"integrity": "sha512-UccLtfwrTjXrZcTxpqA4ggYhuUMbXZtzbUVks8nDVt3emVqU56C3VMvVD8WKXLL8Qmq9cEDXPwIZy7IKRL4mEQ==",
"version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.8.10.tgz",
"integrity": "sha512-YLkT5e6JRwVcXEwLD0++/m1p/wvRQbLj/+m8geXfrcFfrsQyT3uhHNZRFK0GdsjyDslSqJYbalYibJUbgC2sOA==",
"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.9",
"@lingui/conf": "^3.8.9",
"@lingui/babel-plugin-extract-messages": "^3.8.10",
"@lingui/conf": "^3.8.10",
"babel-plugin-macros": "^3.0.1",
"bcp-47": "^1.0.7",
"chalk": "^4.1.0",
@ -1442,19 +1493,6 @@
"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",
@ -1531,9 +1569,9 @@
}
},
"@lingui/conf": {
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.8.9.tgz",
"integrity": "sha512-r0RGchwiALjCE6CSOtOKbOqVrNg1EQ78AXjyvbrtJoPWVlChDasWCckXEF0BSnsoZaRP6nQCAI+dsQiGW1deWg==",
"version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.8.10.tgz",
"integrity": "sha512-4KdH+23WXZ5g+LRlvvise3z3mdd41zLgqSJ/PUCMGk60RfElvTrTdxpnm2tOF/2hr+OyGCQEy6kLq606y639qw==",
"requires": {
"@babel/runtime": "^7.11.2",
"@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2",
@ -1589,9 +1627,9 @@
}
},
"@lingui/core": {
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.8.9.tgz",
"integrity": "sha512-QmEfgukR7w/4/4USZT0LGNt7Yq/RgirFl4088wEta0vgroidxaCRgUXr8RXcdFVjTdtG5dc86JTEj4inZECKvg==",
"version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.8.10.tgz",
"integrity": "sha512-1OzZW8iP5yAXxz49pY/WZ1acLvkekd6HgDh8zH3jMA2Hbig2jk6VGVERMO7lwEwJiyEuxaQpe8fRrhCTB7wA3A==",
"requires": {
"@babel/runtime": "^7.11.2",
"make-plural": "^6.2.2",
@ -1599,12 +1637,12 @@
}
},
"@lingui/macro": {
"version": "3.8.9",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.8.9.tgz",
"integrity": "sha512-9LhlbkJ9wOtOLhlaVRLHCRL55S5wOFyyqEhUM+ujUmCskTmMmXzjnRsw5f11nJTK1JJETMT/VlUB5/p7D7Edkw==",
"version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.8.10.tgz",
"integrity": "sha512-oZZ/F7HsNQkDsnHFroxzGFuEIXM624H72RIj8j2ClpR64nt+xYDxXYC6TYFicQLtBGcKKBTBoM+zbDaoIv74qQ==",
"requires": {
"@babel/runtime": "^7.11.2",
"@lingui/conf": "^3.8.9",
"@lingui/conf": "^3.8.10",
"ramda": "^0.27.1"
}
},
@ -1828,13 +1866,13 @@
}
},
"@sentry/browser": {
"version": "6.2.5",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.2.5.tgz",
"integrity": "sha512-nlvaE+D7oaj4MxoY9ikw+krQDOjftnDYJQnOwOraXPk7KYM6YwmkakLuE+x/AkaH3FQVTQF330VAa9d6SWETlA==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.0.tgz",
"integrity": "sha512-Rse9j5XwN9n7GnfW1mNscTS4YQ0oiBNJcaSk3Mw/vQT872Wh60yKyx5wxAw5GujFZI0NgdyPlZwZ/tGQwirRxA==",
"requires": {
"@sentry/core": "6.2.5",
"@sentry/types": "6.2.5",
"@sentry/utils": "6.2.5",
"@sentry/core": "6.3.0",
"@sentry/types": "6.3.0",
"@sentry/utils": "6.3.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -1846,14 +1884,14 @@
}
},
"@sentry/core": {
"version": "6.2.5",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.2.5.tgz",
"integrity": "sha512-I+AkgIFO6sDUoHQticP6I27TT3L+i6TUS03in3IEtpBcSeP2jyhlxI8l/wdA7gsBqUPdQ4GHOOaNgtFIcr8qag==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.0.tgz",
"integrity": "sha512-voot/lJ9gRXB6bx6tVqbEbD6jOd4Sx6Rfmm6pzfpom9C0q+fjIZTatTLq8GdXj8DzxaH1MBDSwtaq/eC3NqYpA==",
"requires": {
"@sentry/hub": "6.2.5",
"@sentry/minimal": "6.2.5",
"@sentry/types": "6.2.5",
"@sentry/utils": "6.2.5",
"@sentry/hub": "6.3.0",
"@sentry/minimal": "6.3.0",
"@sentry/types": "6.3.0",
"@sentry/utils": "6.3.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -1865,12 +1903,12 @@
}
},
"@sentry/hub": {
"version": "6.2.5",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.5.tgz",
"integrity": "sha512-YlEFdEhcfqpl2HC+/dWXBsBJEljyMzFS7LRRjCk8QANcOdp9PhwQjwebUB4/ulOBjHPP2WZk7fBBd/IKDasTUg==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.0.tgz",
"integrity": "sha512-lAnW3Om66t9IR+t1wya1NpOF9lGbvYG6Ca8wxJJGJ1t2PxKwyxpZKzRx0q8M1QFhlZ5cETCzxmM7lBEZ4QVCBg==",
"requires": {
"@sentry/types": "6.2.5",
"@sentry/utils": "6.2.5",
"@sentry/types": "6.3.0",
"@sentry/utils": "6.3.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -1882,12 +1920,12 @@
}
},
"@sentry/minimal": {
"version": "6.2.5",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.5.tgz",
"integrity": "sha512-RKP4Qx3p7Cv0oX1cPKAkNVFYM7p2k1t32cNk1+rrVQS4hwlJ7Eg6m6fsqsO+85jd6Ne/FnyYsfo9cDD3ImTlWQ==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.0.tgz",
"integrity": "sha512-ZdPUwdPQkaKroy67NkwQRqmnfKyd/C1OyouM9IqYKyBjAInjOijwwc/Rd91PMHalvCOGfp1scNZYbZ+YFs/qQQ==",
"requires": {
"@sentry/hub": "6.2.5",
"@sentry/types": "6.2.5",
"@sentry/hub": "6.3.0",
"@sentry/types": "6.3.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -1899,17 +1937,51 @@
}
},
"@sentry/tracing": {
"version": "6.2.5",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.2.5.tgz",
"integrity": "sha512-j/hM0BoHxfrNLxPeEJ5Vq4R34hO/TOHMEpLR3FdnunBXbsmjoKMMygIkPxnpML5XWtvukAehbwpDXldwMYz83w==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.0.tgz",
"integrity": "sha512-3UNGgQOrDKBoDqLc4vt+0n27Zv3lbNEoCbBydq4IvGfuYq7ozWMsaTcelsotMsd4ckDuOEh8V/nJTqrDjvL76g==",
"requires": {
"@sentry/hub": "6.2.5",
"@sentry/minimal": "6.2.5",
"@sentry/types": "6.2.5",
"@sentry/utils": "6.2.5",
"@sentry/hub": "6.3.0",
"@sentry/minimal": "6.3.0",
"@sentry/types": "6.3.0",
"@sentry/utils": "6.3.0",
"tslib": "^1.9.3"
},
"dependencies": {
"@sentry/hub": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.0.tgz",
"integrity": "sha512-lAnW3Om66t9IR+t1wya1NpOF9lGbvYG6Ca8wxJJGJ1t2PxKwyxpZKzRx0q8M1QFhlZ5cETCzxmM7lBEZ4QVCBg==",
"requires": {
"@sentry/types": "6.3.0",
"@sentry/utils": "6.3.0",
"tslib": "^1.9.3"
}
},
"@sentry/minimal": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.0.tgz",
"integrity": "sha512-ZdPUwdPQkaKroy67NkwQRqmnfKyd/C1OyouM9IqYKyBjAInjOijwwc/Rd91PMHalvCOGfp1scNZYbZ+YFs/qQQ==",
"requires": {
"@sentry/hub": "6.3.0",
"@sentry/types": "6.3.0",
"tslib": "^1.9.3"
}
},
"@sentry/types": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.0.tgz",
"integrity": "sha512-xWyCYDmFPjS5ex60kxOOHbHEs4vs00qHbm0iShQfjl4OSg9S2azkcWofDmX8Xbn0FSOUXgdPCjNJW1B0bPVhCA=="
},
"@sentry/utils": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.0.tgz",
"integrity": "sha512-NZzw4oLelgvCsVBG2e+ZtFtaBvgA7rZYtcGFbZTphhAlYoJ6JMCQUzYk0iwJK79yR1quh510x4UE0jynvvToWg==",
"requires": {
"@sentry/types": "6.3.0",
"tslib": "^1.9.3"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -1918,16 +1990,16 @@
}
},
"@sentry/types": {
"version": "6.2.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.5.tgz",
"integrity": "sha512-1Sux6CLYrV9bETMsGP/HuLFLouwKoX93CWzG8BjMueW+Di0OGxZphYjXrGuDs8xO8bAKEVGCHgVQdcB2jevS0w=="
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.0.tgz",
"integrity": "sha512-xWyCYDmFPjS5ex60kxOOHbHEs4vs00qHbm0iShQfjl4OSg9S2azkcWofDmX8Xbn0FSOUXgdPCjNJW1B0bPVhCA=="
},
"@sentry/utils": {
"version": "6.2.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.5.tgz",
"integrity": "sha512-fJoLUZHrd5MPylV1dT4qL74yNFDl1Ur/dab+pKNSyvnHPnbZ/LRM7aJ8VaRY/A7ZdpRowU+E14e/Yeem2c6gtQ==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.0.tgz",
"integrity": "sha512-NZzw4oLelgvCsVBG2e+ZtFtaBvgA7rZYtcGFbZTphhAlYoJ6JMCQUzYk0iwJK79yR1quh510x4UE0jynvvToWg==",
"requires": {
"@sentry/types": "6.2.5",
"@sentry/types": "6.3.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -1956,9 +2028,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": "*"
}
@ -2647,9 +2719,9 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
},
"chart.js": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.1.0.tgz",
"integrity": "sha512-bKJi2VbC4fqZXlLbK7LKVvmG9crjoG9anfp96utZLyIGPuCx+YN+5/HDXy98QGt3lf74T8gKUPISUZL222tDJQ=="
"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",
@ -2775,9 +2847,9 @@
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
},
"codemirror": {
"version": "5.60.0",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.60.0.tgz",
"integrity": "sha512-AEL7LhFOlxPlCL8IdTcJDblJm8yrAGib7I+DErJPdZd4l6imx8IMgKK3RblVgBQqz3TZJR4oknQ03bz+uNjBYA=="
"version": "5.61.0",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.61.0.tgz",
"integrity": "sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg=="
},
"collection-visit": {
"version": "1.0.0",
@ -2921,9 +2993,9 @@
}
},
"date-fns": {
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.20.1.tgz",
"integrity": "sha512-8P5M8Kxbnovd0zfvOs7ipkiVJ3/zZQ0F/nrBW4x5E+I0uAZVZ80h6CKd24fSXQ5TLK5hXMtI4yb2O5rEZdUt2A=="
"version": "2.21.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.21.1.tgz",
"integrity": "sha512-m1WR0xGiC6j6jNFAyW4Nvh4WxAi4JF4w9jRJwSI8nBmNcyZXPcP9VUQG+6gHQXAmqaGEKDKhOqAtENDC941UkA=="
},
"debug": {
"version": "4.3.1",

View File

@ -35,34 +35,34 @@
]
},
"dependencies": {
"@babel/core": "^7.13.15",
"@babel/core": "^7.13.16",
"@babel/plugin-proposal-decorators": "^7.13.15",
"@babel/plugin-transform-runtime": "^7.13.15",
"@babel/preset-env": "^7.13.15",
"@babel/preset-typescript": "^7.13.0",
"@fortawesome/fontawesome-free": "^5.15.3",
"@lingui/cli": "^3.8.9",
"@lingui/core": "^3.8.9",
"@lingui/macro": "^3.8.9",
"@lingui/cli": "^3.8.10",
"@lingui/core": "^3.8.10",
"@lingui/macro": "^3.8.10",
"@patternfly/patternfly": "^4.96.2",
"@polymer/iron-form": "^3.0.1",
"@polymer/paper-input": "^3.2.1",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.1",
"@sentry/browser": "^6.2.5",
"@sentry/tracing": "^6.2.5",
"@sentry/browser": "^6.3.0",
"@sentry/tracing": "^6.3.0",
"@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.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.1.0",
"chart.js": "^3.1.1",
"chartjs-adapter-moment": "^1.0.0",
"codemirror": "^5.60.0",
"codemirror": "^5.61.0",
"construct-style-sheets-polyfill": "^2.4.16",
"eslint": "^7.24.0",
"eslint-config-google": "^0.14.0",

View File

@ -1,4 +1,4 @@
Contact: mailto:security@beryju.org
Expires: Sat, 1 Jan 2022 00:00 +0200
Preferred-Languages: en, de
Policy: https://github.com/BeryJu/authentik/blob/master/SECURITY.md
Policy: https://github.com/goauthentik/authentik/blob/master/SECURITY.md

View File

@ -1,4 +1,4 @@
import { Configuration, Middleware, ResponseContext } from "authentik-api";
import { Config, Configuration, Middleware, ResponseContext, RootApi } from "authentik-api";
import { getCookie } from "../utils";
import { API_DRAWER_MIDDLEWARE } from "../elements/notifications/APIDrawer";
import { MessageMiddleware } from "../elements/messages/Middleware";
@ -12,6 +12,14 @@ export class LoggingMiddleware implements Middleware {
}
let globalConfigPromise: Promise<Config>;
export function config(): Promise<Config> {
if (!globalConfigPromise) {
globalConfigPromise = new RootApi(DEFAULT_CONFIG).rootConfigList();
}
return globalConfigPromise;
}
export const DEFAULT_CONFIG = new Configuration({
basePath: "/api/v2beta",
headers: {

View File

@ -2,12 +2,12 @@ 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";
import { config } from "./Config";
import { Config } from "authentik-api";
export function configureSentry(): Promise<Config> {
return new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => {
return config().then((config) => {
if (config.errorReportingEnabled) {
Sentry.init({
dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
@ -19,10 +19,19 @@ export function configureSentry(): Promise<Config> {
],
tracesSampleRate: 0.6,
environment: config.errorReportingEnvironment,
beforeSend(event: Sentry.Event, hint: Sentry.EventHint) {
beforeSend: async (event: Sentry.Event, hint: Sentry.EventHint): Promise<Sentry.Event | null> => {
if (hint.originalException instanceof SentryIgnoredError) {
return null;
}
if (hint.originalException instanceof Response) {
const response = hint.originalException as Response;
// We only care about server errors
if (response.status < 500) {
return null;
}
const body = await response.json();
event.message = `${response.status} ${response.url}: ${JSON.stringify(body)}`
}
if (event.exception) {
me().then(user => {
Sentry.showReportDialog({

View File

@ -1,15 +1,21 @@
import { CoreApi, SessionUser } from "authentik-api";
import { DEFAULT_CONFIG } from "./Config";
let _globalMePromise: Promise<SessionUser>;
let globalMePromise: Promise<SessionUser>;
export function me(): Promise<SessionUser> {
if (!_globalMePromise) {
_globalMePromise = new CoreApi(DEFAULT_CONFIG).coreUsersMe().catch((ex) => {
if (!globalMePromise) {
globalMePromise = new CoreApi(DEFAULT_CONFIG).coreUsersMe().catch((ex) => {
const defaultUser: SessionUser = {
user: {
username: "",
name: ""
}
};
if (ex.status === 401 || ex.status === 403) {
window.location.assign("/");
}
return ex;
return defaultUser;
});
}
return _globalMePromise;
return globalMePromise;
}

View File

@ -3,11 +3,11 @@ 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.2";
export const VERSION = "2021.4.4";
export const PAGE_SIZE = 20;
export const EVENT_REFRESH = "ak-refresh";
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";
export const EVENT_SIDEBAR_TOGGLE = "ak-sidebar-toggle";
export const EVENT_API_DRAWER_REFRESH = "ak-api-drawer-refresh";
export const TITLE_SUFFIX = "authentik";
export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";";

View File

@ -4,7 +4,8 @@ import PFContent from "@patternfly/patternfly/components/Content/content.css";
import AKGlobal from "../authentik.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import { EVENT_SIDEBAR_TOGGLE, TITLE_SUFFIX } from "../constants";
import { EVENT_SIDEBAR_TOGGLE, TITLE_DEFAULT } from "../constants";
import { config } from "../api/Config";
@customElement("ak-page-header")
export class PageHeader extends LitElement {
@ -17,11 +18,13 @@ export class PageHeader extends LitElement {
@property()
set header(value: string) {
if (value !== "") {
document.title = `${value} - ${TITLE_SUFFIX}`;
} else {
document.title = TITLE_SUFFIX;
}
config().then(config => {
if (value !== "") {
document.title = `${value} - ${config.brandingTitle}`;
} else {
document.title = config.brandingTitle || TITLE_DEFAULT;
}
});
this._header = value;
}

View File

@ -36,13 +36,14 @@ import { AuthenticatorValidateStageChallenge } from "./stages/authenticator_vali
import { WebAuthnAuthenticatorRegisterChallenge } from "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
import { CaptchaChallenge } from "./stages/captcha/CaptchaStage";
import { StageHost } from "./stages/base";
import { Challenge, ChallengeTypeEnum, Config, FlowsApi, RootApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../api/Config";
import { Challenge, ChallengeTypeEnum, Config, FlowsApi } from "authentik-api";
import { config, DEFAULT_CONFIG } from "../api/Config";
import { ifDefined } from "lit-html/directives/if-defined";
import { until } from "lit-html/directives/until";
import { AccessDeniedChallenge } from "./access_denied/FlowAccessDenied";
import { PFSize } from "../elements/Spinner";
import { TITLE_SUFFIX } from "../constants";
import { TITLE_DEFAULT } from "../constants";
import { configureSentry } from "../api/Sentry";
@customElement("ak-flow-executor")
export class FlowExecutor extends LitElement implements StageHost {
@ -98,11 +99,13 @@ export class FlowExecutor extends LitElement implements StageHost {
}
private postUpdate(): void {
if (this.challenge?.title) {
document.title = `${this.challenge.title} - ${TITLE_SUFFIX}`;
} else {
document.title = TITLE_SUFFIX;
}
config().then(config => {
if (this.challenge?.title) {
document.title = `${this.challenge.title} - ${config.brandingTitle}`;
} else {
document.title = config.brandingTitle || TITLE_DEFAULT;
}
});
}
submit<T>(formData?: T): Promise<void> {
@ -124,7 +127,7 @@ export class FlowExecutor extends LitElement implements StageHost {
}
firstUpdated(): void {
new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => {
configureSentry().then((config) => {
this.config = config;
});
this.loading = true;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -44,13 +44,13 @@ export class AdminOverviewPage extends LitElement {
<ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header=${t`Apps with most usage`} style="grid-column-end: span 2;grid-row-end: span 3;">
<ak-top-applications-table></ak-top-applications-table>
</ak-aggregate-card>
<ak-admin-status-card-provider class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header=${t`Providers`} headerLink="#/core/providers/">
<ak-admin-status-card-provider class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header=${t`Providers without application`} headerLink="#/core/providers">
</ak-admin-status-card-provider>
<ak-admin-status-card-policy-unbound class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-infrastructure" header=${t`Policies`} headerLink="#/policy/policies">
<ak-admin-status-card-policy-unbound class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-infrastructure" header=${t`Unbound policies`} headerLink="#/policy/policies">
</ak-admin-status-card-policy-unbound>
<ak-admin-status-card-user-count class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-user" header=${t`Users`} headerLink="#/identity/users">
</ak-admin-status-card-user-count>
<ak-admin-status-version class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-bundle" header=${t`Version`} headerLink="https://github.com/BeryJu/authentik/releases">
<ak-admin-status-version class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-bundle" header=${t`Version`} headerLink="https://github.com/goauthentik/authentik/releases">
</ak-admin-status-version>
<ak-admin-status-card-workers class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header=${t`Workers`}>
</ak-admin-status-card-workers>

View File

@ -22,7 +22,7 @@ export class ApplicationListPage extends TablePage<Application> {
return t`Applications`;
}
pageDescription(): string {
return t`External Applications which use authentik as Identity-Provider, utilizing protocols like OAuth2 and SAML.`;
return t`External Applications which use authentik as Identity-Provider, utilizing protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`;
}
pageIcon(): string {
return "pf-icon pf-icon-applications";
@ -37,6 +37,7 @@ export class ApplicationListPage extends TablePage<Application> {
page: page,
pageSize: PAGE_SIZE,
search: this.search || "",
superuserFullList: true,
});
}

View File

@ -43,7 +43,7 @@ export class ApplicationViewPage extends LitElement {
render(): TemplateResult {
return html`<ak-page-header
icon=${this.application?.metaIcon || ""}
header=${this.application?.name}
header=${this.application?.name || t`Loading`}
description=${ifDefined(this.application?.metaPublisher)}
.iconImage=${true}>
</ak-page-header>
@ -134,7 +134,7 @@ export class ApplicationViewPage extends LitElement {
</div>
</div>
</section>
<div slot="page-policy-bindings" data-tab-title="${t`Policy Bindings`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
<div slot="page-policy-bindings" data-tab-title="${t`Policy / Group / User Bindings`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-card__title">${t`These policies control which users can access this application.`}</div>
<ak-bound-policies-list .target=${this.application.pk}>

View File

@ -190,7 +190,7 @@ export class EventInfo extends LitElement {
<ak-expand>${this.defaultResponse()}</ak-expand>`;
case "update_available":
return html`<h3>${t`New version available!`}</h3>
<a target="_blank" href="https://github.com/BeryJu/authentik/releases/tag/version%2F${this.event.context.new_version}">${this.event.context.new_version}</a>
<a target="_blank" href="https://github.com/goauthentik/authentik/releases/tag/version%2F${this.event.context.new_version}">${this.event.context.new_version}</a>
`;
// Action types which typically don't record any extra context.
// If context is not empty, we fall to the default response.

View File

@ -108,7 +108,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
return html`
<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<p>${t`These policies control upon which events this rule triggers. Bindings to
<p>${t`These bindings control upon which events this rule triggers. Bindings to
groups/users are checked against the user of the event.`}</p>
<ak-bound-policies-list .target=${item.pk}>
</ak-bound-policies-list>

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