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]
current_version = 2021.2.1-rc1
current_version = 2021.2.5-stable
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)

View File

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

View File

@ -18,11 +18,11 @@ jobs:
- name: Building Docker Image
run: docker build
--no-cache
-t beryju/authentik:2021.2.1-rc1
-t beryju/authentik:2021.2.5-stable
-t beryju/authentik:latest
-f Dockerfile .
- 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)
run: docker push beryju/authentik:latest
build-proxy:
@ -48,11 +48,11 @@ jobs:
cd outpost/
docker build \
--no-cache \
-t beryju/authentik-proxy:2021.2.1-rc1 \
-t beryju/authentik-proxy:2021.2.5-stable \
-t beryju/authentik-proxy:latest \
-f proxy.Dockerfile .
- 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)
run: docker push beryju/authentik-proxy:latest
build-static:
@ -69,11 +69,11 @@ jobs:
cd web/
docker build \
--no-cache \
-t beryju/authentik-static:2021.2.1-rc1 \
-t beryju/authentik-static:2021.2.5-stable \
-t beryju/authentik-static:latest \
-f Dockerfile .
- 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)
run: docker push beryju/authentik-static:latest
test-release:
@ -107,5 +107,5 @@ jobs:
SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org
with:
tagName: 2021.2.1-rc1
tagName: 2021.2.5-stable
environment: beryjuorg-prod

179
Pipfile.lock generated
View File

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

View File

@ -1,2 +1,2 @@
"""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>
<div slot="modal"></div>
</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">
{% trans 'Refresh' %}
</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 humanize %}
{% load authentik_utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">

View File

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

View File

@ -14,7 +14,7 @@
<span class="pf-c-form__label-text">{% trans 'Passing' %}</span>
</label>
</div>
<div class="pf-c-form__group-control">
<div class="pf-c-form__group-label">
<div class="c-form__horizontal-group">
<span class="pf-c-form__label-text">{{ result.passing|yesno:"Yes,No" }}</span>
</div>
@ -26,7 +26,7 @@
<span class="pf-c-form__label-text">{% trans 'Messages' %}</span>
</label>
</div>
<div class="pf-c-form__group-control">
<div class="pf-c-form__group-label">
<div class="c-form__horizontal-group">
<ul>
{% 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 authentik_utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
@ -63,7 +62,7 @@
{% for source in object_list %}
<tr role="row">
<th role="columnheader">
<a href="/sources/{{ source.slug }}/">
<a href="/sources/{{ source.slug }}">
<div>{{ source.name }}</div>
{% if not source.enabled %}
<small>{% trans 'Disabled' %}</small>
@ -93,10 +92,6 @@
</ak-spinner-button>
<div slot="modal"></div>
</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>
</tr>
{% endfor %}

View File

@ -2,7 +2,6 @@
{% load i18n %}
{% load authentik_utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
@ -88,10 +87,6 @@
</ak-spinner-button>
<div slot="modal"></div>
</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>
</tr>
{% endfor %}

View File

@ -2,7 +2,6 @@
{% load i18n %}
{% load authentik_utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
@ -90,10 +89,6 @@
</ak-spinner-button>
<div slot="modal"></div>
</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>
</tr>
{% 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,
users,
)
from authentik.providers.saml.views import MetadataImportView
from authentik.providers.saml.views.metadata import MetadataImportView
urlpatterns = [
path(
@ -61,7 +61,6 @@ urlpatterns = [
name="token-delete",
),
# Sources
path("sources/", sources.SourceListView.as_view(), name="sources"),
path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
path(
"sources/<uuid:pk>/update/",
@ -113,7 +112,6 @@ urlpatterns = [
name="policy-binding-delete",
),
# Providers
path("providers/", providers.ProviderListView.as_view(), name="providers"),
path(
"providers/create/",
providers.ProviderCreateView.as_view(),
@ -170,22 +168,22 @@ urlpatterns = [
),
# Stage Prompts
path(
"stages/prompts/",
"stages_prompts/",
stages_prompts.PromptListView.as_view(),
name="stage-prompts",
),
path(
"stages/prompts/create/",
"stages_prompts/create/",
stages_prompts.PromptCreateView.as_view(),
name="stage-prompt-create",
),
path(
"stages/prompts/<uuid:pk>/update/",
"stages_prompts/<uuid:pk>/update/",
stages_prompts.PromptUpdateView.as_view(),
name="stage-prompt-update",
),
path(
"stages/prompts/<uuid:pk>/delete/",
"stages_prompts/<uuid:pk>/delete/",
stages_prompts.PromptDeleteView.as_view(),
name="stage-prompt-delete",
),
@ -296,6 +294,11 @@ urlpatterns = [
certificate_key_pair.CertificateKeyPairCreateView.as_view(),
name="certificatekeypair-create",
),
path(
"crypto/certificates/generate/",
certificate_key_pair.CertificateKeyPairGenerateView.as_view(),
name="certificatekeypair-generate",
),
path(
"crypto/certificates/<uuid:pk>/update/",
certificate_key_pair.CertificateKeyPairUpdateView.as_view(),
@ -307,11 +310,6 @@ urlpatterns = [
name="certificatekeypair-delete",
),
# Outposts
path(
"outposts/",
outposts.OutpostListView.as_view(),
name="outposts",
),
path(
"outposts/create/",
outposts.OutpostCreateView.as_view(),
@ -329,22 +327,22 @@ urlpatterns = [
),
# Outpost Service Connections
path(
"outposts/service_connections/",
"outpost_service_connections/",
outposts_service_connections.OutpostServiceConnectionListView.as_view(),
name="outpost-service-connections",
),
path(
"outposts/service_connections/create/",
"outpost_service_connections/create/",
outposts_service_connections.OutpostServiceConnectionCreateView.as_view(),
name="outpost-service-connection-create",
),
path(
"outposts/service_connections/<uuid:pk>/update/",
"outpost_service_connections/<uuid:pk>/update/",
outposts_service_connections.OutpostServiceConnectionUpdateView.as_view(),
name="outpost-service-connection-update",
),
path(
"outposts/service_connections/<uuid:pk>/delete/",
"outpost_service_connections/<uuid:pk>/delete/",
outposts_service_connections.OutpostServiceConnectionDeleteView.as_view(),
name="outpost-service-connection-delete",
),

View File

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

View File

@ -4,9 +4,11 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
)
from django.contrib.messages.views import SuccessMessageMixin
from django.http.response import HttpResponse
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView
from django.views.generic.edit import FormView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from authentik.admin.views.utils import (
@ -15,7 +17,11 @@ from authentik.admin.views.utils import (
SearchListMixin,
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.lib.views import CreateAssignPermView
@ -52,7 +58,35 @@ class CertificateKeyPairCreateView(
template_name = "generic/create.html"
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(

View File

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

View File

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

View File

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

View File

@ -20,7 +20,6 @@ class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
form_class = PolicyCacheClearForm
template_name = "generic/form_non_model.html"
success_url = "/"
success_message = _("Successfully cleared Policy cache")
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
@ -39,7 +38,6 @@ class FlowCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
form_class = FlowCacheClearForm
template_name = "generic/form_non_model.html"
success_url = "/"
success_message = _("Successfully cleared Flow cache")
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")
p_request = PolicyRequest(user)
p_request.debug = True
p_request.http_request = self.request
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.http import HttpResponse
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import FormView
from django.views.generic.detail import DetailView
@ -35,9 +34,8 @@ class PropertyMappingCreateView(
model = PropertyMapping
permission_required = "authentik_core.add_propertymapping"
success_url = "/"
template_name = "generic/create.html"
success_url = reverse_lazy("authentik_admin:property-mappings")
success_message = _("Successfully created Property Mapping")
@ -52,9 +50,8 @@ class PropertyMappingUpdateView(
model = PropertyMapping
permission_required = "authentik_core.change_propertymapping"
success_url = "/"
template_name = "generic/update.html"
success_url = reverse_lazy("authentik_admin:property-mappings")
success_message = _("Successfully updated Property Mapping")
@ -65,9 +62,8 @@ class PropertyMappingDeleteView(
model = PropertyMapping
permission_required = "authentik_core.delete_propertymapping"
success_url = "/"
template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_admin:property-mappings")
success_message = _("Successfully deleted Property Mapping")

View File

@ -4,38 +4,18 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from guardian.mixins import PermissionRequiredMixin
from authentik.admin.views.utils import (
BackSuccessUrlMixin,
DeleteMessageView,
InheritanceCreateView,
InheritanceListView,
InheritanceUpdateView,
SearchListMixin,
UserPaginateListMixin,
)
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(
SuccessMessageMixin,
BackSuccessUrlMixin,
@ -47,9 +27,8 @@ class ProviderCreateView(
model = Provider
permission_required = "authentik_core.add_provider"
success_url = "/"
template_name = "generic/create.html"
success_url = reverse_lazy("authentik_admin:providers")
success_message = _("Successfully created Provider")
@ -64,9 +43,8 @@ class ProviderUpdateView(
model = Provider
permission_required = "authentik_core.change_provider"
success_url = "/"
template_name = "generic/update.html"
success_url = reverse_lazy("authentik_admin:providers")
success_message = _("Successfully updated Provider")
@ -77,7 +55,6 @@ class ProviderDeleteView(
model = Provider
permission_required = "authentik_core.delete_provider"
success_url = "/"
template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_admin:providers")
success_message = _("Successfully deleted Provider")

View File

@ -4,38 +4,18 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
)
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from guardian.mixins import PermissionRequiredMixin
from authentik.admin.views.utils import (
BackSuccessUrlMixin,
DeleteMessageView,
InheritanceCreateView,
InheritanceListView,
InheritanceUpdateView,
SearchListMixin,
UserPaginateListMixin,
)
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(
SuccessMessageMixin,
BackSuccessUrlMixin,
@ -48,8 +28,8 @@ class SourceCreateView(
model = Source
permission_required = "authentik_core.add_source"
success_url = "/"
template_name = "generic/create.html"
success_url = reverse_lazy("authentik_admin:sources")
success_message = _("Successfully created Source")
@ -65,8 +45,8 @@ class SourceUpdateView(
model = Source
permission_required = "authentik_core.change_source"
success_url = "/"
template_name = "generic/update.html"
success_url = reverse_lazy("authentik_admin:sources")
success_message = _("Successfully updated Source")
@ -76,6 +56,6 @@ class SourceDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessag
model = Source
permission_required = "authentik_core.delete_source"
success_url = "/"
template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_admin:sources")
success_message = _("Successfully deleted Source")

View File

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

View File

@ -1,10 +1,18 @@
"""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.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField
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.lib.templatetags.authentik_utils import verbose_name
from authentik.lib.utils.reflection import all_subclasses
class ProviderSerializer(ModelSerializer, MetaNameSerializer):
@ -51,3 +59,26 @@ class ProviderViewSet(ModelViewSet):
def get_queryset(self):
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"""
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.viewsets import ReadOnlyModelViewSet
from authentik.admin.forms.source import SOURCE_SERIALIZER_FIELDS
from authentik.core.api.utils import MetaNameSerializer
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
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):
"""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"""
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:
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):
@ -37,3 +47,19 @@ class SourceViewSet(ReadOnlyModelViewSet):
def get_queryset(self):
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"""
from django.db.models.base import Model
from django.http.response import Http404
from drf_yasg2.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.fields import CharField
from rest_framework.request import Request
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 authentik.core.models import Token
@ -19,6 +22,18 @@ class TokenSerializer(ModelSerializer):
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):
"""Token Viewset"""
@ -26,12 +41,15 @@ class TokenViewSet(ModelViewSet):
queryset = Token.filter_not_expired()
serializer_class = TokenSerializer
@swagger_auto_schema(responses={200: TokenViewSerializer(many=False)})
@action(detail=True)
# pylint: disable=unused-argument
def view_key(self, request: Request, identifier: str) -> Response:
"""Return token key and log access"""
tokens = Token.filter_not_expired(identifier=identifier)
if not tokens.exists():
token: Token = self.get_object()
if token.is_expired:
raise Http404
token = tokens.first()
Event.new(EventAction.TOKEN_VIEW, token=token).from_http(request)
return Response({"key": token.key})
Event.new(EventAction.SECRET_VIEW, secret=token).from_http( # noqa # nosec
request
)
return Response(TokenViewSerializer({"key": token.key}).data)

View File

@ -1,5 +1,6 @@
"""API Utilities"""
from django.db.models import Model
from rest_framework.fields import CharField
from rest_framework.serializers import Serializer, SerializerMethodField
@ -22,3 +23,17 @@ class MetaNameSerializer(Serializer):
def get_verbose_name_plural(self, obj: Model) -> str:
"""Return object's plural verbose_name"""
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_ORIGINAL_USER = "authentik_impersonate_original_user"
LOCAL = local()
RESPONSE_HEADER_ID = "X-authentik-id"
class ImpersonateMiddleware:
@ -43,7 +44,7 @@ class RequestIDMiddleware:
setattr(request, "request_id", request_id)
LOCAL.authentik = {"request_id": request_id}
response = self.get_response(request)
response["X-authentik-id"] = request.request_id
response[RESPONSE_HEADER_ID] = request.request_id
del LOCAL.authentik["request_id"]
return response

View File

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

View File

@ -2,15 +2,29 @@
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import load_pem_x509_certificate
from 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 authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction
class CertificateKeyPairSerializer(ModelSerializer):
"""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):
"""Verify that input is a valid PEM x509 Certificate"""
try:
@ -36,7 +50,31 @@ class CertificateKeyPairSerializer(ModelSerializer):
class Meta:
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):
@ -44,3 +82,31 @@ class CertificateKeyPairViewSet(ModelViewSet):
queryset = CertificateKeyPair.objects.all()
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"""
import datetime
import uuid
from typing import Optional
from cryptography import x509
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.x509.oid import NameOID
from authentik import __version__
from authentik.crypto.models import CertificateKeyPair
class CertificateBuilder:
"""Build self-signed certificates"""
@ -17,19 +21,39 @@ class CertificateBuilder:
__builder = None
__certificate = None
common_name: str
def __init__(self):
self.__public_key = None
self.__private_key = None
self.__builder = 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"""
one_day = datetime.timedelta(1, 0, 0)
self.__private_key = rsa.generate_private_key(
public_exponent=65537, key_size=2048, backend=default_backend()
)
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 = (
x509.CertificateBuilder()
.subject_name(
@ -37,7 +61,7 @@ class CertificateBuilder:
[
x509.NameAttribute(
NameOID.COMMON_NAME,
"authentik Self-signed Certificate",
self.common_name,
),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "authentik"),
x509.NameAttribute(
@ -51,13 +75,16 @@ class CertificateBuilder:
[
x509.NameAttribute(
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_after(datetime.datetime.today() + datetime.timedelta(days=365))
.not_valid_after(
datetime.datetime.today() + datetime.timedelta(days=validity_days)
)
.serial_number(int(uuid.uuid4()))
.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
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):
"""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"
PASSWORD_SET = "password_set" # noqa # nosec
TOKEN_VIEW = "token_view" # nosec
SECRET_VIEW = "secret_view" # noqa # nosec
INVITE_USED = "invitation_used"

View File

@ -2,6 +2,7 @@
from guardian.shortcuts import get_anonymous_user
from structlog import get_logger
from authentik.core.models import User
from authentik.events.models import (
Event,
Notification,
@ -53,7 +54,8 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
return
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.empty_result = False
policy_engine.use_cache = False
@ -67,7 +69,7 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
# Create the notification objects
for transport in trigger.transports.all():
for user in trigger.group.users.all():
LOGGER.debug("created notif")
LOGGER.debug("created notification")
notification = Notification.objects.create(
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
result = _locals["result"]
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)
raise exc
return result

View File

@ -1,30 +1,28 @@
"""Outpost API Views"""
from rest_framework.serializers import JSONField, ModelSerializer
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.outposts.models import (
DockerServiceConnection,
KubernetesServiceConnection,
Outpost,
OutpostServiceConnection,
)
class OutpostSerializer(ModelSerializer):
"""Outpost Serializer"""
_config = JSONField()
class ServiceConnectionSerializer(ModelSerializer):
"""ServiceConnection Serializer"""
class Meta:
model = Outpost
fields = ["pk", "name", "providers", "service_connection", "_config"]
model = OutpostServiceConnection
fields = ["pk", "name"]
class OutpostViewSet(ModelViewSet):
"""Outpost Viewset"""
class ServiceConnectionViewSet(ModelViewSet):
"""ServiceConnection Viewset"""
queryset = Outpost.objects.all()
serializer_class = OutpostSerializer
queryset = OutpostServiceConnection.objects.all()
serializer_class = ServiceConnectionSerializer
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.models.base import Model
from django.forms.models import ModelForm
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
from docker.client import DockerClient
from docker.errors import DockerException
@ -33,7 +32,6 @@ from authentik.crypto.models import CertificateKeyPair
from authentik.lib.config import CONFIG
from authentik.lib.models import InheritanceForeignKey
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.template import render_to_string
from authentik.outposts.docker_tls import DockerInlineTLS
OUR_VERSION = parse(__version__)
@ -378,13 +376,6 @@ class Outpost(models.Model):
objects.append(provider)
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:
return f"Outpost {self.name}"

View File

@ -20,6 +20,7 @@ UPDATE_TRIGGERING_MODELS = (
@receiver(post_save)
# pylint: disable=unused-argument
def post_save_update(sender, instance: Model, **_):
"""If an Outpost is saved, Ensure that token is created/updated
@ -29,7 +30,7 @@ def post_save_update(sender, instance: Model, **_):
return
if instance.__module__ == "__fake__":
return
if sender not in UPDATE_TRIGGERING_MODELS:
if not isinstance(instance, UPDATE_TRIGGERING_MODELS):
return
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):
"""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)
def evaluate(self, expression_source: str) -> PolicyResult:

View File

@ -80,7 +80,7 @@ class PolicyProcess(PROCESS_CLASS):
)
try:
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(
EventAction.POLICY_EXECUTION,
message="Policy Execution",
@ -94,16 +94,18 @@ class PolicyProcess(PROCESS_CLASS):
+ "".join(format_tb(src_exc.__traceback__))
+ str(src_exc)
)
# Create policy exception event
self.create_event(EventAction.POLICY_EXCEPTION, message=error_string)
# Create policy exception event, only when we're not debugging
if not self.request.debug:
self.create_event(EventAction.POLICY_EXCEPTION, message=error_string)
LOGGER.debug("P_ENG(proc): error", exc=src_exc)
policy_result = PolicyResult(False, str(src_exc))
policy_result.source_policy = self.binding.policy
# Invert result if policy.negate is set
if self.binding.negate:
policy_result.passing = not policy_result.passing
key = cache_key(self.binding, self.request)
cache.set(key, policy_result)
if not self.request.debug:
key = cache_key(self.binding, self.request)
cache.set(key, policy_result)
LOGGER.debug(
"P_ENG(proc): finished and cached ",
policy=self.binding.policy,

View File

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

View File

@ -59,11 +59,11 @@ class OAuth2ProviderViewSet(ModelViewSet):
queryset = OAuth2Provider.objects.all()
serializer_class = OAuth2ProviderSerializer
@action(methods=["GET"], detail=True)
@swagger_auto_schema(responses={200: OAuth2ProviderSetupURLs(many=False)})
@action(methods=["GET"], detail=True)
# pylint: disable=invalid-name
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)
data = {
"issuer": provider.get_issuer(request),

View File

@ -23,6 +23,8 @@ return {
"family_name": "",
"preferred_username": 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,
authorized_application=application,
flow=self.executor.plan.flow_pk,
scopes=", ".join(self.params.scope),
).from_http(self.request)
return redirect(self.create_response_uri())
except (ClientIdError, RedirectUriError) as error:

View File

@ -18,7 +18,7 @@ class ProxyProviderForm(forms.ModelForm):
)
self.fields["certificate"].queryset = CertificateKeyPair.objects.filter(
key_data__isnull=False
)
).exclude(key_data="")
def save(self, *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.utils import MetaNameSerializer
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):
@ -54,10 +54,10 @@ class SAMLProviderViewSet(ModelViewSet):
queryset = SAMLProvider.objects.all()
serializer_class = SAMLProviderSerializer
@action(methods=["GET"], detail=True)
@swagger_auto_schema(responses={200: SAMLMetadataSerializer(many=False)})
@action(methods=["GET"], detail=True)
# 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"""
provider = get_object_or_404(SAMLProvider, pk=pk)
metadata = DescriptorDownloadView.get_metadata(request, provider)

View File

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

View File

@ -1,29 +1,29 @@
"""authentik SAML IDP URLs"""
from django.urls import path
from authentik.providers.saml import views
from authentik.providers.saml.views import metadata, sso
urlpatterns = [
# SSO Bindings
path(
"<slug:application_slug>/sso/binding/redirect/",
views.SAMLSSOBindingRedirectView.as_view(),
sso.SAMLSSOBindingRedirectView.as_view(),
name="sso-redirect",
),
path(
"<slug:application_slug>/sso/binding/post/",
views.SAMLSSOBindingPOSTView.as_view(),
sso.SAMLSSOBindingPOSTView.as_view(),
name="sso-post",
),
# SSO IdP Initiated
path(
"<slug:application_slug>/sso/binding/init/",
views.SAMLSSOBindingInitView.as_view(),
sso.SAMLSSOBindingInitView.as_view(),
name="sso-init",
),
path(
"<slug:application_slug>/metadata/",
views.DescriptorDownloadView.as_view(),
metadata.DescriptorDownloadView.as_view(),
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 structlog.stdlib import get_logger
from authentik.core.middleware import RESPONSE_HEADER_ID
# DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py
defuse_stdlib()
@ -67,6 +69,7 @@ class ASGILogger:
status_code: int
start: float
content_length: int
request_id: str
def __init__(self, app: ASGIApp):
self.app = app
@ -75,30 +78,30 @@ class ASGILogger:
self.scope = scope
self.content_length = 0
self.headers = dict(scope.get("headers", []))
self.request_id = ""
async def send_hooked(message: Message) -> None:
"""Hooked send method, which records status code and content-length, and for the final
requests logs it"""
headers = dict(message.get("headers", []))
if "status" in message:
self.status_code = message["status"]
if b"Content-Length" in headers:
self.content_length += int(headers.get(b"Content-Length", b"0"))
if message["type"] == "http.response.body" and not message.get(
"more_body", None
):
runtime = int((time() - self.start) * 10 ** 6)
self.log(runtime)
await send(message)
if message["type"] == "http.response.start":
response_headers = dict(message["headers"])
self.request_id = response_headers.get(
RESPONSE_HEADER_ID.encode(), b""
).decode()
if self.headers.get(b"host", b"") == b"authentik-healthcheck-host":
# Don't log healthcheck/readiness requests
await send({"type": "http.response.start", "status": 204, "headers": []})
await send({"type": "http.response.body", "body": ""})
return
if message["type"] == "http.response.body" and not message.get(
"more_body", True
):
runtime = int((time() - self.start) * 1000)
self.log(runtime, request_id=self.request_id)
await send(message)
self.start = time()
if scope["type"] == "lifespan":
@ -117,7 +120,7 @@ class ASGILogger:
# Check if header has multiple values, and use the first one
return client_ip.split(", ")[0]
def log(self, runtime: float):
def log(self, runtime: float, **kwargs):
"""Outpot access logs in a structured format"""
host = self._get_ip()
query_string = ""
@ -129,8 +132,9 @@ class ASGILogger:
method=self.scope.get("method", ""),
scheme=self.scope.get("scheme", ""),
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,
**kwargs,
)

View File

@ -2,6 +2,8 @@
from base64 import b64encode
from django.conf import settings
from django.db import connections
from django.db.utils import OperationalError
from django.http import HttpRequest, HttpResponse
from django.views import View
from django_prometheus.exports import ExportToDjangoView
@ -23,3 +25,22 @@ class MetricsView(View):
return response
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.lib.utils.reflection import get_apps
from authentik.root.monitoring import MetricsView
from authentik.root.monitoring import LiveView, MetricsView, ReadyView
LOGGER = get_logger()
admin.autodiscover()
@ -57,6 +57,8 @@ for _authentik_app in get_apps():
urlpatterns += [
path("administration/django/", admin.site.urls),
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"),
]

View File

@ -1,18 +1,27 @@
"""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 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.ldap.models import LDAPPropertyMapping, LDAPSource
class LDAPSourceSerializer(ModelSerializer, MetaNameSerializer):
class LDAPSourceSerializer(SourceSerializer):
"""LDAP Source Serializer"""
class Meta:
model = LDAPSource
fields = SOURCE_SERIALIZER_FIELDS + [
fields = SourceSerializer.Meta.fields + [
"server_uri",
"bind_cn",
"bind_password",
@ -34,6 +43,39 @@ class LDAPSourceSerializer(ModelSerializer, MetaNameSerializer):
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):
"""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):
"""LDAP PropertyMapping Viewset"""

View File

@ -1,8 +1,6 @@
"""authentik LDAP Models"""
from datetime import datetime
from typing import Optional, Type
from django.core.cache import cache
from django.db import models
from django.forms import ModelForm
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.lib.models import DomainlessURLValidator
from authentik.lib.utils.template import render_to_string
class LDAPSource(Source):
@ -91,16 +88,6 @@ class LDAPSource(Source):
"""Key by which the ldap source status is saved"""
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
@property

View File

@ -4,6 +4,7 @@ from time import time
from django.core.cache import cache
from django.utils.text import slugify
from ldap3.core.exceptions import LDAPException
from structlog.stdlib import get_logger
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
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.users import UserLDAPSynchronizer
LOGGER = get_logger()
@CELERY_APP.task()
def ldap_sync_all():
@ -21,7 +24,7 @@ def ldap_sync_all():
@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"""
try:
source: LDAPSource = LDAPSource.objects.get(pk=source_pk)
@ -49,4 +52,5 @@ def ldap_sync(self: MonitoredTask, source_pk: int):
)
)
except LDAPException as exc:
LOGGER.debug(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"""
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 authentik.admin.forms.source import SOURCE_SERIALIZER_FIELDS
from authentik.core.api.utils import MetaNameSerializer
from authentik.core.api.sources import SourceSerializer
from authentik.sources.oauth.models import OAuthSource
class OAuthSourceSerializer(ModelSerializer, MetaNameSerializer):
class OAuthSourceSerializer(SourceSerializer):
"""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:
model = OAuthSource
fields = SOURCE_SERIALIZER_FIELDS + [
fields = SourceSerializer.Meta.fields + [
"provider_type",
"request_token_url",
"authorization_url",
@ -20,7 +32,9 @@ class OAuthSourceSerializer(ModelSerializer, MetaNameSerializer):
"profile_url",
"consumer_key",
"consumer_secret",
"callback_url",
]
extra_kwargs = {"consumer_secret": {"write_only": True}}
class OAuthSourceViewSet(ModelViewSet):
@ -28,3 +42,4 @@ class OAuthSourceViewSet(ModelViewSet):
queryset = OAuthSource.objects.all()
serializer_class = OAuthSourceSerializer
lookup_field = "slug"

View File

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

View File

@ -64,14 +64,6 @@ class OAuthSource(Source):
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
def ui_user_settings(self) -> Optional[str]:
view_name = "authentik_sources_oauth:oauth-client-user"

View File

@ -9,13 +9,13 @@
<div class="pf-c-card__body">
{% if connections.exists %}
<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 %}">
{% trans 'Disconnect' %}
</a>
{% else %}
<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 %}">
{% trans 'Connect' %}
</a>

View File

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

View File

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

View File

@ -19,7 +19,7 @@ services:
networks:
- internal
server:
image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.1-rc1}
image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.5-stable}
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
@ -40,12 +40,12 @@ services:
traefik.http.routers.app-router.rule: PathPrefix(`/`)
traefik.http.routers.app-router.service: app-service
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'
env_file:
- .env
worker:
image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.1-rc1}
image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.5-stable}
command: worker
networks:
- internal
@ -62,7 +62,7 @@ services:
env_file:
- .env
static:
image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.1-rc1}
image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.5-stable}
networks:
- internal
labels:

View File

@ -4,7 +4,7 @@ name: authentik
home: https://goauthentik.io
sources:
- 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
dependencies:
- name: postgresql

View File

@ -4,7 +4,7 @@
|-----------------------------------|-------------------------|-------------|
| image.name | beryju/authentik | Image used to run the authentik server and worker |
| image.name_static | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) |
| image.tag | 2021.2.1-rc1 | Image tag |
| image.tag | 2021.2.5-stable | Image tag |
| image.pullPolicy | IfNotPresent | Image Pull Policy used for all deployments |
| serverReplicas | 1 | Replicas for the Server deployment |
| workerReplicas | 1 | Replicas for the Worker deployment |

View File

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

View File

@ -5,7 +5,7 @@ image:
name: beryju/authentik
name_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended
tag: 2021.2.1-rc1
tag: 2021.2.5-stable
pullPolicy: IfNotPresent
serverReplicas: 1

View File

@ -3,40 +3,39 @@ module goauthentik.io/outpost
go 1.14
require (
cloud.google.com/go v0.64.0 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/getsentry/sentry-go v0.9.0
github.com/go-openapi/errors v0.19.9
github.com/go-openapi/runtime v0.19.24
github.com/go-openapi/strfmt v0.19.12
github.com/go-openapi/swag v0.19.12
github.com/go-openapi/validate v0.20.1
github.com/go-openapi/errors v0.20.0
github.com/go-openapi/runtime v0.19.26
github.com/go-openapi/strfmt v0.20.0
github.com/go-openapi/swag v0.19.14
github.com/go-openapi/validate v0.20.2
github.com/go-redis/redis/v7 v7.4.0 // indirect
github.com/go-swagger/go-swagger v0.25.0 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/go-swagger/go-swagger v0.26.1 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/gorilla/websocket v1.4.2
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/justinas/alice v1.2.0
github.com/kr/pretty v0.2.1 // 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/pelletier/go-toml v1.8.1 // indirect
github.com/pkg/errors v0.9.1
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f // indirect
github.com/recws-org/recws v1.2.2
github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect
github.com/recws-org/recws v1.2.1
github.com/sirupsen/logrus v1.7.0
github.com/spf13/afero v1.5.1 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1 // indirect
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 // indirect
golang.org/x/mod v0.4.1 // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect
golang.org/x/text v0.3.5 // indirect
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 // indirect
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 // indirect
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
golang.org/x/tools v0.1.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
)

View File

@ -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.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.64.0 h1:xVP3LPvMjGT4J0a55y02Gw5y/dkY/rxGz58sfK1jqIo=
cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4=
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
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.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
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=
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/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/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
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.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.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.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
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.9 h1:9SnKdGhiPZHF3ttwFMiCBEb8jQ4IDdrK+5+a0oTygA4=
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.20.0 h1:Sxpo9PjEHDzhs3FbnGNonvDgWcMW2U7wGTcDDSFSceM=
github.com/go-openapi/errors v0.20.0/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
@ -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.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
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/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
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.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.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.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.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.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/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.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
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.20.0 h1:HGLc8AJ7ynOxwv0Lq4TsnwLsWMawHAYiJIFzbcML86I=
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.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
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.11 h1:0+YvbNh05rmBkgztd6zHp4OCFn7Mtu30bn46NQo2ZRw=
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.19.12/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
github.com/go-openapi/strfmt v0.20.0 h1:l2omNtmNbMc39IGptl9BuXBEKcZfS8zjrTsPKTiJiDM=
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.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
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.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI=
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.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
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.15 h1:oUHZO8jD7p5oRLANlXF0U8ic9ePBUkDQyRZdN0EhL6M=
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/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/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
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-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-swagger/go-swagger v0.25.0 h1:FxhyrWWV8V/A9P6GtI5szWordAdbb6Y0nqdY/y9So2w=
github.com/go-swagger/go-swagger v0.25.0/go.mod h1:9639ioXrPX9E6BbnbaDklGXjNz7upAXoNBwL4Ok11Vk=
github.com/go-swagger/go-swagger v0.26.1 h1:1XUWLnH6hKxHzeKjJfA2gHkSqcT1Zgi4q/PZp2hDdN8=
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/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=
@ -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.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
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/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3 h1:6amM4HsNPOvMLVc2ZnyqrjeQ92YAVWn7T4WBKK87inY=
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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/handlers v1.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/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
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/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
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/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.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
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/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
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.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
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/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/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.8.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/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
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/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-20200819021114-67c6ae64274f h1:JDEmUDtyiLMyMlFwiaDOv2hxUp35497fkwePcLeV7j4=
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ=
github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e h1:BLqxdwZ6j771IpSCRx7s/GJjXHUE00Hmu7/YegCGdzA=
github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -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-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
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.2/go.mod h1:SxTgwQU/jqYSzEgUh4ifDxq/7enApS150f8nZ5Sczk8=
github.com/recws-org/recws v1.2.1 h1:bYocRkAsS71hlQ9AMCVS+hYXHEgEyQsAbYKXf394gZ8=
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/go-internal v1.1.0/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/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.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.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
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.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
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/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
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.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.5/go.mod h1:Ual6Gkco7ZGQw8wE1t4tLnvBsf6yVSM60qW6TgOeJ5c=
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.4 h1:bsPHfODES+/yx2PCWzUYMH8xj6PVniPI8DQrsJuSXSs=
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/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
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-20181029021203-45a5f77698d3/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-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-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc=
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -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.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
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/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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-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-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
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-20181108010431-42b317875d0f/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/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-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-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c=
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-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/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=
@ -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-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-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-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-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a h1:pdfjQ7VswBeGam3EpuEJ4e8EAb7JgaubV570LO/SIQM=
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e h1:Z2uDrs8MyXUWJbwGc4V+nGjV4Ygo+oubBbWSVQw21/I=
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/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b h1:Lq5JUTFhiybGVf28jB6QRpqd13/JPOaCnET17PVzYJE=
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 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-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.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
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-20190307195333-5fe7a883aa19/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-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-20200815001618-f69a88009b70 h1:wboULUXGF3c5qdUnKp+6gLAccE6PRpa/czkYvQ4UXv8=
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
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/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/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
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-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-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-20190106161140-3f1c8253044a/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
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
// The service account this token belongs to should only have access to a single outpost
outposts, err := apiClient.Outposts.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth)
if err != nil {
panic(err)
log.WithError(err).Panic("Failed to fetch configuration")
}
outpost := outposts.Payload.Results[0]
doGlobalSetup(outpost.Config.(map[string]interface{}))
@ -64,7 +66,7 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
Auth: auth,
token: token,
logger: log.WithField("component", "ak-api-controller"),
logger: log,
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)
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
// Send hello message with our version
@ -52,7 +52,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
}
err := ws.WriteJSON(msg)
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() {
notConnectedBackoff := 1
logger := ac.logger.WithField("loop", "ws-handler")
for {
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
}
var wsMsg websocketMessage
@ -109,7 +104,7 @@ func (ac *APIController) startWSHealth() {
},
}
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 {
ac.logger.WithField("loop", "ws-health").Println("write:", err)
ac.wsConn.CloseAndReconnect()

View File

@ -13,7 +13,12 @@ import (
)
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) {
case "debug":
log.SetLevel(log.DebugLevel)

View File

@ -31,7 +31,7 @@ func (s *Server) bundleProviders(providers []*models.ProxyOutpostConfig) []*prov
bundles[idx] = &providerBundle{
s: s,
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)
}

View File

@ -68,7 +68,7 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
if provider.Certificate != nil {
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(),
KpUUID: *provider.Certificate,
}, 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")
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 {
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to parse certificate")
return providerOpts
}
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
}
@ -120,7 +129,7 @@ func (pb *providerBundle) Build(provider *models.ProxyOutpostConfig) {
log.Printf("%s", err)
os.Exit(1)
}
oauthproxy, err := NewOAuthProxy(opts)
oauthproxy, err := NewOAuthProxy(opts, provider)
if err != nil {
log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
os.Exit(1)

View File

@ -95,7 +95,7 @@ type loggingHandler struct {
func LoggingHandler(h http.Handler) http.Handler {
return loggingHandler{
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
responseLogger := &responseLogger{w: w}
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{
"Client": req.RemoteAddr,
"Host": req.Host,
"Protocol": req.Proto,
"RequestDuration": fmt.Sprintf("%0.3f", duration),
"RequestMethod": req.Method,
"ResponseSize": responseLogger.Size(),
"StatusCode": responseLogger.Status(),
"Timestamp": t,
"Upstream": responseLogger.upstream,
"UserAgent": req.UserAgent(),
"Username": responseLogger.authInfo,
"host": req.RemoteAddr,
"vhost": req.Host,
"request_protocol": req.Proto,
"runtime": fmt.Sprintf("%0.3f", duration),
"method": req.Method,
"size": responseLogger.Size(),
"status": responseLogger.Status(),
"upstream": responseLogger.upstream,
"request_useragent": req.UserAgent(),
"request_username": responseLogger.authInfo,
}).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/upstream"
"github.com/oauth2-proxy/oauth2-proxy/providers"
"goauthentik.io/outpost/pkg/models"
log "github.com/sirupsen/logrus"
)
@ -92,8 +93,8 @@ type OAuthProxy struct {
}
// NewOAuthProxy creates a new instance of OAuthProxy from the options provided
func NewOAuthProxy(opts *options.Options) (*OAuthProxy, error) {
logger := log.WithField("component", "proxy").WithField("client-id", opts.ClientID)
func NewOAuthProxy(opts *options.Options, provider *models.ProxyOutpostConfig) (*OAuthProxy, error) {
logger := log.WithField("logger", "authentik.outpost.proxy").WithField("provider", provider.Name)
sessionStore, err := sessions.NewSessionStore(&opts.Session, &opts.Cookie)
if err != nil {
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))
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
if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]string); ok {
if additionalHeaders == nil {

View File

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

View File

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

View File

@ -565,9 +565,9 @@ paths:
parameters: []
responses:
'200':
description: ''
description: Show token's current key
schema:
$ref: '#/definitions/Token'
$ref: '#/definitions/TokenView'
tags:
- core
parameters:
@ -863,6 +863,44 @@ paths:
required: true
type: string
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/:
get:
operationId: events_events_list
@ -1789,6 +1827,11 @@ paths:
operationId: outposts_outposts_list
description: Outpost Viewset
parameters:
- name: providers__isnull
in: query
description: ''
required: false
type: string
- name: ordering
in: query
description: Which field to use when ordering the results.
@ -1911,6 +1954,28 @@ paths:
required: true
type: string
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/:
get:
operationId: outposts_proxy_list
@ -2037,6 +2102,133 @@ paths:
description: A unique integer value identifying this Proxy Provider.
required: true
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/:
get:
operationId: outposts_service_connections_docker_list
@ -4120,6 +4312,47 @@ paths:
tags:
- providers
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}/:
get:
operationId: providers_all_read
@ -4308,7 +4541,7 @@ paths:
/providers/oauth2/{id}/setup_urls/:
get:
operationId: providers_oauth2_setup_urls
description: Return metadata as XML string
description: Get Providers setup URLs
parameters: []
responses:
'200':
@ -4676,6 +4909,42 @@ paths:
tags:
- sources
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}/:
get:
operationId: sources_all_read
@ -4763,7 +5032,7 @@ paths:
tags:
- sources
parameters: []
/sources/ldap/{pbm_uuid}/:
/sources/ldap/{slug}/:
get:
operationId: sources_ldap_read
description: LDAP Source Viewset
@ -4817,12 +5086,33 @@ paths:
tags:
- sources
parameters:
- name: pbm_uuid
- name: slug
in: path
description: A UUID string identifying this LDAP Source.
description: Internal source name, used in URLs.
required: true
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/:
get:
operationId: sources_oauth_list
@ -4890,7 +5180,7 @@ paths:
tags:
- sources
parameters: []
/sources/oauth/{pbm_uuid}/:
/sources/oauth/{slug}/:
get:
operationId: sources_oauth_read
description: Source Viewset
@ -4944,12 +5234,13 @@ paths:
tags:
- sources
parameters:
- name: pbm_uuid
- name: slug
in: path
description: A UUID string identifying this Generic OAuth Source.
description: Internal source name, used in URLs.
required: true
type: string
format: uuid
format: slug
pattern: ^[-a-zA-Z0-9_]+$
/sources/saml/:
get:
operationId: sources_saml_list
@ -5017,7 +5308,7 @@ paths:
tags:
- sources
parameters: []
/sources/saml/{pbm_uuid}/:
/sources/saml/{slug}/:
get:
operationId: sources_saml_read
description: SAMLSource Viewset
@ -5071,12 +5362,13 @@ paths:
tags:
- sources
parameters:
- name: pbm_uuid
- name: slug
in: path
description: A UUID string identifying this SAML Source.
description: Internal source name, used in URLs.
required: true
type: string
format: uuid
format: slug
pattern: ^[-a-zA-Z0-9_]+$
/stages/all/:
get:
operationId: stages_all_list
@ -7536,6 +7828,15 @@ definitions:
description:
title: Description
type: string
TokenView:
description: Show token's current key
type: object
properties:
key:
title: Key
type: string
readOnly: true
minLength: 1
User:
description: User Serializer
required:
@ -7589,6 +7890,10 @@ definitions:
title: Name
type: string
minLength: 1
fingerprint:
title: Fingerprint
type: string
readOnly: true
certificate_data:
title: 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
for encryption.
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:
description: Event Serializer
required:
@ -7624,7 +7947,7 @@ definitions:
- user_write
- suspicious_request
- password_set
- token_view
- secret_view
- invitation_used
- authorize_application
- source_linked
@ -8023,6 +8346,12 @@ definitions:
items:
type: integer
uniqueItems: true
providers_obj:
description: ''
type: array
items:
$ref: '#/definitions/Provider'
readOnly: true
service_connection:
title: Service connection
description: Select Service-Connection authentik should use to manage this
@ -8030,9 +8359,36 @@ definitions:
type: string
format: uuid
x-nullable: true
token_identifier:
title: Token identifier
type: string
readOnly: true
_config:
title: config
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:
title: 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
Header. If not set, the user's Email address is used.
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:
description: DockerServiceConnection Serializer
required:
@ -8347,7 +8718,7 @@ definitions:
- user_write
- suspicious_request
- password_set
- token_view
- secret_view
- invitation_used
- authorize_application
- source_linked
@ -8711,6 +9082,25 @@ definitions:
title: Verbose name plural
type: string
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:
description: OAuth2Provider Serializer
required:
@ -9157,6 +9547,10 @@ definitions:
type: string
format: uuid
x-nullable: true
object_type:
title: Object type
type: string
readOnly: true
verbose_name:
title: Verbose name
type: string
@ -9165,10 +9559,6 @@ definitions:
title: Verbose name plural
type: string
readOnly: true
__type__:
title: 'type '
type: string
readOnly: true
LDAPSource:
description: LDAP Source Serializer
required:
@ -9213,6 +9603,10 @@ definitions:
type: string
format: uuid
x-nullable: true
object_type:
title: Object type
type: string
readOnly: true
verbose_name:
title: Verbose name
type: string
@ -9298,6 +9692,15 @@ definitions:
type: string
format: uuid
uniqueItems: true
LDAPSourceSyncStatus:
description: LDAP Sync status
type: object
properties:
last_sync:
title: Last sync
type: string
format: date-time
readOnly: true
OAuthSource:
description: OAuth Source Serializer
required:
@ -9344,6 +9747,10 @@ definitions:
type: string
format: uuid
x-nullable: true
object_type:
title: Object type
type: string
readOnly: true
verbose_name:
title: Verbose name
type: string
@ -9389,6 +9796,10 @@ definitions:
title: Consumer secret
type: string
minLength: 1
callback_url:
title: Callback url
type: string
readOnly: true
SAMLSource:
description: SAMLSource Serializer
required:
@ -9397,6 +9808,11 @@ definitions:
- sso_url
type: object
properties:
pk:
title: Pbm uuid
type: string
format: uuid
readOnly: true
name:
title: Name
description: Source's display Name.
@ -9425,6 +9841,18 @@ definitions:
type: string
format: uuid
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:
title: Issuer
description: Also known as Entity ID. Defaults the Metadata URL.

150
web/package-lock.json generated
View File

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

View File

@ -18,11 +18,11 @@
"@types/codemirror": "0.0.108",
"chart.js": "^2.9.4",
"codemirror": "^5.59.2",
"construct-style-sheets-polyfill": "^2.4.6",
"construct-style-sheets-polyfill": "^2.4.9",
"flowchart.js": "^1.15.0",
"lit-element": "^2.4.0",
"lit-html": "^1.3.0",
"rollup": "^2.38.4",
"rollup": "^2.38.5",
"rollup-plugin-copy": "^3.3.0",
"rollup-plugin-cssimport": "^1.0.2",
"rollup-plugin-external-globals": "^0.6.1",
@ -30,8 +30,8 @@
},
"devDependencies": {
"@rollup/plugin-typescript": "^8.1.1",
"@typescript-eslint/eslint-plugin": "^4.14.2",
"@typescript-eslint/parser": "^4.14.2",
"@typescript-eslint/eslint-plugin": "^4.15.0",
"@typescript-eslint/parser": "^4.15.0",
"eslint": "^7.19.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-lit": "^1.3.0",
@ -41,6 +41,6 @@
"rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-terser": "^7.0.2",
"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";
export class Application {
@ -22,8 +22,8 @@ export class Application {
return DefaultClient.fetch<Application>(["core", "applications", slug]);
}
static list(filter?: QueryArguments): Promise<PBResponse<Application>> {
return DefaultClient.fetch<PBResponse<Application>>(["core", "applications"], filter);
static list(filter?: QueryArguments): Promise<AKResponse<Application>> {
return DefaultClient.fetch<AKResponse<Application>>(["core", "applications"], filter);
}
static adminUrl(rest: string): string {

View File

@ -7,6 +7,15 @@ export interface QueryArguments {
[key: string]: number | string | boolean | null;
}
export interface BaseInheritanceModel {
object_type: string;
verbose_name: string;
verbose_name_plural: string;
}
export class Client {
makeUrl(url: string[], query?: QueryArguments): string {
let builtUrl = `/api/${VERSION}/${url.join("/")}/`;
@ -85,7 +94,7 @@ export interface PBPagination {
end_index: number;
}
export interface PBResponse<T> {
export interface AKResponse<T> {
pagination: PBPagination;
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";
export class Notification {
@ -17,8 +17,8 @@ export class Notification {
return DefaultClient.fetch<Notification>(["events", "notifications", pk]);
}
static list(filter?: QueryArguments): Promise<PBResponse<Notification>> {
return DefaultClient.fetch<PBResponse<Notification>>(["events", "notifications"], filter);
static list(filter?: QueryArguments): Promise<AKResponse<Notification>> {
return DefaultClient.fetch<AKResponse<Notification>>(["events", "notifications"], filter);
}
static markSeen(pk: string): Promise<{seen: boolean}> {

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