Compare commits

...

55 Commits

Author SHA1 Message Date
7ac870bfe5 release: 0.14.2-stable 2020-12-31 11:58:43 +01:00
101b973cfe Merge branch 'master' into version-0.14 2020-12-31 11:55:59 +01:00
d8dc1f8bb5 web: fix linting 2020-12-31 11:34:30 +01:00
0f4d5bc3b0 web: fix colour for selected sortable table header 2020-12-31 11:31:49 +01:00
6eed549577 web: add EventInfo case for policy_execution 2020-12-31 11:21:52 +01:00
be54ba4fe2 policies: catch error in process to not hang requests 2020-12-31 11:16:17 +01:00
68b9c34f78 policies: fix obj not being set 2020-12-31 11:16:01 +01:00
3584bdf530 events: fix error when creating an even from policyrequests 2020-12-31 11:15:42 +01:00
e712719333 admin: fix reverse urls for application forms 2020-12-31 10:13:06 +01:00
9a21c2f6bd build(deps): bump boto3 from 1.16.45 to 1.16.46 (#443)
Bumps [boto3](https://github.com/boto/boto3) from 1.16.45 to 1.16.46.
- [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.16.45...1.16.46)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-31 08:55:06 +01:00
0632d8ff37 web: fix loading overlay for site-shell 2020-12-30 23:12:55 +01:00
6bfaf71c12 web: fix link on application list 2020-12-30 22:50:27 +01:00
b6c8c319e5 web: default to open sidebar 2020-12-30 22:45:00 +01:00
4fde1b7365 providers/saml: allow audience to be empty 2020-12-30 22:15:28 +01:00
412f5b9210 providers/saml: fix signing and verification kp not being set correctly 2020-12-30 22:11:24 +01:00
a9e53cd52a providers/saml: fix string being passed to lxml 2020-12-30 22:03:01 +01:00
d0ee7908ab providers/saml: force user to select authz flow for import 2020-12-30 22:02:41 +01:00
e69834dec4 providers/saml: show error message why import failed 2020-12-30 22:02:28 +01:00
1b9d22615c build(deps): bump construct-style-sheets-polyfill in /web (#442)
Bumps [construct-style-sheets-polyfill](https://github.com/calebdwilliams/adoptedStyleSheets) from 2.4.3 to 2.4.6.
- [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.3...v2.4.6)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-30 09:23:02 +01:00
e995536a15 build(deps): bump boto3 from 1.16.44 to 1.16.45 (#440)
Bumps [boto3](https://github.com/boto/boto3) from 1.16.44 to 1.16.45.
- [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.16.44...1.16.45)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-30 09:22:52 +01:00
e6818faab1 build(deps): bump uvicorn from 0.13.2 to 0.13.3 (#441)
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.13.2 to 0.13.3.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.13.2...0.13.3)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-30 09:22:42 +01:00
010e834149 root: update security.md 2020-12-29 21:34:34 +01:00
16d5e1d9ff release: 0.14.1-stable 2020-12-29 21:25:49 +01:00
a1bd6bfe17 release: 0.14.1-stable 2020-12-29 21:25:29 +01:00
ebe0f84460 Merge branch 'master' into version-0.14 2020-12-29 21:25:19 +01:00
765ae80698 providers/oauth2: fix error when creating RefreshToken 2020-12-29 21:22:49 +01:00
bbd0ff24d8 docs: add grafana integration docs 2020-12-29 20:43:27 +01:00
7a403613b2 Suggested changes (#434)
L112 - L115 is a suggestion I'm unsure of, but wanted to somehow improve it. Feel free to discard, but I still recommend signup>sign-up on L114 of origin.

On L124, the resulting webpage shows "Active- Directory" (note the space after the hyphen). Not sure how to fix this though.
2020-12-29 20:17:35 +01:00
4ad184a3fb build(deps-dev): bump @typescript-eslint/parser in /web (#439)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.11.0 to 4.11.1.
- [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.11.1/packages/parser)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-29 14:53:40 +01:00
48d5f28e7a build(deps-dev): bump @typescript-eslint/eslint-plugin in /web (#438)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.11.0 to 4.11.1.
- [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.11.1/packages/eslint-plugin)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-29 14:43:30 +01:00
0cb48121b2 build(deps): bump boto3 from 1.16.43 to 1.16.44 (#436)
Bumps [boto3](https://github.com/boto/boto3) from 1.16.43 to 1.16.44.
- [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.16.43...1.16.44)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-29 13:41:19 +01:00
4194ffe2d4 build(deps): bump channels from 3.0.2 to 3.0.3 (#437)
Bumps [channels](https://github.com/django/channels) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/django/channels/releases)
- [Changelog](https://github.com/django/channels/blob/master/CHANGELOG.txt)
- [Commits](https://github.com/django/channels/compare/3.0.2...3.0.3)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-29 13:34:10 +01:00
4636fe7e64 Add migrate command to 0.14 docs. (#435) 2020-12-28 21:51:22 +01:00
182d714b16 docs: fix typo 2020-12-28 18:15:32 +01:00
540c22ce15 release: 0.14.0-stable 2020-12-28 17:49:45 +01:00
8c3008abce release: 0.14.0-rc2 2020-12-28 17:49:39 +01:00
8a22c86aaa release: 0.14.0-rc1 2020-12-28 17:49:35 +01:00
c8805cc082 release: 0.14.0-stable 2020-12-28 17:45:57 +01:00
db92178d0f Merge branch 'master' into version-0.14 2020-12-28 17:45:49 +01:00
22ce142cb8 outposts: include protocol in outpost deployment ports 2020-12-28 17:21:02 +01:00
1a292feebb outposts: always check metadata on reconcile 2020-12-28 17:11:37 +01:00
09f4d812b3 outposts: set field_manager 2020-12-28 17:11:33 +01:00
ad029d3e0a release: 0.14.0-rc2 2020-12-28 16:03:21 +01:00
b0bd68232d Merge branch 'master' into version-0.14 2020-12-28 16:02:46 +01:00
2bab4ebfe8 core: fix library url pattern not matching SPA 2020-12-28 15:06:25 +01:00
65355372ce Merge branch 'master' into version-0.14 2020-12-28 14:33:28 +01:00
a8647caca9 build(deps): bump @types/codemirror from 0.0.102 to 0.0.103 in /web (#433)
Bumps [@types/codemirror](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/codemirror) from 0.0.102 to 0.0.103.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/codemirror)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-28 14:32:57 +01:00
590597caf6 events: replace list view with SPA Page 2020-12-28 14:32:34 +01:00
7b43777b22 web: reset expanded rows on fetch 2020-12-28 14:04:07 +01:00
77861b52e3 web: fix search loading old results when using enter 2020-12-28 13:56:33 +01:00
5f9c1e229c root: return API dates as timestamp 2020-12-28 13:07:49 +01:00
119adb3e7b web: fix old default URL 2020-12-28 13:07:35 +01:00
5db38bd0b7 web: lazy-render expanded table 2020-12-28 13:07:20 +01:00
0e1587bc1a providers/oauth2: don't write authorization code to event log 2020-12-28 01:07:18 +01:00
dc16a8a4c9 providers/proxy: set proxy-size for nginx for larger response 2020-12-28 00:45:58 +01:00
66 changed files with 724 additions and 352 deletions

View File

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

View File

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

98
Pipfile.lock generated
View File

@ -74,18 +74,18 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:0bb2c3159b9f5e0df50430bf06a155bd7f27f480825b6374dde807d42360a668", "sha256:197926eaf0065c2c503914a15edc75f4ac259c1e5ae6d17eabd1ba5d8ebd1554",
"sha256:a49b3ab4bfa2f6394ba60165cfc468410797dd410f32eed47e22f61451ee986e" "sha256:d6991e6fd7d0f63bf94282687700a91f5299b807e544cb3367e9b2faeeaf8c62"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.16.43" "version": "==1.16.46"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:7398c900dbd4e3d61647269215396ea3e8082f494f3e7b65d9b6aca049c1d463", "sha256:85ca6915ad5471e7f6cd1b00610b74601d2970cbf8e9b1bf255697154cf621a3",
"sha256:795a67338cadb0c3a45014a6c81659da6af623a4e973812f87a6f9d9fb7712e9" "sha256:f7d365c689070368a5a0857aa35a81d7c950556189f23065f42798f810a59cae"
], ],
"version": "==1.19.43" "version": "==1.19.46"
}, },
"cachetools": { "cachetools": {
"hashes": [ "hashes": [
@ -152,11 +152,11 @@
}, },
"channels": { "channels": {
"hashes": [ "hashes": [
"sha256:74db79c9eca616be69d38013b22083ab5d3f9ccda1ab5e69096b1bb7da2d9b18", "sha256:056b72e51080a517a0f33a0a30003e03833b551d75394d6636c885d4edb8188f",
"sha256:f50a6e79757a64c1e45e95e144a2ac5f1e99ee44a0718ab182c501f5e5abd268" "sha256:3f15bdd2138bb4796e76ea588a0a344b12a7964ea9b2e456f992fddb988a4317"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.0.2" "version": "==3.0.3"
}, },
"channels-redis": { "channels-redis": {
"hashes": [ "hashes": [
@ -899,10 +899,10 @@
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
"sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268", "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4",
"sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd" "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"
], ],
"version": "==2020.4" "version": "==2020.5"
}, },
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
@ -1083,11 +1083,11 @@
"standard" "standard"
], ],
"hashes": [ "hashes": [
"sha256:6707fa7f4dbd86fd6982a2d4ecdaad2704e4514d23a1e4278104311288b04691", "sha256:1079c50a06f6338095b4f203e7861dbff318dde5f22f3a324fc6e94c7654164c",
"sha256:d19ca083bebd212843e01f689900e5c637a292c63bb336c7f0735a99300a5f38" "sha256:ef1e0bb5f7941c6fe324e06443ddac0331e1632a776175f87891c7bd02694355"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.13.2" "version": "==0.13.3"
}, },
"uvloop": { "uvloop": {
"hashes": [ "hashes": [
@ -1607,10 +1607,10 @@
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
"sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268", "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4",
"sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd" "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"
], ],
"version": "==2020.4" "version": "==2020.5"
}, },
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
@ -1741,38 +1741,38 @@
}, },
"typed-ast": { "typed-ast": {
"hashes": [ "hashes": [
"sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355", "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1",
"sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919", "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d",
"sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d", "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6",
"sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa", "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd",
"sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652", "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37",
"sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75", "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151",
"sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c", "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07",
"sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01", "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440",
"sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d", "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70",
"sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1", "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496",
"sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907", "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea",
"sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c", "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400",
"sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3", "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc",
"sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d", "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606",
"sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b", "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc",
"sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614", "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581",
"sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c", "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412",
"sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb", "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a",
"sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395", "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2",
"sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b", "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787",
"sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41", "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f",
"sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6", "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937",
"sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34", "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64",
"sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe", "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487",
"sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072", "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b",
"sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298", "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41",
"sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91", "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a",
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4", "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3",
"sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f", "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166",
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7" "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"
], ],
"version": "==1.4.1" "version": "==1.4.2"
}, },
"typing-extensions": { "typing-extensions": {
"hashes": [ "hashes": [

View File

@ -6,9 +6,9 @@ As authentik is currently in a pre-stable, only the latest "stable" version is s
| Version | Supported | | Version | Supported |
| -------- | ------------------ | | -------- | ------------------ |
| 0.11.x | :white_check_mark: |
| 0.12.x | :white_check_mark: | | 0.12.x | :white_check_mark: |
| 0.13.x | :white_check_mark: | | 0.13.x | :white_check_mark: |
| 0.14.x | :white_check_mark: |
## Reporting a Vulnerability ## Reporting a Vulnerability

View File

@ -1,2 +1,2 @@
"""authentik""" """authentik"""
__version__ = "0.14.0-rc1" __version__ = "0.14.2-stable"

View File

@ -29,7 +29,7 @@ class ApplicationCreateView(
permission_required = "authentik_core.add_application" permission_required = "authentik_core.add_application"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_admin:applications") success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully created Application") success_message = _("Successfully created Application")
@ -47,7 +47,7 @@ class ApplicationUpdateView(
permission_required = "authentik_core.change_application" permission_required = "authentik_core.change_application"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("authentik_admin:applications") success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully updated Application") success_message = _("Successfully updated Application")
@ -60,5 +60,5 @@ class ApplicationDeleteView(
permission_required = "authentik_core.delete_application" permission_required = "authentik_core.delete_application"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_admin:applications") success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully deleted Application") success_message = _("Successfully deleted Application")

View File

@ -25,7 +25,7 @@ urlpatterns = [
name="user-tokens-delete", name="user-tokens-delete",
), ),
# Libray # Libray
path("library/", library.LibraryView.as_view(), name="overview"), path("library", library.LibraryView.as_view(), name="overview"),
# Impersonation # Impersonation
path( path(
"-/impersonation/<int:user_id>/", "-/impersonation/<int:user_id>/",

View File

@ -48,6 +48,15 @@ class EventViewSet(ReadOnlyModelViewSet):
queryset = Event.objects.all() queryset = Event.objects.all()
serializer_class = EventSerializer serializer_class = EventSerializer
ordering = ["-created"]
search_fields = [
"user",
"action",
"app",
"context",
"client_ip",
]
filterset_fields = ["action"]
@swagger_auto_schema( @swagger_auto_schema(
method="GET", responses={200: EventTopPerUserSerialier(many=True)} method="GET", responses={200: EventTopPerUserSerialier(many=True)}

View File

@ -10,7 +10,6 @@ class AuthentikEventsConfig(AppConfig):
name = "authentik.events" name = "authentik.events"
label = "authentik_events" label = "authentik_events"
verbose_name = "authentik Events" verbose_name = "authentik Events"
mountpoint = "events/"
def ready(self): def ready(self):
import_module("authentik.events.signals") import_module("authentik.events.signals")

View File

@ -1,90 +0,0 @@
{% extends "base/page.html" %}
{% load i18n %}
{% load authentik_utils %}
{% block page_content %}
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-catalog"></i>
{% trans 'Event Log' %}
</h1>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">
{% include 'partials/toolbar_search.html' %}
<button role="ak-refresh" class="pf-c-button pf-m-primary">
{% trans 'Refresh' %}
</button>
{% 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 'Action' %}</th>
<th role="columnheader" scope="col">{% trans 'Context' %}</th>
<th role="columnheader" scope="col">{% trans 'User' %}</th>
<th role="columnheader" scope="col">{% trans 'Creation Date' %}</th>
<th role="columnheader" scope="col">{% trans 'Client IP' %}</th>
</tr>
</thead>
<tbody role="rowgroup">
{% for entry in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ entry.action }}</div>
<small>{{ entry.app|default:'-' }}</small>
</div>
</th>
<td role="cell">
<div>
<div>
<code>{{ entry.context }}</code>
</div>
{% if entry.user.on_behalf_of %}
<small>
{% blocktrans with username=entry.user.on_behalf_of.username %}
On behalf of {{ username }}
{% endblocktrans %}
</small>
{% endif %}
</div>
</td>
<td role="cell">
<div>
<div>{{ entry.user.username }}</div>
<small>
{% blocktrans with pk=entry.user.pk %}
ID: {{ pk }}
{% endblocktrans %}
</small>
</div>
</td>
<td role="cell">
<span>
{{ entry.created }}
</span>
</td>
<td role="cell">
<span>
{{ entry.client_ip }}
</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-pagination pf-m-bottom">
{% include 'partials/pagination.html' %}
</div>
</div>
</section>
</main>
{% endblock %}

View File

@ -1,9 +0,0 @@
"""authentik events urls"""
from django.urls import path
from authentik.events.views import EventListView
urlpatterns = [
# Event Log
path("log/", EventListView.as_view(), name="log"),
]

View File

@ -11,6 +11,7 @@ from django.views.debug import SafeExceptionReporterFilter
from guardian.utils import get_anonymous_user from guardian.utils import get_anonymous_user
from authentik.core.models import User from authentik.core.models import User
from authentik.policies.types import PolicyRequest
# Special keys which are *not* cleaned, even when the default filter # Special keys which are *not* cleaned, even when the default filter
# is matched # is matched
@ -74,6 +75,11 @@ def sanitize_dict(source: Dict[Any, Any]) -> Dict[Any, Any]:
final_dict = {} final_dict = {}
for key, value in source.items(): for key, value in source.items():
if is_dataclass(value): if is_dataclass(value):
# Because asdict calls `copy.deepcopy(obj)` on everything thats not tuple/dict,
# and deepcopy doesn't work with HttpRequests (neither django nor rest_framework).
# Currently, the only dataclass that actually holds an http request is a PolicyRequest
if isinstance(value, PolicyRequest):
value.http_request = None
value = asdict(value) value = asdict(value)
if isinstance(value, dict): if isinstance(value, dict):
final_dict[key] = sanitize_dict(value) final_dict[key] = sanitize_dict(value)

View File

@ -1,30 +0,0 @@
"""authentik Event administration"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView
from guardian.mixins import PermissionListMixin
from authentik.admin.views.utils import SearchListMixin, UserPaginateListMixin
from authentik.events.models import Event
class EventListView(
PermissionListMixin,
LoginRequiredMixin,
SearchListMixin,
UserPaginateListMixin,
ListView,
):
"""Show list of all invitations"""
model = Event
template_name = "events/list.html"
permission_required = "authentik_events.view_event"
ordering = "-created"
search_fields = [
"user",
"action",
"app",
"context",
"client_ip",
]

View File

@ -78,6 +78,8 @@ class FlowViewSet(ModelViewSet):
queryset = Flow.objects.all() queryset = Flow.objects.all()
serializer_class = FlowSerializer serializer_class = FlowSerializer
lookup_field = "slug" lookup_field = "slug"
search_fields = ["name", "slug", "designation", "title"]
filterset_fields = ["flow_uuid", "name", "slug", "designation"]
@swagger_auto_schema(responses={200: FlowDiagramSerializer()}) @swagger_auto_schema(responses={200: FlowDiagramSerializer()})
@action(detail=True, methods=["get"]) @action(detail=True, methods=["get"])

View File

@ -1,5 +1,5 @@
"""Base Controller""" """Base Controller"""
from typing import Dict, List from dataclasses import dataclass
from structlog import get_logger from structlog import get_logger
from structlog.testing import capture_logs from structlog.testing import capture_logs
@ -7,15 +7,26 @@ from structlog.testing import capture_logs
from authentik.lib.sentry import SentryIgnoredException from authentik.lib.sentry import SentryIgnoredException
from authentik.outposts.models import Outpost, OutpostServiceConnection from authentik.outposts.models import Outpost, OutpostServiceConnection
FIELD_MANAGER = "goauthentik.io"
class ControllerException(SentryIgnoredException): class ControllerException(SentryIgnoredException):
"""Exception raised when anything fails during controller run""" """Exception raised when anything fails during controller run"""
@dataclass
class DeploymentPort:
"""Info about deployment's single port."""
port: int
name: str
protocol: str
class BaseController: class BaseController:
"""Base Outpost deployment controller""" """Base Outpost deployment controller"""
deployment_ports: Dict[str, int] deployment_ports: list[DeploymentPort]
outpost: Outpost outpost: Outpost
connection: OutpostServiceConnection connection: OutpostServiceConnection
@ -24,14 +35,14 @@ class BaseController:
self.outpost = outpost self.outpost = outpost
self.connection = connection self.connection = connection
self.logger = get_logger() self.logger = get_logger()
self.deployment_ports = {} self.deployment_ports = []
# pylint: disable=invalid-name # pylint: disable=invalid-name
def up(self): def up(self):
"""Called by scheduled task to reconcile deployment/service/etc""" """Called by scheduled task to reconcile deployment/service/etc"""
raise NotImplementedError raise NotImplementedError
def up_with_logs(self) -> List[str]: def up_with_logs(self) -> list[str]:
"""Call .up() but capture all log output and return it.""" """Call .up() but capture all log output and return it."""
with capture_logs() as logs: with capture_logs() as logs:
self.up() self.up()

View File

@ -68,7 +68,10 @@ class DockerController(BaseController):
"image": image_name, "image": image_name,
"name": f"authentik-proxy-{self.outpost.uuid.hex}", "name": f"authentik-proxy-{self.outpost.uuid.hex}",
"detach": True, "detach": True,
"ports": {x: x for _, x in self.deployment_ports.items()}, "ports": {
f"{port.port}/{port.protocol.lower()}": port.port
for port in self.deployment_ports
},
"environment": self._get_env(), "environment": self._get_env(),
"labels": self._get_labels(), "labels": self._get_labels(),
} }
@ -139,7 +142,10 @@ class DockerController(BaseController):
def get_static_deployment(self) -> str: def get_static_deployment(self) -> str:
"""Generate docker-compose yaml for proxy, version 3.5""" """Generate docker-compose yaml for proxy, version 3.5"""
ports = [f"{x}:{x}" for _, x in self.deployment_ports.items()] ports = [
f"{port.port}:{port.port}/{port.protocol.lower()}"
for port in self.deployment_ports
]
image_prefix = CONFIG.y("outposts.docker_image_base") image_prefix = CONFIG.y("outposts.docker_image_base")
compose = { compose = {
"version": "3.5", "version": "3.5",
@ -154,6 +160,7 @@ class DockerController(BaseController):
), ),
"AUTHENTIK_TOKEN": self.outpost.token.key, "AUTHENTIK_TOKEN": self.outpost.token.key,
}, },
"labels": self._get_labels(),
} }
}, },
} }

View File

@ -93,7 +93,8 @@ class KubernetesObjectReconciler(Generic[T]):
def reconcile(self, current: T, reference: T): def reconcile(self, current: T, reference: T):
"""Check what operations should be done, should be raised as """Check what operations should be done, should be raised as
ReconcileTrigger""" ReconcileTrigger"""
raise NotImplementedError if current.metadata.annotations != reference.metadata.annotations:
raise NeedsUpdate()
def create(self, reference: T): def create(self, reference: T):
"""API Wrapper to create object""" """API Wrapper to create object"""

View File

@ -18,6 +18,7 @@ from kubernetes.client import (
from authentik import __version__ from authentik import __version__
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import ( from authentik.outposts.controllers.k8s.base import (
KubernetesObjectReconciler, KubernetesObjectReconciler,
NeedsUpdate, NeedsUpdate,
@ -43,6 +44,7 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
return f"authentik-outpost-{self.controller.outpost.uuid.hex}" return f"authentik-outpost-{self.controller.outpost.uuid.hex}"
def reconcile(self, current: V1Deployment, reference: V1Deployment): def reconcile(self, current: V1Deployment, reference: V1Deployment):
super().reconcile(current, reference)
if current.spec.replicas != reference.spec.replicas: if current.spec.replicas != reference.spec.replicas:
raise NeedsUpdate() raise NeedsUpdate()
if ( if (
@ -63,8 +65,14 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
"""Get deployment object for outpost""" """Get deployment object for outpost"""
# Generate V1ContainerPort objects # Generate V1ContainerPort objects
container_ports = [] container_ports = []
for port_name, port in self.controller.deployment_ports.items(): for port in self.controller.deployment_ports:
container_ports.append(V1ContainerPort(container_port=port, name=port_name)) container_ports.append(
V1ContainerPort(
container_port=port.port,
name=port.name,
protocol=port.protocol.upper(),
)
)
meta = self.get_object_meta(name=self.name) meta = self.get_object_meta(name=self.name)
secret_name = f"authentik-outpost-{self.controller.outpost.uuid.hex}-api" secret_name = f"authentik-outpost-{self.controller.outpost.uuid.hex}-api"
image_prefix = CONFIG.y("outposts.docker_image_base") image_prefix = CONFIG.y("outposts.docker_image_base")
@ -118,7 +126,9 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
) )
def create(self, reference: V1Deployment): def create(self, reference: V1Deployment):
return self.api.create_namespaced_deployment(self.namespace, reference) return self.api.create_namespaced_deployment(
self.namespace, reference, field_manager=FIELD_MANAGER
)
def delete(self, reference: V1Deployment): def delete(self, reference: V1Deployment):
return self.api.delete_namespaced_deployment( return self.api.delete_namespaced_deployment(

View File

@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
from kubernetes.client import CoreV1Api, V1Secret from kubernetes.client import CoreV1Api, V1Secret
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import ( from authentik.outposts.controllers.k8s.base import (
KubernetesObjectReconciler, KubernetesObjectReconciler,
NeedsUpdate, NeedsUpdate,
@ -30,6 +31,7 @@ class SecretReconciler(KubernetesObjectReconciler[V1Secret]):
return f"authentik-outpost-{self.controller.outpost.uuid.hex}-api" return f"authentik-outpost-{self.controller.outpost.uuid.hex}-api"
def reconcile(self, current: V1Secret, reference: V1Secret): def reconcile(self, current: V1Secret, reference: V1Secret):
super().reconcile(current, reference)
for key in reference.data.keys(): for key in reference.data.keys():
if current.data[key] != reference.data[key]: if current.data[key] != reference.data[key]:
raise NeedsUpdate() raise NeedsUpdate()
@ -51,7 +53,9 @@ class SecretReconciler(KubernetesObjectReconciler[V1Secret]):
) )
def create(self, reference: V1Secret): def create(self, reference: V1Secret):
return self.api.create_namespaced_secret(self.namespace, reference) return self.api.create_namespaced_secret(
self.namespace, reference, field_manager=FIELD_MANAGER
)
def delete(self, reference: V1Secret): def delete(self, reference: V1Secret):
return self.api.delete_namespaced_secret( return self.api.delete_namespaced_secret(

View File

@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
from kubernetes.client import CoreV1Api, V1Service, V1ServicePort, V1ServiceSpec from kubernetes.client import CoreV1Api, V1Service, V1ServicePort, V1ServiceSpec
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import ( from authentik.outposts.controllers.k8s.base import (
KubernetesObjectReconciler, KubernetesObjectReconciler,
NeedsUpdate, NeedsUpdate,
@ -25,6 +26,7 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
return f"authentik-outpost-{self.controller.outpost.uuid.hex}" return f"authentik-outpost-{self.controller.outpost.uuid.hex}"
def reconcile(self, current: V1Service, reference: V1Service): def reconcile(self, current: V1Service, reference: V1Service):
super().reconcile(current, reference)
if len(current.spec.ports) != len(reference.spec.ports): if len(current.spec.ports) != len(reference.spec.ports):
raise NeedsUpdate() raise NeedsUpdate()
for port in reference.spec.ports: for port in reference.spec.ports:
@ -35,8 +37,15 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
"""Get deployment object for outpost""" """Get deployment object for outpost"""
meta = self.get_object_meta(name=self.name) meta = self.get_object_meta(name=self.name)
ports = [] ports = []
for port_name, port in self.controller.deployment_ports.items(): for port in self.controller.deployment_ports:
ports.append(V1ServicePort(name=port_name, port=port)) ports.append(
V1ServicePort(
name=port.name,
port=port.port,
protocol=port.protocol.upper(),
target_port=port.port,
)
)
selector_labels = DeploymentReconciler(self.controller).get_pod_meta() selector_labels = DeploymentReconciler(self.controller).get_pod_meta()
return V1Service( return V1Service(
metadata=meta, metadata=meta,
@ -44,7 +53,9 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
) )
def create(self, reference: V1Service): def create(self, reference: V1Service):
return self.api.create_namespaced_service(self.namespace, reference) return self.api.create_namespaced_service(
self.namespace, reference, field_manager=FIELD_MANAGER
)
def delete(self, reference: V1Service): def delete(self, reference: V1Service):
return self.api.delete_namespaced_service( return self.api.delete_namespaced_service(

View File

@ -56,6 +56,7 @@ class PolicyEngine:
raise ValueError(f"{pbm} is not instance of PolicyBindingModel") raise ValueError(f"{pbm} is not instance of PolicyBindingModel")
self.__pbm = pbm self.__pbm = pbm
self.request = PolicyRequest(user) self.request = PolicyRequest(user)
self.request.obj = pbm
if request: if request:
self.request.http_request = request self.request.http_request = request
self.__cached_policies = [] self.__cached_policies = []

View File

@ -93,4 +93,8 @@ class PolicyProcess(Process):
span: Span span: Span
span.set_data("policy", self.binding.policy) span.set_data("policy", self.binding.policy)
span.set_data("request", self.request) span.set_data("request", self.request)
self.connection.send(self.execute()) try:
self.connection.send(self.execute())
except Exception as exc: # pylint: disable=broad-except
LOGGER.warning(exc)
self.connection.send(PolicyResult(False, str(exc)))

View File

@ -398,7 +398,7 @@ class AuthorizationCode(ExpiringModel, BaseGrantModel):
verbose_name_plural = _("Authorization Codes") verbose_name_plural = _("Authorization Codes")
def __str__(self): def __str__(self):
return "{0} - {1}".format(self.provider, self.code) return f"Authorization code for {self.provider} for user {self.user}"
@dataclass @dataclass
@ -461,7 +461,7 @@ class RefreshToken(ExpiringModel, BaseGrantModel):
self._id_token = json.dumps(asdict(value)) self._id_token = json.dumps(asdict(value))
def __str__(self): def __str__(self):
return f"{self.provider} - {self.access_token}" return f"Refresh Token for {self.provider} for user {self.user}"
@property @property
def at_hash(self): def at_hash(self):

View File

@ -2,6 +2,7 @@
from typing import Dict from typing import Dict
from urllib.parse import urlparse from urllib.parse import urlparse
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.docker import DockerController from authentik.outposts.controllers.docker import DockerController
from authentik.outposts.models import DockerServiceConnection, Outpost from authentik.outposts.models import DockerServiceConnection, Outpost
from authentik.providers.proxy.models import ProxyProvider from authentik.providers.proxy.models import ProxyProvider
@ -12,10 +13,10 @@ class ProxyDockerController(DockerController):
def __init__(self, outpost: Outpost, connection: DockerServiceConnection): def __init__(self, outpost: Outpost, connection: DockerServiceConnection):
super().__init__(outpost, connection) super().__init__(outpost, connection)
self.deployment_ports = { self.deployment_ports = [
"http": 4180, DeploymentPort(4180, "http", "tcp"),
"https": 4443, DeploymentPort(4443, "https", "tcp"),
} ]
def _get_labels(self) -> Dict[str, str]: def _get_labels(self) -> Dict[str, str]:
hosts = [] hosts = []

View File

@ -15,6 +15,7 @@ from kubernetes.client.models.networking_v1beta1_ingress_rule import (
NetworkingV1beta1IngressRule, NetworkingV1beta1IngressRule,
) )
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import ( from authentik.outposts.controllers.k8s.base import (
KubernetesObjectReconciler, KubernetesObjectReconciler,
NeedsUpdate, NeedsUpdate,
@ -39,6 +40,7 @@ class IngressReconciler(KubernetesObjectReconciler[NetworkingV1beta1Ingress]):
def reconcile( def reconcile(
self, current: NetworkingV1beta1Ingress, reference: NetworkingV1beta1Ingress self, current: NetworkingV1beta1Ingress, reference: NetworkingV1beta1Ingress
): ):
super().reconcile(current, reference)
# Create a list of all expected host and tls hosts # Create a list of all expected host and tls hosts
expected_hosts = [] expected_hosts = []
expected_hosts_tls = [] expected_hosts_tls = []
@ -74,11 +76,13 @@ class IngressReconciler(KubernetesObjectReconciler[NetworkingV1beta1Ingress]):
# goes to the same pod # goes to the same pod
"nginx.ingress.kubernetes.io/affinity": "cookie", "nginx.ingress.kubernetes.io/affinity": "cookie",
"traefik.ingress.kubernetes.io/affinity": "true", "traefik.ingress.kubernetes.io/affinity": "true",
"nginx.ingress.kubernetes.io/proxy-buffers-number": "4",
"nginx.ingress.kubernetes.io/proxy-buffer-size": "16k",
} }
annotations.update( annotations.update(
self.controller.outpost.config.kubernetes_ingress_annotations self.controller.outpost.config.kubernetes_ingress_annotations
) )
return dict() return annotations
def get_reference_object(self) -> NetworkingV1beta1Ingress: def get_reference_object(self) -> NetworkingV1beta1Ingress:
"""Get deployment object for outpost""" """Get deployment object for outpost"""
@ -102,7 +106,7 @@ class IngressReconciler(KubernetesObjectReconciler[NetworkingV1beta1Ingress]):
NetworkingV1beta1HTTPIngressPath( NetworkingV1beta1HTTPIngressPath(
backend=NetworkingV1beta1IngressBackend( backend=NetworkingV1beta1IngressBackend(
service_name=self.name, service_name=self.name,
service_port=self.controller.deployment_ports["http"], service_port="http",
), ),
path="/", path="/",
) )
@ -122,7 +126,9 @@ class IngressReconciler(KubernetesObjectReconciler[NetworkingV1beta1Ingress]):
) )
def create(self, reference: NetworkingV1beta1Ingress): def create(self, reference: NetworkingV1beta1Ingress):
return self.api.create_namespaced_ingress(self.namespace, reference) return self.api.create_namespaced_ingress(
self.namespace, reference, field_manager=FIELD_MANAGER
)
def delete(self, reference: NetworkingV1beta1Ingress): def delete(self, reference: NetworkingV1beta1Ingress):
return self.api.delete_namespaced_ingress( return self.api.delete_namespaced_ingress(

View File

@ -1,4 +1,5 @@
"""Proxy Provider Kubernetes Contoller""" """Proxy Provider Kubernetes Contoller"""
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.kubernetes import KubernetesController from authentik.outposts.controllers.kubernetes import KubernetesController
from authentik.outposts.models import KubernetesServiceConnection, Outpost from authentik.outposts.models import KubernetesServiceConnection, Outpost
from authentik.providers.proxy.controllers.k8s.ingress import IngressReconciler from authentik.providers.proxy.controllers.k8s.ingress import IngressReconciler
@ -9,9 +10,9 @@ class ProxyKubernetesController(KubernetesController):
def __init__(self, outpost: Outpost, connection: KubernetesServiceConnection): def __init__(self, outpost: Outpost, connection: KubernetesServiceConnection):
super().__init__(outpost, connection) super().__init__(outpost, connection)
self.deployment_ports = { self.deployment_ports = [
"http": 4180, DeploymentPort(4180, "http", "tcp"),
"https": 4443, DeploymentPort(4443, "https", "tcp"),
} ]
self.reconcilers["ingress"] = IngressReconciler self.reconcilers["ingress"] = IngressReconciler
self.reconcile_order.append("ingress") self.reconcile_order.append("ingress")

View File

@ -7,7 +7,7 @@ from authentik.providers.proxy.models import ProxyProvider
class ProxyProviderForm(forms.ModelForm): class ProxyProviderForm(forms.ModelForm):
"""Security Gateway Provider form""" """Proxy Provider form"""
instance: ProxyProvider instance: ProxyProvider

View File

@ -36,17 +36,17 @@ class SAMLProviderForm(forms.ModelForm):
"name", "name",
"authorization_flow", "authorization_flow",
"acs_url", "acs_url",
"audience",
"issuer", "issuer",
"sp_binding", "sp_binding",
"audience",
"signing_kp",
"verification_kp",
"property_mappings",
"assertion_valid_not_before", "assertion_valid_not_before",
"assertion_valid_not_on_or_after", "assertion_valid_not_on_or_after",
"session_valid_not_on_or_after", "session_valid_not_on_or_after",
"digest_algorithm", "digest_algorithm",
"signature_algorithm", "signature_algorithm",
"signing_kp",
"verification_kp",
"property_mappings",
] ]
widgets = { widgets = {
"name": forms.TextInput(), "name": forms.TextInput(),
@ -94,6 +94,9 @@ class SAMLProviderImportForm(forms.Form):
"""Create a SAML Provider from SP Metadata.""" """Create a SAML Provider from SP Metadata."""
provider_name = forms.CharField() provider_name = forms.CharField()
authorization_flow = forms.ModelChoiceField(
queryset=Flow.objects.filter(designation=FlowDesignation.AUTHORIZATION)
)
metadata = forms.FileField( metadata = forms.FileField(
validators=[FileExtensionValidator(allowed_extensions=["xml"])] validators=[FileExtensionValidator(allowed_extensions=["xml"])]
) )

View File

@ -0,0 +1,22 @@
# Generated by Django 3.1.4 on 2020-12-30 21:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_saml", "0009_auto_20201112_2016"),
]
operations = [
migrations.AlterField(
model_name="samlprovider",
name="audience",
field=models.TextField(
blank=True,
default="",
help_text="Value of the audience restriction field of the asseration. When left empty, no audience restriction will be added.",
),
),
]

View File

@ -42,7 +42,13 @@ class SAMLProvider(Provider):
acs_url = models.URLField(verbose_name=_("ACS URL")) acs_url = models.URLField(verbose_name=_("ACS URL"))
audience = models.TextField( audience = models.TextField(
default="", default="",
help_text=_("Value of the audience restriction field of the asseration."), blank=True,
help_text=_(
(
"Value of the audience restriction field of the asseration. When left empty, "
"no audience restriction will be added."
)
),
) )
issuer = models.TextField( issuer = models.TextField(
help_text=_("Also known as EntityID"), default="authentik" help_text=_("Also known as EntityID"), default="authentik"

View File

@ -127,11 +127,14 @@ class AssertionProcessor:
conditions = Element(f"{{{NS_SAML_ASSERTION}}}Conditions") conditions = Element(f"{{{NS_SAML_ASSERTION}}}Conditions")
conditions.attrib["NotBefore"] = self._valid_not_before conditions.attrib["NotBefore"] = self._valid_not_before
conditions.attrib["NotOnOrAfter"] = self._valid_not_on_or_after conditions.attrib["NotOnOrAfter"] = self._valid_not_on_or_after
audience_restriction = SubElement( if self.provider.audience != "":
conditions, f"{{{NS_SAML_ASSERTION}}}AudienceRestriction" audience_restriction = SubElement(
) conditions, f"{{{NS_SAML_ASSERTION}}}AudienceRestriction"
audience = SubElement(audience_restriction, f"{{{NS_SAML_ASSERTION}}}Audience") )
audience.text = self.provider.audience audience = SubElement(
audience_restriction, f"{{{NS_SAML_ASSERTION}}}Audience"
)
audience.text = self.provider.audience
return conditions return conditions
def get_name_id(self) -> Element: def get_name_id(self) -> Element:

View File

@ -5,6 +5,7 @@ from typing import Optional
import xmlsec import xmlsec
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.x509 import load_pem_x509_certificate from cryptography.x509 import load_pem_x509_certificate
from defusedxml.lxml import fromstring
from lxml import etree # nosec from lxml import etree # nosec
from structlog import get_logger from structlog import get_logger
@ -23,6 +24,8 @@ LOGGER = get_logger()
def format_pem_certificate(unformatted_cert: str) -> str: def format_pem_certificate(unformatted_cert: str) -> str:
"""Format single, inline certificate into PEM Format""" """Format single, inline certificate into PEM Format"""
# Ensure that all linebreaks are gone
unformatted_cert = unformatted_cert.replace("\n", "")
chunks, chunk_size = len(unformatted_cert), 64 chunks, chunk_size = len(unformatted_cert), 64
lines = [PEM_HEADER] lines = [PEM_HEADER]
for i in range(0, chunks, chunk_size): for i in range(0, chunks, chunk_size):
@ -52,10 +55,14 @@ class ServiceProviderMetadata:
provider.issuer = self.entity_id provider.issuer = self.entity_id
provider.sp_binding = self.acs_binding provider.sp_binding = self.acs_binding
provider.acs_url = self.acs_location provider.acs_url = self.acs_location
if self.signing_keypair: if self.signing_keypair and self.auth_n_request_signed:
self.signing_keypair.name = f"Provider {name} - SAML Signing Certificate" self.signing_keypair.name = f"Provider {name} - SAML Signing Certificate"
self.signing_keypair.save() self.signing_keypair.save()
provider.signing_kp = self.signing_keypair provider.verification_kp = self.signing_keypair
if self.assertion_signed:
provider.signing_kp = CertificateKeyPair.objects.exclude(
key_data__iexact=""
).first()
return provider return provider
@ -104,7 +111,7 @@ class ServiceProviderMetadataParser:
def parse(self, raw_xml: str) -> ServiceProviderMetadata: def parse(self, raw_xml: str) -> ServiceProviderMetadata:
"""Parse raw XML to ServiceProviderMetadata""" """Parse raw XML to ServiceProviderMetadata"""
root = etree.fromstring(raw_xml) # nosec root = fromstring(raw_xml.encode())
entity_id = root.attrib["entityID"] entity_id = root.attrib["entityID"]
sp_sso_descriptors = root.findall(f"{{{NS_SAML_METADATA}}}SPSSODescriptor") sp_sso_descriptors = root.findall(f"{{{NS_SAML_METADATA}}}SPSSODescriptor")

View File

@ -84,7 +84,9 @@ class TestServiceProviderMetadataParser(TestCase):
provider.issuer, "http://localhost:8080/apps/user_saml/saml/metadata" provider.issuer, "http://localhost:8080/apps/user_saml/saml/metadata"
) )
self.assertEqual(provider.sp_binding, SAMLBindings.POST) self.assertEqual(provider.sp_binding, SAMLBindings.POST)
self.assertEqual(provider.signing_kp.certificate_data, CERT) self.assertEqual(provider.verification_kp.certificate_data, CERT)
self.assertIsNotNone(provider.signing_kp)
self.assertEqual(provider.audience, "")
def test_with_signing_cert_invalid_signature(self): def test_with_signing_cert_invalid_signature(self):
"""Test Metadata with signing cert (invalid signature)""" """Test Metadata with signing cert (invalid signature)"""

View File

@ -270,8 +270,14 @@ class MetadataImportView(LoginRequiredMixin, FormView):
form.cleaned_data["metadata"].read().decode() form.cleaned_data["metadata"].read().decode()
) )
provider = metadata.to_provider(form.cleaned_data["provider_name"]) provider = metadata.to_provider(form.cleaned_data["provider_name"])
provider.authorization_flow = form.cleaned_data["authorization_flow"]
provider.save() provider.save()
messages.success(self.request, _("Successfully created Provider")) messages.success(self.request, _("Successfully created Provider"))
except ValueError: except ValueError as exc:
messages.error(self.request, _("Failed to import Metadata.")) LOGGER.warning(exc)
messages.error(
self.request,
_("Failed to import Metadata: %(message)s" % {"message": str(exc)}),
)
return super().form_invalid(form)
return super().form_valid(form) return super().form_valid(form)

View File

@ -142,6 +142,7 @@ SWAGGER_SETTINGS = {
REST_FRAMEWORK = { REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "authentik.api.pagination.Pagination", "DEFAULT_PAGINATION_CLASS": "authentik.api.pagination.Pagination",
"PAGE_SIZE": 100, "PAGE_SIZE": 100,
"DATETIME_FORMAT": "%s",
"DEFAULT_FILTER_BACKENDS": [ "DEFAULT_FILTER_BACKENDS": [
"rest_framework_guardian.filters.ObjectPermissionsFilter", "rest_framework_guardian.filters.ObjectPermissionsFilter",
"django_filters.rest_framework.DjangoFilterBackend", "django_filters.rest_framework.DjangoFilterBackend",

View File

@ -19,7 +19,7 @@ services:
networks: networks:
- internal - internal
server: server:
image: beryju/authentik:${AUTHENTIK_TAG:-0.14.0-rc1} image: beryju/authentik:${AUTHENTIK_TAG:-0.14.2-stable}
command: server command: server
environment: environment:
AUTHENTIK_REDIS__HOST: redis AUTHENTIK_REDIS__HOST: redis
@ -44,7 +44,7 @@ services:
env_file: env_file:
- .env - .env
worker: worker:
image: beryju/authentik:${AUTHENTIK_TAG:-0.14.0-rc1} image: beryju/authentik:${AUTHENTIK_TAG:-0.14.2-stable}
command: worker command: worker
networks: networks:
- internal - internal
@ -60,7 +60,7 @@ services:
env_file: env_file:
- .env - .env
static: static:
image: beryju/authentik-static:${AUTHENTIK_TAG:-0.14.0-rc1} image: beryju/authentik-static:${AUTHENTIK_TAG:-0.14.2-stable}
networks: networks:
- internal - internal
labels: labels:

View File

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

View File

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

View File

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

View File

@ -1,3 +1,3 @@
package pkg package pkg
const VERSION = "0.14.0-rc1" const VERSION = "0.14.2-stable"

View File

@ -868,6 +868,11 @@ paths:
operationId: events_events_list operationId: events_events_list
description: Event Read-Only Viewset description: Event Read-Only Viewset
parameters: parameters:
- name: action
in: query
description: ''
required: false
type: string
- name: ordering - name: ordering
in: query in: query
description: Which field to use when ordering the results. description: Which field to use when ordering the results.
@ -919,6 +924,11 @@ paths:
operationId: events_events_top_per_user operationId: events_events_top_per_user
description: Get the top_n events grouped by user count description: Get the top_n events grouped by user count
parameters: parameters:
- name: action
in: query
description: ''
required: false
type: string
- name: ordering - name: ordering
in: query in: query
description: Which field to use when ordering the results. description: Which field to use when ordering the results.
@ -1194,6 +1204,26 @@ paths:
operationId: flows_instances_list operationId: flows_instances_list
description: Flow Viewset description: Flow Viewset
parameters: parameters:
- name: flow_uuid
in: query
description: ''
required: false
type: string
- name: name
in: query
description: ''
required: false
type: string
- name: slug
in: query
description: ''
required: false
type: string
- name: designation
in: query
description: ''
required: false
type: string
- name: ordering - name: ordering
in: query in: query
description: Which field to use when ordering the results. description: Which field to use when ordering the results.
@ -7974,9 +8004,9 @@ definitions:
minLength: 1 minLength: 1
audience: audience:
title: Audience title: Audience
description: Value of the audience restriction field of the asseration. description: Value of the audience restriction field of the asseration. When
left empty, no audience restriction will be added.
type: string type: string
minLength: 1
issuer: issuer:
title: Issuer title: Issuer
description: Also known as EntityID description: Also known as EntityID
@ -8133,6 +8163,14 @@ definitions:
type: string type: string
format: uuid format: uuid
x-nullable: true x-nullable: true
verbose_name:
title: Verbose name
type: string
readOnly: true
verbose_name_plural:
title: Verbose name plural
type: string
readOnly: true
__type__: __type__:
title: 'type ' title: 'type '
type: string type: string
@ -8181,6 +8219,14 @@ definitions:
type: string type: string
format: uuid format: uuid
x-nullable: true x-nullable: true
verbose_name:
title: Verbose name
type: string
readOnly: true
verbose_name_plural:
title: Verbose name plural
type: string
readOnly: true
server_uri: server_uri:
title: Server URI title: Server URI
type: string type: string
@ -8296,6 +8342,14 @@ definitions:
type: string type: string
format: uuid format: uuid
x-nullable: true x-nullable: true
verbose_name:
title: Verbose name
type: string
readOnly: true
verbose_name_plural:
title: Verbose name plural
type: string
readOnly: true
provider_type: provider_type:
title: Provider type title: Provider type
type: string type: string

138
web/package-lock.json generated
View File

@ -304,9 +304,9 @@
} }
}, },
"@types/codemirror": { "@types/codemirror": {
"version": "0.0.102", "version": "0.0.103",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.102.tgz", "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.103.tgz",
"integrity": "sha512-WZZW8VL8BAzzAZWkiYnM5VsYz1qWYieRqHPPtC/BB015QXd3LPXtBlbRYA8lauKgM10qWYeLH8p5LsIn2SLXfA==", "integrity": "sha512-dYQTrIcZal0pnYz/ODjpJB+yadKJhGHywylAlHKjE8VSzGiw2A+6S+hD6jfyXw02ToFR9DO52X+O1pvHn31sbg==",
"requires": { "requires": {
"@types/tern": "*" "@types/tern": "*"
} }
@ -393,13 +393,13 @@
} }
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.11.1.tgz",
"integrity": "sha512-x4arJMXBxyD6aBXLm3W7mSDZRiABzy+2PCLJbL7OPqlp53VXhaA1HKK7R2rTee5OlRhnUgnp8lZyVIqjnyPT6g==", "integrity": "sha512-fABclAX2QIEDmTMk6Yd7Muv1CzFLwWM4505nETzRHpP3br6jfahD9UUJkhnJ/g2m7lwfz8IlswcwGGPGiq9exw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "4.11.0", "@typescript-eslint/experimental-utils": "4.11.1",
"@typescript-eslint/scope-manager": "4.11.0", "@typescript-eslint/scope-manager": "4.11.1",
"debug": "^4.1.1", "debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0", "regexpp": "^3.0.0",
@ -408,71 +408,71 @@
}, },
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.11.1.tgz",
"integrity": "sha512-6VSTm/4vC2dHM3ySDW9Kl48en+yLNfVV6LECU8jodBHQOhO8adAVizaZ1fV0QGZnLQjQ/y0aBj5/KXPp2hBTjA==", "integrity": "sha512-Al2P394dx+kXCl61fhrrZ1FTI7qsRDIUiVSuN6rTwss6lUn8uVO2+nnF4AvO0ug8vMsy3ShkbxLu/uWZdTtJMQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"@typescript-eslint/visitor-keys": "4.11.0" "@typescript-eslint/visitor-keys": "4.11.1"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.11.1.tgz",
"integrity": "sha512-XXOdt/NPX++txOQHM1kUMgJUS43KSlXGdR/aDyEwuAEETwuPt02Nc7v+s57PzuSqMbNLclblQdv3YcWOdXhQ7g==", "integrity": "sha512-5kvd38wZpqGY4yP/6W3qhYX6Hz0NwUbijVsX2rxczpY6OXaMxh0+5E5uLJKVFwaBM7PJe1wnMym85NfKYIh6CA==",
"dev": true "dev": true
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.1.tgz",
"integrity": "sha512-tRYKyY0i7cMk6v4UIOCjl1LhuepC/pc6adQqJk4Is3YcC6k46HvsV9Wl7vQoLbm9qADgeujiT7KdLrylvFIQ+A==", "integrity": "sha512-IrlBhD9bm4bdYcS8xpWarazkKXlE7iYb1HzRuyBP114mIaj5DJPo11Us1HgH60dTt41TCZXMaTCAW+OILIYPOg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
} }
} }
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.11.1.tgz",
"integrity": "sha512-1VC6mSbYwl1FguKt8OgPs8xxaJgtqFpjY/UzUYDBKq4pfQ5lBvN2WVeqYkzf7evW42axUHYl2jm9tNyFsb8oLg==", "integrity": "sha512-mAlWowT4A6h0TC9F+J5pdbEhjNiEMO+kqPKQ4sc3fVieKL71dEqfkKgtcFVSX3cjSBwYwhImaQ/mXQF0oaI38g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.3", "@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.11.0", "@typescript-eslint/scope-manager": "4.11.1",
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"@typescript-eslint/typescript-estree": "4.11.0", "@typescript-eslint/typescript-estree": "4.11.1",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0" "eslint-utils": "^2.0.0"
}, },
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.11.1.tgz",
"integrity": "sha512-6VSTm/4vC2dHM3ySDW9Kl48en+yLNfVV6LECU8jodBHQOhO8adAVizaZ1fV0QGZnLQjQ/y0aBj5/KXPp2hBTjA==", "integrity": "sha512-Al2P394dx+kXCl61fhrrZ1FTI7qsRDIUiVSuN6rTwss6lUn8uVO2+nnF4AvO0ug8vMsy3ShkbxLu/uWZdTtJMQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"@typescript-eslint/visitor-keys": "4.11.0" "@typescript-eslint/visitor-keys": "4.11.1"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.11.1.tgz",
"integrity": "sha512-XXOdt/NPX++txOQHM1kUMgJUS43KSlXGdR/aDyEwuAEETwuPt02Nc7v+s57PzuSqMbNLclblQdv3YcWOdXhQ7g==", "integrity": "sha512-5kvd38wZpqGY4yP/6W3qhYX6Hz0NwUbijVsX2rxczpY6OXaMxh0+5E5uLJKVFwaBM7PJe1wnMym85NfKYIh6CA==",
"dev": true "dev": true
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.1.tgz",
"integrity": "sha512-eA6sT5dE5RHAFhtcC+b5WDlUIGwnO9b0yrfGa1mIOIAjqwSQCpXbLiFmKTdRbQN/xH2EZkGqqLDrKUuYOZ0+Hg==", "integrity": "sha512-tC7MKZIMRTYxQhrVAFoJq/DlRwv1bnqA4/S2r3+HuHibqvbrPcyf858lNzU7bFmy4mLeIHFYr34ar/1KumwyRw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"@typescript-eslint/visitor-keys": "4.11.0", "@typescript-eslint/visitor-keys": "4.11.1",
"debug": "^4.1.1", "debug": "^4.1.1",
"globby": "^11.0.1", "globby": "^11.0.1",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -482,12 +482,12 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.1.tgz",
"integrity": "sha512-tRYKyY0i7cMk6v4UIOCjl1LhuepC/pc6adQqJk4Is3YcC6k46HvsV9Wl7vQoLbm9qADgeujiT7KdLrylvFIQ+A==", "integrity": "sha512-IrlBhD9bm4bdYcS8xpWarazkKXlE7iYb1HzRuyBP114mIaj5DJPo11Us1HgH60dTt41TCZXMaTCAW+OILIYPOg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
} }
}, },
@ -508,41 +508,41 @@
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.11.1.tgz",
"integrity": "sha512-NBTtKCC7ZtuxEV5CrHUO4Pg2s784pvavc3cnz6V+oJvVbK4tH9135f/RBP6eUA2KHiFKAollSrgSctQGmHbqJQ==", "integrity": "sha512-BJ3jwPQu1jeynJ5BrjLuGfK/UJu6uwHxJ/di7sanqmUmxzmyIcd3vz58PMR7wpi8k3iWq2Q11KMYgZbUpRoIPw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/scope-manager": "4.11.0", "@typescript-eslint/scope-manager": "4.11.1",
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"@typescript-eslint/typescript-estree": "4.11.0", "@typescript-eslint/typescript-estree": "4.11.1",
"debug": "^4.1.1" "debug": "^4.1.1"
} }
}, },
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.11.1.tgz",
"integrity": "sha512-6VSTm/4vC2dHM3ySDW9Kl48en+yLNfVV6LECU8jodBHQOhO8adAVizaZ1fV0QGZnLQjQ/y0aBj5/KXPp2hBTjA==", "integrity": "sha512-Al2P394dx+kXCl61fhrrZ1FTI7qsRDIUiVSuN6rTwss6lUn8uVO2+nnF4AvO0ug8vMsy3ShkbxLu/uWZdTtJMQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"@typescript-eslint/visitor-keys": "4.11.0" "@typescript-eslint/visitor-keys": "4.11.1"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.11.1.tgz",
"integrity": "sha512-XXOdt/NPX++txOQHM1kUMgJUS43KSlXGdR/aDyEwuAEETwuPt02Nc7v+s57PzuSqMbNLclblQdv3YcWOdXhQ7g==", "integrity": "sha512-5kvd38wZpqGY4yP/6W3qhYX6Hz0NwUbijVsX2rxczpY6OXaMxh0+5E5uLJKVFwaBM7PJe1wnMym85NfKYIh6CA==",
"dev": true "dev": true
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.1.tgz",
"integrity": "sha512-eA6sT5dE5RHAFhtcC+b5WDlUIGwnO9b0yrfGa1mIOIAjqwSQCpXbLiFmKTdRbQN/xH2EZkGqqLDrKUuYOZ0+Hg==", "integrity": "sha512-tC7MKZIMRTYxQhrVAFoJq/DlRwv1bnqA4/S2r3+HuHibqvbrPcyf858lNzU7bFmy4mLeIHFYr34ar/1KumwyRw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"@typescript-eslint/visitor-keys": "4.11.0", "@typescript-eslint/visitor-keys": "4.11.1",
"debug": "^4.1.1", "debug": "^4.1.1",
"globby": "^11.0.1", "globby": "^11.0.1",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -568,12 +568,12 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.11.0", "version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.1.tgz",
"integrity": "sha512-tRYKyY0i7cMk6v4UIOCjl1LhuepC/pc6adQqJk4Is3YcC6k46HvsV9Wl7vQoLbm9qADgeujiT7KdLrylvFIQ+A==", "integrity": "sha512-IrlBhD9bm4bdYcS8xpWarazkKXlE7iYb1HzRuyBP114mIaj5DJPo11Us1HgH60dTt41TCZXMaTCAW+OILIYPOg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.11.0", "@typescript-eslint/types": "4.11.1",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
} }
}, },
@ -968,9 +968,9 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
}, },
"construct-style-sheets-polyfill": { "construct-style-sheets-polyfill": {
"version": "2.4.3", "version": "2.4.6",
"resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.3.tgz", "resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.6.tgz",
"integrity": "sha512-ECo96zFPsdghrMJmJ0vomcHsqLOIYpudobcNCIPeMubyFzBcLCfljuY0oFA3DD7btiFqB0sZ+0szbsiE8I24VA==" "integrity": "sha512-lU0to7dFDjKslMF+M5NUa4s0RQMBRVyZMXvD/vp7vmjdEPgziTkHSfZHQxfoIvVWajWRJUVJMLfrMwcx8fTh4A=="
}, },
"copy-descriptor": { "copy-descriptor": {
"version": "0.1.1", "version": "0.1.1",

View File

@ -12,10 +12,10 @@
"@sentry/browser": "^5.29.2", "@sentry/browser": "^5.29.2",
"@sentry/tracing": "^5.29.2", "@sentry/tracing": "^5.29.2",
"@types/chart.js": "^2.9.29", "@types/chart.js": "^2.9.29",
"@types/codemirror": "0.0.102", "@types/codemirror": "0.0.103",
"chart.js": "^2.9.4", "chart.js": "^2.9.4",
"codemirror": "^5.59.0", "codemirror": "^5.59.0",
"construct-style-sheets-polyfill": "^2.4.3", "construct-style-sheets-polyfill": "^2.4.6",
"flowchart.js": "^1.15.0", "flowchart.js": "^1.15.0",
"lit-element": "^2.4.0", "lit-element": "^2.4.0",
"lit-html": "^1.3.0", "lit-html": "^1.3.0",
@ -27,8 +27,8 @@
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-typescript": "^8.1.0", "@rollup/plugin-typescript": "^8.1.0",
"@typescript-eslint/eslint-plugin": "^4.11.0", "@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^4.11.0", "@typescript-eslint/parser": "^4.11.1",
"eslint": "^7.16.0", "eslint": "^7.16.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-lit": "^1.3.0", "eslint-plugin-lit": "^1.3.0",

View File

@ -1,6 +1,37 @@
import { DefaultClient } from "./Client"; import { DefaultClient, PBResponse, QueryArguments } from "./Client";
export interface EventUser {
pk: number;
email?: string;
username: string;
on_behalf_of?: EventUser;
}
export interface EventContext {
[key: string]: EventContext | string | number | string[];
}
export class Event { export class Event {
pk: string;
user: EventUser;
action: string;
app: string;
context: EventContext;
client_ip: string;
created: string;
constructor() {
throw Error();
}
static get(pk: string): Promise<Event> {
return DefaultClient.fetch<Event>(["events", "events", pk]);
}
static list(filter?: QueryArguments): Promise<PBResponse<Event>> {
return DefaultClient.fetch<PBResponse<Event>>(["events", "events"], filter);
}
// events/events/top_per_user/?filter_action=authorize_application // events/events/top_per_user/?filter_action=authorize_application
static topForUser(action: string): Promise<TopNEvent[]> { static topForUser(action: string): Promise<TopNEvent[]> {
return DefaultClient.fetch<TopNEvent[]>(["events", "events", "top_per_user"], { return DefaultClient.fetch<TopNEvent[]>(["events", "events", "top_per_user"], {

View File

@ -137,6 +137,9 @@ select[multiple] {
--pf-c-table--cell--Color: var(--ak-dark-foreground); --pf-c-table--cell--Color: var(--ak-dark-foreground);
} }
.pf-c-table__text { .pf-c-table__text {
color: var(--ak-dark-foreground);
}
.pf-c-table__sort:not(.pf-m-selected) .pf-c-table__button .pf-c-table__text {
color: var(--ak-dark-foreground) !important; color: var(--ak-dark-foreground) !important;
} }
.pf-c-table__sort-indicator i { .pf-c-table__sort-indicator i {

View File

@ -28,4 +28,4 @@ export const ColorStyles = css`
background-color: var(--pf-global--danger-color--100); background-color: var(--pf-global--danger-color--100);
} }
`; `;
export const VERSION = "0.14.0-rc1"; export const VERSION = "0.14.2-stable";

View File

@ -115,6 +115,7 @@ export abstract class Table<T> extends LitElement {
this.apiEndpoint(this.page).then((r) => { this.apiEndpoint(this.page).then((r) => {
this.data = r; this.data = r;
this.page = r.pagination.current; this.page = r.pagination.current;
this.expandedRows = [];
}); });
} }
@ -144,7 +145,7 @@ export abstract class Table<T> extends LitElement {
<tr role="row"> <tr role="row">
<td role="cell" colspan="8"> <td role="cell" colspan="8">
<div class="pf-l-bullseye"> <div class="pf-l-bullseye">
${inner ? inner : html`<ak-empty-state header="none"></ak-empty-state>`} ${inner ? inner : html`<ak-empty-state header="${gettext("No elements found.")}"></ak-empty-state>`}
</div> </div>
</td> </td>
</tr> </tr>
@ -178,7 +179,7 @@ export abstract class Table<T> extends LitElement {
</tr> </tr>
<tr class="pf-c-table__expandable-row ${this.expandedRows[idx] ? "pf-m-expanded" : ""}" role="row"> <tr class="pf-c-table__expandable-row ${this.expandedRows[idx] ? "pf-m-expanded" : ""}" role="row">
<td></td> <td></td>
${this.renderExpanded(item)} ${this.expandedRows[idx] ? this.renderExpanded(item) : html``}
</tr> </tr>
</tbody>`; </tbody>`;
}); });

View File

@ -26,9 +26,9 @@ export class TableSearch extends LitElement {
if (el.value === "") return; if (el.value === "") return;
this.onSearch(el?.value); this.onSearch(el?.value);
}}> }}>
<input class="pf-c-form-control" name="search" type="search" placeholder="Search..." value="${ifDefined(this.value)}" @search=${() => { <input class="pf-c-form-control" name="search" type="search" placeholder="Search..." value="${ifDefined(this.value)}" @search=${(ev: Event) => {
if (!this.onSearch) return; if (!this.onSearch) return;
this.onSearch(""); this.onSearch((ev.target as HTMLInputElement).value);
}}> }}>
<button class="pf-c-button pf-m-control" type="submit"> <button class="pf-c-button pf-m-control" type="submit">
<i class="fas fa-search" aria-hidden="true"></i> <i class="fas fa-search" aria-hidden="true"></i>

View File

@ -36,7 +36,7 @@
class="pf-c-page__main" class="pf-c-page__main"
tabindex="-1" tabindex="-1"
id="main-content" id="main-content"
defaultUrl="/library/" defaultUrl="/library"
> >
</ak-router-outlet> </ak-router-outlet>
</div> </div>

View File

@ -9,16 +9,16 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [
new SidebarItem("Monitor").children( new SidebarItem("Monitor").children(
new SidebarItem("Overview", "/administration/overview"), new SidebarItem("Overview", "/administration/overview"),
new SidebarItem("System Tasks", "/administration/tasks/"), new SidebarItem("System Tasks", "/administration/tasks/"),
new SidebarItem("Events", "/events/log/"), new SidebarItem("Events", "/events"),
).when((): Promise<boolean> => { ).when((): Promise<boolean> => {
return User.me().then(u => u.is_superuser); return User.me().then(u => u.is_superuser);
}), }),
new SidebarItem("Administration").children( new SidebarItem("Administration").children(
new SidebarItem("Applications", "/applications").activeWhen( new SidebarItem("Applications", "/applications").activeWhen(
`^/applications/(?<slug>${SLUG_REGEX})/$` `^/applications/(?<slug>${SLUG_REGEX})$`
), ),
new SidebarItem("Sources", "/administration/sources/").activeWhen( new SidebarItem("Sources", "/administration/sources/").activeWhen(
`^/sources/(?<slug>${SLUG_REGEX})/$`, `^/sources/(?<slug>${SLUG_REGEX})$`,
), ),
new SidebarItem("Providers", "/administration/providers/"), new SidebarItem("Providers", "/administration/providers/"),
new SidebarItem("Outposts", "/administration/outposts/"), new SidebarItem("Outposts", "/administration/outposts/"),

View File

@ -8,7 +8,7 @@ import "../elements/sidebar/SidebarHamburger";
export abstract class Interface extends LitElement { export abstract class Interface extends LitElement {
@property({type: Boolean}) @property({type: Boolean})
sidebarOpen?: boolean; sidebarOpen = true;
abstract get sidebar(): SidebarItem[]; abstract get sidebar(): SidebarItem[];
@ -36,7 +36,7 @@ export abstract class Interface extends LitElement {
<ak-sidebar class="pf-c-page__sidebar ${this.sidebarOpen ? "pf-m-expanded" : "pf-m-collapsed"}" .items=${this.sidebar}> <ak-sidebar class="pf-c-page__sidebar ${this.sidebarOpen ? "pf-m-expanded" : "pf-m-collapsed"}" .items=${this.sidebar}>
</ak-sidebar> </ak-sidebar>
<main class="pf-c-page__main"> <main class="pf-c-page__main">
<ak-router-outlet role="main" class="pf-c-page__main" tabindex="-1" id="main-content" defaultUrl="/library/"> <ak-router-outlet role="main" class="pf-c-page__main" tabindex="-1" id="main-content" defaultUrl="/library">
</ak-router-outlet> </ak-router-outlet>
</main> </main>
</div>`; </div>`;

View File

@ -51,7 +51,7 @@ export class ApplicationListPage extends TablePage<Application> {
${item.meta_icon ? ${item.meta_icon ?
html`<img class="app-icon pf-c-avatar" src="${item.meta_icon}" alt="${gettext("Application Icon")}">` : html`<img class="app-icon pf-c-avatar" src="${item.meta_icon}" alt="${gettext("Application Icon")}">` :
html`<i class="pf-icon pf-icon-arrow"></i>`}`, html`<i class="pf-icon pf-icon-arrow"></i>`}`,
html`<a href="#/applications/${item.slug}/"> html`<a href="#/applications/${item.slug}">
<div> <div>
${item.name} ${item.name}
</div> </div>

View File

@ -0,0 +1,139 @@
import { gettext } from "django";
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { until } from "lit-html/directives/until";
import { Event, EventContext } from "../../api/Events";
import { Flow } from "../../api/Flows";
import { COMMON_STYLES } from "../../common/styles";
import "../../elements/Spinner";
import { SpinnerSize } from "../../elements/Spinner";
@customElement("ak-event-info")
export class EventInfo extends LitElement {
@property({attribute: false})
event?: Event;
static get styles(): CSSResult[] {
return COMMON_STYLES.concat(
css`
code {
display: block;
white-space: pre-wrap;
}
`
);
}
getModelInfo(context: EventContext): TemplateResult {
return html`<ul class="pf-c-list">
<li>${gettext("UID")}: ${context.pk as string}</li>
<li>${gettext("Name")}: ${context.name as string}</li>
<li>${gettext("App")}: ${context.app as string}</li>
<li>${gettext("Model Name")}: ${context.model_name as string}</li>
</ul>`;
}
defaultResponse(): TemplateResult {
return html`<div class="pf-l-flex">
<div class="pf-l-flex__item">
<h3>${gettext("Context")}</h3>
<code>${JSON.stringify(this.event?.context)}</code>
</div>
<div class="pf-l-flex__item">
<h3>${gettext("User")}</h3>
<code>${JSON.stringify(this.event?.user)}</code>
</div>
</div>`;
}
render(): TemplateResult {
if (!this.event) {
return html`<ak-spinner size=${SpinnerSize.Medium}></ak-spinner>`;
}
switch (this.event?.action) {
case "model_created":
case "model_updated":
case "model_deleted":
return html`
<h3>${gettext("Affected model:")}</h3><hr>
${this.getModelInfo(this.event.context.model as EventContext)}
`;
case "authorize_application":
return html`<div class="pf-l-flex">
<div class="pf-l-flex__item">
<h3>${gettext("Authorized application:")}</h3><hr>
${this.getModelInfo(this.event.context.authorized_application as EventContext)}
</div>
<div class="pf-l-flex__item">
<h3>${gettext("Using flow")}</h3>
<span>${until(Flow.list({
flow_uuid: this.event.context.flow as string,
}).then(resp => {
return html`<a href="#/flows/${resp.results[0].slug}">${resp.results[0].name}</a>`;
}), html`<ak-spinner size=${SpinnerSize.Medium}></ak-spinner>`)}</span>
</div>
</div>`;
case "login_failed":
return html`
<h3>${gettext(`Attempted to log in as ${this.event.context.username}`)}</h3>
`;
case "token_view":
return html`
<h3>${gettext("Token:")}</h3><hr>
${this.getModelInfo(this.event.context.token as EventContext)}
`;
case "property_mapping_exception":
case "policy_exception":
return html`<div class="pf-l-flex">
<div class="pf-l-flex__item">
<h3>${gettext("Exception")}</h3>
<code>${this.event.context.error}</code>
</div>
<div class="pf-l-flex__item">
<h3>${gettext("Expression")}</h3>
<code>${this.event.context.expression}</code>
</div>
</div>`;
case "policy_execution":
return html`<div class="pf-l-flex">
<div class="pf-l-flex__item">
<h3>${gettext("Request")}</h3>
<ul class="pf-c-list">
<li>${gettext("Object")}: ${(this.event.context.request as EventContext).obj as string}</li>
<li><span>${gettext("Context")}: <code>${JSON.stringify((this.event.context.request as EventContext).context)}</code></span></li>
</ul>
</div>
<div class="pf-l-flex__item">
<h3>${gettext("Result")}</h3>
<ul class="pf-c-list">
<li>${gettext("Passing")}: ${(this.event.context.result as EventContext).passing}</li>
<li>${gettext("Messages")}:
<ul class="pf-c-list">
${((this.event.context.result as EventContext).messages as string[]).map(msg => {
return html`<li>${msg}</li>`;
})}
</ul>
</li>
</ul>
</div>
</div>`;
case "configuration_error":
return html`<h3>${this.event.context.message}</h3>`;
case "update_available":
return html`<h3>${gettext("New version available!")}</h3>
<a target="_blank" href="https://github.com/BeryJu/authentik/releases/tag/version%2F${this.event.context.new_version}">${this.event.context.new_version}</a>
`;
// Action types which typically don't record any extra context.
// If context is not empty, we fall to the default response.
case "login":
case "logout":
if (this.event.context === {}) {
return html`<span>${gettext("No additional data available.")}</span>`;
}
return this.defaultResponse();
default:
return this.defaultResponse();
}
}
}

View File

@ -0,0 +1,71 @@
import { gettext } from "django";
import { customElement, html, property, TemplateResult } from "lit-element";
import { PBResponse } from "../../api/Client";
import { Event } from "../../api/Events";
import { TableColumn } from "../../elements/table/Table";
import { TablePage } from "../../elements/table/TablePage";
import { time } from "../../utils";
import "./EventInfo";
@customElement("ak-event-list")
export class EventListPage extends TablePage<Event> {
expandable = true;
pageTitle(): string {
return "Event Log";
}
pageDescription(): string | undefined {
return;
}
pageIcon(): string {
return "pf-icon pf-icon-catalog";
}
searchEnabled(): boolean {
return true;
}
@property()
order = "-created";
apiEndpoint(page: number): Promise<PBResponse<Event>> {
return Event.list({
ordering: this.order,
page: page,
search: this.search || "",
});
}
columns(): TableColumn[] {
return [
new TableColumn("Action", "action"),
new TableColumn("User", "user"),
new TableColumn("Creation Date", "created"),
new TableColumn("Client IP", "client_ip"),
];
}
row(item: Event): TemplateResult[] {
return [
html`<div>${item.action}</div>
<small>${item.app}</small>`,
html`<div>${item.user.username}</div>
${item.user.on_behalf_of ? html`<small>
${gettext(`On behalf of ${item.user.on_behalf_of.username}`)}
</small>` : html``}`,
html`<span>${time(item.created).toLocaleString()}</span>`,
html`<span>${item.client_ip}</span>`,
];
}
renderExpanded(item: Event): TemplateResult {
return html`
<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<ak-event-info .event=${item}></ak-event-info>
</div>
</td>
<td></td>
<td></td>
<td></td>`;
}
}

View File

@ -30,6 +30,12 @@ export class SiteShell extends LitElement {
::slotted(*) { ::slotted(*) {
height: 100%; height: 100%;
} }
.pf-l-bullseye {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
`, `,
BackdropStyle, BackdropStyle,
BullseyeStyle, BullseyeStyle,

View File

@ -7,6 +7,7 @@ import "./pages/applications/ApplicationListPage";
import "./pages/applications/ApplicationViewPage"; import "./pages/applications/ApplicationViewPage";
import "./pages/sources/SourceViewPage"; import "./pages/sources/SourceViewPage";
import "./pages/flows/FlowViewPage"; import "./pages/flows/FlowViewPage";
import "./pages/events/EventListPage";
export const ROUTES: Route[] = [ export const ROUTES: Route[] = [
// Prevent infinite Shell loops // Prevent infinite Shell loops
@ -24,4 +25,5 @@ export const ROUTES: Route[] = [
new Route(new RegExp(`^/flows/(?<slug>${SLUG_REGEX})$`)).then((args) => { new Route(new RegExp(`^/flows/(?<slug>${SLUG_REGEX})$`)).then((args) => {
return html`<ak-flow-view .args=${args}></ak-flow-view>`; return html`<ak-flow-view .args=${args}></ak-flow-view>`;
}), }),
new Route(new RegExp("^/events$"), html`<ak-event-list></ak-event-list>`),
]; ];

View File

@ -50,3 +50,7 @@ export function loading<T>(v: T, actual: TemplateResult): TemplateResult {
} }
return actual; return actual;
} }
export function time(t: string): Date {
return new Date(parseInt(t, 10) * 1000);
}

View File

@ -15,7 +15,7 @@ Download the latest `docker-compose.yml` from [here](https://raw.githubuserconte
To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env` To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env`
To optionally deploy a different version run `echo AUTHENTIK_TAG=0.14.0-rc1 >> .env` To optionally deploy a different version run `echo AUTHENTIK_TAG=0.14.2-stable >> .env`
If this is a fresh authentik install run the following commands to generate a password: If this is a fresh authentik install run the following commands to generate a password:

View File

@ -22,7 +22,7 @@ image:
name: beryju/authentik name: beryju/authentik
name_static: beryju/authentik-static name_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended
tag: 0.14.0-rc1 tag: 0.14.2-stable
serverReplicas: 1 serverReplicas: 1
workerReplicas: 1 workerReplicas: 1

View File

@ -0,0 +1,66 @@
---
title: Grafana
---
## What is Grafana
From https://en.wikipedia.org/wiki/Grafana
:::note
Grafana is a multi-platform open source analytics and interactive visualization web application. It provides charts, graphs, and alerts for the web when connected to supported data sources, Grafana Enterprise version with additional capabilities is also available. It is expandable through a plug-in system.
:::
## Preparation
The following placeholders will be used:
- `grafana.company` is the FQDN of the Grafana install.
- `authentik.company` is the FQDN of the authentik install.
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
- Client Type: `Confidential`
- JWT Algorithm: `RS256`
- Scopes: OpenID, Email and Profile
- RSA Key: Select any available key
- Redirect URIs: `https://grafana.company/login/generic_oauth`
Note the Client ID and Client Secret values. Create an application, using the provider you've created above. Note the slug of the application you've created.
## Grafana
If your Grafana is running in docker, set the following environment variables:
```yaml
environment:
GF_AUTH_GENERIC_OAUTH_ENABLED: "true"
GF_AUTH_GENERIC_OAUTH_NAME: "authentik"
GF_AUTH_GENERIC_OAUTH_CLIENT_ID: "<Client ID from above>"
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET: "<Client Secret from above>"
GF_AUTH_GENERIC_OAUTH_SCOPES: "openid profile email"
GF_AUTH_GENERIC_OAUTH_AUTH_URL: "https://authentik.company/application/o/authorize/"
GF_AUTH_GENERIC_OAUTH_TOKEN_URL: "https://authentik.company/application/o/token/"
GF_AUTH_GENERIC_OAUTH_API_URL: "https://authentik.company/application/o/userinfo/"
GF_AUTH_SIGNOUT_REDIRECT_URL: "https://authentik.company/application/o/<Slug of the application from above>/end-session/"
# Optionally enable auto-login
GF_AUTH_OAUTH_AUTO_LOGIN: "true"
```
If you are using a config-file instead, you have to set these options:
```ini
[auth]
signout_redirect_url = https://authentik.company/application/o/<Slug of the application from above>/end-session/
# Optionally enable auto-login
oauth_auto_login = true
[auth.generic_oauth]
name = authentik
enabled = true
client_id = <Client ID from above>
client_secret = <Client Secret from above>
scopes = openid email profile
auth_url = https://authentik.company/application/o/authorize/
token_url = https://authentik.company/application/o/token/
api_url = https://authentik.company/application/o/userinfo/
```

View File

@ -17,13 +17,14 @@ The following placeholders will be used:
- `harbor.company` is the FQDN of the Harbor install. - `harbor.company` is the FQDN of the Harbor install.
- `authentik.company` is the FQDN of the authentik install. - `authentik.company` is the FQDN of the authentik install.
Create an application in authentik. Create an OpenID provider with the following parameters: Create an OAuth2/OpenID provider with the following parameters:
- Client Type: `Confidential` - Client Type: `Confidential`
- Response types: `code (Authorization Code Flow)`
- JWT Algorithm: `RS256` - JWT Algorithm: `RS256`
- Redirect URIs: `https://harbor.company/c/oidc/callback` - Redirect URIs: `https://harbor.company/c/oidc/callback`
- Scopes: `openid` - Scopes: OpenID, Email and Profile
Note the Client ID and Client Secret values. Create an application, using the provider you've created above.
## Harbor ## Harbor

View File

@ -51,12 +51,11 @@ Under _Sources_, click _Edit_ and ensure that "Autogenerated Active Directory Ma
### Step 3 ### Step 3
Under _Providers_, create an OAuth2/OpenID Provider with these settings: Under _Providers_, create an OAuth2/OpenID provider with these settings:
- Client Type: Confidential - Client Type: Confidential
- JWT Algorithm: RS256 - JWT Algorithm: RS256
- Redirect URI: `https://vcenter.company/ui/login/oauth2/authcode` - Redirect URI: `https://vcenter.company/ui/login/oauth2/authcode`
- Post Logout Redirect URIs: `https://vcenter.company/ui/login`
- Sub Mode: If your Email address Schema matches your UPN, select "Based on the User's Email...", otherwise select "Based on the User's UPN...". - Sub Mode: If your Email address Schema matches your UPN, select "Based on the User's Email...", otherwise select "Based on the User's UPN...".
- Scopes: Select the Scope Mapping you've created in Step 1 - Scopes: Select the Scope Mapping you've created in Step 1

View File

@ -20,7 +20,7 @@ title: Release 0.14
- The OAuth2 Provider has been updated to closer match the OpenID Connect Specifications - The OAuth2 Provider has been updated to closer match the OpenID Connect Specifications
Response time now longer has to be configured manually. The issuer field can be configured now (the default behaviour is the same as pre-0.14) Response type no longer has to be configured manually. The issuer field can be configured now (the default behaviour is the same as pre-0.14)
Authorization Codes are now generated as a JWT Token, which is not specified as spec, but seems to be a quasi-standard. Authorization Codes are now generated as a JWT Token, which is not specified as spec, but seems to be a quasi-standard.
@ -50,7 +50,7 @@ This release does not introduce any new requirements.
### docker-compose ### docker-compose
Download the latest docker-compose file from [here](https://raw.githubusercontent.com/BeryJu/authentik/version-0.14/docker-compose.yml) and run `docker-compose up -d`. Download the latest docker-compose file from [here](https://raw.githubusercontent.com/BeryJu/authentik/version-0.14/docker-compose.yml). Aftewards, simply run `docker-compose up -d` and then the normal upgrade command of `docker-compose run --rm server migrate`.
### Kubernetes ### Kubernetes

View File

@ -101,6 +101,7 @@ module.exports = {
"integrations/services/aws/index", "integrations/services/aws/index",
"integrations/services/awx-tower/index", "integrations/services/awx-tower/index",
"integrations/services/gitlab/index", "integrations/services/gitlab/index",
"integrations/services/grafana/index",
"integrations/services/harbor/index", "integrations/services/harbor/index",
"integrations/services/home-assistant/index", "integrations/services/home-assistant/index",
"integrations/services/nextcloud/index", "integrations/services/nextcloud/index",

View File

@ -11,8 +11,8 @@ const features = [
title: "Easy to Use", title: "Easy to Use",
description: ( description: (
<> <>
Identity made easy. authentik makes single-sign on, user Identity made easy. authentik makes single-sign on (SSO), user
enrollment and access control simple. enrollment, and access control simple.
</> </>
), ),
}, },
@ -20,7 +20,7 @@ const features = [
title: "Realise your workflow", title: "Realise your workflow",
description: ( description: (
<> <>
authentik lets you build your Workflow as you need it, no authentik lets you build your workflow as you need it, no
limitations. limitations.
</> </>
), ),
@ -109,20 +109,20 @@ function Home() {
authentik is an open-source Identity Provider authentik is an open-source Identity Provider
focused on flexibility and versatility. You focused on flexibility and versatility. You
can use authentik in an existing environment can use authentik in an existing environment
to add support for new protocols. authentik to add support for new protocols, implement
is also a great solution for implementing sign-up/recovery/etc. in your application so
signup/recovery/etc in your application, so you don't have to deal with it, and many other
you don't have to deal with it. things.
</p> </p>
</div> </div>
</div> </div>
<div className="row"> <div className="row">
<div className="col col--5 col--offset-2 padding-vert--xl"> <div className="col col--5 col--offset-2 padding-vert--xl">
<h2>Upmost flexibility</h2> <h2>Utmost flexibility</h2>
<p> <p>
You can adopt authentik to your environment, You can adopt authentik to your environment,
regardless of your requirements. Need an Active- regardless of your requirements. Need an Active-Directory
Directory integrated SSO Provider? Do you want integrated SSO Provider? Do you want
to implement a custom enrollment process for your to implement a custom enrollment process for your
customers? Are you developing an application and customers? Are you developing an application and
don't want to deal with User verification and recovery? don't want to deal with User verification and recovery?