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] [bumpversion]
current_version = 2021.4.2 current_version = 2021.4.4
tag = True tag = True
commit = True commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*) parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)

View File

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

128
Pipfile.lock generated
View File

@ -116,18 +116,18 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:73bcd04f6f919e7f8acc27c9d83dab5aee22225fe624c028b2e1c7feaf771098", "sha256:1e55df93aa47a84e2a12a639c7f145e16e6e9ef959542d69d5526d50d2e92692",
"sha256:c45e7d3aef8965ae1b42c9855c31ded19fbb38cfad0a34cc37dc880ded3672c2" "sha256:eab42daaaf68cdad5b112d31dcb0684162098f6558ba7b64156be44f993525fa"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.17.51" "version": "==1.17.54"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:ae45ea7451513373666b7571064c173d649e61fd3e8f413f0e1f1f9db26b3513", "sha256:20a864fc6570ba11d52532c72c3ccabab5c71a9b4a9418601a313d56f1d2ce5b",
"sha256:c853d6c2321e2f2328282c7d49d7b1a06201826ba0e7049c6975ab5f22927ea8" "sha256:37ec76ea2df8609540ba6cb0fe360ae1c589d2e1ee91eb642fd767823f3fcedd"
], ],
"version": "==1.20.51" "version": "==1.20.54"
}, },
"cachetools": { "cachetools": {
"hashes": [ "hashes": [
@ -437,10 +437,10 @@
}, },
"google-auth": { "google-auth": {
"hashes": [ "hashes": [
"sha256:186fe2564634d67fbbb64f3daf8bc8c9cecbb2a7f535ed1a8a71795e50db8d87", "sha256:010f011c4e27d3d5eb01106fba6aac39d164842dfcd8709955c4638f5b11ccf8",
"sha256:70b39558712826e41f65e5f05a8d879361deaf84df8883e5dd0ec3d0da6ab66e" "sha256:f30a672a64d91cc2e3137765d088c5deec26416246f7a9e956eaf69a8d7ed49c"
], ],
"version": "==1.28.1" "version": "==1.29.0"
}, },
"gunicorn": { "gunicorn": {
"hashes": [ "hashes": [
@ -1106,10 +1106,10 @@
}, },
"s3transfer": { "s3transfer": {
"hashes": [ "hashes": [
"sha256:35627b86af8ff97e7ac27975fe0a98a312814b46c6333d8a6b889627bcd80994", "sha256:af1af6384bd7fb8208b06480f9be73d0295d965c4c073a5c95ea5b6661dccc18",
"sha256:efa5bd92a897b6a8d5c1383828dca3d52d0790e0756d49740563a3fb6ed03246" "sha256:f3dfd791cad2799403e3c8051810a7ca6ee1d2e630e5d2a8f9649d892bdb3db6"
], ],
"version": "==0.3.7" "version": "==0.4.0"
}, },
"sentry-sdk": { "sentry-sdk": {
"hashes": [ "hashes": [
@ -1374,59 +1374,59 @@
}, },
"zope.interface": { "zope.interface": {
"hashes": [ "hashes": [
"sha256:02d3535aa18e34ce97c58d241120b7554f7d1cf4f8002fc9675cc7e7745d20e8", "sha256:08f9636e99a9d5410181ba0729e0408d3d8748026ea938f3b970a0249daa8192",
"sha256:0378a42ec284b65706d9ef867600a4a31701a0d6773434e6537cfc744e3343f4", "sha256:0b465ae0962d49c68aa9733ba92a001b2a0933c317780435f00be7ecb959c702",
"sha256:07d289358a8c565ea09e426590dd1179f93cf5ac3dd17d43fcc4fc63c1a9d275", "sha256:0cba8477e300d64a11a9789ed40ee8932b59f9ee05f85276dbb4b59acee5dd09",
"sha256:0e6cdbdd94ae94d1433ab51f46a76df0f2cd041747c31baec1c1ffa4e76bd0c1", "sha256:0cee5187b60ed26d56eb2960136288ce91bcf61e2a9405660d271d1f122a69a4",
"sha256:11354fb8b8bdc5cdd66358ed4f1f0ce739d78ff6d215d33b8f3ae282258c0f11", "sha256:0ea1d73b7c9dcbc5080bb8aaffb776f1c68e807767069b9ccdd06f27a161914a",
"sha256:12588a46ae0a99f172c4524cbbc3bb870f32e0f8405e9fa11a5ef3fa3a808ad7", "sha256:0f91b5b948686659a8e28b728ff5e74b1be6bf40cb04704453617e5f1e945ef3",
"sha256:16caa44a06f6b0b2f7626ced4b193c1ae5d09c1b49c9b4962c93ae8aa2134f55", "sha256:15e7d1f7a6ee16572e21e3576d2012b2778cbacf75eb4b7400be37455f5ca8bf",
"sha256:18c478b89b6505756f007dcf76a67224a23dcf0f365427742ed0c0473099caa4", "sha256:17776ecd3a1fdd2b2cd5373e5ef8b307162f581c693575ec62e7c5399d80794c",
"sha256:221b41442cf4428fcda7fc958c9721c916709e2a3a9f584edd70f1493a09a762", "sha256:194d0bcb1374ac3e1e023961610dc8f2c78a0f5f634d0c737691e215569e640d",
"sha256:26109c50ccbcc10f651f76277cfc05fba8418a907daccc300c9247f24b3158a2", "sha256:1c0e316c9add0db48a5b703833881351444398b04111188069a26a61cfb4df78",
"sha256:28d8157f8c77662a1e0796a7d3cfa8910289131d4b4dd4e10b2686ab1309b67b", "sha256:205e40ccde0f37496904572035deea747390a8b7dc65146d30b96e2dd1359a83",
"sha256:2c51689b7b40c7d9c7e8a678350e73dc647945a13b4e416e7a02bbf0c37bdb01", "sha256:273f158fabc5ea33cbc936da0ab3d4ba80ede5351babc4f577d768e057651531",
"sha256:2ec58e1e1691dde4fbbd97f8610de0f8f1b1a38593653f7d3b8e931b9cd6d67f", "sha256:2876246527c91e101184f63ccd1d716ec9c46519cc5f3d5375a3351c46467c46",
"sha256:416feb6500f7b6fc00d32271f6b8495e67188cb5eb51fc8e289b81fdf465a9cb", "sha256:2c98384b254b37ce50eddd55db8d381a5c53b4c10ee66e1e7fe749824f894021",
"sha256:520352b18adea5478bbf387e9c77910a914985671fe36bc5ef19fdcb67a854bc", "sha256:2e5a26f16503be6c826abca904e45f1a44ff275fdb7e9d1b75c10671c26f8b94",
"sha256:527415b5ca201b4add44026f70278fbc0b942cf0801a26ca5527cb0389b6151e", "sha256:334701327f37c47fa628fc8b8d28c7d7730ce7daaf4bda1efb741679c2b087fc",
"sha256:54243053316b5eec92affe43bbace7c8cd946bc0974a4aa39ff1371df0677b22", "sha256:3748fac0d0f6a304e674955ab1365d515993b3a0a865e16a11ec9d86fb307f63",
"sha256:61b8454190b9cc87279232b6de28dee0bad040df879064bb2f0e505cda907918", "sha256:3c02411a3b62668200910090a0dff17c0b25aaa36145082a5a6adf08fa281e54",
"sha256:672668729edcba0f2ee522ab177fcad91c81cfce991c24d8767765e2637d3515", "sha256:3dd4952748521205697bc2802e4afac5ed4b02909bb799ba1fe239f77fd4e117",
"sha256:67aa26097e194947d29f2b5a123830e03da1519bcce10cac034a51fcdb99c34f", "sha256:3f24df7124c323fceb53ff6168da70dbfbae1442b4f3da439cd441681f54fe25",
"sha256:6e7305e42b5f54e5ccf51820de46f0a7c951ba7cb9e3f519e908545b0f5628d0", "sha256:469e2407e0fe9880ac690a3666f03eb4c3c444411a5a5fddfdabc5d184a79f05",
"sha256:7234ac6782ca43617de803735949f79b894f0c5d353fbc001d745503c69e6d1d", "sha256:4de4bc9b6d35c5af65b454d3e9bc98c50eb3960d5a3762c9438df57427134b8e",
"sha256:7426bea25bdf92f00fa52c7b30fcd2a2f71c21cf007178971b1f248b6c2d3145", "sha256:5208ebd5152e040640518a77827bdfcc73773a15a33d6644015b763b9c9febc1",
"sha256:74b331c5d5efdddf5bbd9e1f7d8cb91a0d6b9c4ba45ca3e9003047a84dca1a3b", "sha256:52de7fc6c21b419078008f697fd4103dbc763288b1406b4562554bd47514c004",
"sha256:79b6db1a18253db86e9bf1e99fa829d60fd3fc7ac04f4451c44e4bdcf6756a42", "sha256:5bb3489b4558e49ad2c5118137cfeaf59434f9737fa9c5deefc72d22c23822e2",
"sha256:7d79cd354ae0a033ac7b86a2889c9e8bb0bb48243a6ed27fc5064ce49b842ada", "sha256:5dba5f530fec3f0988d83b78cc591b58c0b6eb8431a85edd1569a0539a8a5a0e",
"sha256:823d1b4a6a028b8327e64865e2c81a8959ae9f4e7c9c8e0eec814f4f9b36b362", "sha256:5dd9ca406499444f4c8299f803d4a14edf7890ecc595c8b1c7115c2342cadc5f",
"sha256:8715717a5861932b7fe7f3cbd498c82ff4132763e2fea182cc95e53850394ec1", "sha256:5f931a1c21dfa7a9c573ec1f50a31135ccce84e32507c54e1ea404894c5eb96f",
"sha256:89a6091f2d07936c8a96ce56f2000ecbef20fb420a94845e7d53913c558a6378", "sha256:63b82bb63de7c821428d513607e84c6d97d58afd1fe2eb645030bdc185440120",
"sha256:8af4b3116e4a37059bc8c7fe36d4a73d7c1d8802a1d8b6e549f1380d13a40160", "sha256:66c0061c91b3b9cf542131148ef7ecbecb2690d48d1612ec386de9d36766058f",
"sha256:8b4b0034e6c7f30133fa64a1cc276f8f1a155ef9529e7eb93a3c1728b40c0f5c", "sha256:6f0c02cbb9691b7c91d5009108f975f8ffeab5dff8f26d62e21c493060eff2a1",
"sha256:92195df3913c1de80062635bf64cd7bd0d0934a7fa1689b6d287d1cbbd16922c", "sha256:71aace0c42d53abe6fc7f726c5d3b60d90f3c5c055a447950ad6ea9cec2e37d9",
"sha256:96c2e68385f3848d58f19b2975a675532abdb65c8fa5f04d94b95b27b6b1ffa7", "sha256:7d97a4306898b05404a0dcdc32d9709b7d8832c0c542b861d9a826301719794e",
"sha256:9c7044dbbf8c58420a9ef4ed6901f5a8b7698d90cd984d7f57a18c78474686f6", "sha256:7df1e1c05304f26faa49fa752a8c690126cf98b40b91d54e6e9cc3b7d6ffe8b7",
"sha256:a1937efed7e3fe0ee74630e1960df887d8aa83c571e1cf4db9d15b9c181d457d", "sha256:8270252effc60b9642b423189a2fe90eb6b59e87cbee54549db3f5562ff8d1b8",
"sha256:a38c10423a475a1658e2cb8f52cf84ec20a4c0adff724dd43a6b45183f499bc1", "sha256:867a5ad16892bf20e6c4ea2aab1971f45645ff3102ad29bd84c86027fa99997b",
"sha256:a413c424199bcbab71bf5fa7538246f27177fbd6dd74b2d9c5f34878658807f8", "sha256:877473e675fdcc113c138813a5dd440da0769a2d81f4d86614e5d62b69497155",
"sha256:b18a855f8504743e0a2d8b75d008c7720d44e4c76687e13f959e35d9a13eb397", "sha256:8892f89999ffd992208754851e5a052f6b5db70a1e3f7d54b17c5211e37a98c7",
"sha256:b4d59ab3608538e550a72cea13d3c209dd72b6e19e832688da7884081c01594e", "sha256:9a9845c4c6bb56e508651f005c4aeb0404e518c6f000d5a1123ab077ab769f5c",
"sha256:b51d3f1cd87f488455f43046d72003689024b0fa9b2d53635db7523033b19996", "sha256:a1e6e96217a0f72e2b8629e271e1b280c6fa3fe6e59fa8f6701bec14e3354325",
"sha256:c02105deda867d09cdd5088d08708f06d75759df6f83d8f7007b06f422908a30", "sha256:a8156e6a7f5e2a0ff0c5b21d6bcb45145efece1909efcbbbf48c56f8da68221d",
"sha256:c7b6032dc4490b0dcaf078f09f5b382dc35493cb7f473840368bf0de3196c2b6", "sha256:a9506a7e80bcf6eacfff7f804c0ad5350c8c95b9010e4356a4b36f5322f09abb",
"sha256:c95b355dba2aaf5177dff943b25ded0529a7feb80021d5fdb114a99f0a1ef508", "sha256:af310ec8335016b5e52cae60cda4a4f2a60a788cbb949a4fbea13d441aa5a09e",
"sha256:c980ae87863d76b1ea9a073d6d95554b4135032d34bc541be50c07d4a085821b", "sha256:b0297b1e05fd128d26cc2460c810d42e205d16d76799526dfa8c8ccd50e74959",
"sha256:d12895cd083e35e9e032eb4b57645b91116f8979527381a8d864d1f6b8cb4a2e", "sha256:bf68f4b2b6683e52bec69273562df15af352e5ed25d1b6641e7efddc5951d1a7",
"sha256:d3cd9bad547a8e5fbe712a1dc1413aff1b917e8d39a2cd1389a6f933b7a21460", "sha256:d0c1bc2fa9a7285719e5678584f6b92572a5b639d0e471bb8d4b650a1a910920",
"sha256:e8809b01f27f679e3023b9e2013051e0a3f17abff4228cb5197663afd8a0f2c7", "sha256:d4d9d6c1a455d4babd320203b918ccc7fcbefe308615c521062bc2ba1aa4d26e",
"sha256:f3c37b0dc1898e305aad4f7a1d75f6da83036588c28a9ce0afc681ff5245a601", "sha256:db1fa631737dab9fa0b37f3979d8d2631e348c3b4e8325d6873c2541d0ae5a48",
"sha256:f966765f54b536e791541458de84a737a6adba8467190f17a8fe7f85354ba908", "sha256:dd93ea5c0c7f3e25335ab7d22a507b1dc43976e1345508f845efc573d3d779d8",
"sha256:fa939c2e2468142c9773443d4038e7c915b0cc1b670d3c9192bdc503f7ea73e9", "sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4",
"sha256:fcc5c1f95102989d2e116ffc8467963554ce89f30a65a3ea86a4d06849c498d8" "sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263"
], ],
"version": "==5.3.0" "version": "==5.4.0"
} }
}, },
"develop": { "develop": {

View File

@ -4,13 +4,13 @@
--- ---
[![](https://img.shields.io/discord/809154715984199690?label=Discord&style=flat-square)](https://discord.gg/KPnmtNWy) [![](https://img.shields.io/discord/809154715984199690?label=Discord&style=flat-square)](https://discord.gg/jg33eMhnj6)
[![CI Build status](https://img.shields.io/azure-devops/build/beryjuorg/authentik/1?style=flat-square)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=1) [![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/1?compact_message&style=flat-square)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=1) [![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/beryju/authentik?style=flat-square)](https://codecov.io/gh/BeryJu/authentik) [![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) ![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) ![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? ## What is authentik?
@ -31,7 +31,7 @@ Light | Dark
## Development ## Development
See [Development Documentation](https://goauthentik.io/docs/development/local-dev-environment) See [Development Documentation](https://goauthentik.io/developer-docs/)
## Security ## Security

View File

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

View File

@ -4,7 +4,7 @@ from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = { CELERY_BEAT_SCHEDULE = {
"admin_latest_version": { "admin_latest_version": {
"task": "authentik.admin.tasks.update_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"}, "options": {"queue": "authentik_scheduled"},
} }
} }

View File

@ -23,7 +23,9 @@ URL_FINDER = URLValidator.regex.pattern[1:]
def update_latest_version(self: MonitoredTask): def update_latest_version(self: MonitoredTask):
"""Update latest version info""" """Update latest version info"""
try: 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() response.raise_for_status()
data = response.json() data = response.json()
tag_name = data.get("tag_name") tag_name = data.get("tag_name")

View File

@ -4,6 +4,7 @@ from binascii import Error
from typing import Any, Optional, Union from typing import Any, Optional, Union
from rest_framework.authentication import BaseAuthentication, get_authorization_header from rest_framework.authentication import BaseAuthentication, get_authorization_header
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.request import Request from rest_framework.request import Request
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
@ -14,7 +15,7 @@ LOGGER = get_logger()
# pylint: disable=too-many-return-statements # pylint: disable=too-many-return-statements
def token_from_header(raw_header: bytes) -> Optional[Token]: 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() auth_credentials = raw_header.decode()
if auth_credentials == "": if auth_credentials == "":
return None return None
@ -25,28 +26,27 @@ def token_from_header(raw_header: bytes) -> Optional[Token]:
auth_type, body = plain.split() auth_type, body = plain.split()
auth_credentials = f"{auth_type} {b64encode(body.encode()).decode()}" auth_credentials = f"{auth_type} {b64encode(body.encode()).decode()}"
except (UnicodeDecodeError, Error): except (UnicodeDecodeError, Error):
return None raise AuthenticationFailed("Malformed header")
auth_type, auth_credentials = auth_credentials.split() auth_type, auth_credentials = auth_credentials.split()
if auth_type.lower() not in ["basic", "bearer"]: if auth_type.lower() not in ["basic", "bearer"]:
LOGGER.debug("Unsupported authentication type, denying", type=auth_type.lower()) LOGGER.debug("Unsupported authentication type, denying", type=auth_type.lower())
return None raise AuthenticationFailed("Unsupported authentication type")
password = auth_credentials password = auth_credentials
if auth_type.lower() == "basic": if auth_type.lower() == "basic":
try: try:
auth_credentials = b64decode(auth_credentials.encode()).decode() auth_credentials = b64decode(auth_credentials.encode()).decode()
except (UnicodeDecodeError, Error): except (UnicodeDecodeError, Error):
return None raise AuthenticationFailed("Malformed header")
# Accept credentials with username and without # Accept credentials with username and without
if ":" in auth_credentials: if ":" in auth_credentials:
_, password = auth_credentials.split(":") _, password = auth_credentials.split(":")
else: else:
password = auth_credentials password = auth_credentials
if password == "": # nosec if password == "": # nosec
return None raise AuthenticationFailed("Malformed header")
tokens = Token.filter_not_expired(key=password, intent=TokenIntents.INTENT_API) tokens = Token.filter_not_expired(key=password, intent=TokenIntents.INTENT_API)
if not tokens.exists(): if not tokens.exists():
LOGGER.debug("Token not found") raise AuthenticationFailed("Token invalid/expired")
return None
return tokens.first() return tokens.first()
@ -58,10 +58,8 @@ class AuthentikTokenAuthentication(BaseAuthentication):
auth = get_authorization_header(request) auth = get_authorization_header(request)
token = token_from_header(auth) token = token_from_header(auth)
# None is only returned when the header isn't set.
if not token: if not token:
return None return None
return (token.user, None) return (token.user, None)
def authenticate_header(self, request: Request) -> str:
return "Bearer"

View File

@ -11,6 +11,29 @@ authentik API Browser
{% endblock %} {% endblock %}
{% block body %} {% 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 <rapi-doc
spec-url="{{ path }}" spec-url="{{ path }}"
heading-text="authentik" heading-text="authentik"

View File

@ -3,6 +3,7 @@ from base64 import b64encode
from django.test import TestCase from django.test import TestCase
from guardian.shortcuts import get_anonymous_user from guardian.shortcuts import get_anonymous_user
from rest_framework.exceptions import AuthenticationFailed
from authentik.api.auth import token_from_header from authentik.api.auth import token_from_header
from authentik.core.models import Token, TokenIntents from authentik.core.models import Token, TokenIntents
@ -28,17 +29,21 @@ class TestAPIAuth(TestCase):
def test_invalid_type(self): def test_invalid_type(self):
"""Test invalid type""" """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): def test_invalid_decode(self):
"""Test invalid bas64""" """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): def test_invalid_empty_password(self):
"""Test invalid with empty password""" """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): def test_invalid_no_token(self):
"""Test invalid with no token""" """Test invalid with no token"""
auth = b64encode(":abc".encode()).decode() with self.assertRaises(AuthenticationFailed):
self.assertIsNone(token_from_header(f"Basic :{auth}".encode())) 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("core/tokens", TokenViewSet)
router.register("outposts/outposts", OutpostViewSet) router.register("outposts/outposts", OutpostViewSet)
router.register("outposts/instances", OutpostViewSet)
router.register("outposts/service_connections/all", ServiceConnectionViewSet) router.register("outposts/service_connections/all", ServiceConnectionViewSet)
router.register("outposts/service_connections/docker", DockerServiceConnectionViewSet) router.register("outposts/service_connections/docker", DockerServiceConnectionViewSet)
router.register( router.register(
@ -195,7 +196,8 @@ info = openapi.Info(
default_version="v2beta", default_version="v2beta",
contact=openapi.Contact(email="hello@beryju.org"), contact=openapi.Contact(email="hello@beryju.org"),
license=openapi.License( 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,)) SchemaView = get_schema_view(info, public=True, permission_classes=(AllowAny,))

View File

@ -91,6 +91,15 @@ class ApplicationViewSet(ModelViewSet):
applications.append(application) applications.append(application)
return applications 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: def list(self, request: Request) -> Response:
"""Custom list method that checks Policy based access instead of guardian""" """Custom list method that checks Policy based access instead of guardian"""
queryset = self._filter_queryset_for_list(self.get_queryset()) queryset = self._filter_queryset_for_list(self.get_queryset())
@ -98,6 +107,13 @@ class ApplicationViewSet(ModelViewSet):
should_cache = request.GET.get("search", "") == "" 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 = [] allowed_applications = []
if not should_cache: if not should_cache:
allowed_applications = self._get_allowed_applications(queryset) allowed_applications = self._get_allowed_applications(queryset)

View File

@ -1,6 +1,7 @@
"""PropertyMapping API Views""" """PropertyMapping API Views"""
from json import dumps from json import dumps
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from guardian.shortcuts import get_objects_for_user from guardian.shortcuts import get_objects_for_user
from rest_framework import mixins from rest_framework import mixins
@ -93,6 +94,7 @@ class PropertyMappingViewSet(
"description": subclass.__doc__, "description": subclass.__doc__,
# pyright: reportGeneralTypeIssues=false # pyright: reportGeneralTypeIssues=false
"component": subclass().component, "component": subclass().component,
"model_name": subclass._meta.model_name,
} }
) )
return Response(TypeCreateSerializer(data, many=True).data) return Response(TypeCreateSerializer(data, many=True).data)
@ -101,6 +103,13 @@ class PropertyMappingViewSet(
@swagger_auto_schema( @swagger_auto_schema(
request_body=PolicyTestSerializer(), request_body=PolicyTestSerializer(),
responses={200: PropertyMappingTestResultSerializer, 400: "Invalid parameters"}, 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"]) @action(detail=True, pagination_class=None, filter_backends=[], methods=["POST"])
# pylint: disable=unused-argument, invalid-name # pylint: disable=unused-argument, invalid-name
@ -111,6 +120,8 @@ class PropertyMappingViewSet(
if not test_params.is_valid(): if not test_params.is_valid():
return Response(test_params.errors, status=400) 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 # User permission check, only allow mapping testing for users that are readable
users = get_objects_for_user(request.user, "authentik_core.view_user").filter( users = get_objects_for_user(request.user, "authentik_core.view_user").filter(
pk=test_params.validated_data["user"].pk pk=test_params.validated_data["user"].pk
@ -125,7 +136,9 @@ class PropertyMappingViewSet(
self.request, self.request,
**test_params.validated_data.get("context", {}), **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 except Exception as exc: # pylint: disable=broad-except
response_data["result"] = str(exc) response_data["result"] = str(exc)
response_data["successful"] = False response_data["successful"] = False

View File

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

View File

@ -81,6 +81,7 @@ class SourceViewSet(
"name": subclass._meta.verbose_name, "name": subclass._meta.verbose_name,
"description": subclass.__doc__, "description": subclass.__doc__,
"component": component, "component": component,
"model_name": subclass._meta.model_name,
} }
) )
return Response(TypeCreateSerializer(data, many=True).data) 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.api.decorators import permission_required
from authentik.core.api.users import UserSerializer from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import PassiveSerializer 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.events.models import Event, EventAction
from authentik.managed.api import ManagedSerializer from authentik.managed.api import ManagedSerializer
@ -64,7 +64,7 @@ class TokenViewSet(ModelViewSet):
ordering = ["expires"] ordering = ["expires"]
def perform_create(self, serializer: TokenSerializer): 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") @permission_required("authentik_core.view_token_key")
@swagger_auto_schema( @swagger_auto_schema(

View File

@ -48,6 +48,7 @@ class TypeCreateSerializer(PassiveSerializer):
name = CharField(required=True) name = CharField(required=True)
description = CharField(required=True) description = CharField(required=True)
component = CharField(required=True) component = CharField(required=True)
model_name = CharField(required=True)
class CacheSerializer(PassiveSerializer): class CacheSerializer(PassiveSerializer):

View File

@ -1,6 +1,7 @@
"""Channels base classes""" """Channels base classes"""
from channels.exceptions import DenyConnection from channels.exceptions import DenyConnection
from channels.generic.websocket import JsonWebsocketConsumer from channels.generic.websocket import JsonWebsocketConsumer
from rest_framework.exceptions import AuthenticationFailed
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.api.auth import token_from_header from authentik.api.auth import token_from_header
@ -22,9 +23,13 @@ class AuthJsonConsumer(JsonWebsocketConsumer):
raw_header = headers[b"authorization"] raw_header = headers[b"authorization"]
token = token_from_header(raw_header) try:
if not token: token = token_from_header(raw_header)
LOGGER.warning("Failed to authenticate") # 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() raise DenyConnection()
self.user = token.user self.user = token.user

View File

@ -2,7 +2,7 @@
from django.urls.base import reverse from django.urls.base import reverse
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from authentik.core.models import Token, User from authentik.core.models import Token, TokenIntents, User
class TestTokenAPI(APITestCase): class TestTokenAPI(APITestCase):
@ -19,4 +19,6 @@ class TestTokenAPI(APITestCase):
reverse("authentik_api:token-list"), {"identifier": "test-token"} reverse("authentik_api:token-list"), {"identifier": "test-token"}
) )
self.assertEqual(response.status_code, 201) 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 = [] data = []
for value, name in EventAction.choices: for value, name in EventAction.choices:
data.append( data.append(
{ {"name": name, "description": "", "component": value, "model_name": ""}
"name": name,
"description": "",
"component": value,
}
) )
return Response(TypeCreateSerializer(data, many=True).data) return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -80,6 +80,7 @@ class StageViewSet(
"name": subclass._meta.verbose_name, "name": subclass._meta.verbose_name,
"description": subclass.__doc__, "description": subclass.__doc__,
"component": subclass().component, "component": subclass().component,
"model_name": subclass._meta.model_name,
} }
) )
data = sorted(data, key=lambda x: x["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 drf_yasg.utils import no_body, swagger_auto_schema
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.views import APIView from rest_framework.views import APIView
from sentry_sdk import capture_exception
from structlog.stdlib import BoundLogger, get_logger from structlog.stdlib import BoundLogger, get_logger
from authentik.core.models import USER_ATTRIBUTE_DEBUG 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) stage_response = self.current_stage_view.get(request, *args, **kwargs)
return to_stage_response(request, stage_response) return to_stage_response(request, stage_response)
except Exception as exc: # pylint: disable=broad-except 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)) return to_stage_response(request, FlowErrorResponse(request, exc))
@swagger_auto_schema( @swagger_auto_schema(
@ -180,7 +182,8 @@ class FlowExecutorView(APIView):
stage_response = self.current_stage_view.post(request, *args, **kwargs) stage_response = self.current_stage_view.post(request, *args, **kwargs)
return to_stage_response(request, stage_response) return to_stage_response(request, stage_response)
except Exception as exc: # pylint: disable=broad-except 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)) return to_stage_response(request, FlowErrorResponse(request, exc))
def _initiate_plan(self) -> FlowPlan: def _initiate_plan(self) -> FlowPlan:

View File

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

View File

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

View File

@ -1,17 +1,8 @@
"""authentik outposts app config""" """authentik outposts app config"""
from importlib import import_module 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.apps import AppConfig
from django.db import ProgrammingError 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 from structlog.stdlib import get_logger
LOGGER = get_logger() LOGGER = get_logger()
@ -27,49 +18,8 @@ class AuthentikOutpostConfig(AppConfig):
def ready(self): def ready(self):
import_module("authentik.outposts.signals") import_module("authentik.outposts.signals")
try: try:
AuthentikOutpostConfig.init_local_connection() from authentik.outposts.tasks import outpost_local_connection
outpost_local_connection.delay()
except ProgrammingError: except ProgrammingError:
pass 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": { "outposts_service_connection_check": {
"task": "authentik.outposts.tasks.outpost_service_connection_monitor", "task": "authentik.outposts.tasks.outpost_service_connection_monitor",
"schedule": crontab(minute=0, hour="*"), "schedule": crontab(minute="*/60"),
"options": {"queue": "authentik_scheduled"}, "options": {"queue": "authentik_scheduled"},
}, },
"outpost_token_ensurer": { "outpost_token_ensurer": {
@ -17,4 +17,9 @@ CELERY_BEAT_SCHEDULE = {
"schedule": crontab(minute="*/5"), "schedule": crontab(minute="*/5"),
"options": {"queue": "authentik_scheduled"}, "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""" """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 typing import Any
from urllib.parse import urlparse
import yaml
from asgiref.sync import async_to_sync from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer from channels.layers import get_channel_layer
from django.core.cache import cache from django.core.cache import cache
from django.db.models.base import Model from django.db.models.base import Model
from django.utils.text import slugify 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 structlog.stdlib import get_logger
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus 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) outpost: Outpost = Outpost.objects.get(pk=outpost_pk)
self.set_uid(slugify(outpost.name)) self.set_uid(slugify(outpost.name))
try: try:
if not outpost.service_connection:
return
if outpost.type == OutpostType.PROXY: if outpost.type == OutpostType.PROXY:
service_connection = outpost.service_connection service_connection = outpost.service_connection
if isinstance(service_connection, DockerServiceConnection): if isinstance(service_connection, DockerServiceConnection):
@ -183,3 +194,42 @@ def _outpost_single_update(outpost: Outpost, layer=None):
for state in OutpostState.for_outpost(outpost): for state in OutpostState.for_outpost(outpost):
LOGGER.debug("sending update", channel=state.uid, outpost=outpost) LOGGER.debug("sending update", channel=state.uid, outpost=outpost)
async_to_sync(layer.send)(state.uid, {"type": "event.update"}) 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, "name": subclass._meta.verbose_name,
"description": subclass.__doc__, "description": subclass.__doc__,
"component": subclass().component, "component": subclass().component,
"model_name": subclass._meta.model_name,
} }
) )
return Response(TypeCreateSerializer(data, many=True).data) return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -1,13 +1,23 @@
"""Test authorize view""" """Test authorize view"""
from django.test import RequestFactory, TestCase 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.flows.models import Flow
from authentik.providers.oauth2.errors import ( from authentik.providers.oauth2.errors import (
AuthorizeError, AuthorizeError,
ClientIdError, ClientIdError,
RedirectUriError, 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 from authentik.providers.oauth2.views.authorize import OAuthAuthorizationParams
@ -32,15 +42,194 @@ class TestViewsAuthorize(TestCase):
) )
OAuthAuthorizationParams.from_request(request) OAuthAuthorizationParams.from_request(request)
def test_missing_redirect_uri(self): def test_request(self):
"""test missing redirect URI""" """test request param"""
OAuth2Provider.objects.create( OAuth2Provider.objects.create(
name="test", name="test",
client_id="test", client_id="test",
authorization_flow=Flow.objects.first(), 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): with self.assertRaises(RedirectUriError):
request = self.factory.get( request = self.factory.get(
"/", data={"response_type": "code", "client_id": "test"} "/", data={"response_type": "code", "client_id": "test"}
) )
OAuthAuthorizationParams.from_request(request) 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 import re
from base64 import b64decode from base64 import b64decode
from binascii import Error 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 import HttpRequest, HttpResponse, JsonResponse
from django.http.response import HttpResponseRedirect
from django.utils.cache import patch_vary_headers from django.utils.cache import patch_vary_headers
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
@ -25,15 +27,34 @@ class TokenResponse(JsonResponse):
self["Pragma"] = "no-cache" self["Pragma"] = "no-cache"
def cors_allow_any(request, response): def cors_allow(request: HttpRequest, response: HttpResponse, *allowed_origins: str):
""" """Add headers to permit CORS requests from allowed_origins, with or without credentials,
Add headers to permit CORS requests from any origin, with or without credentials, with any headers."""
with any headers.
"""
origin = request.META.get("HTTP_ORIGIN") origin = request.META.get("HTTP_ORIGIN")
if not origin: if not origin:
return response 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. # From the CORS spec: The string "*" cannot be used for a resource that supports credentials.
response["Access-Control-Allow-Origin"] = origin response["Access-Control-Allow-Origin"] = origin
patch_vary_headers(response, ["Origin"]) patch_vary_headers(response, ["Origin"])
@ -141,3 +162,18 @@ def protected_resource_view(scopes: list[str]):
return view_wrapper return view_wrapper
return 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 dataclasses import dataclass, field
from datetime import timedelta from datetime import timedelta
from typing import Optional 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 uuid import uuid4
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.http.response import Http404, HttpResponseRedirect from django.http.response import Http404, HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
@ -46,6 +46,7 @@ from authentik.providers.oauth2.models import (
OAuth2Provider, OAuth2Provider,
ResponseTypes, ResponseTypes,
) )
from authentik.providers.oauth2.utils import HttpResponseRedirectScheme
from authentik.providers.oauth2.views.userinfo import UserInfoView from authentik.providers.oauth2.views.userinfo import UserInfoView
from authentik.stages.consent.models import ConsentMode, ConsentStage from authentik.stages.consent.models import ConsentMode, ConsentStage
from authentik.stages.consent.stage import ( from authentik.stages.consent.stage import (
@ -233,9 +234,17 @@ class OAuthFulfillmentStage(StageView):
params: OAuthAuthorizationParams params: OAuthAuthorizationParams
provider: OAuth2Provider 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 # pylint: disable=unused-argument
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""final Stage of an OAuth2 Flow""" """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( self.params: OAuthAuthorizationParams = self.executor.plan.context.pop(
PLAN_CONTEXT_PARAMS PLAN_CONTEXT_PARAMS
) )
@ -258,7 +267,7 @@ class OAuthFulfillmentStage(StageView):
flow=self.executor.plan.flow_pk, flow=self.executor.plan.flow_pk,
scopes=", ".join(self.params.scope), scopes=", ".join(self.params.scope),
).from_http(self.request) ).from_http(self.request)
return redirect(self.create_response_uri()) return self.redirect(self.create_response_uri())
except (ClientIdError, RedirectUriError) as error: except (ClientIdError, RedirectUriError) as error:
error.to_event(application=application).from_http(request) error.to_event(application=application).from_http(request)
self.executor.stage_invalid() self.executor.stage_invalid()
@ -267,7 +276,7 @@ class OAuthFulfillmentStage(StageView):
except AuthorizeError as error: except AuthorizeError as error:
error.to_event(application=application).from_http(request) error.to_event(application=application).from_http(request)
self.executor.stage_invalid() self.executor.stage_invalid()
return redirect(error.create_uri()) return self.redirect(error.create_uri())
def create_response_uri(self) -> str: def create_response_uri(self) -> str:
"""Create a final Response URI the user is redirected to.""" """Create a final Response URI the user is redirected to."""
@ -301,7 +310,7 @@ class OAuthFulfillmentStage(StageView):
return urlunsplit(uri) return urlunsplit(uri)
raise OAuth2Error() raise OAuth2Error()
except OAuth2Error as error: 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( raise AuthorizeError(
self.params.redirect_uri, self.params.redirect_uri,
"server_error", "server_error",

View File

@ -19,7 +19,7 @@ from authentik.providers.oauth2.models import (
ResponseTypes, ResponseTypes,
ScopeMapping, ScopeMapping,
) )
from authentik.providers.oauth2.utils import cors_allow_any from authentik.providers.oauth2.utils import cors_allow
LOGGER = get_logger() LOGGER = get_logger()
@ -30,6 +30,8 @@ PLAN_CONTEXT_SCOPES = "scopes"
class ProviderInfoView(View): class ProviderInfoView(View):
"""OpenID-compliant Provider Info""" """OpenID-compliant Provider Info"""
provider: OAuth2Provider
def get_info(self, provider: OAuth2Provider) -> dict[str, Any]: def get_info(self, provider: OAuth2Provider) -> dict[str, Any]:
"""Get dictionary for OpenID Connect information""" """Get dictionary for OpenID Connect information"""
scopes = list( scopes = list(
@ -95,19 +97,20 @@ class ProviderInfoView(View):
} }
# pylint: disable=unused-argument # pylint: disable=unused-argument
def get( def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
self, request: HttpRequest, application_slug: str, *args, **kwargs
) -> HttpResponse:
"""OpenID-compliant Provider Info""" """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) 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 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) response = super().dispatch(request, *args, **kwargs)
cors_allow_any(request, response) cors_allow(request, response, *self.provider.redirect_uris.split("\n"))
return response return response

View File

@ -19,7 +19,11 @@ from authentik.providers.oauth2.models import (
OAuth2Provider, OAuth2Provider,
RefreshToken, 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() LOGGER = get_logger()
@ -154,7 +158,18 @@ class TokenParams:
class TokenView(View): class TokenView(View):
"""Generate tokens for clients""" """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: def post(self, request: HttpRequest) -> HttpResponse:
"""Generate tokens for clients""" """Generate tokens for clients"""
@ -198,7 +213,7 @@ class TokenView(View):
response_dict = { response_dict = {
"access_token": refresh_token.access_token, "access_token": refresh_token.access_token,
"refresh_token": refresh_token.refresh_token, "refresh_token": refresh_token.refresh_token,
"token_type": "Bearer", "token_type": "bearer",
"expires_in": timedelta_from_string( "expires_in": timedelta_from_string(
self.params.provider.token_validity self.params.provider.token_validity
).seconds, ).seconds,

View File

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

View File

@ -21,7 +21,7 @@ class ProxyScopeMappingManager(ObjectManager):
EnsureExists( EnsureExists(
ScopeMapping, ScopeMapping,
"goauthentik.io/providers/proxy/scope-proxy", "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, scope_name=SCOPE_AK_PROXY,
expression=SCOPE_AK_PROXY_EXPRESSION, expression=SCOPE_AK_PROXY_EXPRESSION,
), ),

View File

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

View File

@ -8,7 +8,7 @@ AUTHENTICATION_BACKENDS = [
CELERY_BEAT_SCHEDULE = { CELERY_BEAT_SCHEDULE = {
"sources_ldap_sync": { "sources_ldap_sync": {
"task": "authentik.sources.ldap.tasks.ldap_sync_all", "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"}, "options": {"queue": "authentik_scheduled"},
} }
} }

View File

@ -9,6 +9,7 @@ from requests.models import Response
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik import __version__ from authentik import __version__
from authentik.events.models import Event, EventAction
from authentik.sources.oauth.models import OAuthSource from authentik.sources.oauth.models import OAuthSource
LOGGER = get_logger() LOGGER = get_logger()
@ -39,8 +40,11 @@ class BaseOAuthClient:
def get_profile_info(self, token: dict[str, str]) -> Optional[dict[str, Any]]: def get_profile_info(self, token: dict[str, str]) -> Optional[dict[str, Any]]:
"Fetch user profile information." "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: 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() response.raise_for_status()
except RequestException as exc: except RequestException as exc:
LOGGER.warning("Unable to fetch user profile", exc=exc) LOGGER.warning("Unable to fetch user profile", exc=exc)
@ -59,7 +63,16 @@ class BaseOAuthClient:
args.update(additional) args.update(additional)
params = urlencode(args) params = urlencode(args)
LOGGER.info("redirect args", **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]: def parse_raw_token(self, raw_token: str) -> dict[str, Any]:
"Parse token and secret from raw token response." "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: if raw_token is not None and verifier is not None:
token = self.parse_raw_token(raw_token) token = self.parse_raw_token(raw_token)
try: 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( response = self.do_request(
"post", "post",
self.source.access_token_url, access_token_url,
token=token, token=token,
headers=self._default_headers, headers=self._default_headers,
oauth_verifier=verifier, oauth_verifier=verifier,
@ -48,9 +51,12 @@ class OAuthClient(BaseOAuthClient):
"Fetch the OAuth request token. Only required for OAuth 1.0." "Fetch the OAuth request token. Only required for OAuth 1.0."
callback = self.request.build_absolute_uri(self.callback) callback = self.request.build_absolute_uri(self.callback)
try: 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( response = self.do_request(
"post", "post",
self.source.request_token_url, request_token_url,
headers=self._default_headers, headers=self._default_headers,
oauth_callback=callback, oauth_callback=callback,
) )

View File

@ -56,9 +56,12 @@ class OAuth2Client(BaseOAuthClient):
LOGGER.warning("No code returned by the source") LOGGER.warning("No code returned by the source")
return None return None
try: 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( response = self.session.request(
"post", "post",
self.source.access_token_url, access_token_url,
data=args, data=args,
headers=self._default_headers, 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) provider_type = models.CharField(max_length=255)
request_token_url = models.CharField( request_token_url = models.CharField(
blank=True, null=True,
max_length=255, max_length=255,
verbose_name=_("Request Token URL"), verbose_name=_("Request Token URL"),
help_text=_( help_text=_(
@ -28,19 +28,19 @@ class OAuthSource(Source):
) )
authorization_url = models.CharField( authorization_url = models.CharField(
max_length=255, max_length=255,
blank=True, null=True,
verbose_name=_("Authorization URL"), verbose_name=_("Authorization URL"),
help_text=_("URL the user is redirect to to conest the flow."), help_text=_("URL the user is redirect to to conest the flow."),
) )
access_token_url = models.CharField( access_token_url = models.CharField(
max_length=255, max_length=255,
blank=True, null=True,
verbose_name=_("Access Token URL"), verbose_name=_("Access Token URL"),
help_text=_("URL used by authentik to retrive tokens."), help_text=_("URL used by authentik to retrive tokens."),
) )
profile_url = models.CharField( profile_url = models.CharField(
max_length=255, max_length=255,
blank=True, null=True,
verbose_name=_("Profile URL"), verbose_name=_("Profile URL"),
help_text=_("URL used by authentik to get user information."), help_text=_("URL used by authentik to get user information."),
) )
@ -163,6 +163,16 @@ class OpenIDOAuthSource(OAuthSource):
verbose_name_plural = _("OpenID OAuth Sources") 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): class UserOAuthSourceConnection(UserSourceConnection):
"""Authorized remote OAuth provider.""" """Authorized remote OAuth provider."""

View File

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

View File

@ -1,5 +1,5 @@
"""AzureAD OAuth2 Views""" """AzureAD OAuth2 Views"""
from typing import Any from typing import Any, Optional
from uuid import UUID from uuid import UUID
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
@ -10,8 +10,11 @@ from authentik.sources.oauth.views.callback import OAuthCallback
class AzureADOAuthCallback(OAuthCallback): class AzureADOAuthCallback(OAuthCallback):
"""AzureAD OAuth2 Callback""" """AzureAD OAuth2 Callback"""
def get_user_id(self, source: OAuthSource, info: dict[str, Any]) -> str: def get_user_id(self, source: OAuthSource, info: dict[str, Any]) -> Optional[str]:
return str(UUID(info.get("objectId")).int) try:
return str(UUID(info.get("objectId")).int)
except TypeError:
return None
def get_user_enroll_context( def get_user_enroll_context(
self, 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 typing import Optional, Type
from django.http.request import HttpRequest 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.base import BaseOAuthClient
from authentik.sources.oauth.clients.oauth1 import OAuthClient from authentik.sources.oauth.clients.oauth1 import OAuthClient
from authentik.sources.oauth.clients.oauth2 import OAuth2Client from authentik.sources.oauth.clients.oauth2 import OAuth2Client
from authentik.sources.oauth.models import OAuthSource from authentik.sources.oauth.models import OAuthSource
LOGGER = get_logger()
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
class OAuthClientMixin: class OAuthClientMixin:
@ -22,6 +25,9 @@ class OAuthClientMixin:
if self.client_class is not None: if self.client_class is not None:
# pylint: disable=not-callable # pylint: disable=not-callable
return self.client_class(source, self.request, **kwargs) return self.client_class(source, self.request, **kwargs)
if source.request_token_url: if source.type.request_token_url or source.request_token_url:
return OAuthClient(source, self.request, **kwargs) client = OAuthClient(source, self.request, **kwargs)
return OAuth2Client(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( return redirect(
reverse( reverse(
"authentik_sources_oauth:oauth-client-user", "authentik_core:if-admin",
kwargs={"source_slug": self.source.slug},
) )
+ f"#/user;page-{self.source.slug}"
) )
def handle_enroll( 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.sources.saml.processors.request import SESSION_REQUEST_ID
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
from authentik.stages.user_login.stage import DEFAULT_BACKEND
LOGGER = get_logger() LOGGER = get_logger()
if TYPE_CHECKING: if TYPE_CHECKING:
from xml.etree.ElementTree import Element # nosec from xml.etree.ElementTree import Element # nosec
CACHE_SEEN_REQUEST_ID = "authentik_saml_seen_ids_%s" CACHE_SEEN_REQUEST_ID = "authentik_saml_seen_ids_%s"
DEFAULT_BACKEND = "django.contrib.auth.backends.ModelBackend"
class ResponseProcessor: class ResponseProcessor:

View File

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

View File

@ -1,5 +1,6 @@
"""Identification stage logic""" """Identification stage logic"""
from dataclasses import asdict from dataclasses import asdict
from time import sleep
from typing import Optional from typing import Optional
from django.db.models import Q from django.db.models import Q
@ -46,6 +47,7 @@ class IdentificationChallengeResponse(ChallengeResponse):
"""Validate that user exists""" """Validate that user exists"""
pre_user = self.stage.get_user(value) pre_user = self.stage.get_user(value)
if not pre_user: if not pre_user:
sleep(0.150)
LOGGER.debug("invalid_login", identifier=value) LOGGER.debug("invalid_login", identifier=value)
raise ValidationError("Failed to authenticate.") raise ValidationError("Failed to authenticate.")
self.pre_user = pre_user self.pre_user = pre_user
@ -68,7 +70,7 @@ class IdentificationStageView(ChallengeStageView):
else: else:
model_field += "__exact" model_field += "__exact"
query |= Q(**{model_field: uid_value}) query |= Q(**{model_field: uid_value})
users = User.objects.filter(query) users = User.objects.filter(query, is_active=True)
if users.exists(): if users.exists():
LOGGER.debug("Found user", user=users.first(), query=query) LOGGER.debug("Found user", user=users.first(), query=query)
return users.first() 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 from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
LOGGER = get_logger() LOGGER = get_logger()
DEFAULT_BACKEND = "django.contrib.auth.backends.ModelBackend"
class UserLoginStageView(StageView): class UserLoginStageView(StageView):
@ -23,12 +24,9 @@ class UserLoginStageView(StageView):
messages.error(request, message) messages.error(request, message)
LOGGER.debug(message) LOGGER.debug(message)
return self.executor.stage_invalid() return self.executor.stage_invalid()
if PLAN_CONTEXT_AUTHENTICATION_BACKEND not in self.executor.plan.context: backend = self.executor.plan.context.get(
message = _("Pending user has no backend.") PLAN_CONTEXT_AUTHENTICATION_BACKEND, DEFAULT_BACKEND
messages.error(request, message) )
LOGGER.debug(message)
return self.executor.stage_invalid()
backend = self.executor.plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND]
login( login(
self.request, self.request,
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER], 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.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.tests.test_views import TO_STAGE_RESPONSE_MOCK from authentik.flows.tests.test_views import TO_STAGE_RESPONSE_MOCK
from authentik.flows.views import SESSION_KEY_PLAN 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 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()] flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
) )
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
plan.context[
PLAN_CONTEXT_AUTHENTICATION_BACKEND
] = "django.contrib.auth.backends.ModelBackend"
session = self.client.session session = self.client.session
session[SESSION_KEY_PLAN] = plan session[SESSION_KEY_PLAN] = plan
session.save() session.save()
@ -82,32 +78,3 @@ class TestUserLoginStage(TestCase):
"type": ChallengeTypes.NATIVE.value, "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 coverage-unittest/unittest.xml
mergeTestResults: true mergeTestResults: true
- task: CmdLine@2 - task: CmdLine@2
env:
CODECOV_TOKEN: $(CODECOV_TOKEN)
inputs: inputs:
script: bash <(curl -s https://codecov.io/bash) script: bash <(curl -s https://codecov.io/bash)
- stage: Build - stage: Build

View File

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

View File

@ -3,9 +3,9 @@ description: authentik is an open-source Identity Provider focused on flexibilit
name: authentik name: authentik
home: https://goauthentik.io home: https://goauthentik.io
sources: sources:
- https://github.com/BeryJu/authentik - https://github.com/goauthentik/authentik
version: "2021.4.2" version: "2021.4.4"
icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg icon: https://raw.githubusercontent.com/goauthentik/authentik/master/web/icons/icon.svg
dependencies: dependencies:
- name: postgresql - name: postgresql
version: 9.4.1 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 | 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.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 | | image.pullPolicy | IfNotPresent | Image Pull Policy used for all deployments |
| serverReplicas | 1 | Replicas for the Server deployment | | serverReplicas | 1 | Replicas for the Server deployment |
| workerReplicas | 1 | Replicas for the Worker 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 | | 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.enabled | false | Enable/disable error reporting |
| config.errorReporting.environment | customer | Environment sent with the 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 | | config.errorReporting.sendPii | false | Whether to send Personally-identifiable data with the error reporting |

View File

@ -43,29 +43,6 @@ spec:
values: values:
- web - web
topologyKey: "kubernetes.io/hostname" 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: containers:
- name: {{ .Chart.Name }} - name: {{ .Chart.Name }}
image: "{{ .Values.image.name }}:{{ .Values.image.tag }}" image: "{{ .Values.image.name }}:{{ .Values.image.tag }}"

View File

@ -5,7 +5,7 @@ image:
name: beryju/authentik name: beryju/authentik
name_static: beryju/authentik-static name_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended 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 pullPolicy: IfNotPresent
serverReplicas: 1 serverReplicas: 1

View File

@ -2,13 +2,13 @@
python -m lifecycle.wait_for_db python -m lifecycle.wait_for_db
printf '{"event": "Bootstrap completed", "level": "info", "logger": "bootstrap", "command": "%s"}\n' "$@" > /dev/stderr printf '{"event": "Bootstrap completed", "level": "info", "logger": "bootstrap", "command": "%s"}\n' "$@" > /dev/stderr
if [[ "$1" == "server" ]]; then if [[ "$1" == "server" ]]; then
python -m lifecycle.migrate
gunicorn -c /lifecycle/gunicorn.conf.py authentik.root.asgi:application gunicorn -c /lifecycle/gunicorn.conf.py authentik.root.asgi:application
elif [[ "$1" == "worker" ]]; then 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 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 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 lifecycle.migrate
python -m manage migrate
elif [[ "$1" == "backup" ]]; then elif [[ "$1" == "backup" ]]; then
python -m manage dbbackup --clean python -m manage dbbackup --clean
elif [[ "$1" == "restore" ]]; then elif [[ "$1" == "restore" ]]; then

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
"""System Migration handler""" """System Migration handler"""
import os
from importlib.util import module_from_spec, spec_from_file_location from importlib.util import module_from_spec, spec_from_file_location
from inspect import getmembers, isclass from inspect import getmembers, isclass
from pathlib import Path from pathlib import Path
@ -11,6 +12,7 @@ from structlog.stdlib import get_logger
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
LOGGER = get_logger() LOGGER = get_logger()
ADV_LOCK_UID = 1000
class BaseMigration: class BaseMigration:
@ -40,18 +42,36 @@ if __name__ == "__main__":
host=CONFIG.y("postgresql.host"), host=CONFIG.y("postgresql.host"),
) )
curr = conn.cursor() 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"): for name, sub in getmembers(mod, isclass):
spec = spec_from_file_location("lifecycle.system_migrations", migration) if name != "Migration":
mod = module_from_spec(spec) continue
# pyright: reportGeneralTypeIssues=false migration = sub(curr, conn)
spec.loader.exec_module(mod) if migration.needs_migration():
LOGGER.info("Migration needs to be applied", migration=sub)
for name, sub in getmembers(mod, isclass): migration.run()
if name != "Migration": LOGGER.info("Migration finished applying", migration=sub)
continue LOGGER.info("applying django migrations")
migration = sub(curr, conn) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
if migration.needs_migration(): try:
LOGGER.info("Migration needs to be applied", migration=sub) from django.core.management import execute_from_command_line
migration.run() except ImportError as exc:
LOGGER.info("Migration finished applying", migration=sub) 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 # 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) ![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. 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 - task: GoTool@0
inputs: inputs:
version: '1.16.3' version: '1.16.3'
- task: Go@0
inputs:
command: 'get'
arguments: '-u golang.org/x/lint/golint'
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
inputs: inputs:
buildType: 'current' buildType: 'current'
@ -53,7 +49,7 @@ stages:
- task: CmdLine@2 - task: CmdLine@2
inputs: inputs:
script: | 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/' workingDirectory: 'outpost/'
- stage: proxy_build_go - stage: proxy_build_go
jobs: jobs:

View File

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

View File

@ -8,12 +8,12 @@ require (
github.com/getsentry/sentry-go v0.10.0 github.com/getsentry/sentry-go v0.10.0
github.com/go-openapi/analysis v0.20.1 // indirect github.com/go-openapi/analysis v0.20.1 // indirect
github.com/go-openapi/errors v0.20.0 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/strfmt v0.20.1
github.com/go-openapi/swag v0.19.15 github.com/go-openapi/swag v0.19.15
github.com/go-openapi/validate v0.20.2 github.com/go-openapi/validate v0.20.2
github.com/go-redis/redis/v7 v7.4.0 // indirect 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/gorilla/websocket v1.4.2
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/justinas/alice v1.2.0 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/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc
github.com/pelletier/go-toml v1.9.0 // indirect github.com/pelletier/go-toml v1.9.0 // indirect
github.com/pkg/errors v0.9.1 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/recws-org/recws v1.3.1
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/spf13/afero v1.6.0 // indirect github.com/spf13/afero v1.6.0 // indirect
@ -30,10 +31,11 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1 // indirect github.com/spf13/viper v1.7.1 // indirect
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 // indirect golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // 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/appengine v1.6.7 // indirect
google.golang.org/grpc v1.31.0 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // 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/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/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 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/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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/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/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 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 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= 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.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.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 h1:zdVbw8yoD4SWZeq+cWdGgquaB0W4VrsJvDJHJND/Ktc=
github.com/go-openapi/analysis v0.20.1/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= 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.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 h1:Sxpo9PjEHDzhs3FbnGNonvDgWcMW2U7wGTcDDSFSceM=
github.com/go-openapi/errors v0.20.0/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= 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.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.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= 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.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.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.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
github.com/go-openapi/runtime v0.19.27 h1:4zrQCJoP7rqNCUaApDv1MdPkaa5TuPfO05Lq5WLhX9I= github.com/go-openapi/runtime v0.19.28 h1:9lYu6axek8LJrVkMVViVirRcpoaCxXX7+sSvmizGVnA=
github.com/go-openapi/runtime v0.19.27/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M= 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.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.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= 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-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 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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/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.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= 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.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 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.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.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/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.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.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.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.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-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/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/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/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 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 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.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 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/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/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/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 h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= 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= 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.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.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= 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 h1:NOd0BRdOKpPf0SxkL3HxSQOG7rNh+4kl6PHcBPFs7Q0=
github.com/pelletier/go-toml v1.9.0/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.0/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible 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/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/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-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 h1:jWKYCNlX4J5s8M0nHYkh7Y7c9gRVDEb3mq51j5J0F5M=
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ= github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.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.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/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 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 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/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= 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/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.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 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.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 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/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.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.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 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 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= 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 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 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/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.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 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= 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.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/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.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-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-20191213034115-f46add6fdb5c/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0= 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-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-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-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 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-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-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-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-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/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-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 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.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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.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-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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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-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-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-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-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-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-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-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-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-20210415231046-e915ea6b2b7d h1:BgJvlyh+UqCUaPlscHJ+PN8GcpfrFdr7NHjd1JL0+Gs=
golang.org/x/net v0.0.0-20210331060903-cb1fcc7394e5/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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-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-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-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-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 h1:D7nTwh4J0i+5mW4Zjzn5omvlr6YBcWywE6KOcatyNxY=
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-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-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-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-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-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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-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-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-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-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-20210415045647-66c3f260301c h1:6L+uOeS3OQt/f4eFHXZcTxeZrGCuz+CLElgEBjbcTA4=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/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.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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/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-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-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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.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.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.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.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-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"net/url" "net/url"
"os"
"time" "time"
"github.com/go-openapi/runtime" "github.com/go-openapi/runtime"
@ -30,8 +31,7 @@ type APIController struct {
Server Outpost Server Outpost
lastBundleHash string logger *log.Entry
logger *log.Entry
reloadOffset time.Duration reloadOffset time.Duration
@ -39,8 +39,8 @@ type APIController struct {
} }
// NewAPIController initialise new API Controller instance from URL and API token // NewAPIController initialise new API Controller instance from URL and API token
func NewAPIController(pbURL url.URL, token string) *APIController { func NewAPIController(akURL url.URL, token string) *APIController {
transport := httptransport.New(pbURL.Host, client.DefaultBasePath, []string{pbURL.Scheme}) transport := httptransport.New(akURL.Host, client.DefaultBasePath, []string{akURL.Scheme})
transport.Transport = SetUserAgent(getTLSTransport(), fmt.Sprintf("authentik-proxy@%s", pkg.VERSION)) transport.Transport = SetUserAgent(getTLSTransport(), fmt.Sprintf("authentik-proxy@%s", pkg.VERSION))
// create the transport // 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 // 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 // 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 { 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] outpost := outposts.Payload.Results[0]
doGlobalSetup(outpost.Config.(map[string]interface{})) doGlobalSetup(outpost.Config.(map[string]interface{}))
@ -69,18 +70,12 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
logger: log, logger: log,
reloadOffset: time.Duration(rand.Intn(10)) * time.Second, reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
lastBundleHash: "",
} }
ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset) ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset)
ac.initWS(pbURL, outpost.Pk) ac.initWS(akURL, outpost.Pk)
return ac return ac
} }
func (a *APIController) GetLastBundleHash() string {
return a.lastBundleHash
}
// Start Starts all handlers, non-blocking // Start Starts all handlers, non-blocking
func (a *APIController) Start() error { func (a *APIController) Start() error {
err := a.Server.Refresh() err := a.Server.Refresh()
@ -96,7 +91,10 @@ func (a *APIController) Start() error {
a.startWSHealth() a.startWSHealth()
}() }()
go func() { go func() {
a.Server.Start() err := a.Server.Start()
if err != nil {
panic(err)
}
}() }()
return nil return nil
} }

View File

@ -1,10 +1,6 @@
package ak package ak
import ( import (
"crypto/sha512"
"encoding/hex"
"encoding/json"
"goauthentik.io/outpost/pkg/client/outposts" "goauthentik.io/outpost/pkg/client/outposts"
"goauthentik.io/outpost/pkg/models" "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") a.logger.WithError(err).Error("Failed to fetch providers")
return nil, err 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 return providers.Payload.Results, nil
} }

View File

@ -15,9 +15,9 @@ import (
"goauthentik.io/outpost/pkg" "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/" 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) 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", 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") 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) ac.logger.Println("write close:", err)
return return
} }
return
} }
func (ac *APIController) startWSHandler() { func (ac *APIController) startWSHandler() {
@ -92,7 +91,8 @@ func (ac *APIController) startWSHandler() {
} }
func (ac *APIController) startWSHealth() { 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() { if !ac.wsConn.IsConnected() {
continue continue
} }

View File

@ -38,7 +38,11 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
return nil return nil
} }
providerOpts := &options.Options{} 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.ClientID = provider.ClientID
providerOpts.ClientSecret = provider.ClientSecret providerOpts.ClientSecret = provider.ClientSecret
@ -62,7 +66,7 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
ID: "default", ID: "default",
URI: *provider.InternalHost, URI: *provider.InternalHost,
Path: "/", Path: "/",
InsecureSkipTLSVerify: *&provider.InternalHostSslValidation, InsecureSkipTLSVerify: provider.InternalHostSslValidation,
}, },
} }
@ -85,7 +89,7 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
return providerOpts 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 { if err != nil {
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to parse certificate") pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to parse certificate")
return providerOpts return providerOpts
@ -135,7 +139,7 @@ func (pb *providerBundle) Build(provider *models.ProxyOutpostConfig) {
os.Exit(1) os.Exit(1)
} }
if *&provider.BasicAuthEnabled { if provider.BasicAuthEnabled {
oauthproxy.SetBasicAuth = true oauthproxy.SetBasicAuth = true
oauthproxy.BasicAuthUserAttribute = provider.BasicAuthUserAttribute oauthproxy.BasicAuthUserAttribute = provider.BasicAuthUserAttribute
oauthproxy.BasicAuthPasswordAttribute = provider.BasicAuthPasswordAttribute 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) duration := float64(time.Since(t)) / float64(time.Millisecond)
h.logger.WithFields(log.Fields{ h.logger.WithFields(log.Fields{
"host": req.RemoteAddr, "host": req.RemoteAddr,
"vhost": req.Host, "vhost": getHost(req),
"request_protocol": req.Proto, "request_protocol": req.Proto,
"runtime": fmt.Sprintf("%0.3f", duration), "runtime": fmt.Sprintf("%0.3f", duration),
"method": req.Method, "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()) p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
return 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) 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 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 { if err != nil {
p.logger.Errorf("Error redeeming code during OAuth2 callback: %v", err) p.logger.Errorf("Error redeeming code during OAuth2 callback: %v", err)
p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Internal Error") 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) w.WriteHeader(204)
return return
} }
handler, ok := s.Handlers[r.Host] host := getHost(r)
handler, ok := s.Handlers[host]
if !ok { if !ok {
// If we only have one handler, host name switching doesn't matter // If we only have one handler, host name switching doesn't matter
if len(s.Handlers) == 1 { if len(s.Handlers) == 1 {
@ -56,7 +57,7 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
for k := range s.Handlers { for k := range s.Handlers {
hostKeys = append(hostKeys, k) 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) w.WriteHeader(404)
return 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 package pkg
const VERSION = "2021.4.2" const VERSION = "2021.4.4"

View File

@ -5,7 +5,7 @@ info:
email: hello@beryju.org email: hello@beryju.org
license: license:
name: GNU GPLv3 name: GNU GPLv3
url: https://github.com/BeryJu/authentik/blob/master/LICENSE url: https://github.com/goauthentik/authentik/blob/master/LICENSE
version: v2beta version: v2beta
basePath: /api/v2beta basePath: /api/v2beta
consumes: consumes:
@ -1152,6 +1152,9 @@ paths:
description: Page Size description: Page Size
required: false required: false
type: integer type: integer
- name: superuser_full_list
in: query
type: boolean
responses: responses:
'200': '200':
description: '' description: ''
@ -4366,6 +4369,281 @@ paths:
description: A unique integer value identifying this OAuth2 Token. description: A unique integer value identifying this OAuth2 Token.
required: true required: true
type: integer 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/: /outposts/outposts/:
get: get:
operationId: outposts_outposts_list operationId: outposts_outposts_list
@ -7671,6 +7949,9 @@ paths:
required: true required: true
schema: schema:
$ref: '#/definitions/PolicyTest' $ref: '#/definitions/PolicyTest'
- name: format_result
in: query
type: boolean
responses: responses:
'200': '200':
description: '' description: ''
@ -14771,6 +15052,7 @@ definitions:
- name - name
- description - description
- component - component
- model_name
type: object type: object
properties: properties:
name: name:
@ -14785,6 +15067,10 @@ definitions:
title: Component title: Component
type: string type: string
minLength: 1 minLength: 1
model_name:
title: Model name
type: string
minLength: 1
EventTopPerUser: EventTopPerUser:
required: required:
- application - application
@ -17029,21 +17315,29 @@ definitions:
for OAuth 1. for OAuth 1.
type: string type: string
maxLength: 255 maxLength: 255
minLength: 1
x-nullable: true
authorization_url: authorization_url:
title: Authorization URL title: Authorization URL
description: URL the user is redirect to to conest the flow. description: URL the user is redirect to to conest the flow.
type: string type: string
maxLength: 255 maxLength: 255
minLength: 1
x-nullable: true
access_token_url: access_token_url:
title: Access Token URL title: Access Token URL
description: URL used by authentik to retrive tokens. description: URL used by authentik to retrive tokens.
type: string type: string
maxLength: 255 maxLength: 255
minLength: 1
x-nullable: true
profile_url: profile_url:
title: Profile URL title: Profile URL
description: URL used by authentik to get user information. description: URL used by authentik to get user information.
type: string type: string
maxLength: 255 maxLength: 255
minLength: 1
x-nullable: true
consumer_key: consumer_key:
title: Consumer key title: Consumer key
type: string type: string

View File

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

View File

@ -4,6 +4,7 @@ from sys import platform
from time import sleep from time import sleep
from typing import Any, Optional from typing import Any, Optional
from unittest.case import skipUnless from unittest.case import skipUnless
from unittest.mock import Mock, patch
from django.test import override_settings from django.test import override_settings
from docker.models.containers import Container from docker.models.containers import Container
@ -22,12 +23,31 @@ from authentik.providers.oauth2.generators import (
generate_client_secret, generate_client_secret,
) )
from authentik.sources.oauth.models import OAuthSource 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 from tests.e2e.utils import SeleniumTestCase, apply_migration, object_manager, retry
CONFIG_PATH = "/tmp/dex.yml" # nosec CONFIG_PATH = "/tmp/dex.yml" # nosec
LOGGER = get_logger() 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") @skipUnless(platform.startswith("linux"), "requires local docker")
class TestSourceOAuth2(SeleniumTestCase): class TestSourceOAuth2(SeleniumTestCase):
"""test OAuth Source flow""" """test OAuth Source flow"""
@ -291,10 +311,6 @@ class TestSourceOAuth1(SeleniumTestCase):
authentication_flow=authentication_flow, authentication_flow=authentication_flow,
enrollment_flow=enrollment_flow, enrollment_flow=enrollment_flow,
provider_type="twitter", 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_key=self.client_id,
consumer_secret=self.client_secret, consumer_secret=self.client_secret,
) )
@ -304,6 +320,10 @@ class TestSourceOAuth1(SeleniumTestCase):
@apply_migration("authentik_flows", "0008_default_flows") @apply_migration("authentik_flows", "0008_default_flows")
@apply_migration("authentik_flows", "0009_source_flows") @apply_migration("authentik_flows", "0009_source_flows")
@apply_migration("authentik_crypto", "0002_create_self_signed_kp") @apply_migration("authentik_crypto", "0002_create_self_signed_kp")
@patch(
"authentik.sources.oauth.types.manager.SourceTypeManager.find_type",
SOURCE_TYPE_MOCK,
)
@object_manager @object_manager
def test_oauth_enroll(self): def test_oauth_enroll(self):
"""test OAuth Source With With OIDC""" """test OAuth Source With With OIDC"""

View File

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

View File

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

View File

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

View File

@ -3,8 +3,8 @@ import yaml
from django.test import TestCase from django.test import TestCase
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.outposts.apps import AuthentikOutpostConfig
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType 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.controllers.kubernetes import ProxyKubernetesController
from authentik.providers.proxy.models import ProxyProvider from authentik.providers.proxy.models import ProxyProvider
@ -14,7 +14,7 @@ class TestProxyKubernetes(TestCase):
def setUp(self): def setUp(self):
# Ensure that local connection have been created # Ensure that local connection have been created
AuthentikOutpostConfig.init_local_connection() outpost_local_connection()
def test_kubernetes_controller_static(self): def test_kubernetes_controller_static(self):
"""Test Kubernetes Controller""" """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/ { location /static/ {
expires 31d; expires 31d;
add_header Cache-Control "public, no-transform"; 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; 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==" "integrity": "sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ=="
}, },
"@babel/core": { "@babel/core": {
"version": "7.13.15", "version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.15.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.16.tgz",
"integrity": "sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ==", "integrity": "sha512-sXHpixBiWWFti0AV2Zq7avpTasr6sIAu7Y396c608541qAU2ui4a193m0KSQmfPSKFZLnQ3cvlKDOm3XkuXm3Q==",
"requires": { "requires": {
"@babel/code-frame": "^7.12.13", "@babel/code-frame": "^7.12.13",
"@babel/generator": "^7.13.9", "@babel/generator": "^7.13.16",
"@babel/helper-compilation-targets": "^7.13.13", "@babel/helper-compilation-targets": "^7.13.16",
"@babel/helper-module-transforms": "^7.13.14", "@babel/helper-module-transforms": "^7.13.14",
"@babel/helpers": "^7.13.10", "@babel/helpers": "^7.13.16",
"@babel/parser": "^7.13.15", "@babel/parser": "^7.13.16",
"@babel/template": "^7.12.13", "@babel/template": "^7.12.13",
"@babel/traverse": "^7.13.15", "@babel/traverse": "^7.13.15",
"@babel/types": "^7.13.14", "@babel/types": "^7.13.16",
"convert-source-map": "^1.7.0", "convert-source-map": "^1.7.0",
"debug": "^4.1.0", "debug": "^4.1.0",
"gensync": "^1.0.0-beta.2", "gensync": "^1.0.0-beta.2",
@ -55,6 +55,32 @@
"@babel/highlight": "^7.12.13" "@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": { "@babel/helper-validator-identifier": {
"version": "7.12.11", "version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
@ -71,25 +97,34 @@
} }
}, },
"@babel/parser": { "@babel/parser": {
"version": "7.13.15", "version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.16.tgz",
"integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==" "integrity": "sha512-6bAg36mCwuqLO0hbR+z7PHuqWiCeP7Dzg73OpQwsAB1Eb8HnGEz5xYBzCfbu+YjoaJsJs+qheDxVAuqbt3ILEw=="
}, },
"@babel/traverse": { "@babel/traverse": {
"version": "7.13.15", "version": "7.13.17",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.17.tgz",
"integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", "integrity": "sha512-BMnZn0R+X6ayqm3C3To7o1j7Q020gWdqdyP50KEoVqaCO2c/Im7sYZSmVgvefp8TTMQ+9CtwuBp0Z1CZ8V3Pvg==",
"requires": { "requires": {
"@babel/code-frame": "^7.12.13", "@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-function-name": "^7.12.13",
"@babel/helper-split-export-declaration": "^7.12.13", "@babel/helper-split-export-declaration": "^7.12.13",
"@babel/parser": "^7.13.15", "@babel/parser": "^7.13.16",
"@babel/types": "^7.13.14", "@babel/types": "^7.13.17",
"debug": "^4.1.0", "debug": "^4.1.0",
"globals": "^11.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": { "globals": {
"version": "11.12.0", "version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@ -355,13 +390,87 @@
} }
}, },
"@babel/helpers": { "@babel/helpers": {
"version": "7.13.10", "version": "7.13.17",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.17.tgz",
"integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==", "integrity": "sha512-Eal4Gce4kGijo1/TGJdqp3WuhllaMLSrW6XcL0ulyUAQOuxHcCafZE8KHg9857gcTehsm/v7RcOx2+jp0Ryjsg==",
"requires": { "requires": {
"@babel/template": "^7.12.13", "@babel/template": "^7.12.13",
"@babel/traverse": "^7.13.0", "@babel/traverse": "^7.13.17",
"@babel/types": "^7.13.0" "@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": { "@babel/highlight": {
@ -1336,86 +1445,28 @@
} }
}, },
"@lingui/babel-plugin-extract-messages": { "@lingui/babel-plugin-extract-messages": {
"version": "3.8.9", "version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.8.9.tgz", "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.8.10.tgz",
"integrity": "sha512-zPpSl89nvUrLyGHfVosZHCP9fylfCfkEMc29wGdjE6f0U+frJ59NRLilWMy7xaE8uz97cD5vkhYaaF1wnavhxA==", "integrity": "sha512-16EnNRb1HXNjdDLMY3xS7jh0wKA00x21LC1CIKRAki80u92jvkSMOJYk+lD6yhdrcl0dH5OMAbdluAm1+rpEPw==",
"requires": { "requires": {
"@babel/generator": "^7.11.6", "@babel/generator": "^7.11.6",
"@babel/runtime": "^7.11.2", "@babel/runtime": "^7.11.2",
"@lingui/conf": "^3.8.9", "@lingui/conf": "^3.8.10",
"mkdirp": "^1.0.4" "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": { "@lingui/cli": {
"version": "3.8.9", "version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.8.9.tgz", "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.8.10.tgz",
"integrity": "sha512-UccLtfwrTjXrZcTxpqA4ggYhuUMbXZtzbUVks8nDVt3emVqU56C3VMvVD8WKXLL8Qmq9cEDXPwIZy7IKRL4mEQ==", "integrity": "sha512-YLkT5e6JRwVcXEwLD0++/m1p/wvRQbLj/+m8geXfrcFfrsQyT3uhHNZRFK0GdsjyDslSqJYbalYibJUbgC2sOA==",
"requires": { "requires": {
"@babel/generator": "^7.11.6", "@babel/generator": "^7.11.6",
"@babel/parser": "^7.11.5", "@babel/parser": "^7.11.5",
"@babel/plugin-syntax-jsx": "^7.10.4", "@babel/plugin-syntax-jsx": "^7.10.4",
"@babel/runtime": "^7.11.2", "@babel/runtime": "^7.11.2",
"@babel/types": "^7.11.5", "@babel/types": "^7.11.5",
"@lingui/babel-plugin-extract-messages": "^3.8.9", "@lingui/babel-plugin-extract-messages": "^3.8.10",
"@lingui/conf": "^3.8.9", "@lingui/conf": "^3.8.10",
"babel-plugin-macros": "^3.0.1", "babel-plugin-macros": "^3.0.1",
"bcp-47": "^1.0.7", "bcp-47": "^1.0.7",
"chalk": "^4.1.0", "chalk": "^4.1.0",
@ -1442,19 +1493,6 @@
"ramda": "^0.27.1" "ramda": "^0.27.1"
}, },
"dependencies": { "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": { "ansi-styles": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@ -1531,9 +1569,9 @@
} }
}, },
"@lingui/conf": { "@lingui/conf": {
"version": "3.8.9", "version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.8.9.tgz", "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.8.10.tgz",
"integrity": "sha512-r0RGchwiALjCE6CSOtOKbOqVrNg1EQ78AXjyvbrtJoPWVlChDasWCckXEF0BSnsoZaRP6nQCAI+dsQiGW1deWg==", "integrity": "sha512-4KdH+23WXZ5g+LRlvvise3z3mdd41zLgqSJ/PUCMGk60RfElvTrTdxpnm2tOF/2hr+OyGCQEy6kLq606y639qw==",
"requires": { "requires": {
"@babel/runtime": "^7.11.2", "@babel/runtime": "^7.11.2",
"@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2", "@endemolshinegroup/cosmiconfig-typescript-loader": "^3.0.2",
@ -1589,9 +1627,9 @@
} }
}, },
"@lingui/core": { "@lingui/core": {
"version": "3.8.9", "version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.8.9.tgz", "resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.8.10.tgz",
"integrity": "sha512-QmEfgukR7w/4/4USZT0LGNt7Yq/RgirFl4088wEta0vgroidxaCRgUXr8RXcdFVjTdtG5dc86JTEj4inZECKvg==", "integrity": "sha512-1OzZW8iP5yAXxz49pY/WZ1acLvkekd6HgDh8zH3jMA2Hbig2jk6VGVERMO7lwEwJiyEuxaQpe8fRrhCTB7wA3A==",
"requires": { "requires": {
"@babel/runtime": "^7.11.2", "@babel/runtime": "^7.11.2",
"make-plural": "^6.2.2", "make-plural": "^6.2.2",
@ -1599,12 +1637,12 @@
} }
}, },
"@lingui/macro": { "@lingui/macro": {
"version": "3.8.9", "version": "3.8.10",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.8.9.tgz", "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.8.10.tgz",
"integrity": "sha512-9LhlbkJ9wOtOLhlaVRLHCRL55S5wOFyyqEhUM+ujUmCskTmMmXzjnRsw5f11nJTK1JJETMT/VlUB5/p7D7Edkw==", "integrity": "sha512-oZZ/F7HsNQkDsnHFroxzGFuEIXM624H72RIj8j2ClpR64nt+xYDxXYC6TYFicQLtBGcKKBTBoM+zbDaoIv74qQ==",
"requires": { "requires": {
"@babel/runtime": "^7.11.2", "@babel/runtime": "^7.11.2",
"@lingui/conf": "^3.8.9", "@lingui/conf": "^3.8.10",
"ramda": "^0.27.1" "ramda": "^0.27.1"
} }
}, },
@ -1828,13 +1866,13 @@
} }
}, },
"@sentry/browser": { "@sentry/browser": {
"version": "6.2.5", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.2.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.0.tgz",
"integrity": "sha512-nlvaE+D7oaj4MxoY9ikw+krQDOjftnDYJQnOwOraXPk7KYM6YwmkakLuE+x/AkaH3FQVTQF330VAa9d6SWETlA==", "integrity": "sha512-Rse9j5XwN9n7GnfW1mNscTS4YQ0oiBNJcaSk3Mw/vQT872Wh60yKyx5wxAw5GujFZI0NgdyPlZwZ/tGQwirRxA==",
"requires": { "requires": {
"@sentry/core": "6.2.5", "@sentry/core": "6.3.0",
"@sentry/types": "6.2.5", "@sentry/types": "6.3.0",
"@sentry/utils": "6.2.5", "@sentry/utils": "6.3.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -1846,14 +1884,14 @@
} }
}, },
"@sentry/core": { "@sentry/core": {
"version": "6.2.5", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.2.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.0.tgz",
"integrity": "sha512-I+AkgIFO6sDUoHQticP6I27TT3L+i6TUS03in3IEtpBcSeP2jyhlxI8l/wdA7gsBqUPdQ4GHOOaNgtFIcr8qag==", "integrity": "sha512-voot/lJ9gRXB6bx6tVqbEbD6jOd4Sx6Rfmm6pzfpom9C0q+fjIZTatTLq8GdXj8DzxaH1MBDSwtaq/eC3NqYpA==",
"requires": { "requires": {
"@sentry/hub": "6.2.5", "@sentry/hub": "6.3.0",
"@sentry/minimal": "6.2.5", "@sentry/minimal": "6.3.0",
"@sentry/types": "6.2.5", "@sentry/types": "6.3.0",
"@sentry/utils": "6.2.5", "@sentry/utils": "6.3.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -1865,12 +1903,12 @@
} }
}, },
"@sentry/hub": { "@sentry/hub": {
"version": "6.2.5", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.0.tgz",
"integrity": "sha512-YlEFdEhcfqpl2HC+/dWXBsBJEljyMzFS7LRRjCk8QANcOdp9PhwQjwebUB4/ulOBjHPP2WZk7fBBd/IKDasTUg==", "integrity": "sha512-lAnW3Om66t9IR+t1wya1NpOF9lGbvYG6Ca8wxJJGJ1t2PxKwyxpZKzRx0q8M1QFhlZ5cETCzxmM7lBEZ4QVCBg==",
"requires": { "requires": {
"@sentry/types": "6.2.5", "@sentry/types": "6.3.0",
"@sentry/utils": "6.2.5", "@sentry/utils": "6.3.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -1882,12 +1920,12 @@
} }
}, },
"@sentry/minimal": { "@sentry/minimal": {
"version": "6.2.5", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.0.tgz",
"integrity": "sha512-RKP4Qx3p7Cv0oX1cPKAkNVFYM7p2k1t32cNk1+rrVQS4hwlJ7Eg6m6fsqsO+85jd6Ne/FnyYsfo9cDD3ImTlWQ==", "integrity": "sha512-ZdPUwdPQkaKroy67NkwQRqmnfKyd/C1OyouM9IqYKyBjAInjOijwwc/Rd91PMHalvCOGfp1scNZYbZ+YFs/qQQ==",
"requires": { "requires": {
"@sentry/hub": "6.2.5", "@sentry/hub": "6.3.0",
"@sentry/types": "6.2.5", "@sentry/types": "6.3.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -1899,17 +1937,51 @@
} }
}, },
"@sentry/tracing": { "@sentry/tracing": {
"version": "6.2.5", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.2.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.0.tgz",
"integrity": "sha512-j/hM0BoHxfrNLxPeEJ5Vq4R34hO/TOHMEpLR3FdnunBXbsmjoKMMygIkPxnpML5XWtvukAehbwpDXldwMYz83w==", "integrity": "sha512-3UNGgQOrDKBoDqLc4vt+0n27Zv3lbNEoCbBydq4IvGfuYq7ozWMsaTcelsotMsd4ckDuOEh8V/nJTqrDjvL76g==",
"requires": { "requires": {
"@sentry/hub": "6.2.5", "@sentry/hub": "6.3.0",
"@sentry/minimal": "6.2.5", "@sentry/minimal": "6.3.0",
"@sentry/types": "6.2.5", "@sentry/types": "6.3.0",
"@sentry/utils": "6.2.5", "@sentry/utils": "6.3.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "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": { "tslib": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -1918,16 +1990,16 @@
} }
}, },
"@sentry/types": { "@sentry/types": {
"version": "6.2.5", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.0.tgz",
"integrity": "sha512-1Sux6CLYrV9bETMsGP/HuLFLouwKoX93CWzG8BjMueW+Di0OGxZphYjXrGuDs8xO8bAKEVGCHgVQdcB2jevS0w==" "integrity": "sha512-xWyCYDmFPjS5ex60kxOOHbHEs4vs00qHbm0iShQfjl4OSg9S2azkcWofDmX8Xbn0FSOUXgdPCjNJW1B0bPVhCA=="
}, },
"@sentry/utils": { "@sentry/utils": {
"version": "6.2.5", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.0.tgz",
"integrity": "sha512-fJoLUZHrd5MPylV1dT4qL74yNFDl1Ur/dab+pKNSyvnHPnbZ/LRM7aJ8VaRY/A7ZdpRowU+E14e/Yeem2c6gtQ==", "integrity": "sha512-NZzw4oLelgvCsVBG2e+ZtFtaBvgA7rZYtcGFbZTphhAlYoJ6JMCQUzYk0iwJK79yR1quh510x4UE0jynvvToWg==",
"requires": { "requires": {
"@sentry/types": "6.2.5", "@sentry/types": "6.3.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -1956,9 +2028,9 @@
} }
}, },
"@types/codemirror": { "@types/codemirror": {
"version": "0.0.108", "version": "0.0.109",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.108.tgz", "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.109.tgz",
"integrity": "sha512-3FGFcus0P7C2UOGCNUVENqObEb4SFk+S8Dnxq7K6aIsLVs/vDtlangl3PEO0ykaKXyK56swVF6Nho7VsA44uhw==", "integrity": "sha512-cSdiHeeLjvGn649lRTNeYrVCDOgDrtP+bDDSFDd1TF+i0jKGPDRozno2NOJ9lTniso+taiv4kiVS8dgM8Jm5lg==",
"requires": { "requires": {
"@types/tern": "*" "@types/tern": "*"
} }
@ -2647,9 +2719,9 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
}, },
"chart.js": { "chart.js": {
"version": "3.1.0", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.1.0.tgz", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.1.1.tgz",
"integrity": "sha512-bKJi2VbC4fqZXlLbK7LKVvmG9crjoG9anfp96utZLyIGPuCx+YN+5/HDXy98QGt3lf74T8gKUPISUZL222tDJQ==" "integrity": "sha512-ghNJersc9VD9MECwa5bL8gqvCkndW6RSCicdEHL9lIriNtXwKawlSmwo+u6KNXLYT2+f24GdFPBoynKW3ke4MQ=="
}, },
"chartjs-adapter-moment": { "chartjs-adapter-moment": {
"version": "1.0.0", "version": "1.0.0",
@ -2775,9 +2847,9 @@
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
}, },
"codemirror": { "codemirror": {
"version": "5.60.0", "version": "5.61.0",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.60.0.tgz", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.61.0.tgz",
"integrity": "sha512-AEL7LhFOlxPlCL8IdTcJDblJm8yrAGib7I+DErJPdZd4l6imx8IMgKK3RblVgBQqz3TZJR4oknQ03bz+uNjBYA==" "integrity": "sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg=="
}, },
"collection-visit": { "collection-visit": {
"version": "1.0.0", "version": "1.0.0",
@ -2921,9 +2993,9 @@
} }
}, },
"date-fns": { "date-fns": {
"version": "2.20.1", "version": "2.21.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.20.1.tgz", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.21.1.tgz",
"integrity": "sha512-8P5M8Kxbnovd0zfvOs7ipkiVJ3/zZQ0F/nrBW4x5E+I0uAZVZ80h6CKd24fSXQ5TLK5hXMtI4yb2O5rEZdUt2A==" "integrity": "sha512-m1WR0xGiC6j6jNFAyW4Nvh4WxAi4JF4w9jRJwSI8nBmNcyZXPcP9VUQG+6gHQXAmqaGEKDKhOqAtENDC941UkA=="
}, },
"debug": { "debug": {
"version": "4.3.1", "version": "4.3.1",

View File

@ -35,34 +35,34 @@
] ]
}, },
"dependencies": { "dependencies": {
"@babel/core": "^7.13.15", "@babel/core": "^7.13.16",
"@babel/plugin-proposal-decorators": "^7.13.15", "@babel/plugin-proposal-decorators": "^7.13.15",
"@babel/plugin-transform-runtime": "^7.13.15", "@babel/plugin-transform-runtime": "^7.13.15",
"@babel/preset-env": "^7.13.15", "@babel/preset-env": "^7.13.15",
"@babel/preset-typescript": "^7.13.0", "@babel/preset-typescript": "^7.13.0",
"@fortawesome/fontawesome-free": "^5.15.3", "@fortawesome/fontawesome-free": "^5.15.3",
"@lingui/cli": "^3.8.9", "@lingui/cli": "^3.8.10",
"@lingui/core": "^3.8.9", "@lingui/core": "^3.8.10",
"@lingui/macro": "^3.8.9", "@lingui/macro": "^3.8.10",
"@patternfly/patternfly": "^4.96.2", "@patternfly/patternfly": "^4.96.2",
"@polymer/iron-form": "^3.0.1", "@polymer/iron-form": "^3.0.1",
"@polymer/paper-input": "^3.2.1", "@polymer/paper-input": "^3.2.1",
"@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^2.4.2", "@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.1", "@rollup/plugin-typescript": "^8.2.1",
"@sentry/browser": "^6.2.5", "@sentry/browser": "^6.3.0",
"@sentry/tracing": "^6.2.5", "@sentry/tracing": "^6.3.0",
"@types/chart.js": "^2.9.32", "@types/chart.js": "^2.9.32",
"@types/codemirror": "0.0.108", "@types/codemirror": "0.0.109",
"@types/grecaptcha": "^3.0.1", "@types/grecaptcha": "^3.0.1",
"@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0", "@typescript-eslint/parser": "^4.22.0",
"authentik-api": "file:api", "authentik-api": "file:api",
"babel-plugin-macros": "^3.0.1", "babel-plugin-macros": "^3.0.1",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"chart.js": "^3.1.0", "chart.js": "^3.1.1",
"chartjs-adapter-moment": "^1.0.0", "chartjs-adapter-moment": "^1.0.0",
"codemirror": "^5.60.0", "codemirror": "^5.61.0",
"construct-style-sheets-polyfill": "^2.4.16", "construct-style-sheets-polyfill": "^2.4.16",
"eslint": "^7.24.0", "eslint": "^7.24.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",

View File

@ -1,4 +1,4 @@
Contact: mailto:security@beryju.org Contact: mailto:security@beryju.org
Expires: Sat, 1 Jan 2022 00:00 +0200 Expires: Sat, 1 Jan 2022 00:00 +0200
Preferred-Languages: en, de 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 { getCookie } from "../utils";
import { API_DRAWER_MIDDLEWARE } from "../elements/notifications/APIDrawer"; import { API_DRAWER_MIDDLEWARE } from "../elements/notifications/APIDrawer";
import { MessageMiddleware } from "../elements/messages/Middleware"; 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({ export const DEFAULT_CONFIG = new Configuration({
basePath: "/api/v2beta", basePath: "/api/v2beta",
headers: { headers: {

View File

@ -2,12 +2,12 @@ import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing"; import { Integrations } from "@sentry/tracing";
import { VERSION } from "../constants"; import { VERSION } from "../constants";
import { SentryIgnoredError } from "../common/errors"; import { SentryIgnoredError } from "../common/errors";
import { Config, RootApi } from "authentik-api";
import { me } from "./Users"; import { me } from "./Users";
import { DEFAULT_CONFIG } from "./Config"; import { config } from "./Config";
import { Config } from "authentik-api";
export function configureSentry(): Promise<Config> { export function configureSentry(): Promise<Config> {
return new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => { return config().then((config) => {
if (config.errorReportingEnabled) { if (config.errorReportingEnabled) {
Sentry.init({ Sentry.init({
dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
@ -19,10 +19,19 @@ export function configureSentry(): Promise<Config> {
], ],
tracesSampleRate: 0.6, tracesSampleRate: 0.6,
environment: config.errorReportingEnvironment, 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) { if (hint.originalException instanceof SentryIgnoredError) {
return null; 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) { if (event.exception) {
me().then(user => { me().then(user => {
Sentry.showReportDialog({ Sentry.showReportDialog({

View File

@ -1,15 +1,21 @@
import { CoreApi, SessionUser } from "authentik-api"; import { CoreApi, SessionUser } from "authentik-api";
import { DEFAULT_CONFIG } from "./Config"; import { DEFAULT_CONFIG } from "./Config";
let _globalMePromise: Promise<SessionUser>; let globalMePromise: Promise<SessionUser>;
export function me(): Promise<SessionUser> { export function me(): Promise<SessionUser> {
if (!_globalMePromise) { if (!globalMePromise) {
_globalMePromise = new CoreApi(DEFAULT_CONFIG).coreUsersMe().catch((ex) => { globalMePromise = new CoreApi(DEFAULT_CONFIG).coreUsersMe().catch((ex) => {
const defaultUser: SessionUser = {
user: {
username: "",
name: ""
}
};
if (ex.status === 401 || ex.status === 403) { if (ex.status === 401 || ex.status === 403) {
window.location.assign("/"); 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 ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress"; export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current"; 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 PAGE_SIZE = 20;
export const EVENT_REFRESH = "ak-refresh"; export const EVENT_REFRESH = "ak-refresh";
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle"; export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";
export const EVENT_SIDEBAR_TOGGLE = "ak-sidebar-toggle"; export const EVENT_SIDEBAR_TOGGLE = "ak-sidebar-toggle";
export const EVENT_API_DRAWER_REFRESH = "ak-api-drawer-refresh"; export const EVENT_API_DRAWER_REFRESH = "ak-api-drawer-refresh";
export const TITLE_SUFFIX = "authentik"; export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";"; 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 AKGlobal from "../authentik.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFButton from "@patternfly/patternfly/components/Button/button.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") @customElement("ak-page-header")
export class PageHeader extends LitElement { export class PageHeader extends LitElement {
@ -17,11 +18,13 @@ export class PageHeader extends LitElement {
@property() @property()
set header(value: string) { set header(value: string) {
if (value !== "") { config().then(config => {
document.title = `${value} - ${TITLE_SUFFIX}`; if (value !== "") {
} else { document.title = `${value} - ${config.brandingTitle}`;
document.title = TITLE_SUFFIX; } else {
} document.title = config.brandingTitle || TITLE_DEFAULT;
}
});
this._header = value; this._header = value;
} }

View File

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

View File

@ -22,7 +22,7 @@ export class ApplicationListPage extends TablePage<Application> {
return t`Applications`; return t`Applications`;
} }
pageDescription(): string { 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 { pageIcon(): string {
return "pf-icon pf-icon-applications"; return "pf-icon pf-icon-applications";
@ -37,6 +37,7 @@ export class ApplicationListPage extends TablePage<Application> {
page: page, page: page,
pageSize: PAGE_SIZE, pageSize: PAGE_SIZE,
search: this.search || "", search: this.search || "",
superuserFullList: true,
}); });
} }

View File

@ -43,7 +43,7 @@ export class ApplicationViewPage extends LitElement {
render(): TemplateResult { render(): TemplateResult {
return html`<ak-page-header return html`<ak-page-header
icon=${this.application?.metaIcon || ""} icon=${this.application?.metaIcon || ""}
header=${this.application?.name} header=${this.application?.name || t`Loading`}
description=${ifDefined(this.application?.metaPublisher)} description=${ifDefined(this.application?.metaPublisher)}
.iconImage=${true}> .iconImage=${true}>
</ak-page-header> </ak-page-header>
@ -134,7 +134,7 @@ export class ApplicationViewPage extends LitElement {
</div> </div>
</div> </div>
</section> </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">
<div class="pf-c-card__title">${t`These policies control which users can access this application.`}</div> <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}> <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>`; <ak-expand>${this.defaultResponse()}</ak-expand>`;
case "update_available": case "update_available":
return html`<h3>${t`New version available!`}</h3> 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. // Action types which typically don't record any extra context.
// If context is not empty, we fall to the default response. // 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` return html`
<td role="cell" colspan="4"> <td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content"> <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> groups/users are checked against the user of the event.`}</p>
<ak-bound-policies-list .target=${item.pk}> <ak-bound-policies-list .target=${item.pk}>
</ak-bound-policies-list> </ak-bound-policies-list>

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