Compare commits

..

90 Commits

Author SHA1 Message Date
e81d3dad3e release: 2021.2.5-stable 2021-02-24 09:54:06 +01:00
5aabaebd96 root: fix request_id not being logged for actual asgi requests 2021-02-24 09:45:52 +01:00
7b60bca297 web: fix SiteShell breaking links when handlers are updated twice 2021-02-24 09:45:08 +01:00
a07d7456c8 web: fix outpost edit/delete buttons 2021-02-24 09:44:55 +01:00
f33369bf0c helm: add initial wait for healthcheck 2021-02-24 09:44:39 +01:00
1abcff39c7 outpost: improve logging output, ensure fields match api server 2021-02-24 09:44:24 +01:00
c1caf84d92 events: fix user QuerySet being passed 2021-02-24 09:44:05 +01:00
86c069fe64 admin: fix policy list not having a refresh button 2021-02-24 09:43:57 +01:00
ce0140ef67 events: pass Event's user to Notification policy engine when present 2021-02-24 09:43:50 +01:00
bba43c5109 sources/oauth: fix buttons not being ak-root-link 2021-02-24 09:23:44 +01:00
d99a415502 web: fix library not being full height, again 2021-02-24 09:23:40 +01:00
bd48955f39 release: 2021.2.4-stable 2021-02-23 23:00:43 +01:00
53adcd9157 core: fix user-settings not loading sources 2021-02-23 22:55:08 +01:00
c5a2bb8914 admin: fix success_urls 2021-02-23 22:55:01 +01:00
7da90ff7e4 release: 2021.2.3-stable 2021-02-10 20:47:33 +01:00
61b5714652 docs: update release notes 2021-02-10 20:47:06 +01:00
d2df426489 core: fix tokens using wrong lookup 2021-02-10 20:32:54 +01:00
e6c75ed173 web: fix untranslated strings 2021-02-10 20:21:04 +01:00
a353c6956e web: fix missing source create button 2021-02-10 20:13:12 +01:00
a367d8515f core: add source endpoint 2021-02-10 20:12:07 +01:00
2b7a22a29a core: add providers/types endpoint 2021-02-10 20:11:54 +01:00
e6712a50d2 docs: update changelog 2021-02-10 13:45:24 +01:00
c621f62d92 release: 2021.2.2-stable 2021-02-10 13:33:23 +01:00
a0648cd925 build(deps-dev): bump typescript from 4.1.3 to 4.1.4 in /web (#544)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.1.3...v4.1.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-10 09:40:39 +01:00
2650e672bb build(deps): bump boto3 from 1.17.4 to 1.17.5 (#545)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.4 to 1.17.5.
- [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.4...1.17.5)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-10 09:40:29 +01:00
53b9376789 build(deps): bump pycryptodome from 3.9.9 to 3.10.1 (#546)
Bumps [pycryptodome](https://github.com/Legrandin/pycryptodome) from 3.9.9 to 3.10.1.
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.9.9...v3.10.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-10 09:39:50 +01:00
d15e50025c root: log runtime in milliseconds 2021-02-09 23:33:25 +01:00
0af66a26ab crypto: move certificate and key data to separate api calls to create events 2021-02-09 21:47:00 +01:00
bf754369d9 providers/proxy: fix certificates without key being selectable 2021-02-09 21:11:44 +01:00
02dc112f8f outposts: fix ProxyProvider update not triggering outpost update 2021-02-09 20:59:39 +01:00
2d4e7ebab5 admin: remove unnecessary success_urls 2021-02-09 20:58:46 +01:00
a7d0a50859 events: rename context.token to context.secret 2021-02-09 20:10:43 +01:00
71c9108f89 events: rename token_view to secret_view 2021-02-09 18:20:28 +01:00
f8bcdb26b3 web: PBResponse -> AKResponse 2021-02-09 17:04:55 +01:00
45f1d95bf9 sources/oauth: add callback URL to api 2021-02-09 16:58:19 +01:00
5dab198c47 web: add new sources view 2021-02-09 16:24:27 +01:00
ad91abe9de admin: remove old sources view 2021-02-09 16:17:48 +01:00
fa30755241 web: make ActionButton's method configurable 2021-02-09 16:14:51 +01:00
552f8c6a9a sources/*: switch API to use slug in URL 2021-02-09 16:08:30 +01:00
101f916247 web: add source list page 2021-02-09 10:22:49 +01:00
2acdcf74e1 sources/ldap: add API for sync status 2021-02-09 10:21:59 +01:00
ddb8610032 web: fix modalbutton for non-fetched sites 2021-02-09 10:00:33 +01:00
22ad850e6c web: fix overflow on modalbutton 2021-02-09 09:57:59 +01:00
57925ed60a build(deps-dev): bump @typescript-eslint/eslint-plugin in /web (#543)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.14.2 to 4.15.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.15.0/packages/eslint-plugin)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-09 09:57:06 +01:00
48cc2f17c1 build(deps-dev): bump @typescript-eslint/parser in /web (#542)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.14.2 to 4.15.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.15.0/packages/parser)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-09 09:30:41 +01:00
448108fca0 build(deps): bump boto3 from 1.17.3 to 1.17.4 (#541)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.3 to 1.17.4.
- [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.3...1.17.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-09 09:30:30 +01:00
c1254f6212 web: update SiteShell to not use innerHTML 2021-02-08 23:16:20 +01:00
c8120c0d3e web: fix ModalButton working in global scope, causing issues on 2nd use 2021-02-08 23:10:45 +01:00
52016e0806 policies: skip cache on debug request 2021-02-08 22:14:23 +01:00
e555bdd42b lib: fix stacktrace for general expressions 2021-02-08 22:14:13 +01:00
1a619c90de Merge branch 'version-2021.2' 2021-02-08 21:51:59 +01:00
18faf30b0c docs: update release notes 2021-02-08 21:51:53 +01:00
b3bd979ecd release: 2021.2.1-stable 2021-02-08 21:34:05 +01:00
db113c5e8f Merge branch 'master' into version-2021.2 2021-02-08 21:33:58 +01:00
78bcb90a1e outposts: ensure Outpost API is backwards compatible 2021-02-08 19:51:46 +01:00
b64ecbde22 web: fix linting 2021-02-08 19:42:49 +01:00
43bab840ec web: fix sidebar being active when stage prompts is selected 2021-02-08 19:08:39 +01:00
f020b79384 admin: remove old code 2021-02-08 19:07:25 +01:00
820f658b49 web: add outpost list page 2021-02-08 19:04:19 +01:00
5d460a2537 admin: remove outposts list 2021-02-08 19:02:39 +01:00
efc46f52e6 outposts: move health to API 2021-02-08 19:01:10 +01:00
9fac51f8c7 outpost: downgrade recws for now
see https://github.com/recws-org/recws/issues/29
2021-02-08 17:56:58 +01:00
fe4b2d1a34 providers/oauth2: add authorized scopes to AUTHORIZE_APPLICATION event 2021-02-08 11:51:38 +01:00
f8abe3e210 providers/oauth2: add unofficial groups attribute to default profile claim 2021-02-08 11:50:26 +01:00
3ced67b151 sources/*: simplify source api 2021-02-08 10:25:59 +01:00
cd5631ec76 admin: fix link in source list 2021-02-08 10:25:59 +01:00
95df7c7f30 build(deps): bump construct-style-sheets-polyfill in /web (#540)
Bumps [construct-style-sheets-polyfill](https://github.com/calebdwilliams/adoptedStyleSheets) from 2.4.6 to 2.4.9.
- [Release notes](https://github.com/calebdwilliams/adoptedStyleSheets/releases)
- [Changelog](https://github.com/calebdwilliams/construct-style-sheets/blob/master/CHANGELOG.md)
- [Commits](https://github.com/calebdwilliams/adoptedStyleSheets/compare/v2.4.6...v2.4.9)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-08 09:22:59 +01:00
1e934aa5d5 release: 2021.2.1-rc2 2021-02-07 19:04:43 +01:00
d93927755a Merge branch 'master' into version-2021.2 2021-02-07 19:04:37 +01:00
ddb3b71dce outpost: cap reconnect backoff at 60 seconds, reset backoff on successful connection 2021-02-07 18:30:05 +01:00
bf9826873e web: fix outpost item in sidebar being active on service connection views 2021-02-07 18:21:13 +01:00
6869b3c16a admin: add button to generate certificate-key pair 2021-02-07 16:15:55 +01:00
9b71b8da5f docs: update rancher docs and add to affected for update 2021-02-07 15:03:26 +01:00
bfc8e9200f providers/saml: split views into separate files 2021-02-07 13:39:33 +01:00
c4311abc9f web: fix link to provider list on overview page 2021-02-06 22:46:09 +01:00
ec42869e00 policies: add debug flag to PolicyRequest to prevent alerts from testing policies 2021-02-06 21:45:38 +01:00
45963c2ffc admin: improve layout for policy testing 2021-02-06 21:43:14 +01:00
1aa27b5e80 website: update ini dependency 2021-02-06 21:25:03 +01:00
1737feec91 build(deps): bump @docusaurus/core in /website (#537)
Bumps [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) from 2.0.0-alpha.66 to 2.0.0-alpha.70.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/master/CHANGELOG-2.x.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v2.0.0-alpha.70/packages/docusaurus)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-06 21:23:04 +01:00
a0e0fb930a build(deps): bump @mdx-js/react from 1.6.21 to 1.6.22 in /website (#538)
Bumps [@mdx-js/react](https://github.com/mdx-js/mdx) from 1.6.21 to 1.6.22.
- [Release notes](https://github.com/mdx-js/mdx/releases)
- [Changelog](https://github.com/mdx-js/mdx/blob/main/changelog.md)
- [Commits](https://github.com/mdx-js/mdx/compare/v1.6.21...v1.6.22)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens L <jens@beryju.org>
2021-02-06 21:22:43 +01:00
4a32c3ca11 build(deps): bump @docusaurus/preset-classic in /website (#535)
Bumps [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) from 2.0.0-alpha.66 to 2.0.0-alpha.70.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/master/CHANGELOG-2.x.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v2.0.0-alpha.70/packages/docusaurus-preset-classic)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-06 21:19:46 +01:00
d307539fd0 build(deps): bump rollup from 2.38.4 to 2.38.5 in /web (#534)
Bumps [rollup](https://github.com/rollup/rollup) from 2.38.4 to 2.38.5.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v2.38.4...v2.38.5)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-06 21:19:33 +01:00
c060a3eec2 build(deps-dev): bump prettier from 2.1.2 to 2.2.1 in /website (#536)
Bumps [prettier](https://github.com/prettier/prettier) from 2.1.2 to 2.2.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.1.2...2.2.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-06 21:19:20 +01:00
4612ae1ff4 Merge pull request #539 from BeryJu/dependabot/pip/boto3-1.17.3
build(deps): bump boto3 from 1.17.2 to 1.17.3
2021-02-06 21:19:06 +01:00
7af883d80c root: add dedicated live and readiness views 2021-02-06 21:07:05 +01:00
4a5374d03f admin: remove provider list view 2021-02-06 20:54:50 +01:00
3b536f6e55 admin: fix property-mapping views redirecting to invalid URL 2021-02-06 20:54:12 +01:00
6aa13a8666 providers/saml: force-set friendly_name to empty string for managed mappings 2021-02-06 20:52:52 +01:00
24e4924dec docs: fix minor formatting errors 2021-02-06 20:52:29 +01:00
a252f303c0 docs: add docs to dependabot 2021-02-06 20:51:43 +01:00
152 changed files with 5535 additions and 2288 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 2021.2.1-rc1 current_version = 2021.2.5-stable
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

@ -16,6 +16,14 @@ updates:
open-pull-requests-limit: 10 open-pull-requests-limit: 10
assignees: assignees:
- BeryJu - BeryJu
- package-ecosystem: npm
directory: "/website"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
assignees:
- BeryJu
- package-ecosystem: pip - package-ecosystem: pip
directory: "/" directory: "/"
schedule: schedule:

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.2.1-rc1 -t beryju/authentik:2021.2.5-stable
-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.2.1-rc1 run: docker push beryju/authentik:2021.2.5-stable
- 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.2.1-rc1 \ -t beryju/authentik-proxy:2021.2.5-stable \
-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.2.1-rc1 run: docker push beryju/authentik-proxy:2021.2.5-stable
- 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:
@ -69,11 +69,11 @@ jobs:
cd web/ cd web/
docker build \ docker build \
--no-cache \ --no-cache \
-t beryju/authentik-static:2021.2.1-rc1 \ -t beryju/authentik-static:2021.2.5-stable \
-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.2.1-rc1 run: docker push beryju/authentik-static:2021.2.5-stable
- 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:
@ -107,5 +107,5 @@ jobs:
SENTRY_PROJECT: authentik SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org SENTRY_URL: https://sentry.beryju.org
with: with:
tagName: 2021.2.1-rc1 tagName: 2021.2.5-stable
environment: beryjuorg-prod environment: beryjuorg-prod

179
Pipfile.lock generated
View File

@ -53,10 +53,10 @@
}, },
"autobahn": { "autobahn": {
"hashes": [ "hashes": [
"sha256:410a93e0e29882c8b5d5ab05d220b07609b886ef5f23c0b8d39153254ffd6895", "sha256:93df8fc9d1821c9dabff9fed52181a9ad6eea5e9989d53102c391607d7c1666e",
"sha256:52ee4236ff9a1fcbbd9500439dcf3284284b37f8a6b31ecc8a36e00cf9f95049" "sha256:cceed2121b7a93024daa93c91fae33007f8346f0e522796421f36a6183abea99"
], ],
"version": "==20.12.3" "version": "==21.1.1"
}, },
"automat": { "automat": {
"hashes": [ "hashes": [
@ -74,17 +74,17 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:1a282c1cd7d5028cbb3a75d747df32162295253f55d263ac85840e264830963b" "sha256:d6aafb804fca2b67c65dda78ad8b4afed901e004071208b84c804d345ad9ebba"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.17.2" "version": "==1.17.5"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:7442fdbbdc841bfac7f94f92ecb807de070e32ed205743eb72d4ea27c5e8e778", "sha256:04a1df759681f5f171accb354d863bfed0774d64a4e8ee35ff49835755660a4e",
"sha256:bf587b044983a91a0124cc133ff167b8528c19fbbc8f0b956d9a1ac256cad7d7" "sha256:3c55f0db5e08920727f4fa24a87aed60060643f4b0b5665c62ec762f79e82d6b"
], ],
"version": "==1.20.2" "version": "==1.20.5"
}, },
"cachetools": { "cachetools": {
"hashes": [ "hashes": [
@ -223,22 +223,15 @@
}, },
"cryptography": { "cryptography": {
"hashes": [ "hashes": [
"sha256:0003a52a123602e1acee177dc90dd201f9bb1e73f24a070db7d36c588e8f5c7d", "sha256:287032b6a7d86abc98e8e977b20138c53fea40e5b24e29090d5a675a973dcd10",
"sha256:0e85aaae861d0485eb5a79d33226dd6248d2a9f133b81532c8f5aae37de10ff7", "sha256:288c65eea20bd89b11102c47b118bc1e0749386b0a0dfebba414076c5d4c8188",
"sha256:594a1db4511bc4d960571536abe21b4e5c3003e8750ab8365fafce71c5d86901", "sha256:7eed937ad9b53280a5f53570d3a7dc93cb4412b6a3d58d4c6bb78cc26319c729",
"sha256:69e836c9e5ff4373ce6d3ab311c1a2eed274793083858d3cd4c7d12ce20d5f9c", "sha256:dab437c2e84628703e3358f0f06555a6259bc5039209d51aa3b05af667ff4fd0",
"sha256:788a3c9942df5e4371c199d10383f44a105d67d401fb4304178020142f020244", "sha256:ee5e19f0856b6fbbdbab15c2787ca65d203801d2d65d0b8de6218f424206c848",
"sha256:7e177e4bea2de937a584b13645cab32f25e3d96fc0bc4a4cf99c27dc77682be6", "sha256:f21be9ec6b44c223b2024bbe59d394fadc7be320d18a8d595419afadb6cd5620",
"sha256:83d9d2dfec70364a74f4e7c70ad04d3ca2e6a08b703606993407bf46b97868c5", "sha256:f6ea140d2736b7e1f0de4f988c43f76b0b3f3d365080e091715429ba218dce28"
"sha256:84ef7a0c10c24a7773163f917f1cb6b4444597efd505a8aed0a22e8c4780f27e",
"sha256:9e21301f7a1e7c03dbea73e8602905a4ebba641547a462b26dd03451e5769e7c",
"sha256:9f6b0492d111b43de5f70052e24c1f0951cb9e6022188ebcb1cc3a3d301469b0",
"sha256:a69bd3c68b98298f490e84519b954335154917eaab52cf582fa2c5c7efc6e812",
"sha256:b4890d5fb9b7a23e3bf8abf5a8a7da8e228f1e97dc96b30b95685df840b6914a",
"sha256:c366df0401d1ec4e548bebe8f91d55ebcc0ec3137900d214dd7aac8427ef3030",
"sha256:dc42f645f8f3a489c3dd416730a514e7a91a59510ddaadc09d04224c098d3302"
], ],
"version": "==3.3.1" "version": "==3.4.4"
}, },
"dacite": { "dacite": {
"hashes": [ "hashes": [
@ -790,84 +783,74 @@
}, },
"pycryptodome": { "pycryptodome": {
"hashes": [ "hashes": [
"sha256:19cb674df6c74a14b8b408aa30ba8a89bd1c01e23505100fb45f930fbf0ed0d9", "sha256:09c1555a3fa450e7eaca41ea11cd00afe7c91fef52353488e65663777d8524e0",
"sha256:1cfdb92dca388e27e732caa72a1cc624520fe93752a665c3b6cd8f1a91b34916", "sha256:12222a5edc9ca4a29de15fbd5339099c4c26c56e13c2ceddf0b920794f26165d",
"sha256:27397aee992af69d07502126561d851ba3845aa808f0e55c71ad0efa264dd7d4", "sha256:1723ebee5561628ce96748501cdaa7afaa67329d753933296321f0be55358dce",
"sha256:28f75e58d02019a7edc7d4135203d2501dfc47256d175c72c9798f9a129a49a7", "sha256:1c5e1ca507de2ad93474be5cfe2bfa76b7cf039a1a32fc196f40935944871a06",
"sha256:2a68df525b387201a43b27b879ce8c08948a430e883a756d6c9e3acdaa7d7bd8", "sha256:2603c98ae04aac675fefcf71a6c87dc4bb74a75e9071ae3923bbc91a59f08d35",
"sha256:411745c6dce4eff918906eebcde78771d44795d747e194462abb120d2e537cd9", "sha256:2dea65df54349cdfa43d6b2e8edb83f5f8d6861e5cf7b1fbc3e34c5694c85e27",
"sha256:46e96aeb8a9ca8b1edf9b1fd0af4bf6afcf3f1ca7fa35529f5d60b98f3e4e959", "sha256:31c1df17b3dc5f39600a4057d7db53ac372f492c955b9b75dd439f5d8b460129",
"sha256:4ed27951b0a17afd287299e2206a339b5b6d12de9321e1a1575261ef9c4a851b", "sha256:38661348ecb71476037f1e1f553159b80d256c00f6c0b00502acac891f7116d9",
"sha256:50826b49fbca348a61529693b0031cdb782c39060fb9dca5ac5dff858159dc5a", "sha256:3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673",
"sha256:5598dc6c9dbfe882904e54584322893eff185b98960bbe2cdaaa20e8a437b6e5", "sha256:3f840c49d38986f6e17dbc0673d37947c88bc9d2d9dba1c01b979b36f8447db1",
"sha256:5c3c4865730dfb0263f822b966d6d58429d8b1e560d1ddae37685fd9e7c63161", "sha256:501ab36aae360e31d0ec370cf5ce8ace6cb4112060d099b993bc02b36ac83fb6",
"sha256:5f19e6ef750f677d924d9c7141f54bade3cd56695bbfd8a9ef15d0378557dfe4", "sha256:60386d1d4cfaad299803b45a5bc2089696eaf6cdd56f9fc17479a6f89595cfc8",
"sha256:60febcf5baf70c566d9d9351c47fbd8321da9a4edf2eff45c4c31c86164ca794", "sha256:6260e24d41149268122dd39d4ebd5941e9d107f49463f7e071fd397e29923b0c",
"sha256:62c488a21c253dadc9f731a32f0ac61e4e436d81a1ea6f7d1d9146ed4d20d6bd", "sha256:6bbf7fee7b7948b29d7e71fcacf48bac0c57fb41332007061a933f2d996f9713",
"sha256:6d3baaf82681cfb1a842f1c8f77beac791ceedd99af911e4f5fabec32bae2259", "sha256:6d2df5223b12437e644ce0a3be7809471ffa71de44ccd28b02180401982594a6",
"sha256:6e4227849e4231a3f5b35ea5bdedf9a82b3883500e5624f00a19156e9a9ef861", "sha256:758949ca62690b1540dfb24ad773c6da9cd0e425189e83e39c038bbd52b8e438",
"sha256:6e89bb3826e6f84501e8e3b205c22595d0c5492c2f271cbb9ee1c48eb1866645", "sha256:77997519d8eb8a4adcd9a47b9cec18f9b323e296986528186c0e9a7a15d6a07e",
"sha256:70d807d11d508433daf96244ec1c64e55039e8a35931fc5ea9eee94dbe3cb6b5", "sha256:7fd519b89585abf57bf47d90166903ec7b43af4fe23c92273ea09e6336af5c07",
"sha256:76b1a34d74bb2c91bce460cdc74d1347592045627a955e9a252554481c17c52f", "sha256:98213ac2b18dc1969a47bc65a79a8fca02a414249d0c8635abb081c7f38c91b6",
"sha256:7798e73225a699651888489fbb1dbc565e03a509942a8ce6194bbe6fb582a41f", "sha256:99b2f3fc51d308286071d0953f92055504a6ffe829a832a9fc7a04318a7683dd",
"sha256:834b790bbb6bd18956f625af4004d9c15eed12d5186d8e57851454ae76d52215", "sha256:9b6f711b25e01931f1c61ce0115245a23cdc8b80bf8539ac0363bdcf27d649b6",
"sha256:843e5f10ecdf9d307032b8b91afe9da1d6ed5bb89d0bbec5c8dcb4ba44008e11", "sha256:a3105a0eb63eacf98c2ecb0eb4aa03f77f40fbac2bdde22020bb8a536b226bb8",
"sha256:8f9f84059039b672a5a705b3c5aa21747867bacc30a72e28bf0d147cc8ef85ed", "sha256:a8eb8b6ea09ec1c2535bf39914377bc8abcab2c7d30fa9225eb4fe412024e427",
"sha256:9000877383e2189dafd1b2fc68c6c726eca9a3cfb6d68148fbb72ccf651959b6", "sha256:a92d5c414e8ee1249e850789052608f582416e82422502dc0ac8c577808a9067",
"sha256:910e202a557e1131b1c1b3f17a63914d57aac55cf9fb9b51644962841c3995c4", "sha256:d3d6958d53ad307df5e8469cc44474a75393a434addf20ecd451f38a72fe29b8",
"sha256:946399d15eccebafc8ce0257fc4caffe383c75e6b0633509bd011e357368306c", "sha256:e0a4d5933a88a2c98bbe19c0c722f5483dc628d7a38338ac2cb64a7dbd34064b",
"sha256:a199e9ca46fc6e999e5f47fce342af4b56c7de85fae893c69ab6aa17531fb1e1", "sha256:e3bf558c6aeb49afa9f0c06cee7fb5947ee5a1ff3bd794b653d39926b49077fa",
"sha256:a3d8a9efa213be8232c59cdc6b65600276508e375e0a119d710826248fd18d37", "sha256:e61e363d9a5d7916f3a4ce984a929514c0df3daf3b1b2eb5e6edbb131ee771cf",
"sha256:a4599c0ca0fc027c780c1c45ed996d5bef03e571470b7b1c7171ec1e1a90914c", "sha256:f977cdf725b20f6b8229b0c87acb98c7717e742ef9f46b113985303ae12a99da",
"sha256:b4e6b269a8ddaede774e5c3adbef6bf452ee144e6db8a716d23694953348cd86", "sha256:fc7489a50323a0df02378bc2fff86eb69d94cc5639914346c736be981c6a02e7"
"sha256:b68794fba45bdb367eeb71249c26d23e61167510a1d0c3d6cf0f2f14636e62ee",
"sha256:d7ec2bd8f57c559dd24e71891c51c25266a8deb66fc5f02cc97c7fb593d1780a",
"sha256:e15bde67ccb7d4417f627dd16ffe2f5a4c2941ce5278444e884cb26d73ecbc61",
"sha256:eb01f9997e4d6a8ec8a1ad1f676ba5a362781ff64e8189fe2985258ba9cb9706",
"sha256:faa682c404c218e8788c3126c9a4b8fbcc54dc245b5b6e8ea5b46f3b63bd0c84"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.9.9" "version": "==3.10.1"
}, },
"pycryptodomex": { "pycryptodomex": {
"hashes": [ "hashes": [
"sha256:15c03ffdac17731b126880622823d30d0a3cc7203cd219e6b9814140a44e7fab", "sha256:00a584ee52bf5e27d540129ca9bf7c4a7e7447f24ff4a220faa1304ad0c09bcd",
"sha256:20fb7f4efc494016eab1bc2f555bc0a12dd5ca61f35c95df8061818ffb2c20a3", "sha256:04265a7a84ae002001249bd1de2823bcf46832bd4b58f6965567cb8a07cf4f00",
"sha256:28ee3bcb4d609aea3040cad995a8e2c9c6dc57c12183dadd69e53880c35333b9", "sha256:0bd35af6a18b724c689e56f2dbbdd8e409288be71952d271ba3d9614b31d188c",
"sha256:305e3c46f20d019cd57543c255e7ba49e432e275d7c0de8913b6dbe57a851bc8", "sha256:20c45a30f3389148f94edb77f3b216c677a277942f62a2b81a1cc0b6b2dde7fc",
"sha256:3547b87b16aad6afb28c9b3a9cd870e11b5e7b5ac649b74265258d96d8de1130", "sha256:2959304d1ce31ab303d9fb5db2b294814278b35154d9b30bf7facc52d6088d0a",
"sha256:3642252d7bfc4403a42050e18ba748bedebd5a998a8cba89665a4f42aea4c380", "sha256:36dab7f506948056ceba2d57c1ade74e898401960de697cefc02f3519bd26c1b",
"sha256:404faa3e518f8bea516aae2aac47d4d960397199a15b4bd6f66cad97825469a0", "sha256:37ec1b407ec032c7a0c1fdd2da12813f560bad38ae61ad9c7ce3c0573b3e5e30",
"sha256:42669638e4f7937b7141044a2fbd1019caca62bd2cdd8b535f731426ab07bde1", "sha256:3b8eb85b3cc7f083d87978c264d10ff9de3b4bfc46f1c6fdc2792e7d7ebc87bb",
"sha256:4632d55a140b28e20be3cd7a3057af52fb747298ff0fd3290d4e9f245b5004ba", "sha256:3dfce70c4e425607ae87b8eae67c9c7dbba59a33b62d70f79417aef0bc5c735b",
"sha256:4a88c9383d273bdce3afc216020282c9c5c39ec0bd9462b1a206af6afa377cf0", "sha256:418f51c61eab52d9920f4ef468d22c89dab1be5ac796f71cf3802f6a6e667df0",
"sha256:4ce1fc1e6d2fd2d6dc197607153327989a128c093e0e94dca63408f506622c3e", "sha256:4195604f75cdc1db9bccdb9e44d783add3c817319c30aaff011670c9ed167690",
"sha256:55cf4e99b3ba0122dee570dc7661b97bf35c16aab3e2ccb5070709d282a1c7ab", "sha256:4344ab16faf6c2d9df2b6772995623698fb2d5f114dace4ab2ff335550cf71d5",
"sha256:5e486cab2dfcfaec934dd4f5d5837f4a9428b690f4d92a3b020fd31d1497ca64", "sha256:541cd3e3e252fb19a7b48f420b798b53483302b7fe4d9954c947605d0a263d62",
"sha256:65ec88c8271448d2ea109d35c1f297b09b872c57214ab7e832e413090d3469a9", "sha256:564063e3782474c92cbb333effd06e6eb718471783c6e67f28c63f0fc3ac7b23",
"sha256:6c95a3361ce70068cf69526a58751f73ddac5ba27a3c2379b057efa2f5338c8c", "sha256:72f44b5be46faef2a1bf2a85902511b31f4dd7b01ce0c3978e92edb2cc812a82",
"sha256:73240335f4a1baf12880ebac6df66ab4d3a9212db9f3efe809c36a27280d16f8", "sha256:8a98e02cbf8f624add45deff444539bf26345b479fc04fa0937b23cd84078d91",
"sha256:7651211e15109ac0058a49159265d9f6e6423c8a81c65434d3c56d708417a05b", "sha256:940db96449d7b2ebb2c7bf190be1514f3d67914bd37e54e8d30a182bd375a1a9",
"sha256:7b5b7c5896f8172ea0beb283f7f9428e0ab88ec248ce0a5b8c98d73e26267d51", "sha256:961333e7ee896651f02d4692242aa36b787b8e8e0baa2256717b2b9d55ae0a3c",
"sha256:836fe39282e75311ce4c38468be148f7fac0df3d461c5de58c5ff1ddb8966bac", "sha256:9f713ffb4e27b5575bd917c70bbc3f7b348241a351015dbbc514c01b7061ff7e",
"sha256:871852044f55295449fbf225538c2c4118525093c32f0a6c43c91bed0452d7e3", "sha256:a6584ae58001d17bb4dc0faa8a426919c2c028ef4d90ceb4191802ca6edb8204",
"sha256:892e93f3e7e10c751d6c17fa0dc422f7984cfd5eb6690011f9264dc73e2775fc", "sha256:c2b680987f418858e89dbb4f09c8c919ece62811780a27051ace72b2f69fb1be",
"sha256:934e460c5058346c6f1d62fdf3db5680fbdfbfd212722d24d8277bf47cd9ebdc", "sha256:d8fae5ba3d34c868ae43614e0bd6fb61114b2687ac3255798791ce075d95aece",
"sha256:9736f3f3e1761024200637a080a4f922f5298ad5d780e10dbb5634fe8c65b34c", "sha256:dbd2c361db939a4252589baa94da4404d45e3fc70da1a31e541644cdf354336e",
"sha256:a1d38a96da57e6103423a446079ead600b450cf0f8ebf56a231895abf77e7ffc", "sha256:e090a8609e2095aa86978559b140cf8968af99ee54b8791b29ff804838f29f10",
"sha256:a385fceaa0cdb97f0098f1c1e9ec0b46cc09186ddf60ec23538e871b1dddb6dc", "sha256:e4a1245e7b846e88ba63e7543483bda61b9acbaee61eadbead5a1ce479d94740",
"sha256:a7cf1c14e47027d9fb9d26aa62e5d603994227bd635e58a8df4b1d2d1b6a8ed7", "sha256:ec9901d19cadb80d9235ee41cc58983f18660314a0eb3fc7b11b0522ac3b6c4a",
"sha256:a9aac1a30b00b5038d3d8e48248f3b58ea15c827b67325c0d18a447552e30fc8", "sha256:f2abeb4c4ce7584912f4d637b2c57f23720d35dd2892bfeb1b2c84b6fb7a8c88",
"sha256:b696876ee583d15310be57311e90e153a84b7913ac93e6b99675c0c9867926d0", "sha256:f3bb267df679f70a9f40f17d62d22fe12e8b75e490f41807e7560de4d3e6bf9f",
"sha256:bef9e9d39393dc7baec39ba4bac6c73826a4db02114cdeade2552a9d6afa16e2", "sha256:f933ecf4cb736c7af60a6a533db2bf569717f2318b265f92907acff1db43bc34",
"sha256:c885fe4d5f26ce8ca20c97d02e88f5fdd92c01e1cc771ad0951b21e1641faf6d", "sha256:fc9c55dc1ed57db76595f2d19a479fc1c3a1be2c9da8de798a93d286c5f65f38"
"sha256:d2d1388595cb5d27d9220d5cbaff4f37c6ec696a25882eb06d224d241e6e93fb",
"sha256:d2e853e0f9535e693fade97768cf7293f3febabecc5feb1e9b2ffdfe1044ab96",
"sha256:d62fbab185a6b01c5469eda9f0795f3d1a5bba24f5a5813f362e4b73a3c4dc70",
"sha256:f20a62397e09704049ce9007bea4f6bad965ba9336a760c6f4ef1b4192e12d6d",
"sha256:f81f7311250d9480e36dec819127897ae772e7e8de07abfabe931b8566770b8e"
], ],
"version": "==3.9.9" "version": "==3.10.1"
}, },
"pyhamcrest": { "pyhamcrest": {
"hashes": [ "hashes": [
@ -1445,10 +1428,10 @@
}, },
"gitpython": { "gitpython": {
"hashes": [ "hashes": [
"sha256:42dbefd8d9e2576c496ed0059f3103dcef7125b9ce16f9d5f9c834aed44a1dac", "sha256:8621a7e777e276a5ec838b59280ba5272dd144a18169c36c903d8b38b99f750a",
"sha256:867ec3dfb126aac0f8296b19fb63b8c4a399f32b4b6fafe84c4b10af5fa9f7b5" "sha256:c5347c81d232d9b8e7f47b68a83e5dc92e7952127133c5f2df9133f2c75a1b29"
], ],
"version": "==3.1.12" "version": "==3.1.13"
}, },
"iniconfig": { "iniconfig": {
"hashes": [ "hashes": [

View File

@ -1,2 +1,2 @@
"""authentik""" """authentik"""
__version__ = "2021.2.1-rc1" __version__ = "2021.2.5-stable"

View File

@ -1,19 +0,0 @@
"""authentik core source form fields"""
SOURCE_FORM_FIELDS = [
"name",
"slug",
"enabled",
"authentication_flow",
"enrollment_flow",
]
SOURCE_SERIALIZER_FIELDS = [
"pk",
"name",
"slug",
"enabled",
"authentication_flow",
"enrollment_flow",
"verbose_name",
"verbose_name_plural",
]

View File

@ -0,0 +1,14 @@
{% extends base_template|default:"generic/form.html" %}
{% load authentik_utils %}
{% load i18n %}
{% block above_form %}
<h1>
{% trans 'Generate Certificate-Key Pair' %}
</h1>
{% endblock %}
{% block action %}
{% trans 'Generate Certificate-Key Pair' %}
{% endblock %}

View File

@ -26,6 +26,12 @@
</ak-spinner-button> </ak-spinner-button>
<div slot="modal"></div> <div slot="modal"></div>
</ak-modal-button> </ak-modal-button>
<ak-modal-button href="{% url 'authentik_admin:certificatekeypair-generate' %}">
<ak-spinner-button slot="trigger" class="pf-m-primary">
{% trans 'Generate' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
<button role="ak-refresh" class="pf-c-button pf-m-primary"> <button role="ak-refresh" class="pf-c-button pf-m-primary">
{% trans 'Refresh' %} {% trans 'Refresh' %}
</button> </button>

View File

@ -1,149 +0,0 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load humanize %}
{% load authentik_utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-zone"></i>
{% trans 'Outposts' %}
</h1>
<p>{% trans "Outposts are deployments of authentik components to support different environments and protocols, like reverse proxies." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">
{% include 'partials/toolbar_search.html' %}
<div class="pf-c-toolbar__bulk-select">
<ak-modal-button href="{% url 'authentik_admin:outpost-create' %}">
<ak-spinner-button slot="trigger" class="pf-m-primary">
{% trans 'Create' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
<button role="ak-refresh" class="pf-c-button pf-m-primary">
{% trans 'Refresh' %}
</button>
</div>
{% include 'partials/pagination.html' %}
</div>
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Providers' %}</th>
<th role="columnheader" scope="col">{% trans 'Health' %}</th>
<th role="columnheader" scope="col">{% trans 'Version' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for outpost in object_list %}
<tr role="row">
<th role="columnheader">
<span>{{ outpost.name }}</span>
</th>
<td role="cell">
<span>
{{ outpost.providers.all.select_subclasses|join:", " }}
</span>
</td>
{% with states=outpost.state %}
{% if states|length > 0 %}
<td role="cell">
{% for state in states %}
<div>
{% if state.last_seen %}
<i class="fas fa-check pf-m-success"></i> {{ state.last_seen|naturaltime }}
{% else %}
<i class="fas fa-times pf-m-danger"></i> {% trans 'Unhealthy' %}
{% endif %}
</div>
{% endfor %}
</td>
<td role="cell">
{% for state in states %}
<div>
{% if not state.version %}
<i class="fas fa-question-circle"></i>
{% elif state.version_outdated %}
<i class="fas fa-times pf-m-danger"></i> {% blocktrans with is=state.version should=state.version_should %}{{ is }}, should be {{ should }}{% endblocktrans %}
{% else %}
<i class="fas fa-check pf-m-success"></i> {{ state.version }}
{% endif %}
</div>
{% endfor %}
</td>
{% else %}
<td role="cell">
<i class="fas fa-question-circle"></i>
</td>
<td role="cell">
<i class="fas fa-question-circle"></i>
</td>
{% endif %}
{% endwith %}
<td>
<ak-modal-button href="{% url 'authentik_admin:outpost-update' pk=outpost.pk %}">
<ak-spinner-button slot="trigger" class="pf-m-secondary">
{% trans 'Edit' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
<ak-modal-button href="{% url 'authentik_admin:outpost-delete' pk=outpost.pk %}">
<ak-spinner-button slot="trigger" class="pf-m-danger">
{% trans 'Delete' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
{% get_htmls outpost as htmls %}
{% for html in htmls %}
{{ html|safe }}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-pagination pf-m-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">
{% include 'partials/toolbar_search.html' %}
</div>
</div>
<div class="pf-c-empty-state">
<div class="pf-c-empty-state__content">
<i class="fas fa-map-marker pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Outposts.' %}
</h1>
<div class="pf-c-empty-state__body">
{% if request.GET.search != "" %}
{% trans "Your search query doesn't match any outposts." %}
{% else %}
{% trans 'Currently no outposts exist. Click the button below to create one.' %}
{% endif %}
</div>
<ak-modal-button href="{% url 'authentik_admin:outpost-create' %}">
<ak-spinner-button slot="trigger" class="pf-m-primary">
{% trans 'Create' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
</div>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -3,7 +3,6 @@
{% load i18n %} {% load i18n %}
{% load humanize %} {% load humanize %}
{% load authentik_utils %} {% load authentik_utils %}
{% load admin_reflection %}
{% block content %} {% block content %}
<section class="pf-c-page__main-section pf-m-light"> <section class="pf-c-page__main-section pf-m-light">

View File

@ -41,6 +41,9 @@
{% endfor %} {% endfor %}
</ul> </ul>
</ak-dropdown> </ak-dropdown>
<button role="ak-refresh" class="pf-c-button pf-m-primary">
{% trans 'Refresh' %}
</button>
</div> </div>
{% include 'partials/pagination.html' %} {% include 'partials/pagination.html' %}
</div> </div>

View File

@ -14,7 +14,7 @@
<span class="pf-c-form__label-text">{% trans 'Passing' %}</span> <span class="pf-c-form__label-text">{% trans 'Passing' %}</span>
</label> </label>
</div> </div>
<div class="pf-c-form__group-control"> <div class="pf-c-form__group-label">
<div class="c-form__horizontal-group"> <div class="c-form__horizontal-group">
<span class="pf-c-form__label-text">{{ result.passing|yesno:"Yes,No" }}</span> <span class="pf-c-form__label-text">{{ result.passing|yesno:"Yes,No" }}</span>
</div> </div>
@ -26,7 +26,7 @@
<span class="pf-c-form__label-text">{% trans 'Messages' %}</span> <span class="pf-c-form__label-text">{% trans 'Messages' %}</span>
</label> </label>
</div> </div>
<div class="pf-c-form__group-control"> <div class="pf-c-form__group-label">
<div class="c-form__horizontal-group"> <div class="c-form__horizontal-group">
<ul> <ul>
{% for m in result.messages %} {% for m in result.messages %}

View File

@ -1,139 +0,0 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load authentik_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-blueprint"></i>
{% trans 'Property Mappings' %}
</h1>
<p>{% trans "Control how authentik exposes and interprets information." %}
</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">
{% include 'partials/toolbar_search.html' %}
<div class="pf-c-toolbar__bulk-select">
<ak-dropdown class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<ak-modal-button href="{% url 'authentik_admin:property-mapping-create' %}?type={{ type }}">
<button slot="trigger" class="pf-c-dropdown__menu-item">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</button>
<div slot="modal"></div>
</ak-modal-button>
</li>
{% endfor %}
</ul>
</ak-dropdown>
<button role="ak-refresh" class="pf-c-button pf-m-primary">
{% trans 'Refresh' %}
</button>
</div>
{% include 'partials/pagination.html' %}
</div>
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for property_mapping in object_list %}
<tr role="row">
<td role="cell">
<span>
{{ property_mapping.name }}
</span>
</td>
<td role="cell">
<span>
{{ property_mapping|verbose_name }}
</span>
</td>
<td>
<ak-modal-button href="{% url 'authentik_admin:property-mapping-update' pk=property_mapping.pk %}">
<ak-spinner-button slot="trigger" class="pf-m-secondary">
{% trans 'Edit' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
<ak-modal-button href="{% url 'authentik_admin:property-mapping-delete' pk=property_mapping.pk %}">
<ak-spinner-button slot="trigger" class="pf-m-danger">
{% trans 'Delete' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-pagination pf-m-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">
{% include 'partials/toolbar_search.html' %}
</div>
</div>
<div class="pf-c-empty-state">
<div class="pf-c-empty-state__content">
<i class="pf-icon pf-icon-blueprint pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Property Mappings.' %}
</h1>
<div class="pf-c-empty-state__body">
{% if request.GET.search != "" %}
{% trans "Your search query doesn't match any property mappings." %}
{% else %}
{% trans 'Currently no property mappings exist. Click the button below to create one.' %}
{% endif %}
</div>
<ak-dropdown class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<ak-modal-button href="{% url 'authentik_admin:property-mapping-create' %}?type={{ type }}">
<button slot="trigger" class="pf-c-dropdown__menu-item">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</button>
<div slot="modal"></div>
</ak-modal-button>
</li>
{% endfor %}
</ul>
</ak-dropdown>
</div>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -1,181 +0,0 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load authentik_utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-integration"></i>
{% trans 'Providers' %}
</h1>
<p>{% trans "Provide support for protocols like SAML and OAuth to assigned applications." %}
</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
{% if object_list %}
<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">
{% include 'partials/toolbar_search.html' %}
<div class="pf-c-toolbar__bulk-select">
<ak-dropdown class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<ak-modal-button href="{% url 'authentik_admin:provider-create' %}?type={{ type }}">
<button slot="trigger" class="pf-c-dropdown__menu-item">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</button>
<div slot="modal"></div>
</ak-modal-button>
</li>
{% endfor %}
<li>
<ak-modal-button href="{% url 'authentik_admin:provider-saml-from-metadata' %}">
<button slot="trigger" class="pf-c-dropdown__menu-item">
{% trans 'SAML Provider from Metadata' %}<br>
<small>
{% trans "Create a SAML Provider by importing its Metadata." %}
</small>
</button>
<div slot="modal"></div>
</ak-modal-button>
</li>
</ul>
</ak-dropdown>
<button role="ak-refresh" class="pf-c-button pf-m-primary">
{% trans 'Refresh' %}
</button>
</div>
{% include 'partials/pagination.html' %}
</div>
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for provider in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ provider.name }}</div>
{% if not provider.application %}
<i class="pf-icon pf-icon-warning-triangle"></i>
<small>{% trans 'Warning: Provider not assigned to any application.' %}</small>
{% else %}
<i class="pf-icon pf-icon-ok"></i>
<small>
{% blocktrans with app=provider.application %}
Assigned to application {{ app }}.
{% endblocktrans %}
</small>
{% endif %}
</div>
</th>
<td role="cell">
<span>
{{ provider|verbose_name }}
</span>
</td>
<td>
<ak-modal-button href="{% url 'authentik_admin:provider-update' pk=provider.pk %}">
<ak-spinner-button slot="trigger" class="pf-m-secondary">
{% trans 'Edit' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
<ak-modal-button href="{% url 'authentik_admin:provider-delete' pk=provider.pk %}">
<ak-spinner-button slot="trigger" class="pf-m-danger">
{% trans 'Delete' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
{% get_links provider as links %}
{% for name, href in links.items %}
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
{% endfor %}
{% get_htmls provider as htmls %}
{% for html in htmls %}
{{ html|safe }}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-pagination pf-m-bottom">
{% include 'partials/pagination.html' %}
</div>
{% else %}
<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">
{% include 'partials/toolbar_search.html' %}
</div>
</div>
<div class="pf-c-empty-state">
<div class="pf-c-empty-state__content">
<i class="pf-icon-integration pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans 'No Providers.' %}
</h1>
<div class="pf-c-empty-state__body">
{% if request.GET.search != "" %}
{% trans "Your search query doesn't match any providers." %}
{% else %}
{% trans 'Currently no providers exist. Click the button below to create one.' %}
{% endif %}
</div>
<ak-dropdown class="pf-c-dropdown">
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button>
<ul class="pf-c-dropdown__menu" hidden>
{% for type, name in types.items %}
<li>
<ak-modal-button href="{% url 'authentik_admin:provider-create' %}?type={{ type }}">
<button slot="trigger" class="pf-c-dropdown__menu-item">
{{ name|verbose_name }}<br>
<small>
{{ name|doc }}
</small>
</button>
<div slot="modal"></div>
</ak-modal-button>
</li>
{% endfor %}
<li>
<ak-modal-button href="{% url 'authentik_admin:provider-saml-from-metadata' %}">
<button slot="trigger" class="pf-c-dropdown__menu-item">
{% trans 'SAML Provider from Metadata' %}<br>
<small>
{% trans "Create a SAML Provider by importing its Metadata." %}
</small>
</button>
<div slot="modal"></div>
</ak-modal-button>
</li>
</ul>
</ak-dropdown>
</div>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -2,7 +2,6 @@
{% load i18n %} {% load i18n %}
{% load authentik_utils %} {% load authentik_utils %}
{% load admin_reflection %}
{% block content %} {% block content %}
<section class="pf-c-page__main-section pf-m-light"> <section class="pf-c-page__main-section pf-m-light">
@ -63,7 +62,7 @@
{% for source in object_list %} {% for source in object_list %}
<tr role="row"> <tr role="row">
<th role="columnheader"> <th role="columnheader">
<a href="/sources/{{ source.slug }}/"> <a href="/sources/{{ source.slug }}">
<div>{{ source.name }}</div> <div>{{ source.name }}</div>
{% if not source.enabled %} {% if not source.enabled %}
<small>{% trans 'Disabled' %}</small> <small>{% trans 'Disabled' %}</small>
@ -93,10 +92,6 @@
</ak-spinner-button> </ak-spinner-button>
<div slot="modal"></div> <div slot="modal"></div>
</ak-modal-button> </ak-modal-button>
{% get_links source as links %}
{% for name, href in links %}
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
{% endfor %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -2,7 +2,6 @@
{% load i18n %} {% load i18n %}
{% load authentik_utils %} {% load authentik_utils %}
{% load admin_reflection %}
{% block content %} {% block content %}
<section class="pf-c-page__main-section pf-m-light"> <section class="pf-c-page__main-section pf-m-light">
@ -88,10 +87,6 @@
</ak-spinner-button> </ak-spinner-button>
<div slot="modal"></div> <div slot="modal"></div>
</ak-modal-button> </ak-modal-button>
{% get_links stage as links %}
{% for name, href in links.items %}
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
{% endfor %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -2,7 +2,6 @@
{% load i18n %} {% load i18n %}
{% load authentik_utils %} {% load authentik_utils %}
{% load admin_reflection %}
{% block content %} {% block content %}
<section class="pf-c-page__main-section pf-m-light"> <section class="pf-c-page__main-section pf-m-light">
@ -90,10 +89,6 @@
</ak-spinner-button> </ak-spinner-button>
<div slot="modal"></div> <div slot="modal"></div>
</ak-modal-button> </ak-modal-button>
{% get_links prompt as links %}
{% for name, href in links.items %}
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
{% endfor %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -1,62 +0,0 @@
"""authentik admin templatetags"""
from django import template
from django.db.models import Model
from django.utils.html import mark_safe
from structlog.stdlib import get_logger
register = template.Library()
LOGGER = get_logger()
@register.simple_tag()
def get_links(model_instance):
"""Find all link_ methods on an object instance, run them and return as dict"""
prefix = "link_"
links = {}
if not isinstance(model_instance, Model):
LOGGER.warning("Model is not instance of Model", model_instance=model_instance)
return links
try:
for name in dir(model_instance):
if not name.startswith(prefix):
continue
value = getattr(model_instance, name)
if not callable(value):
continue
human_name = name.replace(prefix, "").replace("_", " ").capitalize()
link = value()
if link:
links[human_name] = link
except NotImplementedError:
pass
return links
@register.simple_tag(takes_context=True)
def get_htmls(context, model_instance):
"""Find all html_ methods on an object instance, run them and return as dict"""
prefix = "html_"
htmls = []
if not isinstance(model_instance, Model):
LOGGER.warning("Model is not instance of Model", model_instance=model_instance)
return htmls
try:
for name in dir(model_instance):
if not name.startswith(prefix):
continue
value = getattr(model_instance, name)
if not callable(value):
continue
if name.startswith(prefix):
html = value(context.get("request"))
if html:
htmls.append(mark_safe(html))
except NotImplementedError:
pass
return htmls

View File

@ -24,7 +24,7 @@ from authentik.admin.views import (
tokens, tokens,
users, users,
) )
from authentik.providers.saml.views import MetadataImportView from authentik.providers.saml.views.metadata import MetadataImportView
urlpatterns = [ urlpatterns = [
path( path(
@ -61,7 +61,6 @@ urlpatterns = [
name="token-delete", name="token-delete",
), ),
# Sources # Sources
path("sources/", sources.SourceListView.as_view(), name="sources"),
path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"), path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
path( path(
"sources/<uuid:pk>/update/", "sources/<uuid:pk>/update/",
@ -113,7 +112,6 @@ urlpatterns = [
name="policy-binding-delete", name="policy-binding-delete",
), ),
# Providers # Providers
path("providers/", providers.ProviderListView.as_view(), name="providers"),
path( path(
"providers/create/", "providers/create/",
providers.ProviderCreateView.as_view(), providers.ProviderCreateView.as_view(),
@ -170,22 +168,22 @@ urlpatterns = [
), ),
# Stage Prompts # Stage Prompts
path( path(
"stages/prompts/", "stages_prompts/",
stages_prompts.PromptListView.as_view(), stages_prompts.PromptListView.as_view(),
name="stage-prompts", name="stage-prompts",
), ),
path( path(
"stages/prompts/create/", "stages_prompts/create/",
stages_prompts.PromptCreateView.as_view(), stages_prompts.PromptCreateView.as_view(),
name="stage-prompt-create", name="stage-prompt-create",
), ),
path( path(
"stages/prompts/<uuid:pk>/update/", "stages_prompts/<uuid:pk>/update/",
stages_prompts.PromptUpdateView.as_view(), stages_prompts.PromptUpdateView.as_view(),
name="stage-prompt-update", name="stage-prompt-update",
), ),
path( path(
"stages/prompts/<uuid:pk>/delete/", "stages_prompts/<uuid:pk>/delete/",
stages_prompts.PromptDeleteView.as_view(), stages_prompts.PromptDeleteView.as_view(),
name="stage-prompt-delete", name="stage-prompt-delete",
), ),
@ -296,6 +294,11 @@ urlpatterns = [
certificate_key_pair.CertificateKeyPairCreateView.as_view(), certificate_key_pair.CertificateKeyPairCreateView.as_view(),
name="certificatekeypair-create", name="certificatekeypair-create",
), ),
path(
"crypto/certificates/generate/",
certificate_key_pair.CertificateKeyPairGenerateView.as_view(),
name="certificatekeypair-generate",
),
path( path(
"crypto/certificates/<uuid:pk>/update/", "crypto/certificates/<uuid:pk>/update/",
certificate_key_pair.CertificateKeyPairUpdateView.as_view(), certificate_key_pair.CertificateKeyPairUpdateView.as_view(),
@ -307,11 +310,6 @@ urlpatterns = [
name="certificatekeypair-delete", name="certificatekeypair-delete",
), ),
# Outposts # Outposts
path(
"outposts/",
outposts.OutpostListView.as_view(),
name="outposts",
),
path( path(
"outposts/create/", "outposts/create/",
outposts.OutpostCreateView.as_view(), outposts.OutpostCreateView.as_view(),
@ -329,22 +327,22 @@ urlpatterns = [
), ),
# Outpost Service Connections # Outpost Service Connections
path( path(
"outposts/service_connections/", "outpost_service_connections/",
outposts_service_connections.OutpostServiceConnectionListView.as_view(), outposts_service_connections.OutpostServiceConnectionListView.as_view(),
name="outpost-service-connections", name="outpost-service-connections",
), ),
path( path(
"outposts/service_connections/create/", "outpost_service_connections/create/",
outposts_service_connections.OutpostServiceConnectionCreateView.as_view(), outposts_service_connections.OutpostServiceConnectionCreateView.as_view(),
name="outpost-service-connection-create", name="outpost-service-connection-create",
), ),
path( path(
"outposts/service_connections/<uuid:pk>/update/", "outpost_service_connections/<uuid:pk>/update/",
outposts_service_connections.OutpostServiceConnectionUpdateView.as_view(), outposts_service_connections.OutpostServiceConnectionUpdateView.as_view(),
name="outpost-service-connection-update", name="outpost-service-connection-update",
), ),
path( path(
"outposts/service_connections/<uuid:pk>/delete/", "outpost_service_connections/<uuid:pk>/delete/",
outposts_service_connections.OutpostServiceConnectionDeleteView.as_view(), outposts_service_connections.OutpostServiceConnectionDeleteView.as_view(),
name="outpost-service-connection-delete", name="outpost-service-connection-delete",
), ),

View File

@ -4,7 +4,6 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin, PermissionRequiredMixin as DjangoPermissionRequiredMixin,
) )
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import UpdateView from django.views.generic import UpdateView
from guardian.mixins import PermissionRequiredMixin from guardian.mixins import PermissionRequiredMixin
@ -28,8 +27,8 @@ class ApplicationCreateView(
form_class = ApplicationForm form_class = ApplicationForm
permission_required = "authentik_core.add_application" permission_required = "authentik_core.add_application"
success_url = "/"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully created Application") success_message = _("Successfully created Application")
@ -46,8 +45,8 @@ class ApplicationUpdateView(
form_class = ApplicationForm form_class = ApplicationForm
permission_required = "authentik_core.change_application" permission_required = "authentik_core.change_application"
success_url = "/"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully updated Application") success_message = _("Successfully updated Application")
@ -59,6 +58,6 @@ class ApplicationDeleteView(
model = Application model = Application
permission_required = "authentik_core.delete_application" permission_required = "authentik_core.delete_application"
success_url = "/"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully deleted Application") success_message = _("Successfully deleted Application")

View File

@ -4,9 +4,11 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin, PermissionRequiredMixin as DjangoPermissionRequiredMixin,
) )
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.http.response import HttpResponse
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView from django.views.generic import ListView, UpdateView
from django.views.generic.edit import FormView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from authentik.admin.views.utils import ( from authentik.admin.views.utils import (
@ -15,7 +17,11 @@ from authentik.admin.views.utils import (
SearchListMixin, SearchListMixin,
UserPaginateListMixin, UserPaginateListMixin,
) )
from authentik.crypto.forms import CertificateKeyPairForm from authentik.crypto.builder import CertificateBuilder
from authentik.crypto.forms import (
CertificateKeyPairForm,
CertificateKeyPairGenerateForm,
)
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.lib.views import CreateAssignPermView from authentik.lib.views import CreateAssignPermView
@ -52,7 +58,35 @@ class CertificateKeyPairCreateView(
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_admin:certificate_key_pair") success_url = reverse_lazy("authentik_admin:certificate_key_pair")
success_message = _("Successfully created CertificateKeyPair") success_message = _("Successfully created Certificate-Key Pair")
class CertificateKeyPairGenerateView(
SuccessMessageMixin,
BackSuccessUrlMixin,
LoginRequiredMixin,
DjangoPermissionRequiredMixin,
FormView,
):
"""Generate new CertificateKeyPair"""
model = CertificateKeyPair
form_class = CertificateKeyPairGenerateForm
permission_required = "authentik_crypto.add_certificatekeypair"
template_name = "administration/certificatekeypair/generate.html"
success_url = reverse_lazy("authentik_admin:certificate_key_pair")
success_message = _("Successfully generated Certificate-Key Pair")
def form_valid(self, form: CertificateKeyPairGenerateForm) -> HttpResponse:
builder = CertificateBuilder()
builder.common_name = form.data["common_name"]
builder.build(
subject_alt_names=form.data.get("subject_alt_name", "").split(","),
validity_days=int(form.data["validity_days"]),
)
builder.save()
return super().form_valid(form)
class CertificateKeyPairUpdateView( class CertificateKeyPairUpdateView(

View File

@ -4,7 +4,6 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin, PermissionRequiredMixin as DjangoPermissionRequiredMixin,
) )
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import UpdateView from django.views.generic import UpdateView
from guardian.mixins import PermissionRequiredMixin from guardian.mixins import PermissionRequiredMixin
@ -28,8 +27,8 @@ class NotificationRuleCreateView(
form_class = NotificationRuleForm form_class = NotificationRuleForm
permission_required = "authentik_events.add_NotificationRule" permission_required = "authentik_events.add_NotificationRule"
success_url = "/"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully created Notification Rule") success_message = _("Successfully created Notification Rule")
@ -46,8 +45,8 @@ class NotificationRuleUpdateView(
form_class = NotificationRuleForm form_class = NotificationRuleForm
permission_required = "authentik_events.change_NotificationRule" permission_required = "authentik_events.change_NotificationRule"
success_url = "/"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully updated Notification Rule") success_message = _("Successfully updated Notification Rule")
@ -59,6 +58,6 @@ class NotificationRuleDeleteView(
model = NotificationRule model = NotificationRule
permission_required = "authentik_events.delete_NotificationRule" permission_required = "authentik_events.delete_NotificationRule"
success_url = "/"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully deleted Notification Rule") success_message = _("Successfully deleted Notification Rule")

View File

@ -4,7 +4,6 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin, PermissionRequiredMixin as DjangoPermissionRequiredMixin,
) )
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import UpdateView from django.views.generic import UpdateView
from guardian.mixins import PermissionRequiredMixin from guardian.mixins import PermissionRequiredMixin
@ -27,9 +26,8 @@ class NotificationTransportCreateView(
model = NotificationTransport model = NotificationTransport
form_class = NotificationTransportForm form_class = NotificationTransportForm
permission_required = "authentik_events.add_notificationtransport" permission_required = "authentik_events.add_notificationtransport"
success_url = "/"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully created Notification Transport") success_message = _("Successfully created Notification Transport")
@ -45,9 +43,8 @@ class NotificationTransportUpdateView(
model = NotificationTransport model = NotificationTransport
form_class = NotificationTransportForm form_class = NotificationTransportForm
permission_required = "authentik_events.change_notificationtransport" permission_required = "authentik_events.change_notificationtransport"
success_url = "/"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully updated Notification Transport") success_message = _("Successfully updated Notification Transport")
@ -58,7 +55,6 @@ class NotificationTransportDeleteView(
model = NotificationTransport model = NotificationTransport
permission_required = "authentik_events.delete_notificationtransport" permission_required = "authentik_events.delete_notificationtransport"
success_url = "/"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully deleted Notification Transport") success_message = _("Successfully deleted Notification Transport")

View File

@ -7,38 +7,16 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin, PermissionRequiredMixin as DjangoPermissionRequiredMixin,
) )
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView from django.views.generic import UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin from guardian.mixins import PermissionRequiredMixin
from authentik.admin.views.utils import ( from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView
BackSuccessUrlMixin,
DeleteMessageView,
SearchListMixin,
UserPaginateListMixin,
)
from authentik.lib.views import CreateAssignPermView from authentik.lib.views import CreateAssignPermView
from authentik.outposts.forms import OutpostForm from authentik.outposts.forms import OutpostForm
from authentik.outposts.models import Outpost, OutpostConfig from authentik.outposts.models import Outpost, OutpostConfig
class OutpostListView(
LoginRequiredMixin,
PermissionListMixin,
UserPaginateListMixin,
SearchListMixin,
ListView,
):
"""Show list of all outposts"""
model = Outpost
permission_required = "authentik_outposts.view_outpost"
ordering = "name"
template_name = "administration/outpost/list.html"
search_fields = ["name", "_config"]
class OutpostCreateView( class OutpostCreateView(
SuccessMessageMixin, SuccessMessageMixin,
BackSuccessUrlMixin, BackSuccessUrlMixin,
@ -51,9 +29,8 @@ class OutpostCreateView(
model = Outpost model = Outpost
form_class = OutpostForm form_class = OutpostForm
permission_required = "authentik_outposts.add_outpost" permission_required = "authentik_outposts.add_outpost"
success_url = "/"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_admin:outposts")
success_message = _("Successfully created Outpost") success_message = _("Successfully created Outpost")
def get_initial(self) -> Dict[str, Any]: def get_initial(self) -> Dict[str, Any]:
@ -76,9 +53,8 @@ class OutpostUpdateView(
model = Outpost model = Outpost
form_class = OutpostForm form_class = OutpostForm
permission_required = "authentik_outposts.change_outpost" permission_required = "authentik_outposts.change_outpost"
success_url = "/"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("authentik_admin:outposts")
success_message = _("Successfully updated Outpost") success_message = _("Successfully updated Outpost")
@ -87,7 +63,6 @@ class OutpostDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessa
model = Outpost model = Outpost
permission_required = "authentik_outposts.delete_outpost" permission_required = "authentik_outposts.delete_outpost"
success_url = "/"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_admin:outposts")
success_message = _("Successfully deleted Outpost") success_message = _("Successfully deleted Outpost")

View File

@ -20,7 +20,6 @@ class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
form_class = PolicyCacheClearForm form_class = PolicyCacheClearForm
template_name = "generic/form_non_model.html" template_name = "generic/form_non_model.html"
success_url = "/"
success_message = _("Successfully cleared Policy cache") success_message = _("Successfully cleared Policy cache")
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
@ -39,7 +38,6 @@ class FlowCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
form_class = FlowCacheClearForm form_class = FlowCacheClearForm
template_name = "generic/form_non_model.html" template_name = "generic/form_non_model.html"
success_url = "/"
success_message = _("Successfully cleared Flow cache") success_message = _("Successfully cleared Flow cache")
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:

View File

@ -115,6 +115,7 @@ class PolicyTestView(LoginRequiredMixin, DetailView, PermissionRequiredMixin, Fo
user = form.cleaned_data.get("user") user = form.cleaned_data.get("user")
p_request = PolicyRequest(user) p_request = PolicyRequest(user)
p_request.debug = True
p_request.http_request = self.request p_request.http_request = self.request
p_request.context = form.cleaned_data.get("context", {}) p_request.context = form.cleaned_data.get("context", {})

View File

@ -8,7 +8,6 @@ from django.contrib.auth.mixins import (
) )
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.http import HttpResponse from django.http import HttpResponse
from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import FormView from django.views.generic import FormView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
@ -35,9 +34,8 @@ class PropertyMappingCreateView(
model = PropertyMapping model = PropertyMapping
permission_required = "authentik_core.add_propertymapping" permission_required = "authentik_core.add_propertymapping"
success_url = "/"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_admin:property-mappings")
success_message = _("Successfully created Property Mapping") success_message = _("Successfully created Property Mapping")
@ -52,9 +50,8 @@ class PropertyMappingUpdateView(
model = PropertyMapping model = PropertyMapping
permission_required = "authentik_core.change_propertymapping" permission_required = "authentik_core.change_propertymapping"
success_url = "/"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("authentik_admin:property-mappings")
success_message = _("Successfully updated Property Mapping") success_message = _("Successfully updated Property Mapping")
@ -65,9 +62,8 @@ class PropertyMappingDeleteView(
model = PropertyMapping model = PropertyMapping
permission_required = "authentik_core.delete_propertymapping" permission_required = "authentik_core.delete_propertymapping"
success_url = "/"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_admin:property-mappings")
success_message = _("Successfully deleted Property Mapping") success_message = _("Successfully deleted Property Mapping")

View File

@ -4,38 +4,18 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin, PermissionRequiredMixin as DjangoPermissionRequiredMixin,
) )
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin from guardian.mixins import PermissionRequiredMixin
from authentik.admin.views.utils import ( from authentik.admin.views.utils import (
BackSuccessUrlMixin, BackSuccessUrlMixin,
DeleteMessageView, DeleteMessageView,
InheritanceCreateView, InheritanceCreateView,
InheritanceListView,
InheritanceUpdateView, InheritanceUpdateView,
SearchListMixin,
UserPaginateListMixin,
) )
from authentik.core.models import Provider from authentik.core.models import Provider
class ProviderListView(
LoginRequiredMixin,
PermissionListMixin,
UserPaginateListMixin,
SearchListMixin,
InheritanceListView,
):
"""Show list of all providers"""
model = Provider
permission_required = "authentik_core.add_provider"
template_name = "administration/provider/list.html"
ordering = "pk"
search_fields = ["pk", "name"]
class ProviderCreateView( class ProviderCreateView(
SuccessMessageMixin, SuccessMessageMixin,
BackSuccessUrlMixin, BackSuccessUrlMixin,
@ -47,9 +27,8 @@ class ProviderCreateView(
model = Provider model = Provider
permission_required = "authentik_core.add_provider" permission_required = "authentik_core.add_provider"
success_url = "/"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_admin:providers")
success_message = _("Successfully created Provider") success_message = _("Successfully created Provider")
@ -64,9 +43,8 @@ class ProviderUpdateView(
model = Provider model = Provider
permission_required = "authentik_core.change_provider" permission_required = "authentik_core.change_provider"
success_url = "/"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("authentik_admin:providers")
success_message = _("Successfully updated Provider") success_message = _("Successfully updated Provider")
@ -77,7 +55,6 @@ class ProviderDeleteView(
model = Provider model = Provider
permission_required = "authentik_core.delete_provider" permission_required = "authentik_core.delete_provider"
success_url = "/"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_admin:providers")
success_message = _("Successfully deleted Provider") success_message = _("Successfully deleted Provider")

View File

@ -4,38 +4,18 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin, PermissionRequiredMixin as DjangoPermissionRequiredMixin,
) )
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin from guardian.mixins import PermissionRequiredMixin
from authentik.admin.views.utils import ( from authentik.admin.views.utils import (
BackSuccessUrlMixin, BackSuccessUrlMixin,
DeleteMessageView, DeleteMessageView,
InheritanceCreateView, InheritanceCreateView,
InheritanceListView,
InheritanceUpdateView, InheritanceUpdateView,
SearchListMixin,
UserPaginateListMixin,
) )
from authentik.core.models import Source from authentik.core.models import Source
class SourceListView(
LoginRequiredMixin,
PermissionListMixin,
UserPaginateListMixin,
SearchListMixin,
InheritanceListView,
):
"""Show list of all sources"""
model = Source
permission_required = "authentik_core.view_source"
ordering = "name"
template_name = "administration/source/list.html"
search_fields = ["name", "slug"]
class SourceCreateView( class SourceCreateView(
SuccessMessageMixin, SuccessMessageMixin,
BackSuccessUrlMixin, BackSuccessUrlMixin,
@ -48,8 +28,8 @@ class SourceCreateView(
model = Source model = Source
permission_required = "authentik_core.add_source" permission_required = "authentik_core.add_source"
success_url = "/"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_admin:sources")
success_message = _("Successfully created Source") success_message = _("Successfully created Source")
@ -65,8 +45,8 @@ class SourceUpdateView(
model = Source model = Source
permission_required = "authentik_core.change_source" permission_required = "authentik_core.change_source"
success_url = "/"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("authentik_admin:sources")
success_message = _("Successfully updated Source") success_message = _("Successfully updated Source")
@ -76,6 +56,6 @@ class SourceDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessag
model = Source model = Source
permission_required = "authentik_core.delete_source" permission_required = "authentik_core.delete_source"
success_url = "/"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_admin:sources")
success_message = _("Successfully deleted Source") success_message = _("Successfully deleted Source")

View File

@ -29,11 +29,12 @@ from authentik.flows.api import (
FlowViewSet, FlowViewSet,
StageViewSet, StageViewSet,
) )
from authentik.outposts.api import ( from authentik.outposts.api.outpost_service_connections import (
DockerServiceConnectionViewSet, DockerServiceConnectionViewSet,
KubernetesServiceConnectionViewSet, KubernetesServiceConnectionViewSet,
OutpostViewSet, ServiceConnectionViewSet,
) )
from authentik.outposts.api.outposts import OutpostViewSet
from authentik.policies.api import ( from authentik.policies.api import (
PolicyBindingViewSet, PolicyBindingViewSet,
PolicyCacheViewSet, PolicyCacheViewSet,
@ -88,6 +89,7 @@ router.register("core/users", UserViewSet)
router.register("core/tokens", TokenViewSet) router.register("core/tokens", TokenViewSet)
router.register("outposts/outposts", OutpostViewSet) router.register("outposts/outposts", OutpostViewSet)
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(
"outposts/service_connections/kubernetes", KubernetesServiceConnectionViewSet "outposts/service_connections/kubernetes", KubernetesServiceConnectionViewSet

View File

@ -1,10 +1,18 @@
"""Provider API Views""" """Provider API Views"""
from django.shortcuts import reverse
from django.utils.translation import gettext_lazy as _
from drf_yasg2.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.fields import ReadOnlyField from rest_framework.fields import ReadOnlyField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.core.api.utils import MetaNameSerializer from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
from authentik.core.models import Provider from authentik.core.models import Provider
from authentik.lib.templatetags.authentik_utils import verbose_name
from authentik.lib.utils.reflection import all_subclasses
class ProviderSerializer(ModelSerializer, MetaNameSerializer): class ProviderSerializer(ModelSerializer, MetaNameSerializer):
@ -51,3 +59,26 @@ class ProviderViewSet(ModelViewSet):
def get_queryset(self): def get_queryset(self):
return Provider.objects.select_subclasses() return Provider.objects.select_subclasses()
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False)
def types(self, request: Request) -> Response:
"""Get all creatable provider types"""
data = []
for subclass in all_subclasses(self.queryset.model):
data.append(
{
"name": verbose_name(subclass),
"description": subclass.__doc__,
"link": reverse("authentik_admin:provider-create")
+ f"?type={subclass.__name__}",
}
)
data.append(
{
"name": _("SAML Provider from Metadata"),
"description": _("Create a SAML Provider by importing its Metadata."),
"link": reverse("authentik_admin:provider-saml-from-metadata"),
}
)
return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -1,31 +1,41 @@
"""Source API Views""" """Source API Views"""
from django.shortcuts import reverse
from drf_yasg2.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
from authentik.admin.forms.source import SOURCE_SERIALIZER_FIELDS from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
from authentik.core.api.utils import MetaNameSerializer
from authentik.core.models import Source from authentik.core.models import Source
from authentik.lib.templatetags.authentik_utils import verbose_name
from authentik.lib.utils.reflection import all_subclasses
class SourceSerializer(ModelSerializer, MetaNameSerializer): class SourceSerializer(ModelSerializer, MetaNameSerializer):
"""Source Serializer""" """Source Serializer"""
__type__ = SerializerMethodField(method_name="get_type") object_type = SerializerMethodField()
def get_type(self, obj): def get_object_type(self, obj):
"""Get object type so that we know which API Endpoint to use to get the full object""" """Get object type so that we know which API Endpoint to use to get the full object"""
return obj._meta.object_name.lower().replace("source", "") return obj._meta.object_name.lower().replace("source", "")
def to_representation(self, instance: Source):
# pyright: reportGeneralTypeIssues=false
if instance.__class__ == Source:
return super().to_representation(instance)
return instance.serializer(instance=instance).data
class Meta: class Meta:
model = Source model = Source
fields = SOURCE_SERIALIZER_FIELDS + ["__type__"] fields = SOURCE_SERIALIZER_FIELDS = [
"pk",
"name",
"slug",
"enabled",
"authentication_flow",
"enrollment_flow",
"object_type",
"verbose_name",
"verbose_name_plural",
]
class SourceViewSet(ReadOnlyModelViewSet): class SourceViewSet(ReadOnlyModelViewSet):
@ -37,3 +47,19 @@ class SourceViewSet(ReadOnlyModelViewSet):
def get_queryset(self): def get_queryset(self):
return Source.objects.select_subclasses() return Source.objects.select_subclasses()
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False)
def types(self, request: Request) -> Response:
"""Get all creatable source types"""
data = []
for subclass in all_subclasses(self.queryset.model):
data.append(
{
"name": verbose_name(subclass),
"description": subclass.__doc__,
"link": reverse("authentik_admin:source-create")
+ f"?type={subclass.__name__}",
}
)
return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -1,9 +1,12 @@
"""Tokens API Viewset""" """Tokens API Viewset"""
from django.db.models.base import Model
from django.http.response import Http404 from django.http.response import Http404
from drf_yasg2.utils import swagger_auto_schema
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.fields import CharField
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer, Serializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.core.models import Token from authentik.core.models import Token
@ -19,6 +22,18 @@ class TokenSerializer(ModelSerializer):
fields = ["pk", "identifier", "intent", "user", "description"] fields = ["pk", "identifier", "intent", "user", "description"]
class TokenViewSerializer(Serializer):
"""Show token's current key"""
key = CharField(read_only=True)
def create(self, validated_data: dict) -> Model:
raise NotImplementedError
def update(self, instance: Model, validated_data: dict) -> Model:
raise NotImplementedError
class TokenViewSet(ModelViewSet): class TokenViewSet(ModelViewSet):
"""Token Viewset""" """Token Viewset"""
@ -26,12 +41,15 @@ class TokenViewSet(ModelViewSet):
queryset = Token.filter_not_expired() queryset = Token.filter_not_expired()
serializer_class = TokenSerializer serializer_class = TokenSerializer
@swagger_auto_schema(responses={200: TokenViewSerializer(many=False)})
@action(detail=True) @action(detail=True)
# pylint: disable=unused-argument
def view_key(self, request: Request, identifier: str) -> Response: def view_key(self, request: Request, identifier: str) -> Response:
"""Return token key and log access""" """Return token key and log access"""
tokens = Token.filter_not_expired(identifier=identifier) token: Token = self.get_object()
if not tokens.exists(): if token.is_expired:
raise Http404 raise Http404
token = tokens.first() Event.new(EventAction.SECRET_VIEW, secret=token).from_http( # noqa # nosec
Event.new(EventAction.TOKEN_VIEW, token=token).from_http(request) request
return Response({"key": token.key}) )
return Response(TokenViewSerializer({"key": token.key}).data)

View File

@ -1,5 +1,6 @@
"""API Utilities""" """API Utilities"""
from django.db.models import Model from django.db.models import Model
from rest_framework.fields import CharField
from rest_framework.serializers import Serializer, SerializerMethodField from rest_framework.serializers import Serializer, SerializerMethodField
@ -22,3 +23,17 @@ class MetaNameSerializer(Serializer):
def get_verbose_name_plural(self, obj: Model) -> str: def get_verbose_name_plural(self, obj: Model) -> str:
"""Return object's plural verbose_name""" """Return object's plural verbose_name"""
return obj._meta.verbose_name_plural return obj._meta.verbose_name_plural
class TypeCreateSerializer(Serializer):
"""Types of an object that can be created"""
name = CharField(read_only=True)
description = CharField(read_only=True)
link = CharField(read_only=True)
def create(self, validated_data: dict) -> Model:
raise NotImplementedError
def update(self, instance: Model, validated_data: dict) -> Model:
raise NotImplementedError

View File

@ -9,6 +9,7 @@ from django.http import HttpRequest, HttpResponse
SESSION_IMPERSONATE_USER = "authentik_impersonate_user" SESSION_IMPERSONATE_USER = "authentik_impersonate_user"
SESSION_IMPERSONATE_ORIGINAL_USER = "authentik_impersonate_original_user" SESSION_IMPERSONATE_ORIGINAL_USER = "authentik_impersonate_original_user"
LOCAL = local() LOCAL = local()
RESPONSE_HEADER_ID = "X-authentik-id"
class ImpersonateMiddleware: class ImpersonateMiddleware:
@ -43,7 +44,7 @@ class RequestIDMiddleware:
setattr(request, "request_id", request_id) setattr(request, "request_id", request_id)
LOCAL.authentik = {"request_id": request_id} LOCAL.authentik = {"request_id": request_id}
response = self.get_response(request) response = self.get_response(request)
response["X-authentik-id"] = request.request_id response[RESPONSE_HEADER_ID] = request.request_id
del LOCAL.authentik["request_id"] del LOCAL.authentik["request_id"]
return response return response

View File

@ -41,8 +41,8 @@
</section> </section>
{% endfor %} {% endfor %}
{% user_sources as user_sources_loc %} {% user_sources as user_sources_loc %}
{% for source, source_link in user_sources_loc.item %} {% for source, source_link in user_sources_loc.items %}
<section slot="page-{{ source.pk }}" data-tab-title="{{ source|verbose_name }}" class="pf-c-page__main-section pf-m-no-padding-mobile"> <section slot="page-{{ source.pk }}" data-tab-title="{{ source.name }}" class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-u-display-flex pf-u-justify-content-center"> <div class="pf-u-display-flex pf-u-justify-content-center">
<div class="pf-u-w-75"> <div class="pf-u-w-75">
<ak-site-shell url="{{ source_link }}"> <ak-site-shell url="{{ source_link }}">

View File

@ -2,15 +2,29 @@
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import load_pem_x509_certificate from cryptography.x509 import load_pem_x509_certificate
from rest_framework.serializers import ModelSerializer, ValidationError from django.db.models import Model
from drf_yasg2.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.fields import CharField, DateTimeField, SerializerMethodField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, Serializer, ValidationError
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction
class CertificateKeyPairSerializer(ModelSerializer): class CertificateKeyPairSerializer(ModelSerializer):
"""CertificateKeyPair Serializer""" """CertificateKeyPair Serializer"""
cert_expiry = DateTimeField(source="certificate.not_valid_after", read_only=True)
cert_subject = SerializerMethodField()
def get_cert_subject(self, instance: CertificateKeyPair) -> str:
"""Get certificate subject as full rfc4514"""
return instance.certificate.subject.rfc4514_string()
def validate_certificate_data(self, value): def validate_certificate_data(self, value):
"""Verify that input is a valid PEM x509 Certificate""" """Verify that input is a valid PEM x509 Certificate"""
try: try:
@ -36,7 +50,31 @@ class CertificateKeyPairSerializer(ModelSerializer):
class Meta: class Meta:
model = CertificateKeyPair model = CertificateKeyPair
fields = ["pk", "name", "certificate_data", "key_data"] fields = [
"pk",
"name",
"fingerprint",
"certificate_data",
"key_data",
"cert_expiry",
"cert_subject",
]
extra_kwargs = {
"key_data": {"write_only": True},
"certificate_data": {"write_only": True},
}
class CertificateDataSerializer(Serializer):
"""Get CertificateKeyPair's data"""
data = CharField(read_only=True)
def create(self, validated_data: dict) -> Model:
raise NotImplementedError
def update(self, instance: Model, validated_data: dict) -> Model:
raise NotImplementedError
class CertificateKeyPairViewSet(ModelViewSet): class CertificateKeyPairViewSet(ModelViewSet):
@ -44,3 +82,31 @@ class CertificateKeyPairViewSet(ModelViewSet):
queryset = CertificateKeyPair.objects.all() queryset = CertificateKeyPair.objects.all()
serializer_class = CertificateKeyPairSerializer serializer_class = CertificateKeyPairSerializer
@swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)})
@action(detail=True)
# pylint: disable=invalid-name, unused-argument
def view_certificate(self, request: Request, pk: str) -> Response:
"""Return certificate-key pairs certificate and log access"""
certificate: CertificateKeyPair = self.get_object()
Event.new( # noqa # nosec
EventAction.SECRET_VIEW,
secret=certificate,
type="certificate",
).from_http(request)
return Response(
CertificateDataSerializer({"data": certificate.certificate_data}).data
)
@swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)})
@action(detail=True)
# pylint: disable=invalid-name, unused-argument
def view_private_key(self, request: Request, pk: str) -> Response:
"""Return certificate-key pairs private key and log access"""
certificate: CertificateKeyPair = self.get_object()
Event.new( # noqa # nosec
EventAction.SECRET_VIEW,
secret=certificate,
type="private_key",
).from_http(request)
return Response(CertificateDataSerializer({"data": certificate.key_data}).data)

View File

@ -1,6 +1,7 @@
"""Create self-signed certificates""" """Create self-signed certificates"""
import datetime import datetime
import uuid import uuid
from typing import Optional
from cryptography import x509 from cryptography import x509
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
@ -8,6 +9,9 @@ from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID from cryptography.x509.oid import NameOID
from authentik import __version__
from authentik.crypto.models import CertificateKeyPair
class CertificateBuilder: class CertificateBuilder:
"""Build self-signed certificates""" """Build self-signed certificates"""
@ -17,19 +21,39 @@ class CertificateBuilder:
__builder = None __builder = None
__certificate = None __certificate = None
common_name: str
def __init__(self): def __init__(self):
self.__public_key = None self.__public_key = None
self.__private_key = None self.__private_key = None
self.__builder = None self.__builder = None
self.__certificate = None self.__certificate = None
self.common_name = "authentik Self-signed Certificate"
def build(self): def save(self) -> Optional[CertificateKeyPair]:
"""Save generated certificate as model"""
if not self.__certificate:
return None
return CertificateKeyPair.objects.create(
name=self.common_name,
certificate_data=self.certificate,
key_data=self.private_key,
)
def build(
self,
validity_days: int = 365,
subject_alt_names: Optional[list[str]] = None,
):
"""Build self-signed certificate""" """Build self-signed certificate"""
one_day = datetime.timedelta(1, 0, 0) one_day = datetime.timedelta(1, 0, 0)
self.__private_key = rsa.generate_private_key( self.__private_key = rsa.generate_private_key(
public_exponent=65537, key_size=2048, backend=default_backend() public_exponent=65537, key_size=2048, backend=default_backend()
) )
self.__public_key = self.__private_key.public_key() self.__public_key = self.__private_key.public_key()
alt_names: list[x509.GeneralName] = [
x509.DNSName(x) for x in subject_alt_names or []
]
self.__builder = ( self.__builder = (
x509.CertificateBuilder() x509.CertificateBuilder()
.subject_name( .subject_name(
@ -37,7 +61,7 @@ class CertificateBuilder:
[ [
x509.NameAttribute( x509.NameAttribute(
NameOID.COMMON_NAME, NameOID.COMMON_NAME,
"authentik Self-signed Certificate", self.common_name,
), ),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "authentik"), x509.NameAttribute(NameOID.ORGANIZATION_NAME, "authentik"),
x509.NameAttribute( x509.NameAttribute(
@ -51,13 +75,16 @@ class CertificateBuilder:
[ [
x509.NameAttribute( x509.NameAttribute(
NameOID.COMMON_NAME, NameOID.COMMON_NAME,
"authentik Self-signed Certificate", f"authentik {__version__}",
), ),
] ]
) )
) )
.add_extension(x509.SubjectAlternativeName(alt_names), critical=True)
.not_valid_before(datetime.datetime.today() - one_day) .not_valid_before(datetime.datetime.today() - one_day)
.not_valid_after(datetime.datetime.today() + datetime.timedelta(days=365)) .not_valid_after(
datetime.datetime.today() + datetime.timedelta(days=validity_days)
)
.serial_number(int(uuid.uuid4())) .serial_number(int(uuid.uuid4()))
.public_key(self.__public_key) .public_key(self.__public_key)
) )

View File

@ -8,6 +8,14 @@ from django.utils.translation import gettext_lazy as _
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
class CertificateKeyPairGenerateForm(forms.Form):
"""CertificateKeyPair generation form"""
common_name = forms.CharField()
subject_alt_name = forms.CharField(required=False, label=_("Subject-alt name"))
validity_days = forms.IntegerField(initial=365)
class CertificateKeyPairForm(forms.ModelForm): class CertificateKeyPairForm(forms.ModelForm):
"""CertificateKeyPair Form""" """CertificateKeyPair Form"""

View File

@ -0,0 +1,61 @@
# Generated by Django 3.1.6 on 2021-02-09 16:57
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def token_view_to_secret_view(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from authentik.events.models import EventAction
db_alias = schema_editor.connection.alias
Event = apps.get_model("authentik_events", "Event")
events = Event.objects.using(db_alias).filter(action="token_view")
for event in events:
event.context["secret"] = event.context.pop("token")
event.action = EventAction.SECRET_VIEW
Event.objects.using(db_alias).bulk_update(events, ["context", "action"])
class Migration(migrations.Migration):
dependencies = [
("authentik_events", "0012_auto_20210202_1821"),
]
operations = [
migrations.AlterField(
model_name="event",
name="action",
field=models.TextField(
choices=[
("login", "Login"),
("login_failed", "Login Failed"),
("logout", "Logout"),
("user_write", "User Write"),
("suspicious_request", "Suspicious Request"),
("password_set", "Password Set"),
("secret_view", "Secret View"),
("invitation_used", "Invite Used"),
("authorize_application", "Authorize Application"),
("source_linked", "Source Linked"),
("impersonation_started", "Impersonation Started"),
("impersonation_ended", "Impersonation Ended"),
("policy_execution", "Policy Execution"),
("policy_exception", "Policy Exception"),
("property_mapping_exception", "Property Mapping Exception"),
("system_task_execution", "System Task Execution"),
("system_task_exception", "System Task Exception"),
("configuration_error", "Configuration Error"),
("model_created", "Model Created"),
("model_updated", "Model Updated"),
("model_deleted", "Model Deleted"),
("update_available", "Update Available"),
("custom_", "Custom Prefix"),
]
),
),
migrations.RunPython(token_view_to_secret_view),
]

View File

@ -42,7 +42,7 @@ class EventAction(models.TextChoices):
SUSPICIOUS_REQUEST = "suspicious_request" SUSPICIOUS_REQUEST = "suspicious_request"
PASSWORD_SET = "password_set" # noqa # nosec PASSWORD_SET = "password_set" # noqa # nosec
TOKEN_VIEW = "token_view" # nosec SECRET_VIEW = "secret_view" # noqa # nosec
INVITE_USED = "invitation_used" INVITE_USED = "invitation_used"

View File

@ -2,6 +2,7 @@
from guardian.shortcuts import get_anonymous_user from guardian.shortcuts import get_anonymous_user
from structlog import get_logger from structlog import get_logger
from authentik.core.models import User
from authentik.events.models import ( from authentik.events.models import (
Event, Event,
Notification, Notification,
@ -53,7 +54,8 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
return return
LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger) LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger)
policy_engine = PolicyEngine(trigger, get_anonymous_user()) user = User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user()
policy_engine = PolicyEngine(trigger, user)
policy_engine.mode = PolicyEngineMode.MODE_OR policy_engine.mode = PolicyEngineMode.MODE_OR
policy_engine.empty_result = False policy_engine.empty_result = False
policy_engine.use_cache = False policy_engine.use_cache = False
@ -67,7 +69,7 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
# Create the notification objects # Create the notification objects
for transport in trigger.transports.all(): for transport in trigger.transports.all():
for user in trigger.group.users.all(): for user in trigger.group.users.all():
LOGGER.debug("created notif") LOGGER.debug("created notification")
notification = Notification.objects.create( notification = Notification.objects.create(
severity=trigger.severity, body=event.summary, event=event, user=user severity=trigger.severity, body=event.summary, event=event, user=user
) )

View File

@ -98,6 +98,10 @@ class BaseEvaluator:
exec(ast_obj, self._globals, _locals) # nosec # noqa exec(ast_obj, self._globals, _locals) # nosec # noqa
result = _locals["result"] result = _locals["result"]
except Exception as exc: except Exception as exc:
# So, this is a bit questionable. Essentially, we are edit the stacktrace
# so the user only sees information relevant to them
# and none of our surrounding error handling
exc.__traceback__ = exc.__traceback__.tb_next
self.handle_error(exc, expression_source) self.handle_error(exc, expression_source)
raise exc raise exc
return result return result

View File

@ -1,30 +1,28 @@
"""Outpost API Views""" """Outpost API Views"""
from rest_framework.serializers import JSONField, ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.outposts.models import ( from authentik.outposts.models import (
DockerServiceConnection, DockerServiceConnection,
KubernetesServiceConnection, KubernetesServiceConnection,
Outpost, OutpostServiceConnection,
) )
class OutpostSerializer(ModelSerializer): class ServiceConnectionSerializer(ModelSerializer):
"""Outpost Serializer""" """ServiceConnection Serializer"""
_config = JSONField()
class Meta: class Meta:
model = Outpost model = OutpostServiceConnection
fields = ["pk", "name", "providers", "service_connection", "_config"] fields = ["pk", "name"]
class OutpostViewSet(ModelViewSet): class ServiceConnectionViewSet(ModelViewSet):
"""Outpost Viewset""" """ServiceConnection Viewset"""
queryset = Outpost.objects.all() queryset = OutpostServiceConnection.objects.all()
serializer_class = OutpostSerializer serializer_class = ServiceConnectionSerializer
class DockerServiceConnectionSerializer(ModelSerializer): class DockerServiceConnectionSerializer(ModelSerializer):

View File

@ -0,0 +1,79 @@
"""Outpost API Views"""
from django.db.models import Model
from drf_yasg2.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.fields import BooleanField, CharField, DateTimeField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import JSONField, ModelSerializer, Serializer
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.providers import ProviderSerializer
from authentik.outposts.models import Outpost
class OutpostSerializer(ModelSerializer):
"""Outpost Serializer"""
_config = JSONField()
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
class Meta:
model = Outpost
fields = [
"pk",
"name",
"providers",
"providers_obj",
"service_connection",
"token_identifier",
"_config",
]
class OutpostHealthSerializer(Serializer):
"""Outpost health status"""
last_seen = DateTimeField(read_only=True)
version = CharField(read_only=True)
version_should = CharField(read_only=True)
version_outdated = BooleanField(read_only=True)
def create(self, validated_data: dict) -> Model:
raise NotImplementedError
def update(self, instance: Model, validated_data: dict) -> Model:
raise NotImplementedError
class OutpostViewSet(ModelViewSet):
"""Outpost Viewset"""
queryset = Outpost.objects.all()
serializer_class = OutpostSerializer
filterset_fields = {
"providers": ["isnull"],
}
search_fields = [
"name",
"providers__name",
]
@swagger_auto_schema(responses={200: OutpostHealthSerializer(many=True)})
@action(methods=["GET"], detail=True)
# pylint: disable=invalid-name, unused-argument
def health(self, request: Request, pk: int) -> Response:
"""Get outposts current health"""
outpost: Outpost = self.get_object()
states = []
for state in outpost.state:
states.append(
{
"last_seen": state.last_seen,
"version": state.version,
"version_should": state.version_should,
"version_outdated": state.version_outdated,
}
)
return Response(OutpostHealthSerializer(states, many=True).data)

View File

@ -9,7 +9,6 @@ from django.core.cache import cache
from django.db import models, transaction from django.db import models, transaction
from django.db.models.base import Model from django.db.models.base import Model
from django.forms.models import ModelForm from django.forms.models import ModelForm
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from docker.client import DockerClient from docker.client import DockerClient
from docker.errors import DockerException from docker.errors import DockerException
@ -33,7 +32,6 @@ from authentik.crypto.models import CertificateKeyPair
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.lib.models import InheritanceForeignKey from authentik.lib.models import InheritanceForeignKey
from authentik.lib.sentry import SentryIgnoredException from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.template import render_to_string
from authentik.outposts.docker_tls import DockerInlineTLS from authentik.outposts.docker_tls import DockerInlineTLS
OUR_VERSION = parse(__version__) OUR_VERSION = parse(__version__)
@ -378,13 +376,6 @@ class Outpost(models.Model):
objects.append(provider) objects.append(provider)
return objects return objects
def html_deployment_view(self, request: HttpRequest) -> Optional[str]:
"""return template and context modal to view token and other config info"""
return render_to_string(
"outposts/deployment_modal.html",
{"outpost": self, "full_url": request.build_absolute_uri("/")},
)
def __str__(self) -> str: def __str__(self) -> str:
return f"Outpost {self.name}" return f"Outpost {self.name}"

View File

@ -20,6 +20,7 @@ UPDATE_TRIGGERING_MODELS = (
@receiver(post_save) @receiver(post_save)
# pylint: disable=unused-argument
def post_save_update(sender, instance: Model, **_): def post_save_update(sender, instance: Model, **_):
"""If an Outpost is saved, Ensure that token is created/updated """If an Outpost is saved, Ensure that token is created/updated
@ -29,7 +30,7 @@ def post_save_update(sender, instance: Model, **_):
return return
if instance.__module__ == "__fake__": if instance.__module__ == "__fake__":
return return
if sender not in UPDATE_TRIGGERING_MODELS: if not isinstance(instance, UPDATE_TRIGGERING_MODELS):
return return
outpost_post_save.delay(class_to_path(instance.__class__), instance.pk) outpost_post_save.delay(class_to_path(instance.__class__), instance.pk)

View File

@ -1,43 +0,0 @@
{% load i18n %}
<ak-modal-button>
<button slot="trigger" class="pf-c-button pf-m-tertiary">
{% trans 'View Deployment Info' %}
</button>
<div slot="modal">
<div class="pf-c-modal-box__header">
<h1 class="pf-c-title pf-m-2xl" id="modal-title">{% trans 'Outpost Deployment Info' %}</h1>
</div>
<div class="pf-c-modal-box__body" id="modal-description">
<p><a href="https://goauthentik.io/docs/outposts/outposts/#deploy">{% trans 'View deployment documentation' %}</a></p>
<form class="pf-c-form">
<div class="pf-c-form__group">
<label class="pf-c-form__label" for="help-text-simple-form-name">
<span class="pf-c-form__label-text">AUTHENTIK_HOST</span>
</label>
<input class="pf-c-form-control" readonly type="text" value="{{ full_url }}" />
</div>
<div class="pf-c-form__group">
<label class="pf-c-form__label" for="help-text-simple-form-name">
<span class="pf-c-form__label-text">AUTHENTIK_TOKEN</span>
</label>
<div>
<ak-token-copy-button identifier="{{ outpost.token_identifier }}">
{% trans 'Click to copy token' %}
</ak-token-copy-button>
</div>
</div>
<h3>{% trans 'If your authentik Instance is using a self-signed certificate, set this value.' %}</h3>
<div class="pf-c-form__group">
<label class="pf-c-form__label" for="help-text-simple-form-name">
<span class="pf-c-form__label-text">AUTHENTIK_INSECURE</span>
</label>
<input class="pf-c-form-control" readonly type="text" value="true" />
</div>
</form>
</div>
<footer class="pf-c-modal-box__footer pf-m-align-left">
<a class="pf-c-button pf-m-primary">{% trans 'Close' %}</a>
</footer>
</div>
</ak-modal-button>

View File

@ -0,0 +1,46 @@
# Generated by Django 3.1.6 on 2021-02-09 16:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_policies_event_matcher", "0006_auto_20210203_1134"),
]
operations = [
migrations.AlterField(
model_name="eventmatcherpolicy",
name="action",
field=models.TextField(
blank=True,
choices=[
("login", "Login"),
("login_failed", "Login Failed"),
("logout", "Logout"),
("user_write", "User Write"),
("suspicious_request", "Suspicious Request"),
("password_set", "Password Set"),
("secret_view", "Secret View"),
("invitation_used", "Invite Used"),
("authorize_application", "Authorize Application"),
("source_linked", "Source Linked"),
("impersonation_started", "Impersonation Started"),
("impersonation_ended", "Impersonation Ended"),
("policy_execution", "Policy Execution"),
("policy_exception", "Policy Exception"),
("property_mapping_exception", "Property Mapping Exception"),
("system_task_execution", "System Task Execution"),
("system_task_exception", "System Task Exception"),
("configuration_error", "Configuration Error"),
("model_created", "Model Created"),
("model_updated", "Model Updated"),
("model_deleted", "Model Deleted"),
("update_available", "Update Available"),
("custom_", "Custom Prefix"),
],
help_text="Match created events with this action type. When left empty, all action types will be matched.",
),
),
]

View File

@ -55,10 +55,6 @@ class PolicyEvaluator(BaseEvaluator):
def handle_error(self, exc: Exception, expression_source: str): def handle_error(self, exc: Exception, expression_source: str):
"""Exception Handler""" """Exception Handler"""
# So, this is a bit questionable. Essentially, we are edit the stacktrace
# so the user only sees information relevant to them
# and none of our surrounding error handling
exc.__traceback__ = exc.__traceback__.tb_next
raise PolicyException(exc) raise PolicyException(exc)
def evaluate(self, expression_source: str) -> PolicyResult: def evaluate(self, expression_source: str) -> PolicyResult:

View File

@ -80,7 +80,7 @@ class PolicyProcess(PROCESS_CLASS):
) )
try: try:
policy_result = self.binding.policy.passes(self.request) policy_result = self.binding.policy.passes(self.request)
if self.binding.policy.execution_logging: if self.binding.policy.execution_logging and not self.request.debug:
self.create_event( self.create_event(
EventAction.POLICY_EXECUTION, EventAction.POLICY_EXECUTION,
message="Policy Execution", message="Policy Execution",
@ -94,7 +94,8 @@ class PolicyProcess(PROCESS_CLASS):
+ "".join(format_tb(src_exc.__traceback__)) + "".join(format_tb(src_exc.__traceback__))
+ str(src_exc) + str(src_exc)
) )
# Create policy exception event # Create policy exception event, only when we're not debugging
if not self.request.debug:
self.create_event(EventAction.POLICY_EXCEPTION, message=error_string) self.create_event(EventAction.POLICY_EXCEPTION, message=error_string)
LOGGER.debug("P_ENG(proc): error", exc=src_exc) LOGGER.debug("P_ENG(proc): error", exc=src_exc)
policy_result = PolicyResult(False, str(src_exc)) policy_result = PolicyResult(False, str(src_exc))
@ -102,6 +103,7 @@ class PolicyProcess(PROCESS_CLASS):
# Invert result if policy.negate is set # Invert result if policy.negate is set
if self.binding.negate: if self.binding.negate:
policy_result.passing = not policy_result.passing policy_result.passing = not policy_result.passing
if not self.request.debug:
key = cache_key(self.binding, self.request) key = cache_key(self.binding, self.request)
cache.set(key, policy_result) cache.set(key, policy_result)
LOGGER.debug( LOGGER.debug(

View File

@ -20,6 +20,7 @@ class PolicyRequest:
http_request: Optional[HttpRequest] http_request: Optional[HttpRequest]
obj: Optional[Model] obj: Optional[Model]
context: dict[str, Any] context: dict[str, Any]
debug: bool = False
def __init__(self, user: User): def __init__(self, user: User):
super().__init__() super().__init__()

View File

@ -59,11 +59,11 @@ class OAuth2ProviderViewSet(ModelViewSet):
queryset = OAuth2Provider.objects.all() queryset = OAuth2Provider.objects.all()
serializer_class = OAuth2ProviderSerializer serializer_class = OAuth2ProviderSerializer
@action(methods=["GET"], detail=True)
@swagger_auto_schema(responses={200: OAuth2ProviderSetupURLs(many=False)}) @swagger_auto_schema(responses={200: OAuth2ProviderSetupURLs(many=False)})
@action(methods=["GET"], detail=True)
# pylint: disable=invalid-name # pylint: disable=invalid-name
def setup_urls(self, request: Request, pk: int) -> str: def setup_urls(self, request: Request, pk: int) -> str:
"""Return metadata as XML string""" """Get Providers setup URLs"""
provider = get_object_or_404(OAuth2Provider, pk=pk) provider = get_object_or_404(OAuth2Provider, pk=pk)
data = { data = {
"issuer": provider.get_issuer(request), "issuer": provider.get_issuer(request),

View File

@ -23,6 +23,8 @@ return {
"family_name": "", "family_name": "",
"preferred_username": user.username, "preferred_username": user.username,
"nickname": user.username, "nickname": user.username,
# groups is not part of the official userinfo schema, but is a quasi-standard
"groups": [group.name for group in user.ak_groups.all()],
} }
""" """

View File

@ -253,6 +253,7 @@ class OAuthFulfillmentStage(StageView):
EventAction.AUTHORIZE_APPLICATION, EventAction.AUTHORIZE_APPLICATION,
authorized_application=application, authorized_application=application,
flow=self.executor.plan.flow_pk, flow=self.executor.plan.flow_pk,
scopes=", ".join(self.params.scope),
).from_http(self.request) ).from_http(self.request)
return redirect(self.create_response_uri()) return redirect(self.create_response_uri())
except (ClientIdError, RedirectUriError) as error: except (ClientIdError, RedirectUriError) as error:

View File

@ -18,7 +18,7 @@ class ProxyProviderForm(forms.ModelForm):
) )
self.fields["certificate"].queryset = CertificateKeyPair.objects.filter( self.fields["certificate"].queryset = CertificateKeyPair.objects.filter(
key_data__isnull=False key_data__isnull=False
) ).exclude(key_data="")
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
actual_save = super().save(*args, **kwargs) actual_save = super().save(*args, **kwargs)

View File

@ -11,7 +11,7 @@ from rest_framework.viewsets import ModelViewSet
from authentik.core.api.providers import ProviderSerializer from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.utils import MetaNameSerializer from authentik.core.api.utils import MetaNameSerializer
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.views import DescriptorDownloadView from authentik.providers.saml.views.metadata import DescriptorDownloadView
class SAMLProviderSerializer(ProviderSerializer): class SAMLProviderSerializer(ProviderSerializer):
@ -54,10 +54,10 @@ class SAMLProviderViewSet(ModelViewSet):
queryset = SAMLProvider.objects.all() queryset = SAMLProvider.objects.all()
serializer_class = SAMLProviderSerializer serializer_class = SAMLProviderSerializer
@action(methods=["GET"], detail=True)
@swagger_auto_schema(responses={200: SAMLMetadataSerializer(many=False)}) @swagger_auto_schema(responses={200: SAMLMetadataSerializer(many=False)})
@action(methods=["GET"], detail=True)
# pylint: disable=invalid-name # pylint: disable=invalid-name
def metadata(self, request: Request, pk: int) -> str: def metadata(self, request: Request, pk: int) -> Response:
"""Return metadata as XML string""" """Return metadata as XML string"""
provider = get_object_or_404(SAMLProvider, pk=pk) provider = get_object_or_404(SAMLProvider, pk=pk)
metadata = DescriptorDownloadView.get_metadata(request, provider) metadata = DescriptorDownloadView.get_metadata(request, provider)

View File

@ -19,6 +19,7 @@ class SAMLProviderManager(ObjectManager):
name="authentik default SAML Mapping: UPN", name="authentik default SAML Mapping: UPN",
saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn",
expression="return user.attributes.get('upn', user.email)", expression="return user.attributes.get('upn', user.email)",
friendly_name="",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
@ -26,6 +27,7 @@ class SAMLProviderManager(ObjectManager):
name="authentik default SAML Mapping: Name", name="authentik default SAML Mapping: Name",
saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
expression="return user.name", expression="return user.name",
friendly_name="",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
@ -33,6 +35,7 @@ class SAMLProviderManager(ObjectManager):
name="authentik default SAML Mapping: Email", name="authentik default SAML Mapping: Email",
saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
expression="return user.email", expression="return user.email",
friendly_name="",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
@ -40,6 +43,7 @@ class SAMLProviderManager(ObjectManager):
name="authentik default SAML Mapping: Username", name="authentik default SAML Mapping: Username",
saml_name="http://schemas.goauthentik.io/2021/02/saml/username", saml_name="http://schemas.goauthentik.io/2021/02/saml/username",
expression="return user.username", expression="return user.username",
friendly_name="",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
@ -47,6 +51,7 @@ class SAMLProviderManager(ObjectManager):
name="authentik default SAML Mapping: User ID", name="authentik default SAML Mapping: User ID",
saml_name="http://schemas.goauthentik.io/2021/02/saml/uid", saml_name="http://schemas.goauthentik.io/2021/02/saml/uid",
expression="return user.pk", expression="return user.pk",
friendly_name="",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
@ -54,6 +59,7 @@ class SAMLProviderManager(ObjectManager):
name="authentik default SAML Mapping: Groups", name="authentik default SAML Mapping: Groups",
saml_name="http://schemas.xmlsoap.org/claims/Group", saml_name="http://schemas.xmlsoap.org/claims/Group",
expression=GROUP_EXPRESSION, expression=GROUP_EXPRESSION,
friendly_name="",
), ),
EnsureExists( EnsureExists(
SAMLPropertyMapping, SAMLPropertyMapping,
@ -63,5 +69,6 @@ class SAMLProviderManager(ObjectManager):
"http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname" "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
), ),
expression="return user.username", expression="return user.username",
friendly_name="",
), ),
] ]

View File

@ -1,29 +1,29 @@
"""authentik SAML IDP URLs""" """authentik SAML IDP URLs"""
from django.urls import path from django.urls import path
from authentik.providers.saml import views from authentik.providers.saml.views import metadata, sso
urlpatterns = [ urlpatterns = [
# SSO Bindings # SSO Bindings
path( path(
"<slug:application_slug>/sso/binding/redirect/", "<slug:application_slug>/sso/binding/redirect/",
views.SAMLSSOBindingRedirectView.as_view(), sso.SAMLSSOBindingRedirectView.as_view(),
name="sso-redirect", name="sso-redirect",
), ),
path( path(
"<slug:application_slug>/sso/binding/post/", "<slug:application_slug>/sso/binding/post/",
views.SAMLSSOBindingPOSTView.as_view(), sso.SAMLSSOBindingPOSTView.as_view(),
name="sso-post", name="sso-post",
), ),
# SSO IdP Initiated # SSO IdP Initiated
path( path(
"<slug:application_slug>/sso/binding/init/", "<slug:application_slug>/sso/binding/init/",
views.SAMLSSOBindingInitView.as_view(), sso.SAMLSSOBindingInitView.as_view(),
name="sso-init", name="sso-init",
), ),
path( path(
"<slug:application_slug>/metadata/", "<slug:application_slug>/metadata/",
views.DescriptorDownloadView.as_view(), metadata.DescriptorDownloadView.as_view(),
name="metadata", name="metadata",
), ),
] ]

View File

@ -1,284 +0,0 @@
"""authentik SAML IDP Views"""
from typing import Optional
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.validators import URLValidator
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls.base import reverse_lazy
from django.utils.decorators import method_decorator
from django.utils.http import urlencode
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.edit import FormView
from structlog.stdlib import get_logger
from authentik.core.models import Application, Provider
from authentik.events.models import Event, EventAction
from authentik.flows.models import in_memory_stage
from authentik.flows.planner import (
PLAN_CONTEXT_APPLICATION,
PLAN_CONTEXT_SSO,
FlowPlanner,
)
from authentik.flows.stage import StageView
from authentik.flows.views import SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs
from authentik.lib.views import bad_request_message
from authentik.policies.views import PolicyAccessView
from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.forms import SAMLProviderImportForm
from authentik.providers.saml.models import SAMLBindings, SAMLProvider
from authentik.providers.saml.processors.assertion import AssertionProcessor
from authentik.providers.saml.processors.metadata import MetadataProcessor
from authentik.providers.saml.processors.metadata_parser import (
ServiceProviderMetadataParser,
)
from authentik.providers.saml.processors.request_parser import (
AuthNRequest,
AuthNRequestParser,
)
from authentik.providers.saml.utils.encoding import deflate_and_base64_encode, nice64
from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_TEMPLATE
LOGGER = get_logger()
URL_VALIDATOR = URLValidator(schemes=("http", "https"))
REQUEST_KEY_SAML_REQUEST = "SAMLRequest"
REQUEST_KEY_SAML_SIGNATURE = "Signature"
REQUEST_KEY_SAML_SIG_ALG = "SigAlg"
REQUEST_KEY_SAML_RESPONSE = "SAMLResponse"
REQUEST_KEY_RELAY_STATE = "RelayState"
SESSION_KEY_AUTH_N_REQUEST = "authn_request"
class SAMLSSOView(PolicyAccessView):
""" "SAML SSO Base View, which plans a flow and injects our final stage.
Calls get/post handler."""
def resolve_provider_application(self):
self.application = get_object_or_404(
Application, slug=self.kwargs["application_slug"]
)
self.provider: SAMLProvider = get_object_or_404(
SAMLProvider, pk=self.application.provider_id
)
def check_saml_request(self) -> Optional[HttpRequest]:
"""Handler to verify the SAML Request. Must be implemented by a subclass"""
raise NotImplementedError
# pylint: disable=unused-argument
def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""Verify the SAML Request, and if valid initiate the FlowPlanner for the application"""
# Call the method handler, which checks the SAML
# Request and returns a HTTP Response on error
method_response = self.check_saml_request()
if method_response:
return method_response
# Regardless, we start the planner and return to it
planner = FlowPlanner(self.provider.authorization_flow)
planner.allow_empty_flows = True
plan = planner.plan(
request,
{
PLAN_CONTEXT_SSO: True,
PLAN_CONTEXT_APPLICATION: self.application,
PLAN_CONTEXT_CONSENT_TEMPLATE: "providers/saml/consent.html",
},
)
plan.append(in_memory_stage(SAMLFlowFinalView))
request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_flows:flow-executor-shell",
request.GET,
flow_slug=self.provider.authorization_flow.slug,
)
def post(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""GET and POST use the same handler, but we can't
override .dispatch easily because PolicyAccessView's dispatch"""
return self.get(request, application_slug)
class SAMLSSOBindingRedirectView(SAMLSSOView):
"""SAML Handler for SSO/Redirect bindings, which are sent via GET"""
def check_saml_request(self) -> Optional[HttpRequest]:
"""Handle REDIRECT bindings"""
if REQUEST_KEY_SAML_REQUEST not in self.request.GET:
LOGGER.info("handle_saml_request: SAML payload missing")
return bad_request_message(
self.request, "The SAML request payload is missing."
)
try:
auth_n_request = AuthNRequestParser(self.provider).parse_detached(
self.request.GET[REQUEST_KEY_SAML_REQUEST],
self.request.GET.get(REQUEST_KEY_RELAY_STATE),
self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE),
self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG),
)
self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
except CannotHandleAssertion as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
provider=self.provider,
message=str(exc),
).save()
LOGGER.info(str(exc))
return bad_request_message(self.request, str(exc))
return None
@method_decorator(csrf_exempt, name="dispatch")
class SAMLSSOBindingPOSTView(SAMLSSOView):
"""SAML Handler for SSO/POST bindings"""
def check_saml_request(self) -> Optional[HttpRequest]:
"""Handle POST bindings"""
if REQUEST_KEY_SAML_REQUEST not in self.request.POST:
LOGGER.info("check_saml_request: SAML payload missing")
return bad_request_message(
self.request, "The SAML request payload is missing."
)
try:
auth_n_request = AuthNRequestParser(self.provider).parse(
self.request.POST[REQUEST_KEY_SAML_REQUEST],
self.request.POST.get(REQUEST_KEY_RELAY_STATE),
)
self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
except CannotHandleAssertion as exc:
LOGGER.info(str(exc))
return bad_request_message(self.request, str(exc))
return None
class SAMLSSOBindingInitView(SAMLSSOView):
"""SAML Handler for for IdP Initiated login flows"""
def check_saml_request(self) -> Optional[HttpRequest]:
"""Create SAML Response from scratch"""
LOGGER.debug(
"handle_saml_no_request: No SAML Request, using IdP-initiated flow."
)
auth_n_request = AuthNRequestParser(self.provider).idp_initiated()
self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
# This View doesn't have a URL on purpose, as its called by the FlowExecutor
class SAMLFlowFinalView(StageView):
"""View used by FlowExecutor after all stages have passed. Logs the authorization,
and redirects to the SP (if REDIRECT is configured) or shows and auto-submit for
(if POST is configured)."""
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
application: Application = self.executor.plan.context[PLAN_CONTEXT_APPLICATION]
provider: SAMLProvider = get_object_or_404(
SAMLProvider, pk=application.provider_id
)
# Log Application Authorization
Event.new(
EventAction.AUTHORIZE_APPLICATION,
authorized_application=application,
flow=self.executor.plan.flow_pk,
).from_http(self.request)
if SESSION_KEY_AUTH_N_REQUEST not in self.request.session:
return self.executor.stage_invalid()
auth_n_request: AuthNRequest = self.request.session.pop(
SESSION_KEY_AUTH_N_REQUEST
)
response = AssertionProcessor(
provider, request, auth_n_request
).build_response()
if provider.sp_binding == SAMLBindings.POST:
form_attrs = {
"ACSUrl": provider.acs_url,
REQUEST_KEY_SAML_RESPONSE: nice64(response),
}
if auth_n_request.relay_state:
form_attrs[REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
return render(
self.request,
"generic/autosubmit_form.html",
{
"url": provider.acs_url,
"title": _("Redirecting to %(app)s..." % {"app": application.name}),
"attrs": form_attrs,
},
)
if provider.sp_binding == SAMLBindings.REDIRECT:
url_args = {
REQUEST_KEY_SAML_RESPONSE: deflate_and_base64_encode(response),
}
if auth_n_request.relay_state:
url_args[REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
querystring = urlencode(url_args)
return redirect(f"{provider.acs_url}?{querystring}")
return bad_request_message(request, "Invalid sp_binding specified")
class DescriptorDownloadView(View):
"""Replies with the XML Metadata IDSSODescriptor."""
@staticmethod
def get_metadata(request: HttpRequest, provider: SAMLProvider) -> str:
"""Return rendered XML Metadata"""
return MetadataProcessor(provider, request).build_entity_descriptor()
def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""Replies with the XML Metadata IDSSODescriptor."""
application = get_object_or_404(Application, slug=application_slug)
provider: SAMLProvider = get_object_or_404(
SAMLProvider, pk=application.provider_id
)
try:
metadata = DescriptorDownloadView.get_metadata(request, provider)
except Provider.application.RelatedObjectDoesNotExist: # pylint: disable=no-member
return bad_request_message(
request, "Provider is not assigned to an application."
)
else:
response = HttpResponse(metadata, content_type="application/xml")
response[
"Content-Disposition"
] = f'attachment; filename="{provider.name}_authentik_meta.xml"'
return response
class MetadataImportView(LoginRequiredMixin, FormView):
"""Import Metadata from XML, and create provider"""
form_class = SAMLProviderImportForm
template_name = "providers/saml/import.html"
success_url = reverse_lazy("authentik_admin:providers")
def dispatch(self, request, *args, **kwargs):
if not request.user.is_superuser:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form: SAMLProviderImportForm) -> HttpResponse:
try:
metadata = ServiceProviderMetadataParser().parse(
form.cleaned_data["metadata"].read().decode()
)
metadata.to_provider(
form.cleaned_data["provider_name"],
form.cleaned_data["authorization_flow"],
)
messages.success(self.request, _("Successfully created Provider"))
except ValueError as exc:
LOGGER.warning(str(exc))
messages.error(
self.request,
_("Failed to import Metadata: %(message)s" % {"message": str(exc)}),
)
return super().form_invalid(form)
return super().form_valid(form)

View File

@ -0,0 +1,82 @@
"""authentik SAML IDP Views"""
from django.core.validators import URLValidator
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.http import urlencode
from django.utils.translation import gettext_lazy as _
from structlog.stdlib import get_logger
from authentik.core.models import Application
from authentik.events.models import Event, EventAction
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION
from authentik.flows.stage import StageView
from authentik.lib.views import bad_request_message
from authentik.providers.saml.models import SAMLBindings, SAMLProvider
from authentik.providers.saml.processors.assertion import AssertionProcessor
from authentik.providers.saml.processors.request_parser import AuthNRequest
from authentik.providers.saml.utils.encoding import deflate_and_base64_encode, nice64
LOGGER = get_logger()
URL_VALIDATOR = URLValidator(schemes=("http", "https"))
REQUEST_KEY_SAML_REQUEST = "SAMLRequest"
REQUEST_KEY_SAML_SIGNATURE = "Signature"
REQUEST_KEY_SAML_SIG_ALG = "SigAlg"
REQUEST_KEY_SAML_RESPONSE = "SAMLResponse"
REQUEST_KEY_RELAY_STATE = "RelayState"
SESSION_KEY_AUTH_N_REQUEST = "authn_request"
# This View doesn't have a URL on purpose, as its called by the FlowExecutor
class SAMLFlowFinalView(StageView):
"""View used by FlowExecutor after all stages have passed. Logs the authorization,
and redirects to the SP (if REDIRECT is configured) or shows and auto-submit for
(if POST is configured)."""
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
application: Application = self.executor.plan.context[PLAN_CONTEXT_APPLICATION]
provider: SAMLProvider = get_object_or_404(
SAMLProvider, pk=application.provider_id
)
# Log Application Authorization
Event.new(
EventAction.AUTHORIZE_APPLICATION,
authorized_application=application,
flow=self.executor.plan.flow_pk,
).from_http(self.request)
if SESSION_KEY_AUTH_N_REQUEST not in self.request.session:
return self.executor.stage_invalid()
auth_n_request: AuthNRequest = self.request.session.pop(
SESSION_KEY_AUTH_N_REQUEST
)
response = AssertionProcessor(
provider, request, auth_n_request
).build_response()
if provider.sp_binding == SAMLBindings.POST:
form_attrs = {
"ACSUrl": provider.acs_url,
REQUEST_KEY_SAML_RESPONSE: nice64(response),
}
if auth_n_request.relay_state:
form_attrs[REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
return render(
self.request,
"generic/autosubmit_form.html",
{
"url": provider.acs_url,
"title": _("Redirecting to %(app)s..." % {"app": application.name}),
"attrs": form_attrs,
},
)
if provider.sp_binding == SAMLBindings.REDIRECT:
url_args = {
REQUEST_KEY_SAML_RESPONSE: deflate_and_base64_encode(response),
}
if auth_n_request.relay_state:
url_args[REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
querystring = urlencode(url_args)
return redirect(f"{provider.acs_url}?{querystring}")
return bad_request_message(request, "Invalid sp_binding specified")

View File

@ -0,0 +1,82 @@
"""authentik SAML IDP Views"""
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404
from django.urls.base import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.generic.edit import FormView
from structlog.stdlib import get_logger
from authentik.core.models import Application, Provider
from authentik.lib.views import bad_request_message
from authentik.providers.saml.forms import SAMLProviderImportForm
from authentik.providers.saml.models import SAMLProvider
from authentik.providers.saml.processors.metadata import MetadataProcessor
from authentik.providers.saml.processors.metadata_parser import (
ServiceProviderMetadataParser,
)
LOGGER = get_logger()
class DescriptorDownloadView(View):
"""Replies with the XML Metadata IDSSODescriptor."""
@staticmethod
def get_metadata(request: HttpRequest, provider: SAMLProvider) -> str:
"""Return rendered XML Metadata"""
return MetadataProcessor(provider, request).build_entity_descriptor()
def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""Replies with the XML Metadata IDSSODescriptor."""
application = get_object_or_404(Application, slug=application_slug)
provider: SAMLProvider = get_object_or_404(
SAMLProvider, pk=application.provider_id
)
try:
metadata = DescriptorDownloadView.get_metadata(request, provider)
except Provider.application.RelatedObjectDoesNotExist: # pylint: disable=no-member
return bad_request_message(
request, "Provider is not assigned to an application."
)
else:
response = HttpResponse(metadata, content_type="application/xml")
response[
"Content-Disposition"
] = f'attachment; filename="{provider.name}_authentik_meta.xml"'
return response
class MetadataImportView(LoginRequiredMixin, FormView):
"""Import Metadata from XML, and create provider"""
form_class = SAMLProviderImportForm
template_name = "providers/saml/import.html"
success_url = reverse_lazy("authentik_admin:providers")
def dispatch(self, request, *args, **kwargs):
if not request.user.is_superuser:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form: SAMLProviderImportForm) -> HttpResponse:
try:
metadata = ServiceProviderMetadataParser().parse(
form.cleaned_data["metadata"].read().decode()
)
metadata.to_provider(
form.cleaned_data["provider_name"],
form.cleaned_data["authorization_flow"],
)
messages.success(self.request, _("Successfully created Provider"))
except ValueError as exc:
LOGGER.warning(str(exc))
messages.error(
self.request,
_("Failed to import Metadata: %(message)s" % {"message": str(exc)}),
)
return super().form_invalid(form)
return super().form_valid(form)

View File

@ -0,0 +1,150 @@
"""authentik SAML IDP Views"""
from typing import Optional
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from structlog.stdlib import get_logger
from authentik.core.models import Application
from authentik.events.models import Event, EventAction
from authentik.flows.models import in_memory_stage
from authentik.flows.planner import (
PLAN_CONTEXT_APPLICATION,
PLAN_CONTEXT_SSO,
FlowPlanner,
)
from authentik.flows.views import SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs
from authentik.lib.views import bad_request_message
from authentik.policies.views import PolicyAccessView
from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLProvider
from authentik.providers.saml.processors.request_parser import AuthNRequestParser
from authentik.providers.saml.views.flows import (
REQUEST_KEY_RELAY_STATE,
REQUEST_KEY_SAML_REQUEST,
REQUEST_KEY_SAML_SIG_ALG,
REQUEST_KEY_SAML_SIGNATURE,
SESSION_KEY_AUTH_N_REQUEST,
SAMLFlowFinalView,
)
from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_TEMPLATE
LOGGER = get_logger()
class SAMLSSOView(PolicyAccessView):
""" "SAML SSO Base View, which plans a flow and injects our final stage.
Calls get/post handler."""
def resolve_provider_application(self):
self.application = get_object_or_404(
Application, slug=self.kwargs["application_slug"]
)
self.provider: SAMLProvider = get_object_or_404(
SAMLProvider, pk=self.application.provider_id
)
def check_saml_request(self) -> Optional[HttpRequest]:
"""Handler to verify the SAML Request. Must be implemented by a subclass"""
raise NotImplementedError
# pylint: disable=unused-argument
def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""Verify the SAML Request, and if valid initiate the FlowPlanner for the application"""
# Call the method handler, which checks the SAML
# Request and returns a HTTP Response on error
method_response = self.check_saml_request()
if method_response:
return method_response
# Regardless, we start the planner and return to it
planner = FlowPlanner(self.provider.authorization_flow)
planner.allow_empty_flows = True
plan = planner.plan(
request,
{
PLAN_CONTEXT_SSO: True,
PLAN_CONTEXT_APPLICATION: self.application,
PLAN_CONTEXT_CONSENT_TEMPLATE: "providers/saml/consent.html",
},
)
plan.append(in_memory_stage(SAMLFlowFinalView))
request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_flows:flow-executor-shell",
request.GET,
flow_slug=self.provider.authorization_flow.slug,
)
def post(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""GET and POST use the same handler, but we can't
override .dispatch easily because PolicyAccessView's dispatch"""
return self.get(request, application_slug)
class SAMLSSOBindingRedirectView(SAMLSSOView):
"""SAML Handler for SSO/Redirect bindings, which are sent via GET"""
def check_saml_request(self) -> Optional[HttpRequest]:
"""Handle REDIRECT bindings"""
if REQUEST_KEY_SAML_REQUEST not in self.request.GET:
LOGGER.info("handle_saml_request: SAML payload missing")
return bad_request_message(
self.request, "The SAML request payload is missing."
)
try:
auth_n_request = AuthNRequestParser(self.provider).parse_detached(
self.request.GET[REQUEST_KEY_SAML_REQUEST],
self.request.GET.get(REQUEST_KEY_RELAY_STATE),
self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE),
self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG),
)
self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
except CannotHandleAssertion as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
provider=self.provider,
message=str(exc),
).save()
LOGGER.info(str(exc))
return bad_request_message(self.request, str(exc))
return None
@method_decorator(csrf_exempt, name="dispatch")
class SAMLSSOBindingPOSTView(SAMLSSOView):
"""SAML Handler for SSO/POST bindings"""
def check_saml_request(self) -> Optional[HttpRequest]:
"""Handle POST bindings"""
if REQUEST_KEY_SAML_REQUEST not in self.request.POST:
LOGGER.info("check_saml_request: SAML payload missing")
return bad_request_message(
self.request, "The SAML request payload is missing."
)
try:
auth_n_request = AuthNRequestParser(self.provider).parse(
self.request.POST[REQUEST_KEY_SAML_REQUEST],
self.request.POST.get(REQUEST_KEY_RELAY_STATE),
)
self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
except CannotHandleAssertion as exc:
LOGGER.info(str(exc))
return bad_request_message(self.request, str(exc))
return None
class SAMLSSOBindingInitView(SAMLSSOView):
"""SAML Handler for for IdP Initiated login flows"""
def check_saml_request(self) -> Optional[HttpRequest]:
"""Create SAML Response from scratch"""
LOGGER.debug(
"handle_saml_no_request: No SAML Request, using IdP-initiated flow."
)
auth_n_request = AuthNRequestParser(self.provider).idp_initiated()
self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request

View File

@ -18,6 +18,8 @@ from django.core.asgi import get_asgi_application
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.middleware import RESPONSE_HEADER_ID
# DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py # DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py
defuse_stdlib() defuse_stdlib()
@ -67,6 +69,7 @@ class ASGILogger:
status_code: int status_code: int
start: float start: float
content_length: int content_length: int
request_id: str
def __init__(self, app: ASGIApp): def __init__(self, app: ASGIApp):
self.app = app self.app = app
@ -75,30 +78,30 @@ class ASGILogger:
self.scope = scope self.scope = scope
self.content_length = 0 self.content_length = 0
self.headers = dict(scope.get("headers", [])) self.headers = dict(scope.get("headers", []))
self.request_id = ""
async def send_hooked(message: Message) -> None: async def send_hooked(message: Message) -> None:
"""Hooked send method, which records status code and content-length, and for the final """Hooked send method, which records status code and content-length, and for the final
requests logs it""" requests logs it"""
headers = dict(message.get("headers", [])) headers = dict(message.get("headers", []))
if "status" in message: if "status" in message:
self.status_code = message["status"] self.status_code = message["status"]
if b"Content-Length" in headers: if b"Content-Length" in headers:
self.content_length += int(headers.get(b"Content-Length", b"0")) self.content_length += int(headers.get(b"Content-Length", b"0"))
if message["type"] == "http.response.body" and not message.get( if message["type"] == "http.response.start":
"more_body", None response_headers = dict(message["headers"])
): self.request_id = response_headers.get(
runtime = int((time() - self.start) * 10 ** 6) RESPONSE_HEADER_ID.encode(), b""
self.log(runtime) ).decode()
await send(message)
if self.headers.get(b"host", b"") == b"authentik-healthcheck-host": if message["type"] == "http.response.body" and not message.get(
# Don't log healthcheck/readiness requests "more_body", True
await send({"type": "http.response.start", "status": 204, "headers": []}) ):
await send({"type": "http.response.body", "body": ""}) runtime = int((time() - self.start) * 1000)
return self.log(runtime, request_id=self.request_id)
await send(message)
self.start = time() self.start = time()
if scope["type"] == "lifespan": if scope["type"] == "lifespan":
@ -117,7 +120,7 @@ class ASGILogger:
# Check if header has multiple values, and use the first one # Check if header has multiple values, and use the first one
return client_ip.split(", ")[0] return client_ip.split(", ")[0]
def log(self, runtime: float): def log(self, runtime: float, **kwargs):
"""Outpot access logs in a structured format""" """Outpot access logs in a structured format"""
host = self._get_ip() host = self._get_ip()
query_string = "" query_string = ""
@ -129,8 +132,9 @@ class ASGILogger:
method=self.scope.get("method", ""), method=self.scope.get("method", ""),
scheme=self.scope.get("scheme", ""), scheme=self.scope.get("scheme", ""),
status=self.status_code, status=self.status_code,
size=self.content_length / 1000 if self.content_length > 0 else "-", size=self.content_length / 1000 if self.content_length > 0 else 0,
runtime=runtime, runtime=runtime,
**kwargs,
) )

View File

@ -2,6 +2,8 @@
from base64 import b64encode from base64 import b64encode
from django.conf import settings from django.conf import settings
from django.db import connections
from django.db.utils import OperationalError
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.views import View from django.views import View
from django_prometheus.exports import ExportToDjangoView from django_prometheus.exports import ExportToDjangoView
@ -23,3 +25,22 @@ class MetricsView(View):
return response return response
return ExportToDjangoView(request) return ExportToDjangoView(request)
class LiveView(View):
"""View for liveness probe, always returns Http 201"""
def dispatch(self, request: HttpRequest) -> HttpResponse:
return HttpResponse(status=201)
class ReadyView(View):
"""View for liveness probe, always returns Http 201"""
def dispatch(self, request: HttpRequest) -> HttpResponse:
db_conn = connections["default"]
try:
_ = db_conn.cursor()
except OperationalError:
return HttpResponse(status=503)
return HttpResponse(status=201)

View File

@ -9,7 +9,7 @@ from structlog.stdlib import get_logger
from authentik.core.views import error from authentik.core.views import error
from authentik.lib.utils.reflection import get_apps from authentik.lib.utils.reflection import get_apps
from authentik.root.monitoring import MetricsView from authentik.root.monitoring import LiveView, MetricsView, ReadyView
LOGGER = get_logger() LOGGER = get_logger()
admin.autodiscover() admin.autodiscover()
@ -57,6 +57,8 @@ for _authentik_app in get_apps():
urlpatterns += [ urlpatterns += [
path("administration/django/", admin.site.urls), path("administration/django/", admin.site.urls),
path("metrics/", MetricsView.as_view(), name="metrics"), path("metrics/", MetricsView.as_view(), name="metrics"),
path("-/health/live/", LiveView.as_view(), name="health-live"),
path("-/health/ready/", ReadyView.as_view(), name="health-ready"),
path("-/jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"), path("-/jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
] ]

View File

@ -1,18 +1,27 @@
"""Source API Views""" """Source API Views"""
from rest_framework.serializers import ModelSerializer from datetime import datetime
from django.core.cache import cache
from django.db.models.base import Model
from drf_yasg2.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.fields import DateTimeField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, Serializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.admin.forms.source import SOURCE_SERIALIZER_FIELDS from authentik.core.api.sources import SourceSerializer
from authentik.core.api.utils import MetaNameSerializer from authentik.core.api.utils import MetaNameSerializer
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
class LDAPSourceSerializer(ModelSerializer, MetaNameSerializer): class LDAPSourceSerializer(SourceSerializer):
"""LDAP Source Serializer""" """LDAP Source Serializer"""
class Meta: class Meta:
model = LDAPSource model = LDAPSource
fields = SOURCE_SERIALIZER_FIELDS + [ fields = SourceSerializer.Meta.fields + [
"server_uri", "server_uri",
"bind_cn", "bind_cn",
"bind_password", "bind_password",
@ -34,6 +43,39 @@ class LDAPSourceSerializer(ModelSerializer, MetaNameSerializer):
extra_kwargs = {"bind_password": {"write_only": True}} extra_kwargs = {"bind_password": {"write_only": True}}
class LDAPSourceSyncStatusSerializer(Serializer):
"""LDAP Sync status"""
last_sync = DateTimeField(read_only=True)
def create(self, validated_data: dict) -> Model:
raise NotImplementedError
def update(self, instance: Model, validated_data: dict) -> Model:
raise NotImplementedError
class LDAPSourceViewSet(ModelViewSet):
"""LDAP Source Viewset"""
queryset = LDAPSource.objects.all()
serializer_class = LDAPSourceSerializer
lookup_field = "slug"
@swagger_auto_schema(responses={200: LDAPSourceSyncStatusSerializer(many=False)})
@action(methods=["GET"], detail=True)
# pylint: disable=unused-argument
def sync_status(self, request: Request, slug: str) -> Response:
"""Get source's sync status"""
source = self.get_object()
last_sync = cache.get(source.state_cache_prefix("last_sync"), None)
return Response(
LDAPSourceSyncStatusSerializer(
{"last_sync": datetime.fromtimestamp(last_sync)}
).data
)
class LDAPPropertyMappingSerializer(ModelSerializer, MetaNameSerializer): class LDAPPropertyMappingSerializer(ModelSerializer, MetaNameSerializer):
"""LDAP PropertyMapping Serializer""" """LDAP PropertyMapping Serializer"""
@ -49,13 +91,6 @@ class LDAPPropertyMappingSerializer(ModelSerializer, MetaNameSerializer):
] ]
class LDAPSourceViewSet(ModelViewSet):
"""LDAP Source Viewset"""
queryset = LDAPSource.objects.all()
serializer_class = LDAPSourceSerializer
class LDAPPropertyMappingViewSet(ModelViewSet): class LDAPPropertyMappingViewSet(ModelViewSet):
"""LDAP PropertyMapping Viewset""" """LDAP PropertyMapping Viewset"""

View File

@ -1,8 +1,6 @@
"""authentik LDAP Models""" """authentik LDAP Models"""
from datetime import datetime
from typing import Optional, Type from typing import Optional, Type
from django.core.cache import cache
from django.db import models from django.db import models
from django.forms import ModelForm from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -11,7 +9,6 @@ from rest_framework.serializers import Serializer
from authentik.core.models import Group, PropertyMapping, Source from authentik.core.models import Group, PropertyMapping, Source
from authentik.lib.models import DomainlessURLValidator from authentik.lib.models import DomainlessURLValidator
from authentik.lib.utils.template import render_to_string
class LDAPSource(Source): class LDAPSource(Source):
@ -91,16 +88,6 @@ class LDAPSource(Source):
"""Key by which the ldap source status is saved""" """Key by which the ldap source status is saved"""
return f"source_ldap_{self.pk}_state_{suffix}" return f"source_ldap_{self.pk}_state_{suffix}"
@property
def ui_additional_info(self) -> str:
last_sync = cache.get(self.state_cache_prefix("last_sync"), None)
if last_sync:
last_sync = datetime.fromtimestamp(last_sync)
return render_to_string(
"ldap/source_list_status.html", {"source": self, "last_sync": last_sync}
)
_connection: Optional[Connection] = None _connection: Optional[Connection] = None
@property @property

View File

@ -4,6 +4,7 @@ from time import time
from django.core.cache import cache from django.core.cache import cache
from django.utils.text import slugify from django.utils.text import slugify
from ldap3.core.exceptions import LDAPException from ldap3.core.exceptions import LDAPException
from structlog.stdlib import get_logger
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.root.celery import CELERY_APP from authentik.root.celery import CELERY_APP
@ -12,6 +13,8 @@ from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
LOGGER = get_logger()
@CELERY_APP.task() @CELERY_APP.task()
def ldap_sync_all(): def ldap_sync_all():
@ -21,7 +24,7 @@ def ldap_sync_all():
@CELERY_APP.task(bind=True, base=MonitoredTask) @CELERY_APP.task(bind=True, base=MonitoredTask)
def ldap_sync(self: MonitoredTask, source_pk: int): def ldap_sync(self: MonitoredTask, source_pk: str):
"""Synchronization of an LDAP Source""" """Synchronization of an LDAP Source"""
try: try:
source: LDAPSource = LDAPSource.objects.get(pk=source_pk) source: LDAPSource = LDAPSource.objects.get(pk=source_pk)
@ -49,4 +52,5 @@ def ldap_sync(self: MonitoredTask, source_pk: int):
) )
) )
except LDAPException as exc: except LDAPException as exc:
LOGGER.debug(exc)
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc)) self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))

View File

@ -1,8 +0,0 @@
{% load humanize %}
{% load i18n %}
{% if last_sync %}
<i class="fas fa-check pf-m-success"></i> {% blocktrans with last_sync=last_sync|naturaltime %}Synced {{ last_sync }}.{% endblocktrans %}
{% else %}
<i class="fas fa-times pf-m-danger"></i> Not synced yet/Sync in Progress
{% endif %}

View File

@ -1,18 +1,30 @@
"""OAuth Source Serializer""" """OAuth Source Serializer"""
from rest_framework.serializers import ModelSerializer from django.urls.base import reverse_lazy
from rest_framework.fields import SerializerMethodField
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.admin.forms.source import SOURCE_SERIALIZER_FIELDS from authentik.core.api.sources import SourceSerializer
from authentik.core.api.utils import MetaNameSerializer
from authentik.sources.oauth.models import OAuthSource from authentik.sources.oauth.models import OAuthSource
class OAuthSourceSerializer(ModelSerializer, MetaNameSerializer): class OAuthSourceSerializer(SourceSerializer):
"""OAuth Source Serializer""" """OAuth Source Serializer"""
callback_url = SerializerMethodField()
def get_callback_url(self, instance: OAuthSource) -> str:
"""Get OAuth Callback URL"""
relative_url = reverse_lazy(
"authentik_sources_oauth:oauth-client-callback",
kwargs={"source_slug": instance.slug},
)
if "request" not in self.context:
return relative_url
return self.context["request"].build_absolute_uri(relative_url)
class Meta: class Meta:
model = OAuthSource model = OAuthSource
fields = SOURCE_SERIALIZER_FIELDS + [ fields = SourceSerializer.Meta.fields + [
"provider_type", "provider_type",
"request_token_url", "request_token_url",
"authorization_url", "authorization_url",
@ -20,7 +32,9 @@ class OAuthSourceSerializer(ModelSerializer, MetaNameSerializer):
"profile_url", "profile_url",
"consumer_key", "consumer_key",
"consumer_secret", "consumer_secret",
"callback_url",
] ]
extra_kwargs = {"consumer_secret": {"write_only": True}}
class OAuthSourceViewSet(ModelViewSet): class OAuthSourceViewSet(ModelViewSet):
@ -28,3 +42,4 @@ class OAuthSourceViewSet(ModelViewSet):
queryset = OAuthSource.objects.all() queryset = OAuthSource.objects.all()
serializer_class = OAuthSourceSerializer serializer_class = OAuthSourceSerializer
lookup_field = "slug"

View File

@ -2,7 +2,6 @@
from django import forms from django import forms
from authentik.admin.forms.source import SOURCE_FORM_FIELDS
from authentik.flows.models import Flow, FlowDesignation from authentik.flows.models import Flow, FlowDesignation
from authentik.sources.oauth.models import OAuthSource from authentik.sources.oauth.models import OAuthSource
from authentik.sources.oauth.types.manager import MANAGER from authentik.sources.oauth.types.manager import MANAGER
@ -27,7 +26,12 @@ class OAuthSourceForm(forms.ModelForm):
class Meta: class Meta:
model = OAuthSource model = OAuthSource
fields = SOURCE_FORM_FIELDS + [ fields = [
"name",
"slug",
"enabled",
"authentication_flow",
"enrollment_flow",
"provider_type", "provider_type",
"request_token_url", "request_token_url",
"authorization_url", "authorization_url",

View File

@ -64,14 +64,6 @@ class OAuthSource(Source):
name=self.name, name=self.name,
) )
@property
def ui_additional_info(self) -> str:
url = reverse_lazy(
"authentik_sources_oauth:oauth-client-callback",
kwargs={"source_slug": self.slug},
)
return f"Callback URL: <pre>{url}</pre>"
@property @property
def ui_user_settings(self) -> Optional[str]: def ui_user_settings(self) -> Optional[str]:
view_name = "authentik_sources_oauth:oauth-client-user" view_name = "authentik_sources_oauth:oauth-client-user"

View File

@ -9,13 +9,13 @@
<div class="pf-c-card__body"> <div class="pf-c-card__body">
{% if connections.exists %} {% if connections.exists %}
<p>{% trans 'Connected.' %}</p> <p>{% trans 'Connected.' %}</p>
<a class="pf-c-button pf-m-danger" <a class="pf-c-button pf-m-danger ak-root-link"
href="{% url 'authentik_sources_oauth:oauth-client-disconnect' source_slug=source.slug %}"> href="{% url 'authentik_sources_oauth:oauth-client-disconnect' source_slug=source.slug %}">
{% trans 'Disconnect' %} {% trans 'Disconnect' %}
</a> </a>
{% else %} {% else %}
<p>Not connected.</p> <p>Not connected.</p>
<a class="pf-c-button pf-m-primary" <a class="pf-c-button pf-m-primary ak-root-link"
href="{% url 'authentik_sources_oauth:oauth-client-login' source_slug=source.slug %}"> href="{% url 'authentik_sources_oauth:oauth-client-login' source_slug=source.slug %}">
{% trans 'Connect' %} {% trans 'Connect' %}
</a> </a>

View File

@ -1,19 +1,17 @@
"""SAMLSource API Views""" """SAMLSource API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.admin.forms.source import SOURCE_FORM_FIELDS from authentik.core.api.sources import SourceSerializer
from authentik.core.api.utils import MetaNameSerializer
from authentik.sources.saml.models import SAMLSource from authentik.sources.saml.models import SAMLSource
class SAMLSourceSerializer(ModelSerializer, MetaNameSerializer): class SAMLSourceSerializer(SourceSerializer):
"""SAMLSource Serializer""" """SAMLSource Serializer"""
class Meta: class Meta:
model = SAMLSource model = SAMLSource
fields = SOURCE_FORM_FIELDS + [ fields = SourceSerializer.Meta.fields + [
"issuer", "issuer",
"sso_url", "sso_url",
"slo_url", "slo_url",
@ -32,3 +30,4 @@ class SAMLSourceViewSet(ModelViewSet):
queryset = SAMLSource.objects.all() queryset = SAMLSource.objects.all()
serializer_class = SAMLSourceSerializer serializer_class = SAMLSourceSerializer
lookup_field = "slug"

View File

@ -2,7 +2,6 @@
from django import forms from django import forms
from authentik.admin.forms.source import SOURCE_FORM_FIELDS
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow, FlowDesignation from authentik.flows.models import Flow, FlowDesignation
from authentik.sources.saml.models import SAMLSource from authentik.sources.saml.models import SAMLSource
@ -28,7 +27,12 @@ class SAMLSourceForm(forms.ModelForm):
class Meta: class Meta:
model = SAMLSource model = SAMLSource
fields = SOURCE_FORM_FIELDS + [ fields = [
"name",
"slug",
"enabled",
"authentication_flow",
"enrollment_flow",
"issuer", "issuer",
"sso_url", "sso_url",
"slo_url", "slo_url",

View File

@ -19,7 +19,7 @@ services:
networks: networks:
- internal - internal
server: server:
image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.1-rc1} image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.5-stable}
command: server command: server
environment: environment:
AUTHENTIK_REDIS__HOST: redis AUTHENTIK_REDIS__HOST: redis
@ -40,12 +40,12 @@ services:
traefik.http.routers.app-router.rule: PathPrefix(`/`) traefik.http.routers.app-router.rule: PathPrefix(`/`)
traefik.http.routers.app-router.service: app-service traefik.http.routers.app-router.service: app-service
traefik.http.routers.app-router.tls: 'true' traefik.http.routers.app-router.tls: 'true'
traefik.http.services.app-service.loadbalancer.healthcheck.hostname: authentik-healthcheck-host traefik.http.services.app-service.loadbalancer.healthcheck.path: /-/health/live/
traefik.http.services.app-service.loadbalancer.server.port: '8000' traefik.http.services.app-service.loadbalancer.server.port: '8000'
env_file: env_file:
- .env - .env
worker: worker:
image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.1-rc1} image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.5-stable}
command: worker command: worker
networks: networks:
- internal - internal
@ -62,7 +62,7 @@ services:
env_file: env_file:
- .env - .env
static: static:
image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.1-rc1} image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.5-stable}
networks: networks:
- internal - internal
labels: labels:

View File

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

View File

@ -4,7 +4,7 @@
|-----------------------------------|-------------------------|-------------| |-----------------------------------|-------------------------|-------------|
| 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.2.1-rc1 | Image tag | | image.tag | 2021.2.5-stable | 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 |

View File

@ -97,18 +97,14 @@ spec:
protocol: TCP protocol: TCP
livenessProbe: livenessProbe:
httpGet: httpGet:
path: / path: /-/health/live/
port: http port: http
httpHeaders: initialDelaySeconds: 15
- name: Host
value: authentik-healthcheck-host
readinessProbe: readinessProbe:
httpGet: httpGet:
path: / path: /-/health/ready/
port: http port: http
httpHeaders: initialDelaySeconds: 15
- name: Host
value: authentik-healthcheck-host
resources: resources:
requests: requests:
cpu: 100m cpu: 100m

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.2.1-rc1 tag: 2021.2.5-stable
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
serverReplicas: 1 serverReplicas: 1

View File

@ -3,40 +3,39 @@ module goauthentik.io/outpost
go 1.14 go 1.14
require ( require (
cloud.google.com/go v0.64.0 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc v2.2.1+incompatible
github.com/getsentry/sentry-go v0.9.0 github.com/getsentry/sentry-go v0.9.0
github.com/go-openapi/errors v0.19.9 github.com/go-openapi/errors v0.20.0
github.com/go-openapi/runtime v0.19.24 github.com/go-openapi/runtime v0.19.26
github.com/go-openapi/strfmt v0.19.12 github.com/go-openapi/strfmt v0.20.0
github.com/go-openapi/swag v0.19.12 github.com/go-openapi/swag v0.19.14
github.com/go-openapi/validate v0.20.1 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.25.0 // indirect github.com/go-swagger/go-swagger v0.26.1 // indirect
github.com/gorilla/handlers v1.5.1 // indirect github.com/golang/protobuf v1.4.3 // 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
github.com/kr/pretty v0.2.1 // indirect github.com/kr/pretty v0.2.1 // indirect
github.com/magiconair/properties v1.8.4 // indirect github.com/magiconair/properties v1.8.4 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect
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.8.1 // indirect github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f // indirect github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect
github.com/recws-org/recws v1.2.2 github.com/recws-org/recws v1.2.1
github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus v1.7.0
github.com/spf13/afero v1.5.1 // indirect github.com/spf13/afero v1.5.1 // indirect
github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cast v1.3.1 // indirect
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/crypto v0.0.0-20200728195943-123391ffb6de // indirect golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 // indirect
golang.org/x/mod v0.4.1 // indirect golang.org/x/mod v0.4.1 // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 // indirect
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
golang.org/x/text v0.3.5 // indirect golang.org/x/tools v0.1.0 // indirect
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 // indirect google.golang.org/appengine v1.6.7 // 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

@ -13,8 +13,8 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.64.0 h1:xVP3LPvMjGT4J0a55y02Gw5y/dkY/rxGz58sfK1jqIo= cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@ -35,11 +35,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BeryJu/authentik v0.0.0-20210108085217-fd6d99f4f999 h1:ymxzvnxKNUomJIRG1VP3I6ls5mWn8r1xWD82bHASEk0=
github.com/BeryJu/authentik v0.0.0-20210116180903-8acb9dde5f2f h1:pLOJgn8bIzavtn0h874lys3gs7uk1RnMqMWIttOWY8Y=
github.com/BeryJu/authentik/proxy v0.0.0-20210108085217-fd6d99f4f999 h1:XYHeaZx7fm4JNx77MHMO6ek/Gdp+sZa2jIJyjC294Gw=
github.com/BeryJu/authentik/proxy v0.0.0-20210116180903-8acb9dde5f2f h1:bp617AbteaVcZBXMtr4/A+FSSVGKqRWlTo5chcirq8k=
github.com/BeryJu/authentik/proxy v0.0.0-20210116180903-8acb9dde5f2f/go.mod h1:6/VeRMuLHUE3Ywr1uIpjxnmOJJsAfld7OOOi+uocxQw=
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw=
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
@ -167,6 +162,8 @@ github.com/go-openapi/analysis v0.19.10 h1:5BHISBAXOc/aJK25irLZnx2D3s6WyYaY9D4gm
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 h1:Ub9e++M8sDwtHD+S587TYi+6ANBG1NRYGZDihqk0SaY= github.com/go-openapi/analysis v0.19.16 h1:Ub9e++M8sDwtHD+S587TYi+6ANBG1NRYGZDihqk0SaY=
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/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
@ -179,6 +176,8 @@ github.com/go-openapi/errors v0.19.8 h1:doM+tQdZbUm9gydV9yR+iQNmztbjj7I3sW4sIcAw
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.19.9 h1:9SnKdGhiPZHF3ttwFMiCBEb8jQ4IDdrK+5+a0oTygA4= github.com/go-openapi/errors v0.19.9 h1:9SnKdGhiPZHF3ttwFMiCBEb8jQ4IDdrK+5+a0oTygA4=
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/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= 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/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=
@ -193,7 +192,6 @@ github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3Hfo
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
@ -209,14 +207,18 @@ github.com/go-openapi/loads v0.19.7 h1:6cALLpCAq4tYhaic7TMbEzjv8vq/wg+0AFivNy/Bm
github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
github.com/go-openapi/loads v0.20.0 h1:Pymw1O8zDmWeNv4kVsHd0W3cvgdp8juRa4U/U/8D/Pk= github.com/go-openapi/loads v0.20.0 h1:Pymw1O8zDmWeNv4kVsHd0W3cvgdp8juRa4U/U/8D/Pk=
github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4=
github.com/go-openapi/loads v0.20.1/go.mod h1:/6LfFL8fDvTSX8ypmYXIq3U9Q7nfniSOStW22m864WM=
github.com/go-openapi/loads v0.20.2 h1:z5p5Xf5wujMxS1y8aP+vxwW5qYT2zdJBbXKmQUG3lcc=
github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o=
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
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.20/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
github.com/go-openapi/runtime v0.19.24 h1:TqagMVlRAOTwllE/7hNKx6rQ10O6T8ZzeJdMjSTKaD4= github.com/go-openapi/runtime v0.19.24 h1:TqagMVlRAOTwllE/7hNKx6rQ10O6T8ZzeJdMjSTKaD4=
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.26 h1:K/6PoVNj5WJXUnMk+VEbELeXjtBkCS1UxTDa04tdXE0=
github.com/go-openapi/runtime v0.19.26/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=
@ -228,6 +230,10 @@ github.com/go-openapi/spec v0.19.15 h1:uxh8miNJEfMm8l8ekpY7i39LcORm1xSRtoipEGl1J
github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
github.com/go-openapi/spec v0.20.0 h1:HGLc8AJ7ynOxwv0Lq4TsnwLsWMawHAYiJIFzbcML86I= github.com/go-openapi/spec v0.20.0 h1:HGLc8AJ7ynOxwv0Lq4TsnwLsWMawHAYiJIFzbcML86I=
github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ=
github.com/go-openapi/spec v0.20.2/go.mod h1:RW6Xcbs6LOyWLU/mXGdzn2Qc+3aj+ASfI7rvSZh1Vls=
github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
@ -238,8 +244,8 @@ github.com/go-openapi/strfmt v0.19.5 h1:0utjKrw+BAh8s57XE9Xz8DUBsVvPmRUB6styvl9w
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
github.com/go-openapi/strfmt v0.19.11 h1:0+YvbNh05rmBkgztd6zHp4OCFn7Mtu30bn46NQo2ZRw= github.com/go-openapi/strfmt v0.19.11 h1:0+YvbNh05rmBkgztd6zHp4OCFn7Mtu30bn46NQo2ZRw=
github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
github.com/go-openapi/strfmt v0.19.12 h1:GcJYVoo6b2fsAzIxHTL6bTIcGo7vCJTfty+mdyj5VXo= github.com/go-openapi/strfmt v0.20.0 h1:l2omNtmNbMc39IGptl9BuXBEKcZfS8zjrTsPKTiJiDM=
github.com/go-openapi/strfmt v0.19.12/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
@ -249,6 +255,10 @@ github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4az
github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI= github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI=
github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
github.com/go-openapi/swag v0.19.13 h1:233UVgMy1DlmCYYfOiFpta6e2urloh+sEs5id6lyzog=
github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
@ -258,10 +268,10 @@ github.com/go-openapi/validate v0.19.12 h1:mPLM/bfbd00PGOCJlU0yJL7IulkZ+q9VjPv7U
github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4=
github.com/go-openapi/validate v0.19.15 h1:oUHZO8jD7p5oRLANlXF0U8ic9ePBUkDQyRZdN0EhL6M= github.com/go-openapi/validate v0.19.15 h1:oUHZO8jD7p5oRLANlXF0U8ic9ePBUkDQyRZdN0EhL6M=
github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI=
github.com/go-openapi/validate v0.20.0 h1:pzutNCCBZGZlE+u8HD3JZyWdc/TVbtVwlWUp8/vgUKk=
github.com/go-openapi/validate v0.20.0/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
github.com/go-openapi/validate v0.20.1 h1:QGQ5CvK74E28t3DkegGweKR+auemUi5IdpMc4x3UW6s= github.com/go-openapi/validate v0.20.1 h1:QGQ5CvK74E28t3DkegGweKR+auemUi5IdpMc4x3UW6s=
github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
github.com/go-openapi/validate v0.20.2 h1:AhqDegYV3J3iQkMPJSXkvzymHKMTw0BST3RK3hTT4ts=
github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0=
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs= github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4= github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
@ -269,8 +279,8 @@ 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.25.0 h1:FxhyrWWV8V/A9P6GtI5szWordAdbb6Y0nqdY/y9So2w= github.com/go-swagger/go-swagger v0.26.1 h1:1XUWLnH6hKxHzeKjJfA2gHkSqcT1Zgi4q/PZp2hDdN8=
github.com/go-swagger/go-swagger v0.25.0/go.mod h1:9639ioXrPX9E6BbnbaDklGXjNz7upAXoNBwL4Ok11Vk= github.com/go-swagger/go-swagger v0.26.1/go.mod h1:zlf/LHplZpdtU2mYXg9Ajd3+9TgHYltv5f/pEM6LjnI=
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0= 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=
@ -331,6 +341,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
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 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
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/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3 h1:6amM4HsNPOvMLVc2ZnyqrjeQ92YAVWn7T4WBKK87inY= github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3 h1:6amM4HsNPOvMLVc2ZnyqrjeQ92YAVWn7T4WBKK87inY=
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
@ -371,8 +383,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.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= 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/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=
@ -415,7 +425,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.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
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=
@ -483,6 +492,8 @@ github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
@ -544,13 +555,14 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
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.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= 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.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
@ -568,8 +580,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
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 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
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-20200819021114-67c6ae64274f h1:JDEmUDtyiLMyMlFwiaDOv2hxUp35497fkwePcLeV7j4= github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e h1:BLqxdwZ6j771IpSCRx7s/GJjXHUE00Hmu7/YegCGdzA=
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ= github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e/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=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -580,8 +592,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/recws-org/recws v1.2.2 h1:TkyyCEgMjsr1D2fnutY/DPhGnUKCLpJeXDAGy6rLmGE= github.com/recws-org/recws v1.2.1 h1:bYocRkAsS71hlQ9AMCVS+hYXHEgEyQsAbYKXf394gZ8=
github.com/recws-org/recws v1.2.2/go.mod h1:SxTgwQU/jqYSzEgUh4ifDxq/7enApS150f8nZ5Sczk8= github.com/recws-org/recws v1.2.1/go.mod h1:SxTgwQU/jqYSzEgUh4ifDxq/7enApS150f8nZ5Sczk8=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
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=
@ -608,8 +620,6 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.4.1 h1:asw9sl74539yqavKaglDM5hFpdJVK0Y5Dr/JOgQ89nQ=
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg= github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
@ -630,7 +640,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
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 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs= github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
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=
@ -643,6 +652,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
@ -691,11 +702,13 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL
go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
go.mongodb.org/mongo-driver v1.3.4 h1:zs/dKNwX0gYUtzwrN9lLiR15hCO0nDwQj5xXx+vjCdE= go.mongodb.org/mongo-driver v1.3.4 h1:zs/dKNwX0gYUtzwrN9lLiR15hCO0nDwQj5xXx+vjCdE=
go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
go.mongodb.org/mongo-driver v1.3.5/go.mod h1:Ual6Gkco7ZGQw8wE1t4tLnvBsf6yVSM60qW6TgOeJ5c=
go.mongodb.org/mongo-driver v1.4.3 h1:moga+uhicpVshTyaqY9L23E6QqwcHRUv1sqyOsoyOO8= go.mongodb.org/mongo-driver v1.4.3 h1:moga+uhicpVshTyaqY9L23E6QqwcHRUv1sqyOsoyOO8=
go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
go.mongodb.org/mongo-driver v1.4.4 h1:bsPHfODES+/yx2PCWzUYMH8xj6PVniPI8DQrsJuSXSs= go.mongodb.org/mongo-driver v1.4.4 h1:bsPHfODES+/yx2PCWzUYMH8xj6PVniPI8DQrsJuSXSs=
go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
go.mongodb.org/mongo-driver v1.4.5/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
go.mongodb.org/mongo-driver v1.4.6 h1:rh7GdYmDrb8AQSkF8yteAus8qYOgOASWDOv1BWqBXkU=
go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -706,7 +719,6 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
goauthentik.io/outpost v0.0.0-20210108085217-fd6d99f4f999 h1:XYHeaZx7fm4JNx77MHMO6ek/Gdp+sZa2jIJyjC294Gw=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -724,9 +736,8 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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-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-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -759,8 +770,6 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
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 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
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.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/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=
@ -806,14 +815,15 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
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-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
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 h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
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 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
@ -821,6 +831,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
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 h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
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/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=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -881,18 +893,14 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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=
@ -959,17 +967,13 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
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-20200817023811-d00afeaade8f h1:33yHANSyO/TeglgY9rBhUpX43wtonTXoFOsMRtNB6qE= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b h1:Lq5JUTFhiybGVf28jB6QRpqd13/JPOaCnET17PVzYJE=
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a h1:pdfjQ7VswBeGam3EpuEJ4e8EAb7JgaubV570LO/SIQM= golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e h1:Z2uDrs8MyXUWJbwGc4V+nGjV4Ygo+oubBbWSVQw21/I= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 h1:BTs2GMGSMWpgtCpv1CE7vkJTv7XcHdcLLnAMu7UbgTY=
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
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=
@ -1004,6 +1008,8 @@ google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpC
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -1033,8 +1039,8 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70 h1:wboULUXGF3c5qdUnKp+6gLAccE6PRpa/czkYvQ4UXv8= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -1078,7 +1084,6 @@ gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/R
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
@ -1108,6 +1113,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -49,12 +49,14 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
// create the API client, with the transport // create the API client, with the transport
apiClient := client.New(transport, strfmt.Default) apiClient := client.New(transport, strfmt.Default)
log := log.WithField("logger", "authentik.outpost.ak-api-controller")
// 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.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth)
if err != nil { if err != nil {
panic(err) log.WithError(err).Panic("Failed to fetch configuration")
} }
outpost := outposts.Payload.Results[0] outpost := outposts.Payload.Results[0]
doGlobalSetup(outpost.Config.(map[string]interface{})) doGlobalSetup(outpost.Config.(map[string]interface{}))
@ -64,7 +66,7 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
Auth: auth, Auth: auth,
token: token, token: token,
logger: log.WithField("component", "ak-api-controller"), logger: log,
reloadOffset: time.Duration(rand.Intn(10)) * time.Second, reloadOffset: time.Duration(rand.Intn(10)) * time.Second,

View File

@ -40,7 +40,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
} }
ws.Dial(fmt.Sprintf(pathTemplate, scheme, pbURL.Host, outpostUUID.String()), header) ws.Dial(fmt.Sprintf(pathTemplate, scheme, pbURL.Host, outpostUUID.String()), header)
ac.logger.WithField("component", "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")
ac.wsConn = ws ac.wsConn = ws
// Send hello message with our version // Send hello message with our version
@ -52,7 +52,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
} }
err := ws.WriteJSON(msg) err := ws.WriteJSON(msg)
if err != nil { if err != nil {
ac.logger.WithField("component", "ak-ws").WithError(err).Warning("Failed to hello to authentik") ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithError(err).Warning("Failed to hello to authentik")
} }
} }
@ -69,14 +69,9 @@ func (ac *APIController) Shutdown() {
} }
func (ac *APIController) startWSHandler() { func (ac *APIController) startWSHandler() {
notConnectedBackoff := 1
logger := ac.logger.WithField("loop", "ws-handler") logger := ac.logger.WithField("loop", "ws-handler")
for { for {
if !ac.wsConn.IsConnected() { if !ac.wsConn.IsConnected() {
notConnectedWait := time.Duration(notConnectedBackoff) * time.Second
logger.WithField("wait", notConnectedWait).Info("Not connected, trying again...")
time.Sleep(notConnectedWait)
notConnectedBackoff += notConnectedBackoff
continue continue
} }
var wsMsg websocketMessage var wsMsg websocketMessage
@ -109,7 +104,7 @@ func (ac *APIController) startWSHealth() {
}, },
} }
err := ac.wsConn.WriteJSON(aliveMsg) err := ac.wsConn.WriteJSON(aliveMsg)
ac.logger.WithField("loop", "ws-health").Debug("hello'd") ac.logger.WithField("loop", "ws-health").Trace("hello'd")
if err != nil { if err != nil {
ac.logger.WithField("loop", "ws-health").Println("write:", err) ac.logger.WithField("loop", "ws-health").Println("write:", err)
ac.wsConn.CloseAndReconnect() ac.wsConn.CloseAndReconnect()

View File

@ -13,7 +13,12 @@ import (
) )
func doGlobalSetup(config map[string]interface{}) { func doGlobalSetup(config map[string]interface{}) {
log.SetFormatter(&log.JSONFormatter{}) log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
})
switch config[ConfigLogLevel].(string) { switch config[ConfigLogLevel].(string) {
case "debug": case "debug":
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)

View File

@ -31,7 +31,7 @@ func (s *Server) bundleProviders(providers []*models.ProxyOutpostConfig) []*prov
bundles[idx] = &providerBundle{ bundles[idx] = &providerBundle{
s: s, s: s,
Host: externalHost.Host, Host: externalHost.Host,
log: log.WithField("component", "proxy-bundle").WithField("provider", provider.Name), log: log.WithField("logger", "authentik.outpost.proxy-bundle").WithField("provider", provider.Name),
} }
bundles[idx].Build(provider) bundles[idx].Build(provider)
} }

View File

@ -68,7 +68,7 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
if provider.Certificate != nil { if provider.Certificate != nil {
pb.log.WithField("provider", provider.ClientID).Debug("Enabling TLS") pb.log.WithField("provider", provider.ClientID).Debug("Enabling TLS")
cert, err := pb.s.ak.Client.Crypto.CryptoCertificatekeypairsRead(&crypto.CryptoCertificatekeypairsReadParams{ cert, err := pb.s.ak.Client.Crypto.CryptoCertificatekeypairsViewCertificate(&crypto.CryptoCertificatekeypairsViewCertificateParams{
Context: context.Background(), Context: context.Background(),
KpUUID: *provider.Certificate, KpUUID: *provider.Certificate,
}, pb.s.ak.Auth) }, pb.s.ak.Auth)
@ -76,13 +76,22 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch certificate") pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch certificate")
return providerOpts return providerOpts
} }
x509cert, err := tls.X509KeyPair([]byte(*cert.Payload.CertificateData), []byte(cert.Payload.KeyData)) key, err := pb.s.ak.Client.Crypto.CryptoCertificatekeypairsViewPrivateKey(&crypto.CryptoCertificatekeypairsViewPrivateKeyParams{
Context: context.Background(),
KpUUID: *provider.Certificate,
}, pb.s.ak.Auth)
if err != nil {
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch private key")
return providerOpts
}
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
} }
pb.cert = &x509cert pb.cert = &x509cert
pb.log.WithField("provider", provider.ClientID).WithField("certificate-key-pair", *cert.Payload.Name).Debug("Loaded certificates") pb.log.WithField("provider", provider.ClientID).Debug("Loaded certificates")
} }
return providerOpts return providerOpts
} }
@ -120,7 +129,7 @@ func (pb *providerBundle) Build(provider *models.ProxyOutpostConfig) {
log.Printf("%s", err) log.Printf("%s", err)
os.Exit(1) os.Exit(1)
} }
oauthproxy, err := NewOAuthProxy(opts) oauthproxy, err := NewOAuthProxy(opts, provider)
if err != nil { if err != nil {
log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err) log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
os.Exit(1) os.Exit(1)

View File

@ -95,7 +95,7 @@ type loggingHandler struct {
func LoggingHandler(h http.Handler) http.Handler { func LoggingHandler(h http.Handler) http.Handler {
return loggingHandler{ return loggingHandler{
handler: h, handler: h,
logger: log.WithField("component", "proxy-http-server"), logger: log.WithField("logger", "authentik.outpost.proxy-http-server"),
} }
} }
@ -104,19 +104,17 @@ func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
url := *req.URL url := *req.URL
responseLogger := &responseLogger{w: w} responseLogger := &responseLogger{w: w}
h.handler.ServeHTTP(responseLogger, req) h.handler.ServeHTTP(responseLogger, req)
duration := float64(time.Since(t)) / float64(time.Second) duration := float64(time.Since(t)) / float64(time.Millisecond)
h.logger.WithFields(log.Fields{ h.logger.WithFields(log.Fields{
"Client": req.RemoteAddr, "host": req.RemoteAddr,
"Host": req.Host, "vhost": req.Host,
"Protocol": req.Proto, "request_protocol": req.Proto,
"RequestDuration": fmt.Sprintf("%0.3f", duration), "runtime": fmt.Sprintf("%0.3f", duration),
"RequestMethod": req.Method, "method": req.Method,
"ResponseSize": responseLogger.Size(), "size": responseLogger.Size(),
"StatusCode": responseLogger.Status(), "status": responseLogger.Status(),
"Timestamp": t, "upstream": responseLogger.upstream,
"Upstream": responseLogger.upstream, "request_useragent": req.UserAgent(),
"UserAgent": req.UserAgent(), "request_username": responseLogger.authInfo,
"Username": responseLogger.authInfo,
}).Info(url.RequestURI()) }).Info(url.RequestURI())
// logger.PrintReq(responseLogger.authInfo, responseLogger.upstream, req, url, t, , )
} }

View File

@ -21,6 +21,7 @@ import (
"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions" "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions"
"github.com/oauth2-proxy/oauth2-proxy/pkg/upstream" "github.com/oauth2-proxy/oauth2-proxy/pkg/upstream"
"github.com/oauth2-proxy/oauth2-proxy/providers" "github.com/oauth2-proxy/oauth2-proxy/providers"
"goauthentik.io/outpost/pkg/models"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -92,8 +93,8 @@ type OAuthProxy struct {
} }
// NewOAuthProxy creates a new instance of OAuthProxy from the options provided // NewOAuthProxy creates a new instance of OAuthProxy from the options provided
func NewOAuthProxy(opts *options.Options) (*OAuthProxy, error) { func NewOAuthProxy(opts *options.Options, provider *models.ProxyOutpostConfig) (*OAuthProxy, error) {
logger := log.WithField("component", "proxy").WithField("client-id", opts.ClientID) logger := log.WithField("logger", "authentik.outpost.proxy").WithField("provider", provider.Name)
sessionStore, err := sessions.NewSessionStore(&opts.Session, &opts.Cookie) sessionStore, err := sessions.NewSessionStore(&opts.Session, &opts.Cookie)
if err != nil { if err != nil {
return nil, fmt.Errorf("error initialising session store: %v", err) return nil, fmt.Errorf("error initialising session store: %v", err)
@ -434,6 +435,7 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
authVal := b64.StdEncoding.EncodeToString([]byte(username + ":" + password)) authVal := b64.StdEncoding.EncodeToString([]byte(username + ":" + password))
req.Header["Authorization"] = []string{fmt.Sprintf("Basic %s", authVal)} req.Header["Authorization"] = []string{fmt.Sprintf("Basic %s", authVal)}
} }
rw.Header().Set("GAP-Auth", session.PreferredUsername)
// Check if user has additional headers set that we should sent // Check if user has additional headers set that we should sent
if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]string); ok { if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]string); ok {
if additionalHeaders == nil { if additionalHeaders == nil {

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"net" "net"
"net/http" "net/http"
"strings"
"time" "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -30,7 +31,7 @@ func NewServer(ac *ak.APIController) *Server {
} }
return &Server{ return &Server{
Handlers: make(map[string]*providerBundle), Handlers: make(map[string]*providerBundle),
logger: log.WithField("component", "proxy-http-server"), logger: log.WithField("logger", "authentik.outpost.proxy-http-server"),
defaultCert: defaultCert, defaultCert: defaultCert,
ak: ac, ak: ac,
} }
@ -50,12 +51,15 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
return return
} }
} }
s.logger.WithField("host", r.Host).Debug("Host header does not match any we know of") // Get a list of all host keys we know
s.logger.Printf("%v+\n", s.Handlers) hostKeys := make([]string, 0, len(s.Handlers))
w.WriteHeader(400) for k := range s.Handlers {
hostKeys = append(hostKeys, k)
}
s.logger.WithField("host", r.Host).WithField("known-hosts", strings.Join(hostKeys, ", ")).Debug("Host header does not match any we know of")
w.WriteHeader(404)
return return
} }
s.logger.WithField("host", r.Host).Debug("passing request from host head")
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
} }

View File

@ -1,3 +1,3 @@
package pkg package pkg
const VERSION = "2021.2.1-rc1" const VERSION = "2021.2.5-stable"

View File

@ -565,9 +565,9 @@ paths:
parameters: [] parameters: []
responses: responses:
'200': '200':
description: '' description: Show token's current key
schema: schema:
$ref: '#/definitions/Token' $ref: '#/definitions/TokenView'
tags: tags:
- core - core
parameters: parameters:
@ -863,6 +863,44 @@ paths:
required: true required: true
type: string type: string
format: uuid format: uuid
/crypto/certificatekeypairs/{kp_uuid}/view_certificate/:
get:
operationId: crypto_certificatekeypairs_view_certificate
description: Return certificate-key pairs certificate and log access
parameters: []
responses:
'200':
description: Get CertificateKeyPair's data
schema:
$ref: '#/definitions/CertificateData'
tags:
- crypto
parameters:
- name: kp_uuid
in: path
description: A UUID string identifying this Certificate-Key Pair.
required: true
type: string
format: uuid
/crypto/certificatekeypairs/{kp_uuid}/view_private_key/:
get:
operationId: crypto_certificatekeypairs_view_private_key
description: Return certificate-key pairs private key and log access
parameters: []
responses:
'200':
description: Get CertificateKeyPair's data
schema:
$ref: '#/definitions/CertificateData'
tags:
- crypto
parameters:
- name: kp_uuid
in: path
description: A UUID string identifying this Certificate-Key Pair.
required: true
type: string
format: uuid
/events/events/: /events/events/:
get: get:
operationId: events_events_list operationId: events_events_list
@ -1789,6 +1827,11 @@ paths:
operationId: outposts_outposts_list operationId: outposts_outposts_list
description: Outpost Viewset description: Outpost Viewset
parameters: parameters:
- name: providers__isnull
in: query
description: ''
required: false
type: string
- name: ordering - name: ordering
in: query in: query
description: Which field to use when ordering the results. description: Which field to use when ordering the results.
@ -1911,6 +1954,28 @@ paths:
required: true required: true
type: string type: string
format: uuid format: uuid
/outposts/outposts/{uuid}/health/:
get:
operationId: outposts_outposts_health
description: Get outposts current health
parameters: []
responses:
'200':
description: Outpost health status
schema:
description: ''
type: array
items:
$ref: '#/definitions/OutpostHealth'
tags:
- outposts
parameters:
- name: uuid
in: path
description: A UUID string identifying this outpost.
required: true
type: string
format: uuid
/outposts/proxy/: /outposts/proxy/:
get: get:
operationId: outposts_proxy_list operationId: outposts_proxy_list
@ -2037,6 +2102,133 @@ paths:
description: A unique integer value identifying this Proxy Provider. description: A unique integer value identifying this Proxy Provider.
required: true required: true
type: integer type: integer
/outposts/service_connections/all/:
get:
operationId: outposts_service_connections_all_list
description: ServiceConnection Viewset
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: A page number within the paginated result set.
required: false
type: integer
- name: page_size
in: query
description: Number of results to return per page.
required: false
type: integer
responses:
'200':
description: ''
schema:
required:
- count
- results
type: object
properties:
count:
type: integer
next:
type: string
format: uri
x-nullable: true
previous:
type: string
format: uri
x-nullable: true
results:
type: array
items:
$ref: '#/definitions/ServiceConnection'
tags:
- outposts
post:
operationId: outposts_service_connections_all_create
description: ServiceConnection Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/ServiceConnection'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/ServiceConnection'
tags:
- outposts
parameters: []
/outposts/service_connections/all/{uuid}/:
get:
operationId: outposts_service_connections_all_read
description: ServiceConnection Viewset
parameters: []
responses:
'200':
description: ''
schema:
$ref: '#/definitions/ServiceConnection'
tags:
- outposts
put:
operationId: outposts_service_connections_all_update
description: ServiceConnection Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/ServiceConnection'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/ServiceConnection'
tags:
- outposts
patch:
operationId: outposts_service_connections_all_partial_update
description: ServiceConnection Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/ServiceConnection'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/ServiceConnection'
tags:
- outposts
delete:
operationId: outposts_service_connections_all_delete
description: ServiceConnection Viewset
parameters: []
responses:
'204':
description: ''
tags:
- outposts
parameters:
- name: uuid
in: path
description: A UUID string identifying this Outpost Service-Connection.
required: true
type: string
format: uuid
/outposts/service_connections/docker/: /outposts/service_connections/docker/:
get: get:
operationId: outposts_service_connections_docker_list operationId: outposts_service_connections_docker_list
@ -4120,6 +4312,47 @@ paths:
tags: tags:
- providers - providers
parameters: [] parameters: []
/providers/all/types/:
get:
operationId: providers_all_types
description: Get all creatable provider types
parameters:
- name: application__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: A page number within the paginated result set.
required: false
type: integer
- name: page_size
in: query
description: Number of results to return per page.
required: false
type: integer
responses:
'200':
description: Types of an object that can be created
schema:
description: ''
type: array
items:
$ref: '#/definitions/TypeCreate'
tags:
- providers
parameters: []
/providers/all/{id}/: /providers/all/{id}/:
get: get:
operationId: providers_all_read operationId: providers_all_read
@ -4308,7 +4541,7 @@ paths:
/providers/oauth2/{id}/setup_urls/: /providers/oauth2/{id}/setup_urls/:
get: get:
operationId: providers_oauth2_setup_urls operationId: providers_oauth2_setup_urls
description: Return metadata as XML string description: Get Providers setup URLs
parameters: [] parameters: []
responses: responses:
'200': '200':
@ -4676,6 +4909,42 @@ paths:
tags: tags:
- sources - sources
parameters: [] parameters: []
/sources/all/types/:
get:
operationId: sources_all_types
description: Get all creatable source types
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: A page number within the paginated result set.
required: false
type: integer
- name: page_size
in: query
description: Number of results to return per page.
required: false
type: integer
responses:
'200':
description: Types of an object that can be created
schema:
description: ''
type: array
items:
$ref: '#/definitions/TypeCreate'
tags:
- sources
parameters: []
/sources/all/{slug}/: /sources/all/{slug}/:
get: get:
operationId: sources_all_read operationId: sources_all_read
@ -4763,7 +5032,7 @@ paths:
tags: tags:
- sources - sources
parameters: [] parameters: []
/sources/ldap/{pbm_uuid}/: /sources/ldap/{slug}/:
get: get:
operationId: sources_ldap_read operationId: sources_ldap_read
description: LDAP Source Viewset description: LDAP Source Viewset
@ -4817,12 +5086,33 @@ paths:
tags: tags:
- sources - sources
parameters: parameters:
- name: pbm_uuid - name: slug
in: path in: path
description: A UUID string identifying this LDAP Source. description: Internal source name, used in URLs.
required: true required: true
type: string type: string
format: uuid format: slug
pattern: ^[-a-zA-Z0-9_]+$
/sources/ldap/{slug}/sync_status/:
get:
operationId: sources_ldap_sync_status
description: Get source's sync status
parameters: []
responses:
'200':
description: LDAP Sync status
schema:
$ref: '#/definitions/LDAPSourceSyncStatus'
tags:
- sources
parameters:
- name: slug
in: path
description: Internal source name, used in URLs.
required: true
type: string
format: slug
pattern: ^[-a-zA-Z0-9_]+$
/sources/oauth/: /sources/oauth/:
get: get:
operationId: sources_oauth_list operationId: sources_oauth_list
@ -4890,7 +5180,7 @@ paths:
tags: tags:
- sources - sources
parameters: [] parameters: []
/sources/oauth/{pbm_uuid}/: /sources/oauth/{slug}/:
get: get:
operationId: sources_oauth_read operationId: sources_oauth_read
description: Source Viewset description: Source Viewset
@ -4944,12 +5234,13 @@ paths:
tags: tags:
- sources - sources
parameters: parameters:
- name: pbm_uuid - name: slug
in: path in: path
description: A UUID string identifying this Generic OAuth Source. description: Internal source name, used in URLs.
required: true required: true
type: string type: string
format: uuid format: slug
pattern: ^[-a-zA-Z0-9_]+$
/sources/saml/: /sources/saml/:
get: get:
operationId: sources_saml_list operationId: sources_saml_list
@ -5017,7 +5308,7 @@ paths:
tags: tags:
- sources - sources
parameters: [] parameters: []
/sources/saml/{pbm_uuid}/: /sources/saml/{slug}/:
get: get:
operationId: sources_saml_read operationId: sources_saml_read
description: SAMLSource Viewset description: SAMLSource Viewset
@ -5071,12 +5362,13 @@ paths:
tags: tags:
- sources - sources
parameters: parameters:
- name: pbm_uuid - name: slug
in: path in: path
description: A UUID string identifying this SAML Source. description: Internal source name, used in URLs.
required: true required: true
type: string type: string
format: uuid format: slug
pattern: ^[-a-zA-Z0-9_]+$
/stages/all/: /stages/all/:
get: get:
operationId: stages_all_list operationId: stages_all_list
@ -7536,6 +7828,15 @@ definitions:
description: description:
title: Description title: Description
type: string type: string
TokenView:
description: Show token's current key
type: object
properties:
key:
title: Key
type: string
readOnly: true
minLength: 1
User: User:
description: User Serializer description: User Serializer
required: required:
@ -7589,6 +7890,10 @@ definitions:
title: Name title: Name
type: string type: string
minLength: 1 minLength: 1
fingerprint:
title: Fingerprint
type: string
readOnly: true
certificate_data: certificate_data:
title: Certificate data title: Certificate data
description: PEM-encoded Certificate data description: PEM-encoded Certificate data
@ -7599,6 +7904,24 @@ definitions:
description: Optional Private Key. If this is set, you can use this keypair description: Optional Private Key. If this is set, you can use this keypair
for encryption. for encryption.
type: string type: string
cert_expiry:
title: Cert expiry
type: string
format: date-time
readOnly: true
cert_subject:
title: Cert subject
type: string
readOnly: true
CertificateData:
description: Get CertificateKeyPair's data
type: object
properties:
data:
title: Data
type: string
readOnly: true
minLength: 1
Event: Event:
description: Event Serializer description: Event Serializer
required: required:
@ -7624,7 +7947,7 @@ definitions:
- user_write - user_write
- suspicious_request - suspicious_request
- password_set - password_set
- token_view - secret_view
- invitation_used - invitation_used
- authorize_application - authorize_application
- source_linked - source_linked
@ -8023,6 +8346,12 @@ definitions:
items: items:
type: integer type: integer
uniqueItems: true uniqueItems: true
providers_obj:
description: ''
type: array
items:
$ref: '#/definitions/Provider'
readOnly: true
service_connection: service_connection:
title: Service connection title: Service connection
description: Select Service-Connection authentik should use to manage this description: Select Service-Connection authentik should use to manage this
@ -8030,9 +8359,36 @@ definitions:
type: string type: string
format: uuid format: uuid
x-nullable: true x-nullable: true
token_identifier:
title: Token identifier
type: string
readOnly: true
_config: _config:
title: config title: config
type: object type: object
OutpostHealth:
description: Outpost health status
type: object
properties:
last_seen:
title: Last seen
type: string
format: date-time
readOnly: true
version:
title: Version
type: string
readOnly: true
minLength: 1
version_should:
title: Version should
type: string
readOnly: true
minLength: 1
version_outdated:
title: Version outdated
type: boolean
readOnly: true
OpenIDConnectConfiguration: OpenIDConnectConfiguration:
title: Oidc configuration title: Oidc configuration
description: rest_framework Serializer for OIDC Configuration description: rest_framework Serializer for OIDC Configuration
@ -8170,6 +8526,21 @@ definitions:
description: User/Group Attribute used for the user part of the HTTP-Basic description: User/Group Attribute used for the user part of the HTTP-Basic
Header. If not set, the user's Email address is used. Header. If not set, the user's Email address is used.
type: string type: string
ServiceConnection:
description: ServiceConnection Serializer
required:
- name
type: object
properties:
pk:
title: Uuid
type: string
format: uuid
readOnly: true
name:
title: Name
type: string
minLength: 1
DockerServiceConnection: DockerServiceConnection:
description: DockerServiceConnection Serializer description: DockerServiceConnection Serializer
required: required:
@ -8347,7 +8718,7 @@ definitions:
- user_write - user_write
- suspicious_request - suspicious_request
- password_set - password_set
- token_view - secret_view
- invitation_used - invitation_used
- authorize_application - authorize_application
- source_linked - source_linked
@ -8711,6 +9082,25 @@ definitions:
title: Verbose name plural title: Verbose name plural
type: string type: string
readOnly: true readOnly: true
TypeCreate:
description: Types of an object that can be created
type: object
properties:
name:
title: Name
type: string
readOnly: true
minLength: 1
description:
title: Description
type: string
readOnly: true
minLength: 1
link:
title: Link
type: string
readOnly: true
minLength: 1
OAuth2Provider: OAuth2Provider:
description: OAuth2Provider Serializer description: OAuth2Provider Serializer
required: required:
@ -9157,6 +9547,10 @@ definitions:
type: string type: string
format: uuid format: uuid
x-nullable: true x-nullable: true
object_type:
title: Object type
type: string
readOnly: true
verbose_name: verbose_name:
title: Verbose name title: Verbose name
type: string type: string
@ -9165,10 +9559,6 @@ definitions:
title: Verbose name plural title: Verbose name plural
type: string type: string
readOnly: true readOnly: true
__type__:
title: 'type '
type: string
readOnly: true
LDAPSource: LDAPSource:
description: LDAP Source Serializer description: LDAP Source Serializer
required: required:
@ -9213,6 +9603,10 @@ definitions:
type: string type: string
format: uuid format: uuid
x-nullable: true x-nullable: true
object_type:
title: Object type
type: string
readOnly: true
verbose_name: verbose_name:
title: Verbose name title: Verbose name
type: string type: string
@ -9298,6 +9692,15 @@ definitions:
type: string type: string
format: uuid format: uuid
uniqueItems: true uniqueItems: true
LDAPSourceSyncStatus:
description: LDAP Sync status
type: object
properties:
last_sync:
title: Last sync
type: string
format: date-time
readOnly: true
OAuthSource: OAuthSource:
description: OAuth Source Serializer description: OAuth Source Serializer
required: required:
@ -9344,6 +9747,10 @@ definitions:
type: string type: string
format: uuid format: uuid
x-nullable: true x-nullable: true
object_type:
title: Object type
type: string
readOnly: true
verbose_name: verbose_name:
title: Verbose name title: Verbose name
type: string type: string
@ -9389,6 +9796,10 @@ definitions:
title: Consumer secret title: Consumer secret
type: string type: string
minLength: 1 minLength: 1
callback_url:
title: Callback url
type: string
readOnly: true
SAMLSource: SAMLSource:
description: SAMLSource Serializer description: SAMLSource Serializer
required: required:
@ -9397,6 +9808,11 @@ definitions:
- sso_url - sso_url
type: object type: object
properties: properties:
pk:
title: Pbm uuid
type: string
format: uuid
readOnly: true
name: name:
title: Name title: Name
description: Source's display Name. description: Source's display Name.
@ -9425,6 +9841,18 @@ definitions:
type: string type: string
format: uuid format: uuid
x-nullable: true x-nullable: true
object_type:
title: Object type
type: string
readOnly: true
verbose_name:
title: Verbose name
type: string
readOnly: true
verbose_name_plural:
title: Verbose name plural
type: string
readOnly: true
issuer: issuer:
title: Issuer title: Issuer
description: Also known as Entity ID. Defaults the Metadata URL. description: Also known as Entity ID. Defaults the Metadata URL.

152
web/package-lock.json generated
View File

@ -409,13 +409,13 @@
} }
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "4.14.2", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.0.tgz",
"integrity": "sha512-uMGfG7GFYK/nYutK/iqYJv6K/Xuog/vrRRZX9aEP4Zv1jsYXuvFUMDFLhUnc8WFv3D2R5QhNQL3VYKmvLS5zsQ==", "integrity": "sha512-DJgdGZW+8CFUTz5C/dnn4ONcUm2h2T0itWD85Ob5/V27Ndie8hUoX5HKyGssvR8sUMkAIlUc/AMK67Lqa3kBIQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "4.14.2", "@typescript-eslint/experimental-utils": "4.15.0",
"@typescript-eslint/scope-manager": "4.14.2", "@typescript-eslint/scope-manager": "4.15.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15", "lodash": "^4.17.15",
@ -425,59 +425,115 @@
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "4.14.2", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz",
"integrity": "sha512-mV9pmET4C2y2WlyHmD+Iun8SAEqkLahHGBkGqDVslHkmoj3VnxnGP4ANlwuxxfq1BsKdl/MPieDbohCEQgKrwA==", "integrity": "sha512-V4vaDWvxA2zgesg4KPgEGiomWEBpJXvY4ZX34Y3qxK8LUm5I87L+qGIOTd9tHZOARXNRt9pLbblSKiYBlGMawg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.3", "@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.14.2", "@typescript-eslint/scope-manager": "4.15.0",
"@typescript-eslint/types": "4.14.2", "@typescript-eslint/types": "4.15.0",
"@typescript-eslint/typescript-estree": "4.14.2", "@typescript-eslint/typescript-estree": "4.15.0",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0" "eslint-utils": "^2.0.0"
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "4.14.2", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.0.tgz",
"integrity": "sha512-ipqSP6EuUsMu3E10EZIApOJgWSpcNXeKZaFeNKQyzqxnQl8eQCbV+TSNsl+s2GViX2d18m1rq3CWgnpOxDPgHg==", "integrity": "sha512-L6Dtbq8Bc7g2aZwnIBETpmUa9XDKCMzKVwAArnGp5Mn7PRNFjf3mUzq8UeBjL3K8t311hvevnyqXAMSmxO8Gpg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/scope-manager": "4.14.2", "@typescript-eslint/scope-manager": "4.15.0",
"@typescript-eslint/types": "4.14.2", "@typescript-eslint/types": "4.15.0",
"@typescript-eslint/typescript-estree": "4.14.2", "@typescript-eslint/typescript-estree": "4.15.0",
"debug": "^4.1.1" "debug": "^4.1.1"
}
}, },
"dependencies": {
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.14.2", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz",
"integrity": "sha512-cuV9wMrzKm6yIuV48aTPfIeqErt5xceTheAgk70N1V4/2Ecj+fhl34iro/vIssJlb7XtzcaD07hWk7Jk0nKghg==", "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.14.2", "@typescript-eslint/types": "4.15.0",
"@typescript-eslint/visitor-keys": "4.14.2" "@typescript-eslint/visitor-keys": "4.15.0"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.14.2", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz",
"integrity": "sha512-LltxawRW6wXy4Gck6ZKlBD05tCHQUj4KLn4iR69IyRiDHX3d3NCAhO+ix5OR2Q+q9bjCrHE/HKt+riZkd1At8Q==", "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==",
"dev": true "dev": true
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "4.14.2", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz",
"integrity": "sha512-ESiFl8afXxt1dNj8ENEZT12p+jl9PqRur+Y19m0Z/SPikGL6rqq4e7Me60SU9a2M28uz48/8yct97VQYaGl0Vg==", "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.14.2", "@typescript-eslint/types": "4.15.0",
"@typescript-eslint/visitor-keys": "4.14.2", "@typescript-eslint/visitor-keys": "4.15.0",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz",
"integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.15.0",
"eslint-visitor-keys": "^2.0.0"
}
},
"globby": {
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz",
"integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==",
"dev": true,
"requires": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.1.1",
"ignore": "^5.1.4",
"merge2": "^1.3.0",
"slash": "^3.0.0"
}
}
}
},
"@typescript-eslint/scope-manager": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz",
"integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.15.0",
"@typescript-eslint/visitor-keys": "4.15.0"
}
},
"@typescript-eslint/types": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz",
"integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz",
"integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.15.0",
"@typescript-eslint/visitor-keys": "4.15.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"globby": "^11.0.1", "globby": "^11.0.1",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
"lodash": "^4.17.15",
"semver": "^7.3.2", "semver": "^7.3.2",
"tsutils": "^3.17.1" "tsutils": "^3.17.1"
}, },
@ -499,12 +555,12 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.14.2", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz",
"integrity": "sha512-KBB+xLBxnBdTENs/rUgeUKO0UkPBRs2vD09oMRRIkj5BEN8PX1ToXV532desXfpQnZsYTyLLviS7JrPhdL154w==", "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.14.2", "@typescript-eslint/types": "4.15.0",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
} }
}, },
@ -899,9 +955,9 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
}, },
"construct-style-sheets-polyfill": { "construct-style-sheets-polyfill": {
"version": "2.4.6", "version": "2.4.9",
"resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.6.tgz", "resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.9.tgz",
"integrity": "sha512-lU0to7dFDjKslMF+M5NUa4s0RQMBRVyZMXvD/vp7vmjdEPgziTkHSfZHQxfoIvVWajWRJUVJMLfrMwcx8fTh4A==" "integrity": "sha512-kPXZXxsp7CTr/Vs29+omUA29wTrFplkdY6jqxyv0DDWC5Ro79WmwpboH2M9KiOclbtn8r81GCFtc7+t7OjRnCw=="
}, },
"copy-descriptor": { "copy-descriptor": {
"version": "0.1.1", "version": "0.1.1",
@ -1537,9 +1593,9 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
}, },
"fsevents": { "fsevents": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"optional": true "optional": true
}, },
"functional-red-black-tree": { "functional-red-black-tree": {
@ -2638,9 +2694,9 @@
} }
}, },
"rollup": { "rollup": {
"version": "2.38.4", "version": "2.38.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.4.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.5.tgz",
"integrity": "sha512-B0LcJhjiwKkTl79aGVF/u5KdzsH8IylVfV56Ut6c9ouWLJcUK17T83aZBetNYSnZtXf2OHD4+2PbmRW+Fp5ulg==", "integrity": "sha512-VoWt8DysFGDVRGWuHTqZzT02J0ASgjVq/hPs9QcBOGMd7B+jfTr/iqMVEyOi901rE3xq+Deq66GzIT1yt7sGwQ==",
"requires": { "requires": {
"fsevents": "~2.3.1" "fsevents": "~2.3.1"
} }
@ -3304,9 +3360,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "4.1.3", "version": "4.1.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.4.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", "integrity": "sha512-+Uru0t8qIRgjuCpiSPpfGuhHecMllk5Zsazj5LZvVsEStEjmIRRBZe+jHjGQvsgS7M1wONy2PQXd67EMyV6acg==",
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {

View File

@ -18,11 +18,11 @@
"@types/codemirror": "0.0.108", "@types/codemirror": "0.0.108",
"chart.js": "^2.9.4", "chart.js": "^2.9.4",
"codemirror": "^5.59.2", "codemirror": "^5.59.2",
"construct-style-sheets-polyfill": "^2.4.6", "construct-style-sheets-polyfill": "^2.4.9",
"flowchart.js": "^1.15.0", "flowchart.js": "^1.15.0",
"lit-element": "^2.4.0", "lit-element": "^2.4.0",
"lit-html": "^1.3.0", "lit-html": "^1.3.0",
"rollup": "^2.38.4", "rollup": "^2.38.5",
"rollup-plugin-copy": "^3.3.0", "rollup-plugin-copy": "^3.3.0",
"rollup-plugin-cssimport": "^1.0.2", "rollup-plugin-cssimport": "^1.0.2",
"rollup-plugin-external-globals": "^0.6.1", "rollup-plugin-external-globals": "^0.6.1",
@ -30,8 +30,8 @@
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-typescript": "^8.1.1", "@rollup/plugin-typescript": "^8.1.1",
"@typescript-eslint/eslint-plugin": "^4.14.2", "@typescript-eslint/eslint-plugin": "^4.15.0",
"@typescript-eslint/parser": "^4.14.2", "@typescript-eslint/parser": "^4.15.0",
"eslint": "^7.19.0", "eslint": "^7.19.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-lit": "^1.3.0", "eslint-plugin-lit": "^1.3.0",
@ -41,6 +41,6 @@
"rollup-plugin-sourcemaps": "^0.6.3", "rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",
"typescript": "^4.1.3" "typescript": "^4.1.4"
} }
} }

View File

@ -1,4 +1,4 @@
import { DefaultClient, PBResponse, QueryArguments } from "./Client"; import { DefaultClient, AKResponse, QueryArguments } from "./Client";
import { Provider } from "./Providers"; import { Provider } from "./Providers";
export class Application { export class Application {
@ -22,8 +22,8 @@ export class Application {
return DefaultClient.fetch<Application>(["core", "applications", slug]); return DefaultClient.fetch<Application>(["core", "applications", slug]);
} }
static list(filter?: QueryArguments): Promise<PBResponse<Application>> { static list(filter?: QueryArguments): Promise<AKResponse<Application>> {
return DefaultClient.fetch<PBResponse<Application>>(["core", "applications"], filter); return DefaultClient.fetch<AKResponse<Application>>(["core", "applications"], filter);
} }
static adminUrl(rest: string): string { static adminUrl(rest: string): string {

View File

@ -7,6 +7,15 @@ export interface QueryArguments {
[key: string]: number | string | boolean | null; [key: string]: number | string | boolean | null;
} }
export interface BaseInheritanceModel {
object_type: string;
verbose_name: string;
verbose_name_plural: string;
}
export class Client { export class Client {
makeUrl(url: string[], query?: QueryArguments): string { makeUrl(url: string[], query?: QueryArguments): string {
let builtUrl = `/api/${VERSION}/${url.join("/")}/`; let builtUrl = `/api/${VERSION}/${url.join("/")}/`;
@ -85,7 +94,7 @@ export interface PBPagination {
end_index: number; end_index: number;
} }
export interface PBResponse<T> { export interface AKResponse<T> {
pagination: PBPagination; pagination: PBPagination;
results: Array<T>; results: Array<T>;

View File

@ -1,4 +1,4 @@
import { DefaultClient, QueryArguments, PBResponse } from "./Client"; import { DefaultClient, QueryArguments, AKResponse } from "./Client";
import { Event } from "./Events"; import { Event } from "./Events";
export class Notification { export class Notification {
@ -17,8 +17,8 @@ export class Notification {
return DefaultClient.fetch<Notification>(["events", "notifications", pk]); return DefaultClient.fetch<Notification>(["events", "notifications", pk]);
} }
static list(filter?: QueryArguments): Promise<PBResponse<Notification>> { static list(filter?: QueryArguments): Promise<AKResponse<Notification>> {
return DefaultClient.fetch<PBResponse<Notification>>(["events", "notifications"], filter); return DefaultClient.fetch<AKResponse<Notification>>(["events", "notifications"], filter);
} }
static markSeen(pk: string): Promise<{seen: boolean}> { static markSeen(pk: string): Promise<{seen: boolean}> {

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