Compare commits

...

96 Commits

Author SHA1 Message Date
fe6963c428 release: 2021.6.1 2021-06-17 22:14:52 +02:00
19cac4bf43 providers/saml: fix error when getting transient user identifier
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-17 13:52:10 +02:00
4ca564490e providers/saml: add support for NameID type unspecified
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-17 12:45:53 +02:00
fcb795c273 providers/saml: fix NameIDPolicy not being parsed correctly, improve error handling
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-17 12:22:40 +02:00
14c70b3e4a build(deps): bump rollup from 2.52.0 to 2.52.1 in /web (#1039) 2021-06-17 08:53:11 +02:00
ac880c28d7 build(deps): bump rollup from 2.51.2 to 2.52.0 in /web (#1033) 2021-06-17 08:51:31 +02:00
f3c6b9a4f6 build(deps): bump postcss from 8.3.4 to 8.3.5 in /website (#1034) 2021-06-17 08:51:22 +02:00
cba0cf0d76 build(deps): bump @lingui/core from 3.10.3 to 3.10.4 in /web (#1035) 2021-06-17 08:51:11 +02:00
73b67cf0f0 build(deps): bump typescript from 4.3.2 to 4.3.3 in /web (#1036) 2021-06-17 08:51:00 +02:00
23a8052cc8 build(deps): bump boto3 from 1.17.95 to 1.17.96 (#1037) 2021-06-17 08:50:52 +02:00
57c49c3865 build(deps): bump psycopg2-binary from 2.8.6 to 2.9.1 (#1038) 2021-06-17 08:50:43 +02:00
cbea51ae5b stages/authenticator_duo: make Duo-admin viewset writeable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-16 23:17:26 +02:00
8962081d92 website/docs: add wekan (#1032)
* Create index.mdx

Add Wekan example

* updated to include wekan entry
2021-06-16 23:08:58 +02:00
e743f13f81 recovery: fix error when creating multiple keys for the same user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-16 23:04:35 +02:00
b20a8b7c17 stages/authenticator_duo: fix error when enrolling an existing user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-16 23:04:24 +02:00
b53c94d76a flows: fix error when stage has incorrect type
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-16 22:52:00 +02:00
d4419d66c1 core: fix error when creating AuthenticatedSession without key
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-16 22:51:48 +02:00
79044368d2 core: fix error getting stages when enrollment flow isn't set
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-16 22:45:42 +02:00
426686957d website/docs: remove migrate command
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-16 22:43:43 +02:00
28cb803fd9 website/docs: Add a note about Protocol Overwrite (#1031)
Added a note in the Nextcloud section for Protocol overwrite when behind a reverse proxy
2021-06-16 19:38:34 +02:00
85c3a36b62 website: clear up comparison
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-16 14:54:44 +02:00
9ba8a715b1 build(deps): bump @sentry/tracing from 6.7.0 to 6.7.1 in /web (#1026) 2021-06-16 09:26:32 +02:00
358750f66e build(deps): bump drf-spectacular from 0.17.1 to 0.17.2 (#1028) 2021-06-16 08:47:05 +02:00
b9918529b8 build(deps): bump @sentry/browser from 6.7.0 to 6.7.1 in /web (#1027) 2021-06-16 08:46:40 +02:00
a5673b4ec8 build(deps): bump boto3 from 1.17.94 to 1.17.95 (#1029) 2021-06-16 08:46:11 +02:00
d9287d0c0e Merge branch 'next' 2021-06-15 23:43:44 +02:00
d9c2b64116 root: update schema
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-15 23:38:03 +02:00
2b150d3077 website/docs: add changelog for release candidates
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-15 22:19:45 +02:00
dec7a9cfb9 website/docs: add docs for flow executor
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-15 22:14:23 +02:00
e0f48a30b7 release: 2021.6.1-rc6 2021-06-15 21:18:33 +02:00
973f14d911 ci: only build stable images when non-rc version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-15 18:41:26 +02:00
e8978adc1b outpost: fix syntax error when creating an outpost with connection
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-15 18:39:51 +02:00
3ca8d9c968 ci: build and push stable tag when rc not in release name
closes #1023

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-15 17:34:23 +02:00
42636142fa build(deps): bump @typescript-eslint/parser in /web (#1021)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.26.1 to 4.27.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.27.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-15 09:29:14 +02:00
57c459348f build(deps): bump @sentry/tracing from 6.6.0 to 6.7.0 in /web (#1020)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.6.0 to 6.7.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.6.0...6.7.0)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-15 09:28:55 +02:00
493b34cf0d build(deps): bump boto3 from 1.17.93 to 1.17.94 (#1022) 2021-06-15 08:55:32 +02:00
f0493f418b build(deps): bump @sentry/browser from 6.6.0 to 6.7.0 in /web (#1019) 2021-06-15 08:55:05 +02:00
d45a292652 build(deps): bump @babel/core from 7.14.5 to 7.14.6 in /web (#1018) 2021-06-15 08:54:44 +02:00
b21ea360db build(deps): bump @lingui/core from 3.10.2 to 3.10.3 in /web (#1016) 2021-06-15 08:54:36 +02:00
6816f8b851 build(deps): bump postcss from 8.3.2 to 8.3.4 in /website (#1015) 2021-06-15 08:54:18 +02:00
de714f0390 build(deps): bump @typescript-eslint/eslint-plugin in /web (#1017) 2021-06-15 08:54:10 +02:00
800df332b5 stages/authenticator_duo: don't create default duo stage
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 22:55:37 +02:00
16c194d2dc core: fix upload api not checking clear properly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 22:34:47 +02:00
53100a72fe stages/identification: fix challenges not being annotated correctly and API client not loading data correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 22:28:11 +02:00
ec4c3f44cb events: don't create system exception event in debug
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 22:16:27 +02:00
f10bd432b3 policies/reputation: fix race condition in tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 20:40:40 +02:00
4de927ba5b web/admin: fix link for github issue creation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 18:55:43 +02:00
74e578c2bf events: add tenant to event
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 18:43:29 +02:00
e584fd1344 events: catch unhandled exceptions from request as event, add button to open github issue
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 17:22:58 +02:00
0e02925a3d stages/authenticator_validate: add tests for authenticator validation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 16:32:36 +02:00
5b837c3ccc providers/saml: improve error handling for signature errors
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 12:51:42 +02:00
2580371f94 outposts: fix error when getting component for base service connection
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 12:38:29 +02:00
4e9be85353 website/docs: add docs for outpost configuration
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-14 09:21:35 +02:00
79508e1965 core: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 23:41:50 +02:00
3a88dde545 web: fix declaration of Intl
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 23:13:43 +02:00
31fc4d1cb9 web: migrate banner to sidebar
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 22:55:41 +02:00
09cd8f8f63 web/admin: fix ak-application-check-access-form for get api
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 22:40:51 +02:00
d824b09365 outposts/ldap: improve responses for unsuccessful binds
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 22:00:05 +02:00
cabbd18880 core: revert check_access API to get to prevent CSRF errors
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 21:47:49 +02:00
c9dda17c68 web/admin: select service connection by default when only one exists
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 20:12:01 +02:00
bb8559ee18 web: remove base interface
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 19:54:27 +02:00
5ae32e525c web/flows: improve display of allowed fields for identification stage
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 16:30:03 +02:00
0832145a01 web: fix fields for new api schema
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 15:36:25 +02:00
4167276c8f root: fix references to helm chart
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 14:30:44 +02:00
afb84c7bc5 flows: fix error clearing flow background when no files have been uploaded
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 14:14:41 +02:00
82b2c7e3f0 web: add capabilities to sentry event
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 14:08:39 +02:00
fc8004db2b outposts: fix integrity error with tokens
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 13:36:54 +02:00
ddfc943bba root: fix build_hash being set incorrectly for tagged versions
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 13:32:18 +02:00
8c0c12292e build(deps): bump tslib from 2.2.0 to 2.3.0 in /web (#1011)
Bumps [tslib](https://github.com/Microsoft/tslib) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/Microsoft/tslib/releases)
- [Commits](https://github.com/Microsoft/tslib/compare/2.2.0...2.3.0)

---
updated-dependencies:
- dependency-name: tslib
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-13 13:08:50 +02:00
803490d98b build(deps): bump rollup from 2.51.1 to 2.51.2 in /web (#1012)
Bumps [rollup](https://github.com/rollup/rollup) from 2.51.1 to 2.51.2.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v2.51.1...v2.51.2)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-13 13:08:38 +02:00
16835ab478 build(deps): bump boto3 from 1.17.92 to 1.17.93 (#1013)
Bumps [boto3](https://github.com/boto/boto3) from 1.17.92 to 1.17.93.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.17.92...1.17.93)

---
updated-dependencies:
- dependency-name: boto3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-13 13:08:27 +02:00
572b8d87b5 api: fix import error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 12:59:28 +02:00
31d2ea65fd provider/proxy: mark forward_auth flag as deprecated
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 12:39:25 +02:00
f4ac2f50e2 sources/saml: check sessions before deleting user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 12:39:10 +02:00
969a3f0ddd build(deps): bump drf-spectacular from 0.17.0 to 0.17.1 (#1014)
Bumps [drf-spectacular](https://github.com/tfranzel/drf-spectacular) from 0.17.0 to 0.17.1.
- [Release notes](https://github.com/tfranzel/drf-spectacular/releases)
- [Changelog](https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/tfranzel/drf-spectacular/compare/0.17.0...0.17.1)

---
updated-dependencies:
- dependency-name: drf-spectacular
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-13 12:28:24 +02:00
4e18f47f28 web/flows: fix expiry not shown on consent stage when loading
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-13 12:21:11 +02:00
f10286edf8 Merge branch 'version-2021.6' into next 2021-06-12 20:43:12 +02:00
d789dcc28f core: fix impersonation not working with inactive users
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-12 20:41:02 +02:00
715a71427e web/admin: fix user enable/disable modal not matching other modals
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-12 20:31:02 +02:00
84c21d16cf website: fix duplicate plugin ID
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-12 20:15:35 +02:00
2e4e17adb7 web/flows: fix IdentificationStage's label not matching fields
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-12 18:49:50 +02:00
00cbaaf672 web/flows: improve display of errors
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-12 18:18:36 +02:00
74e4e8f6aa core: delete real session when AuthenticatedSession is deleted
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-12 17:37:32 +02:00
d78fda990a release: 2021.6.1-rc5 2021-06-12 15:19:24 +02:00
10d949f7a9 stages/password: add constants for password backends
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-12 12:14:55 +02:00
6661af032d build(deps): bump @sentry/tracing from 6.5.1 to 6.6.0 in /web (#1007)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.5.1 to 6.6.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.5.1...6.6.0)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-11 09:28:58 +02:00
fb5e4a3af8 build(deps): bump postcss from 8.3.1 to 8.3.2 in /website (#1006) 2021-06-11 08:25:06 +02:00
1dfad83a34 build(deps): bump @sentry/browser from 6.5.1 to 6.6.0 in /web (#1008) 2021-06-11 08:24:48 +02:00
70025c648c build(deps): bump boto3 from 1.17.91 to 1.17.92 (#1009) 2021-06-11 08:24:24 +02:00
676b77aa7c stages/identification: add UPN
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-10 22:48:39 +02:00
e35e096266 stages/authenticator_webauthn: use tenant title as RP_NAME
closes #1004

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-10 22:17:25 +02:00
7af12d4fec stages/authenticator_totp: set TOTP issuer based on slug'd tenant title
closes #1004

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-10 22:16:37 +02:00
8d6db0fabf flows: fix configuration URL being set when no flow is configure
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-10 22:07:26 +02:00
8ddcf99bf7 web: fix flow download link
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-10 21:47:40 +02:00
e25f6aea8c release: 2021.6.1-rc4 2021-06-10 18:59:00 +02:00
b1a9eda1d3 ci: fix release test using wrong docker image
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-06-10 18:58:30 +02:00
124 changed files with 1873 additions and 885 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 2021.6.1-rc3 current_version = 2021.6.1
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

@ -1,5 +1,4 @@
env env
helm
static static
htmlcov htmlcov
*.env.yml *.env.yml

View File

@ -33,12 +33,22 @@ jobs:
with: with:
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name == 'release' }}
tags: | tags: |
beryju/authentik:2021.6.1-rc3, beryju/authentik:2021.6.1,
beryju/authentik:latest, beryju/authentik:latest,
ghcr.io/goauthentik/server:2021.6.1-rc3, ghcr.io/goauthentik/server:2021.6.1,
ghcr.io/goauthentik/server:latest ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
context: . context: .
- name: Building Docker Image (stable)
uses: docker/build-push-action@v2
if: ${{ github.event_name == 'release' && !contains('2021.6.1', 'rc') }}
with:
push: true
tags: |
beryju/authentik:stable,
ghcr.io/goauthentik/server:stable
platforms: linux/amd64,linux/arm64
context: .
build-proxy: build-proxy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -66,12 +76,22 @@ jobs:
with: with:
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name == 'release' }}
tags: | tags: |
beryju/authentik-proxy:2021.6.1-rc3, beryju/authentik-proxy:2021.6.1,
beryju/authentik-proxy:latest, beryju/authentik-proxy:latest,
ghcr.io/goauthentik/proxy:2021.6.1-rc3, ghcr.io/goauthentik/proxy:2021.6.1,
ghcr.io/goauthentik/proxy:latest ghcr.io/goauthentik/proxy:latest
file: outpost/proxy.Dockerfile file: outpost/proxy.Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
- name: Building Docker Image (stable)
uses: docker/build-push-action@v2
if: ${{ github.event_name == 'release' && !contains('2021.6.1', 'rc') }}
with:
push: true
tags: |
beryju/authentik-proxy:stable,
ghcr.io/goauthentik/proxy:stable
platforms: linux/amd64,linux/arm64
context: .
build-ldap: build-ldap:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -99,12 +119,22 @@ jobs:
with: with:
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name == 'release' }}
tags: | tags: |
beryju/authentik-ldap:2021.6.1-rc3, beryju/authentik-ldap:2021.6.1,
beryju/authentik-ldap:latest, beryju/authentik-ldap:latest,
ghcr.io/goauthentik/ldap:2021.6.1-rc3, ghcr.io/goauthentik/ldap:2021.6.1,
ghcr.io/goauthentik/ldap:latest ghcr.io/goauthentik/ldap:latest
file: outpost/ldap.Dockerfile file: outpost/ldap.Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
- name: Building Docker Image (stable)
uses: docker/build-push-action@v2
if: ${{ github.event_name == 'release' && !contains('2021.6.1', 'rc') }}
with:
push: true
tags: |
beryju/authentik-ldap:stable,
ghcr.io/goauthentik/ldap:stable
platforms: linux/amd64,linux/arm64
context: .
test-release: test-release:
if: ${{ github.event_name == 'release' }} if: ${{ github.event_name == 'release' }}
needs: needs:
@ -138,5 +168,5 @@ jobs:
SENTRY_PROJECT: authentik SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org SENTRY_URL: https://sentry.beryju.org
with: with:
version: authentik@2021.6.1-rc3 version: authentik@2021.6.1
environment: beryjuorg-prod environment: beryjuorg-prod

View File

@ -20,7 +20,7 @@ jobs:
docker-compose pull -q docker-compose pull -q
docker build \ docker build \
--no-cache \ --no-cache \
-t beryju/authentik:latest \ -t ghcr.io/goauthentik/server:latest \
-f Dockerfile . -f Dockerfile .
docker-compose up --no-start docker-compose up --no-start
docker-compose start postgresql redis docker-compose start postgresql redis

4
.gitignore vendored
View File

@ -193,10 +193,6 @@ pip-selfcheck.json
local.env.yml local.env.yml
.vscode/ .vscode/
### Helm ###
# Chart dependencies
**/charts/*.tgz
# Selenium Screenshots # Selenium Screenshots
selenium_screenshots/ selenium_screenshots/
backups/ backups/

183
Pipfile.lock generated
View File

@ -56,6 +56,7 @@
"sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a", "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a",
"sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95" "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"
], ],
"markers": "python_version >= '3.6'",
"version": "==3.7.4.post0" "version": "==3.7.4.post0"
}, },
"aioredis": { "aioredis": {
@ -70,6 +71,7 @@
"sha256:03e16e94f2b34c31f8bf1206d8ddd3ccaa4c315f7f6a1879b7b1210d229568c2", "sha256:03e16e94f2b34c31f8bf1206d8ddd3ccaa4c315f7f6a1879b7b1210d229568c2",
"sha256:493a2ac6788ce270a2f6a765b017299f60c1998f5a8617908ee9be082f7300fb" "sha256:493a2ac6788ce270a2f6a765b017299f60c1998f5a8617908ee9be082f7300fb"
], ],
"markers": "python_version >= '3.6'",
"version": "==5.0.6" "version": "==5.0.6"
}, },
"asgiref": { "asgiref": {
@ -77,6 +79,7 @@
"sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee", "sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee",
"sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78" "sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"
], ],
"markers": "python_version >= '3.6'",
"version": "==3.3.4" "version": "==3.3.4"
}, },
"async-timeout": { "async-timeout": {
@ -84,6 +87,7 @@
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
], ],
"markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1" "version": "==3.0.1"
}, },
"attrs": { "attrs": {
@ -91,6 +95,7 @@
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.2.0" "version": "==21.2.0"
}, },
"autobahn": { "autobahn": {
@ -98,6 +103,7 @@
"sha256:9195df8af03b0ff29ccd4b7f5abbde957ee90273465942205f9a1bad6c3f07ac", "sha256:9195df8af03b0ff29ccd4b7f5abbde957ee90273465942205f9a1bad6c3f07ac",
"sha256:e126c1f583e872fb59e79d36977cfa1f2d0a8a79f90ae31f406faae7664b8e03" "sha256:e126c1f583e872fb59e79d36977cfa1f2d0a8a79f90ae31f406faae7664b8e03"
], ],
"markers": "python_version >= '3.7'",
"version": "==21.3.1" "version": "==21.3.1"
}, },
"automat": { "automat": {
@ -116,23 +122,26 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:4cfab400cd9ca9b27b7dffb43f5675525ea5d36c81223d64d15542fdb16cdf7e", "sha256:67a4b0578944f061fbfa05206eb5b10c5250374e9849743413739c539584b60e",
"sha256:b0808a58c54c595b6cc6271cbc14a09bb89f0951ca9e8b105d1e94bef3ed24a0" "sha256:c7d6f3f09081440ca80500e679fec19f0b7597648ee380ae940ed29ad5c3768f"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.17.91" "version": "==1.17.96"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:462e75419e6537efb2709b7eb5b8c7ade007d30209416f0476bd7c51a2ddc78d" "sha256:204f7403bfe1ab837784421ddd069fd880be99d946cb59cbf31c72296ea9507a",
"sha256:b18d2d016b371b769a88cb080088ce75582748b4a7efa5748e9ced4f23bdbc99"
], ],
"version": "==1.20.91" "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.20.96"
}, },
"cachetools": { "cachetools": {
"hashes": [ "hashes": [
"sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001", "sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001",
"sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff" "sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"
], ],
"markers": "python_version ~= '3.5'",
"version": "==4.2.2" "version": "==4.2.2"
}, },
"cbor2": { "cbor2": {
@ -151,6 +160,7 @@
"sha256:ce6219986385778b1ab7f9b542f160bb4d3558f52975e914a27b774e47016fb7", "sha256:ce6219986385778b1ab7f9b542f160bb4d3558f52975e914a27b774e47016fb7",
"sha256:d562b2773e14ee1d65ea5b85351a83a64d4f3fd011bc2b4c70a6e813e78203ce" "sha256:d562b2773e14ee1d65ea5b85351a83a64d4f3fd011bc2b4c70a6e813e78203ce"
], ],
"markers": "python_version >= '3.6'",
"version": "==5.4.0" "version": "==5.4.0"
}, },
"celery": { "celery": {
@ -243,6 +253,7 @@
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0" "version": "==4.0.0"
}, },
"click": { "click": {
@ -250,6 +261,7 @@
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==7.1.2" "version": "==7.1.2"
}, },
"click-didyoumean": { "click-didyoumean": {
@ -309,6 +321,7 @@
"sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f", "sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f",
"sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393" "sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393"
], ],
"markers": "python_version >= '3.6'",
"version": "==3.0.2" "version": "==3.0.2"
}, },
"defusedxml": { "defusedxml": {
@ -413,11 +426,11 @@
}, },
"drf-spectacular": { "drf-spectacular": {
"hashes": [ "hashes": [
"sha256:4d35e890b8139e1c056588c5529a2f2066615635482563f0840b96d3b879d7d2", "sha256:6ffbfde7d96a4a2febd19182cc405217e1e86a50280fc739402291c93d1a32b7",
"sha256:f552476dfde647963c21615249672e7f4f9ece3788036b5ee5c6cc5ad50748ab" "sha256:77593024bb899f69227abedcf87def7851a11c9978f781aa4b385a10f67a38b7"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.17.0" "version": "==0.17.2"
}, },
"duo-client": { "duo-client": {
"hashes": [ "hashes": [
@ -439,6 +452,7 @@
"hashes": [ "hashes": [
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.18.2" "version": "==0.18.2"
}, },
"geoip2": { "geoip2": {
@ -451,10 +465,11 @@
}, },
"google-auth": { "google-auth": {
"hashes": [ "hashes": [
"sha256:9b235dbc876e49454cbedc52ae0abd540ef705ebccdf4fbe93553bb13f26b1a4", "sha256:154f7889c5d679a6f626f36adb12afbd4dbb0a9a04ec575d989d6ba79c4fd65e",
"sha256:eb017521276a75492282c6ca4b718f26de112ed3bcbeaeeb02c1b82de425f909" "sha256:6d47c79b5d09fbc7e8355fd9594cc4cf65fdde5d401c63951eaac4baa1ba2ae1"
], ],
"version": "==1.30.2" "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.31.0"
}, },
"gunicorn": { "gunicorn": {
"hashes": [ "hashes": [
@ -469,6 +484,7 @@
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
], ],
"markers": "python_version >= '3.6'",
"version": "==0.12.0" "version": "==0.12.0"
}, },
"hiredis": { "hiredis": {
@ -515,6 +531,7 @@
"sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0", "sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0",
"sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a" "sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a"
], ],
"markers": "python_version >= '3.6'",
"version": "==2.0.0" "version": "==2.0.0"
}, },
"httptools": { "httptools": {
@ -563,6 +580,7 @@
"sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417", "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417",
"sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2" "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"
], ],
"markers": "python_version >= '3.5'",
"version": "==0.5.1" "version": "==0.5.1"
}, },
"jmespath": { "jmespath": {
@ -570,6 +588,7 @@
"sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9", "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
"sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f" "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.0" "version": "==0.10.0"
}, },
"jsonschema": { "jsonschema": {
@ -584,6 +603,7 @@
"sha256:01481d99f4606f6939cdc9b637264ed353ee9e3e4f62cfb582324142c41a572d", "sha256:01481d99f4606f6939cdc9b637264ed353ee9e3e4f62cfb582324142c41a572d",
"sha256:e2dedd8a86c9077c350555153825a31e456a0dc20c15d5751f00137ec9c75f0a" "sha256:e2dedd8a86c9077c350555153825a31e456a0dc20c15d5751f00137ec9c75f0a"
], ],
"markers": "python_version >= '3.6'",
"version": "==5.1.0" "version": "==5.1.0"
}, },
"kubernetes": { "kubernetes": {
@ -597,6 +617,9 @@
"ldap3": { "ldap3": {
"hashes": [ "hashes": [
"sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91", "sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91",
"sha256:4139c91f0eef9782df7b77c8cbc6243086affcb6a8a249b768a9658438e5da59",
"sha256:8c949edbad2be8a03e719ba48bd6779f327ec156929562814b3e84ab56889c8c",
"sha256:afc6fc0d01f02af82cd7bfabd3bbfd5dc96a6ae91e97db0a2dab8a0f1b436056",
"sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57" "sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57"
], ],
"index": "pypi", "index": "pypi",
@ -658,6 +681,7 @@
"hashes": [ "hashes": [
"sha256:47e86a084dd814fac88c99ea34ba3278a74bc9de5a25f4b815b608798747c7dc" "sha256:47e86a084dd814fac88c99ea34ba3278a74bc9de5a25f4b815b608798747c7dc"
], ],
"markers": "python_version >= '3.6'",
"version": "==2.0.3" "version": "==2.0.3"
}, },
"msgpack": { "msgpack": {
@ -733,6 +757,7 @@
"sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281", "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281",
"sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80" "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"
], ],
"markers": "python_version >= '3.6'",
"version": "==5.1.0" "version": "==5.1.0"
}, },
"oauthlib": { "oauthlib": {
@ -740,6 +765,7 @@
"sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc", "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc",
"sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3" "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"
], ],
"markers": "python_version >= '3.6'",
"version": "==3.1.1" "version": "==3.1.1"
}, },
"packaging": { "packaging": {
@ -755,6 +781,7 @@
"sha256:3a8baade6cb80bcfe43297e33e7623f3118d660d41387593758e2fb1ea173a86", "sha256:3a8baade6cb80bcfe43297e33e7623f3118d660d41387593758e2fb1ea173a86",
"sha256:b014bc76815eb1399da8ce5fc84b7717a3e63652b0c0f8804092c9363acab1b2" "sha256:b014bc76815eb1399da8ce5fc84b7717a3e63652b0c0f8804092c9363acab1b2"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.11.0" "version": "==0.11.0"
}, },
"prompt-toolkit": { "prompt-toolkit": {
@ -762,60 +789,77 @@
"sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04", "sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04",
"sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc" "sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"
], ],
"markers": "python_full_version >= '3.6.1'",
"version": "==3.0.18" "version": "==3.0.18"
}, },
"psycopg2-binary": { "psycopg2-binary": {
"hashes": [ "hashes": [
"sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c", "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975",
"sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67", "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd",
"sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0", "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616",
"sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6", "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2",
"sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db", "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90",
"sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94", "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a",
"sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52", "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e",
"sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056", "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d",
"sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b", "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed",
"sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd", "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a",
"sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550", "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140",
"sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679", "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32",
"sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83", "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31",
"sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77", "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a",
"sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2", "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917",
"sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77", "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf",
"sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2", "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7",
"sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd", "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0",
"sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859", "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72",
"sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1", "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698",
"sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25", "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773",
"sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152", "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68",
"sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf", "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76",
"sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f", "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4",
"sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729", "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f",
"sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71", "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34",
"sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66", "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce",
"sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4", "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a",
"sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449", "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e"
"sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da",
"sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a",
"sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c",
"sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb",
"sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4",
"sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.8.6" "version": "==2.9.1"
}, },
"pyasn1": { "pyasn1": {
"hashes": [ "hashes": [
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
"sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba" "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
"sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
], ],
"version": "==0.4.8" "version": "==0.4.8"
}, },
"pyasn1-modules": { "pyasn1-modules": {
"hashes": [ "hashes": [
"sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
"sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199",
"sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
"sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
"sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4",
"sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e", "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
"sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74" "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74",
"sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb",
"sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
"sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
"sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
"sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
"sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"
], ],
"version": "==0.2.8" "version": "==0.2.8"
}, },
@ -824,6 +868,7 @@
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.20" "version": "==2.20"
}, },
"pycryptodome": { "pycryptodome": {
@ -867,6 +912,7 @@
"sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316", "sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316",
"sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29" "sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29"
], ],
"markers": "python_version >= '3.5'",
"version": "==2.0.2" "version": "==2.0.2"
}, },
"pyjwt": { "pyjwt": {
@ -889,12 +935,14 @@
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7" "version": "==2.4.7"
}, },
"pyrsistent": { "pyrsistent": {
"hashes": [ "hashes": [
"sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e" "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"
], ],
"markers": "python_version >= '3.5'",
"version": "==0.17.3" "version": "==0.17.3"
}, },
"python-dateutil": { "python-dateutil": {
@ -902,6 +950,7 @@
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.1" "version": "==2.8.1"
}, },
"python-dotenv": { "python-dotenv": {
@ -958,6 +1007,7 @@
"sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2",
"sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==3.5.3" "version": "==3.5.3"
}, },
"requests": { "requests": {
@ -965,12 +1015,14 @@
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.25.1" "version": "==2.25.1"
}, },
"requests-oauthlib": { "requests-oauthlib": {
"hashes": [ "hashes": [
"sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d", "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
"sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a" "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a",
"sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.3.0" "version": "==1.3.0"
@ -1011,6 +1063,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0" "version": "==1.16.0"
}, },
"sqlparse": { "sqlparse": {
@ -1018,6 +1071,7 @@
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
], ],
"markers": "python_version >= '3.5'",
"version": "==0.4.1" "version": "==0.4.1"
}, },
"structlog": { "structlog": {
@ -1073,6 +1127,7 @@
"sha256:7d6f89745680233f1c4db9ddb748df5e88d2a7a37962be174c0fd04c8dba1dc8", "sha256:7d6f89745680233f1c4db9ddb748df5e88d2a7a37962be174c0fd04c8dba1dc8",
"sha256:c16b55f9a67b2419cfdf8846576e2ec9ba94fe6978a83080c352a80db31c93fb" "sha256:c16b55f9a67b2419cfdf8846576e2ec9ba94fe6978a83080c352a80db31c93fb"
], ],
"markers": "python_version >= '3.6'",
"version": "==21.2.1" "version": "==21.2.1"
}, },
"typing-extensions": { "typing-extensions": {
@ -1096,6 +1151,7 @@
"sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f", "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f",
"sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae" "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.0.1" "version": "==3.0.1"
}, },
"urllib3": { "urllib3": {
@ -1140,6 +1196,7 @@
"sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30", "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30",
"sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e" "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"
], ],
"markers": "python_version >= '3.6'",
"version": "==5.0.0" "version": "==5.0.0"
}, },
"watchgod": { "watchgod": {
@ -1169,6 +1226,7 @@
"sha256:b68e4959d704768fa20e35c9d508c8dc2bbc041fd8d267c0d7345cffe2824568", "sha256:b68e4959d704768fa20e35c9d508c8dc2bbc041fd8d267c0d7345cffe2824568",
"sha256:e5c333bfa9fa739538b652b6f8c8fc2559f1d364243c8a689d7c0e1d41c2e611" "sha256:e5c333bfa9fa739538b652b6f8c8fc2559f1d364243c8a689d7c0e1d41c2e611"
], ],
"markers": "python_version >= '3.6'",
"version": "==1.1.0" "version": "==1.1.0"
}, },
"websockets": { "websockets": {
@ -1266,6 +1324,7 @@
"sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a", "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a",
"sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71" "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"
], ],
"markers": "python_version >= '3.6'",
"version": "==1.6.3" "version": "==1.6.3"
}, },
"zope.interface": { "zope.interface": {
@ -1322,6 +1381,7 @@
"sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4", "sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4",
"sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263" "sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==5.4.0" "version": "==5.4.0"
} }
}, },
@ -1338,6 +1398,7 @@
"sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e", "sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e",
"sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975" "sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"
], ],
"markers": "python_version ~= '3.6'",
"version": "==2.5.6" "version": "==2.5.6"
}, },
"attrs": { "attrs": {
@ -1345,6 +1406,7 @@
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.2.0" "version": "==21.2.0"
}, },
"bandit": { "bandit": {
@ -1383,6 +1445,7 @@
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0" "version": "==4.0.0"
}, },
"click": { "click": {
@ -1390,6 +1453,7 @@
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==7.1.2" "version": "==7.1.2"
}, },
"colorama": { "colorama": {
@ -1463,14 +1527,16 @@
"sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0", "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0",
"sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005" "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"
], ],
"markers": "python_version >= '3.4'",
"version": "==4.0.7" "version": "==4.0.7"
}, },
"gitpython": { "gitpython": {
"hashes": [ "hashes": [
"sha256:29fe82050709760081f588dd50ce83504feddbebdc4da6956d02351552b1c135", "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b",
"sha256:ee24bdc93dce357630764db659edaf6b8d664d4ff5447ccfeedd2dc5c253f41e" "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"
], ],
"version": "==3.1.17" "markers": "python_version >= '3.4'",
"version": "==3.1.14"
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
@ -1491,6 +1557,7 @@
"sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6", "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6",
"sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d" "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"
], ],
"markers": "python_version >= '3.6' and python_version < '4.0'",
"version": "==5.8.0" "version": "==5.8.0"
}, },
"lazy-object-proxy": { "lazy-object-proxy": {
@ -1518,6 +1585,7 @@
"sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93", "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93",
"sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b" "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.6.0" "version": "==1.6.0"
}, },
"mccabe": { "mccabe": {
@ -1554,6 +1622,7 @@
"sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd", "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd",
"sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4" "sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"
], ],
"markers": "python_version >= '2.6'",
"version": "==5.6.0" "version": "==5.6.0"
}, },
"pluggy": { "pluggy": {
@ -1561,6 +1630,7 @@
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1" "version": "==0.13.1"
}, },
"py": { "py": {
@ -1568,6 +1638,7 @@
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.10.0" "version": "==1.10.0"
}, },
"pylint": { "pylint": {
@ -1598,6 +1669,7 @@
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7" "version": "==2.4.7"
}, },
"pytest": { "pytest": {
@ -1702,6 +1774,7 @@
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.25.1" "version": "==2.25.1"
}, },
"requests-mock": { "requests-mock": {
@ -1725,6 +1798,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0" "version": "==1.16.0"
}, },
"smmap": { "smmap": {
@ -1732,6 +1806,7 @@
"sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182", "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182",
"sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2" "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"
], ],
"markers": "python_version >= '3.5'",
"version": "==4.0.0" "version": "==4.0.0"
}, },
"stevedore": { "stevedore": {
@ -1739,6 +1814,7 @@
"sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee", "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee",
"sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a" "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"
], ],
"markers": "python_version >= '3.6'",
"version": "==3.3.0" "version": "==3.3.0"
}, },
"toml": { "toml": {
@ -1746,6 +1822,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2" "version": "==0.10.2"
}, },
"urllib3": { "urllib3": {

View File

@ -21,7 +21,7 @@ authentik is an open-source Identity Provider focused on flexibility and versati
For small/test setups it is recommended to use docker-compose, see the [documentation](https://goauthentik.io/docs/installation/docker-compose/) For small/test setups it is recommended to use docker-compose, see the [documentation](https://goauthentik.io/docs/installation/docker-compose/)
For bigger setups, there is a Helm Chart in the `helm/` directory. This is documented [here](https://goauthentik.io/docs/installation/kubernetes/) For bigger setups, there is a Helm Chart [here])(https://github.com/goauthentik/helm). This is documented [here](https://goauthentik.io/docs/installation/kubernetes/)
## Screenshots ## Screenshots

View File

@ -1,3 +1,3 @@
"""authentik""" """authentik"""
__version__ = "2021.6.1-rc3" __version__ = "2021.6.1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -10,3 +10,25 @@ class AuthentikAPIConfig(AppConfig):
label = "authentik_api" label = "authentik_api"
mountpoint = "api/" mountpoint = "api/"
verbose_name = "authentik API" verbose_name = "authentik API"
def ready(self) -> None:
from drf_spectacular.extensions import OpenApiAuthenticationExtension
from authentik.api.authentication import TokenAuthentication
# Class is defined here as it needs to be created early enough that drf-spectacular will
# find it, but also won't cause any import issues
# pylint: disable=unused-variable
class TokenSchema(OpenApiAuthenticationExtension):
"""Auth schema"""
target_class = TokenAuthentication
name = "authentik"
def get_security_definition(self, auto_schema):
"""Auth schema"""
return {
"type": "apiKey",
"in": "header",
"name": "Authorization",
}

View File

@ -3,7 +3,6 @@ from base64 import b64decode
from binascii import Error from binascii import Error
from typing import Any, Optional, Union from typing import Any, Optional, Union
from drf_spectacular.authentication import OpenApiAuthenticationExtension
from rest_framework.authentication import BaseAuthentication, get_authorization_header from rest_framework.authentication import BaseAuthentication, get_authorization_header
from rest_framework.exceptions import AuthenticationFailed from rest_framework.exceptions import AuthenticationFailed
from rest_framework.request import Request from rest_framework.request import Request
@ -56,18 +55,3 @@ class TokenAuthentication(BaseAuthentication):
return None return None
return (token.user, None) # pragma: no cover return (token.user, None) # pragma: no cover
class TokenSchema(OpenApiAuthenticationExtension):
"""Auth schema"""
target_class = TokenAuthentication
name = "authentik"
def get_security_definition(self, auto_schema):
"""Auth schema"""
return {
"type": "apiKey",
"in": "header",
"name": "Authorization",
}

View File

@ -11,13 +11,7 @@ from drf_spectacular.utils import (
inline_serializer, inline_serializer,
) )
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.fields import ( from rest_framework.fields import BooleanField, CharField, FileField, ReadOnlyField
BooleanField,
CharField,
FileField,
IntegerField,
ReadOnlyField,
)
from rest_framework.parsers import MultiPartParser from rest_framework.parsers import MultiPartParser
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
@ -107,15 +101,19 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
return applications return applications
@extend_schema( @extend_schema(
request=inline_serializer( parameters=[
"CheckAccessRequest", fields={"for_user": IntegerField(required=False)} OpenApiParameter(
), name="for_user",
location=OpenApiParameter.QUERY,
type=OpenApiTypes.INT,
)
],
responses={ responses={
200: PolicyTestResultSerializer(), 200: PolicyTestResultSerializer(),
404: OpenApiResponse(description="for_user user not found"), 404: OpenApiResponse(description="for_user user not found"),
}, },
) )
@action(detail=True, methods=["POST"]) @action(detail=True, methods=["GET"])
# pylint: disable=unused-argument # pylint: disable=unused-argument
def check_access(self, request: Request, slug: str) -> Response: def check_access(self, request: Request, slug: str) -> Response:
"""Check access to a single application by slug""" """Check access to a single application by slug"""
@ -204,7 +202,7 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
"""Set application icon""" """Set application icon"""
app: Application = self.get_object() app: Application = self.get_object()
icon = request.FILES.get("file", None) icon = request.FILES.get("file", None)
clear = request.data.get("clear", False) clear = request.data.get("clear", "false").lower() == "true"
if clear: if clear:
# .delete() saves the model by default # .delete() saves the model by default
app.meta_icon.delete() app.meta_icon.delete()

View File

@ -26,6 +26,8 @@ class ImpersonateMiddleware:
if SESSION_IMPERSONATE_USER in request.session: if SESSION_IMPERSONATE_USER in request.session:
request.user = request.session[SESSION_IMPERSONATE_USER] request.user = request.session[SESSION_IMPERSONATE_USER]
# Ensure that the user is active, otherwise nothing will work
request.user.is_active = True
return self.get_response(request) return self.get_response(request)

View File

@ -494,8 +494,12 @@ class AuthenticatedSession(ExpiringModel):
last_used = models.DateTimeField(auto_now=True) last_used = models.DateTimeField(auto_now=True)
@staticmethod @staticmethod
def from_request(request: HttpRequest, user: User) -> "AuthenticatedSession": def from_request(
request: HttpRequest, user: User
) -> Optional["AuthenticatedSession"]:
"""Create a new session from a http request""" """Create a new session from a http request"""
if not hasattr(request, "session") or not request.session.session_key:
return None
return AuthenticatedSession( return AuthenticatedSession(
session_key=request.session.session_key, session_key=request.session.session_key,
user=user, user=user,

View File

@ -1,11 +1,12 @@
"""authentik core signals""" """authentik core signals"""
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Type
from django.contrib.auth.signals import user_logged_in, user_logged_out from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache from django.core.cache import cache
from django.core.signals import Signal from django.core.signals import Signal
from django.db.models import Model from django.db.models import Model
from django.db.models.signals import post_save from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.http.request import HttpRequest from django.http.request import HttpRequest
from prometheus_client import Gauge from prometheus_client import Gauge
@ -18,7 +19,7 @@ GAUGE_MODELS = Gauge(
) )
if TYPE_CHECKING: if TYPE_CHECKING:
from authentik.core.models import User from authentik.core.models import AuthenticatedSession, User
@receiver(post_save) @receiver(post_save)
@ -48,7 +49,9 @@ def user_logged_in_session(sender, request: HttpRequest, user: "User", **_):
"""Create an AuthenticatedSession from request""" """Create an AuthenticatedSession from request"""
from authentik.core.models import AuthenticatedSession from authentik.core.models import AuthenticatedSession
AuthenticatedSession.from_request(request, user).save() session = AuthenticatedSession.from_request(request, user)
if session:
session.save()
@receiver(user_logged_out) @receiver(user_logged_out)
@ -60,3 +63,17 @@ def user_logged_out_session(sender, request: HttpRequest, user: "User", **_):
AuthenticatedSession.objects.filter( AuthenticatedSession.objects.filter(
session_key=request.session.session_key session_key=request.session.session_key
).delete() ).delete()
@receiver(pre_delete)
def authenticated_session_delete(
sender: Type[Model], instance: "AuthenticatedSession", **_
):
"""Delete session when authenticated session is deleted"""
from authentik.core.models import AuthenticatedSession
if sender != AuthenticatedSession:
return
cache_key = f"{KEY_PREFIX}{instance.session_key}"
cache.delete(cache_key)

View File

@ -33,6 +33,7 @@ from authentik.flows.planner import (
from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs from authentik.lib.utils.urls import redirect_with_qs
from authentik.policies.utils import delete_none_keys from authentik.policies.utils import delete_none_keys
from authentik.stages.password import BACKEND_DJANGO
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
@ -182,6 +183,8 @@ class SourceFlowManager:
# pylint: disable=unused-argument # pylint: disable=unused-argument
def get_stages_to_append(self, flow: Flow) -> list[Stage]: def get_stages_to_append(self, flow: Flow) -> list[Stage]:
"""Hook to override stages which are appended to the flow""" """Hook to override stages which are appended to the flow"""
if not self.source.enrollment_flow:
return []
if flow.slug == self.source.enrollment_flow.slug: if flow.slug == self.source.enrollment_flow.slug:
return [ return [
in_memory_stage(PostUserEnrollmentStage), in_memory_stage(PostUserEnrollmentStage),
@ -198,7 +201,7 @@ class SourceFlowManager:
kwargs.update( kwargs.update(
{ {
# Since we authenticate the user by their token, they have no backend set # Since we authenticate the user by their token, they have no backend set
PLAN_CONTEXT_AUTHENTICATION_BACKEND: "django.contrib.auth.backends.ModelBackend", PLAN_CONTEXT_AUTHENTICATION_BACKEND: BACKEND_DJANGO,
PLAN_CONTEXT_SSO: True, PLAN_CONTEXT_SSO: True,
PLAN_CONTEXT_SOURCE: self.source, PLAN_CONTEXT_SOURCE: self.source,
PLAN_CONTEXT_REDIRECT: final_redirect, PLAN_CONTEXT_REDIRECT: final_redirect,

View File

@ -3,16 +3,6 @@
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% block head %}
{{ block.super }}
<style>
.pf-c-background-image::before {
background-image: url("{% static 'dist/assets/images/flow_background.jpg' %}");
background-position: center;
}
</style>
{% endblock %}
{% block title %} {% block title %}
{% trans 'End session' %} - {{ tenant.branding_title }} {% trans 'End session' %} - {{ tenant.branding_title }}
{% endblock %} {% endblock %}

View File

@ -26,7 +26,7 @@ class TestApplicationsAPI(APITestCase):
def test_check_access(self): def test_check_access(self):
"""Test check_access operation""" """Test check_access operation"""
self.client.force_login(self.user) self.client.force_login(self.user)
response = self.client.post( response = self.client.get(
reverse( reverse(
"authentik_api:application-check-access", "authentik_api:application-check-access",
kwargs={"slug": self.allowed.slug}, kwargs={"slug": self.allowed.slug},
@ -36,7 +36,7 @@ class TestApplicationsAPI(APITestCase):
self.assertJSONEqual( self.assertJSONEqual(
force_str(response.content), {"messages": [], "passing": True} force_str(response.content), {"messages": [], "passing": True}
) )
response = self.client.post( response = self.client.get(
reverse( reverse(
"authentik_api:application-check-access", "authentik_api:application-check-access",
kwargs={"slug": self.denied.slug}, kwargs={"slug": self.denied.slug},

View File

@ -17,6 +17,9 @@ class TestImpersonation(TestCase):
def test_impersonate_simple(self): def test_impersonate_simple(self):
"""test simple impersonation and un-impersonation""" """test simple impersonation and un-impersonation"""
# test with an inactive user to ensure that still works
self.other_user.is_active = False
self.other_user.save()
self.client.force_login(self.akadmin) self.client.force_login(self.akadmin)
self.client.get( self.client.get(

View File

@ -2,7 +2,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Optional
from rest_framework.fields import CharField, DictField from rest_framework.fields import CharField
from authentik.core.api.utils import PassiveSerializer from authentik.core.api.utils import PassiveSerializer
from authentik.flows.challenge import Challenge from authentik.flows.challenge import Challenge
@ -22,18 +22,10 @@ class UILoginButton:
icon_url: Optional[str] = None icon_url: Optional[str] = None
class UILoginButtonSerializer(PassiveSerializer):
"""Serializer for Login buttons of sources"""
name = CharField()
challenge = DictField()
icon_url = CharField(required=False, allow_null=True)
class UserSettingSerializer(PassiveSerializer): class UserSettingSerializer(PassiveSerializer):
"""Serializer for User settings for stages and sources""" """Serializer for User settings for stages and sources"""
object_uid = CharField() object_uid = CharField()
component = CharField() component = CharField()
title = CharField() title = CharField()
configure_url = CharField() configure_url = CharField(required=False)

View File

@ -36,6 +36,7 @@ class EventSerializer(ModelSerializer):
"client_ip", "client_ip",
"created", "created",
"expires", "expires",
"tenant",
] ]
@ -76,6 +77,11 @@ class EventsFilter(django_filters.FilterSet):
field_name="action", field_name="action",
lookup_expr="icontains", lookup_expr="icontains",
) )
tenant_name = django_filters.CharFilter(
field_name="tenant",
lookup_expr="name",
label="Tenant name",
)
# pylint: disable=unused-argument # pylint: disable=unused-argument
def filter_context_model_pk(self, queryset, name, value): def filter_context_model_pk(self, queryset, name, value):

View File

@ -40,9 +40,9 @@ class GeoIPReader:
return return
try: try:
reader = Reader(path) reader = Reader(path)
LOGGER.info("Loaded GeoIP database")
self.__reader = reader self.__reader = reader
self.__last_mtime = stat(path).st_mtime self.__last_mtime = stat(path).st_mtime
LOGGER.info("Loaded GeoIP database", last_write=self.__last_mtime)
except OSError as exc: except OSError as exc:
LOGGER.warning("Failed to load GeoIP database", exc=exc) LOGGER.warning("Failed to load GeoIP database", exc=exc)

View File

@ -2,6 +2,7 @@
from functools import partial from functools import partial
from typing import Callable from typing import Callable
from django.conf import settings
from django.db.models import Model from django.db.models import Model
from django.db.models.signals import post_save, pre_delete from django.db.models.signals import post_save, pre_delete
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
@ -12,6 +13,7 @@ from authentik.core.models import User
from authentik.events.models import Event, EventAction, Notification from authentik.events.models import Event, EventAction, Notification
from authentik.events.signals import EventNewThread from authentik.events.signals import EventNewThread
from authentik.events.utils import model_to_dict from authentik.events.utils import model_to_dict
from authentik.lib.utils.errors import exception_to_string
class AuditMiddleware: class AuditMiddleware:
@ -54,10 +56,19 @@ class AuditMiddleware:
# pylint: disable=unused-argument # pylint: disable=unused-argument
def process_exception(self, request: HttpRequest, exception: Exception): def process_exception(self, request: HttpRequest, exception: Exception):
"""Unregister handlers in case of exception""" """Disconnect handlers in case of exception"""
post_save.disconnect(dispatch_uid=LOCAL.authentik["request_id"]) post_save.disconnect(dispatch_uid=LOCAL.authentik["request_id"])
pre_delete.disconnect(dispatch_uid=LOCAL.authentik["request_id"]) pre_delete.disconnect(dispatch_uid=LOCAL.authentik["request_id"])
if settings.DEBUG:
return
thread = EventNewThread(
EventAction.SYSTEM_EXCEPTION,
request,
message=exception_to_string(exception),
)
thread.run()
@staticmethod @staticmethod
# pylint: disable=unused-argument # pylint: disable=unused-argument
def post_save_handler( def post_save_handler(

View File

@ -0,0 +1,55 @@
# Generated by Django 3.2.4 on 2021-06-14 15:33
from django.db import migrations, models
import authentik.events.models
class Migration(migrations.Migration):
dependencies = [
("authentik_events", "0015_alter_event_action"),
]
operations = [
migrations.AddField(
model_name="event",
name="tenant",
field=models.JSONField(
blank=True, default=authentik.events.models.default_tenant
),
),
migrations.AlterField(
model_name="event",
name="action",
field=models.TextField(
choices=[
("login", "Login"),
("login_failed", "Login Failed"),
("logout", "Logout"),
("user_write", "User Write"),
("suspicious_request", "Suspicious Request"),
("password_set", "Password Set"),
("secret_view", "Secret View"),
("invitation_used", "Invite Used"),
("authorize_application", "Authorize Application"),
("source_linked", "Source Linked"),
("impersonation_started", "Impersonation Started"),
("impersonation_ended", "Impersonation Ended"),
("policy_execution", "Policy Execution"),
("policy_exception", "Policy Exception"),
("property_mapping_exception", "Property Mapping Exception"),
("system_task_execution", "System Task Execution"),
("system_task_exception", "System Task Exception"),
("system_exception", "System Exception"),
("configuration_error", "Configuration Error"),
("model_created", "Model Created"),
("model_updated", "Model Updated"),
("model_deleted", "Model Deleted"),
("email_sent", "Email Sent"),
("update_available", "Update Available"),
("custom_", "Custom Prefix"),
]
),
),
]

View File

@ -21,11 +21,12 @@ from authentik.core.middleware import (
) )
from authentik.core.models import ExpiringModel, Group, User from authentik.core.models import ExpiringModel, Group, User
from authentik.events.geo import GEOIP_READER from authentik.events.geo import GEOIP_READER
from authentik.events.utils import cleanse_dict, get_user, sanitize_dict from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict
from authentik.lib.sentry import SentryIgnoredException from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.http import get_client_ip from authentik.lib.utils.http import get_client_ip
from authentik.policies.models import PolicyBindingModel from authentik.policies.models import PolicyBindingModel
from authentik.stages.email.utils import TemplateEmailMessage from authentik.stages.email.utils import TemplateEmailMessage
from authentik.tenants.utils import DEFAULT_TENANT
LOGGER = get_logger("authentik.events") LOGGER = get_logger("authentik.events")
GAUGE_EVENTS = Gauge( GAUGE_EVENTS = Gauge(
@ -40,6 +41,11 @@ def default_event_duration():
return now() + timedelta(days=365) return now() + timedelta(days=365)
def default_tenant():
"""Get a default value for tenant"""
return sanitize_dict(model_to_dict(DEFAULT_TENANT))
class NotificationTransportError(SentryIgnoredException): class NotificationTransportError(SentryIgnoredException):
"""Error raised when a notification fails to be delivered""" """Error raised when a notification fails to be delivered"""
@ -71,6 +77,7 @@ class EventAction(models.TextChoices):
SYSTEM_TASK_EXECUTION = "system_task_execution" SYSTEM_TASK_EXECUTION = "system_task_execution"
SYSTEM_TASK_EXCEPTION = "system_task_exception" SYSTEM_TASK_EXCEPTION = "system_task_exception"
SYSTEM_EXCEPTION = "system_exception"
CONFIGURATION_ERROR = "configuration_error" CONFIGURATION_ERROR = "configuration_error"
@ -94,6 +101,7 @@ class Event(ExpiringModel):
context = models.JSONField(default=dict, blank=True) context = models.JSONField(default=dict, blank=True)
client_ip = models.GenericIPAddressField(null=True) client_ip = models.GenericIPAddressField(null=True)
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
tenant = models.JSONField(default=default_tenant, blank=True)
# Shadow the expires attribute from ExpiringModel to override the default duration # Shadow the expires attribute from ExpiringModel to override the default duration
expires = models.DateTimeField(default=default_event_duration) expires = models.DateTimeField(default=default_event_duration)
@ -132,6 +140,13 @@ class Event(ExpiringModel):
"""Add data from a Django-HttpRequest, allowing the creation of """Add data from a Django-HttpRequest, allowing the creation of
Events independently from requests. Events independently from requests.
`user` arguments optionally overrides user from requests.""" `user` arguments optionally overrides user from requests."""
if request:
self.context["http_request"] = {
"path": request.get_full_path(),
"method": request.method,
}
if hasattr(request, "tenant"):
self.tenant = sanitize_dict(model_to_dict(request.tenant))
if hasattr(request, "user"): if hasattr(request, "user"):
original_user = None original_user = None
if hasattr(request, "session"): if hasattr(request, "session"):

View File

@ -301,10 +301,14 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
"""Set Flow background""" """Set Flow background"""
flow: Flow = self.get_object() flow: Flow = self.get_object()
background = request.FILES.get("file", None) background = request.FILES.get("file", None)
clear = request.data.get("clear", False) clear = request.data.get("clear", "false").lower() == "true"
if clear: if clear:
if flow.background_url.startswith("/media"):
# .delete() saves the model by default # .delete() saves the model by default
flow.background.delete() flow.background.delete()
else:
flow.background = None
flow.save()
return Response({}) return Response({})
if background: if background:
flow.background = background flow.background = background

View File

@ -93,7 +93,7 @@ class StageViewSet(
if not user_settings: if not user_settings:
continue continue
user_settings.initial_data["object_uid"] = str(stage.pk) user_settings.initial_data["object_uid"] = str(stage.pk)
if hasattr(stage, "configure_flow"): if hasattr(stage, "configure_flow") and stage.configure_flow:
user_settings.initial_data["configure_url"] = reverse( user_settings.initial_data["configure_url"] = reverse(
"authentik_flows:configure", "authentik_flows:configure",
kwargs={"stage_uuid": stage.pk}, kwargs={"stage_uuid": stage.pk},

View File

@ -6,6 +6,7 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from authentik.flows.models import FlowDesignation from authentik.flows.models import FlowDesignation
from authentik.stages.identification.models import UserFields from authentik.stages.identification.models import UserFields
from authentik.stages.password import BACKEND_DJANGO, BACKEND_LDAP
def create_default_authentication_flow( def create_default_authentication_flow(
@ -31,7 +32,7 @@ def create_default_authentication_flow(
password_stage, _ = PasswordStage.objects.using(db_alias).update_or_create( password_stage, _ = PasswordStage.objects.using(db_alias).update_or_create(
name="default-authentication-password", name="default-authentication-password",
defaults={"backends": ["django.contrib.auth.backends.ModelBackend"]}, defaults={"backends": [BACKEND_DJANGO, BACKEND_LDAP]},
) )
login_stage, _ = UserLoginStage.objects.using(db_alias).update_or_create( login_stage, _ = UserLoginStage.objects.using(db_alias).update_or_create(

View File

@ -15,9 +15,6 @@ PREFILL_POLICY_EXPRESSION = """# This policy sets the user for the currently run
# by injecting "pending_user" # by injecting "pending_user"
akadmin = ak_user_by(username="akadmin") akadmin = ak_user_by(username="akadmin")
context["pending_user"] = akadmin context["pending_user"] = akadmin
# We're also setting the backend for the user, so we can
# directly login without having to identify again
context["user_backend"] = "django.contrib.auth.backends.ModelBackend"
return True""" return True"""

View File

@ -18,27 +18,11 @@ from authentik.flows.challenge import (
) )
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.views import FlowExecutorView from authentik.flows.views import FlowExecutorView
from authentik.lib.sentry import SentryIgnoredException
PLAN_CONTEXT_PENDING_USER_IDENTIFIER = "pending_user_identifier" PLAN_CONTEXT_PENDING_USER_IDENTIFIER = "pending_user_identifier"
LOGGER = get_logger() LOGGER = get_logger()
class InvalidChallengeError(SentryIgnoredException):
"""Error raised when a challenge from a stage is not valid"""
def __init__(self, errors, stage_view: View, challenge: Challenge) -> None:
super().__init__()
self.errors = errors
self.stage_view = stage_view
self.challenge = challenge
def __str__(self) -> str:
return (
f"Invalid challenge from {self.stage_view}: {self.errors}\n{self.challenge}"
)
class StageView(View): class StageView(View):
"""Abstract Stage, inherits TemplateView but can be combined with FormView""" """Abstract Stage, inherits TemplateView but can be combined with FormView"""

View File

@ -44,6 +44,7 @@ from authentik.flows.planner import (
FlowPlan, FlowPlan,
FlowPlanner, FlowPlanner,
) )
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.reflection import all_subclasses, class_to_path from authentik.lib.utils.reflection import all_subclasses, class_to_path
from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs
from authentik.tenants.models import Tenant from authentik.tenants.models import Tenant
@ -93,6 +94,10 @@ def challenge_response_types():
return Inner() return Inner()
class InvalidStageError(SentryIgnoredException):
"""Error raised when a challenge from a stage is not valid"""
@method_decorator(xframe_options_sameorigin, name="dispatch") @method_decorator(xframe_options_sameorigin, name="dispatch")
class FlowExecutorView(APIView): class FlowExecutorView(APIView):
"""Stage 1 Flow executor, passing requests to Stage Views""" """Stage 1 Flow executor, passing requests to Stage Views"""
@ -164,17 +169,24 @@ class FlowExecutorView(APIView):
current_stage=self.current_stage, current_stage=self.current_stage,
flow_slug=self.flow.slug, flow_slug=self.flow.slug,
) )
try:
stage_cls = self.current_stage.type stage_cls = self.current_stage.type
except NotImplementedError as exc:
self._logger.debug("Error getting stage type", exc=exc)
return self.stage_invalid()
self.current_stage_view = stage_cls(self) self.current_stage_view = stage_cls(self)
self.current_stage_view.args = self.args self.current_stage_view.args = self.args
self.current_stage_view.kwargs = self.kwargs self.current_stage_view.kwargs = self.kwargs
self.current_stage_view.request = request self.current_stage_view.request = request
try:
return super().dispatch(request) return super().dispatch(request)
except InvalidStageError as exc:
return self.stage_invalid(str(exc))
@extend_schema( @extend_schema(
responses={ responses={
200: PolymorphicProxySerializer( 200: PolymorphicProxySerializer(
component_name="FlowChallengeRequest", component_name="ChallengeTypes",
serializers=challenge_types(), serializers=challenge_types(),
resource_type_field_name="component", resource_type_field_name="component",
), ),
@ -214,7 +226,7 @@ class FlowExecutorView(APIView):
@extend_schema( @extend_schema(
responses={ responses={
200: PolymorphicProxySerializer( 200: PolymorphicProxySerializer(
component_name="FlowChallengeRequest", component_name="ChallengeTypes",
serializers=challenge_types(), serializers=challenge_types(),
resource_type_field_name="component", resource_type_field_name="component",
), ),

View File

@ -0,0 +1,10 @@
"""error utils"""
from traceback import format_tb
TRACEBACK_HEADER = "Traceback (most recent call last):\n"
def exception_to_string(exc: Exception) -> str:
"""Convert exception to string stackrace"""
# Either use passed original exception or whatever we have
return TRACEBACK_HEADER + "".join(format_tb(exc.__traceback__)) + str(exc)

View File

@ -33,6 +33,13 @@ class ServiceConnectionSerializer(ModelSerializer, MetaNameSerializer):
component = ReadOnlyField() component = ReadOnlyField()
def get_component(self, obj: OutpostServiceConnection) -> str:
"""Get object type so that we know how to edit the object"""
# pyright: reportGeneralTypeIssues=false
if obj.__class__ == OutpostServiceConnection:
return ""
return obj.component
class Meta: class Meta:
model = OutpostServiceConnection model = OutpostServiceConnection

View File

@ -63,7 +63,7 @@ class DockerController(BaseController):
self.client.images.pull(image_name) self.client.images.pull(image_name)
container_args = { container_args = {
"image": image_name, "image": image_name,
"name": f"authentik-proxy-{self.outpost.uuid.hex}", "name": container_name,
"detach": True, "detach": True,
"ports": { "ports": {
f"{port.port}/{port.protocol.lower()}": port.inner_port or port.port f"{port.port}/{port.protocol.lower()}": port.inner_port or port.port

View File

@ -8,7 +8,7 @@ from uuid import uuid4
from dacite import from_dict from dacite import from_dict
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.core.cache import cache from django.core.cache import cache
from django.db import models, transaction from django.db import IntegrityError, models, transaction
from django.db.models.base import Model from django.db.models.base import Model
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from docker.client import DockerClient from docker.client import DockerClient
@ -50,6 +50,8 @@ class ServiceConnectionInvalid(SentryIgnoredException):
class OutpostConfig: class OutpostConfig:
"""Configuration an outpost uses to configure it self""" """Configuration an outpost uses to configure it self"""
# update website/docs/outposts/outposts.md
authentik_host: str authentik_host: str
authentik_host_insecure: bool = False authentik_host_insecure: bool = False
@ -141,7 +143,9 @@ class OutpostServiceConnection(models.Model):
@property @property
def component(self) -> str: def component(self) -> str:
"""Return component used to edit this object""" """Return component used to edit this object"""
raise NotImplementedError # This is called when creating an outpost with a service connection
# since the response doesn't use the correct inheritance
return ""
class Meta: class Meta:
@ -380,13 +384,11 @@ class Outpost(models.Model):
tokens = Token.filter_not_expired( tokens = Token.filter_not_expired(
identifier=self.token_identifier, identifier=self.token_identifier,
intent=TokenIntents.INTENT_API, intent=TokenIntents.INTENT_API,
managed=managed,
) )
if tokens.exists(): if tokens.exists():
token = tokens.first() return tokens.first()
if not token.managed: try:
token.managed = managed
token.save()
return token
return Token.objects.create( return Token.objects.create(
user=self.user, user=self.user,
identifier=self.token_identifier, identifier=self.token_identifier,
@ -395,6 +397,11 @@ class Outpost(models.Model):
expiring=False, expiring=False,
managed=managed, managed=managed,
) )
except IntegrityError:
# Integrity error happens mostly when managed is re-used
Token.objects.filter(managed=managed).delete()
Token.objects.filter(identifier=self.token_identifier).delete()
return self.token
def get_required_objects(self) -> Iterable[Union[models.Model, str]]: def get_required_objects(self) -> Iterable[Union[models.Model, str]]:
"""Get an iterator of all objects the user needs read access to""" """Get an iterator of all objects the user needs read access to"""

View File

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

View File

@ -1,7 +1,6 @@
"""authentik policy task""" """authentik policy task"""
from multiprocessing import get_context from multiprocessing import get_context
from multiprocessing.connection import Connection from multiprocessing.connection import Connection
from traceback import format_tb
from typing import Optional from typing import Optional
from django.core.cache import cache from django.core.cache import cache
@ -11,12 +10,12 @@ from sentry_sdk.tracing import Span
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.lib.utils.errors import exception_to_string
from authentik.policies.exceptions import PolicyException from authentik.policies.exceptions import PolicyException
from authentik.policies.models import PolicyBinding from authentik.policies.models import PolicyBinding
from authentik.policies.types import PolicyRequest, PolicyResult from authentik.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()
TRACEBACK_HEADER = "Traceback (most recent call last):\n"
FORK_CTX = get_context("fork") FORK_CTX = get_context("fork")
PROCESS_CLASS = FORK_CTX.Process PROCESS_CLASS = FORK_CTX.Process
@ -106,11 +105,7 @@ class PolicyProcess(PROCESS_CLASS):
except PolicyException as exc: except PolicyException as exc:
# Either use passed original exception or whatever we have # Either use passed original exception or whatever we have
src_exc = exc.src_exc if exc.src_exc else exc src_exc = exc.src_exc if exc.src_exc else exc
error_string = ( error_string = exception_to_string(src_exc)
TRACEBACK_HEADER
+ "".join(format_tb(src_exc.__traceback__))
+ str(src_exc)
)
# Create policy exception event, only when we're not debugging # Create policy exception event, only when we're not debugging
if not self.request.debug: if not self.request.debug:
self.create_event(EventAction.POLICY_EXCEPTION, message=error_string) self.create_event(EventAction.POLICY_EXCEPTION, message=error_string)

View File

@ -3,11 +3,13 @@ from django.core.cache import cache
from django.db import models from django.db import models
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer
from structlog import get_logger
from authentik.lib.utils.http import get_client_ip from authentik.lib.utils.http import get_client_ip
from authentik.policies.models import Policy from authentik.policies.models import Policy
from authentik.policies.types import PolicyRequest, PolicyResult from authentik.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger()
CACHE_KEY_IP_PREFIX = "authentik_reputation_ip_" CACHE_KEY_IP_PREFIX = "authentik_reputation_ip_"
CACHE_KEY_USER_PREFIX = "authentik_reputation_user_" CACHE_KEY_USER_PREFIX = "authentik_reputation_user_"
@ -35,9 +37,16 @@ class ReputationPolicy(Policy):
if self.check_ip: if self.check_ip:
score = cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0) score = cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0)
passing = passing and score <= self.threshold passing = passing and score <= self.threshold
LOGGER.debug("Score for IP", ip=remote_ip, score=score, passing=passing)
if self.check_username: if self.check_username:
score = cache.get_or_set(CACHE_KEY_USER_PREFIX + request.user.username, 0) score = cache.get_or_set(CACHE_KEY_USER_PREFIX + request.user.username, 0)
passing = passing and score <= self.threshold passing = passing and score <= self.threshold
LOGGER.debug(
"Score for Username",
username=request.user.username,
score=score,
passing=passing,
)
return PolicyResult(passing) return PolicyResult(passing)
class Meta: class Meta:

View File

@ -1,9 +1,10 @@
"""test reputation signals and policy""" """test reputation signals and policy"""
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.core.cache import cache from django.core.cache import cache
from django.test import TestCase from django.test import RequestFactory, TestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.lib.utils.http import DEFAULT_IP
from authentik.policies.reputation.models import ( from authentik.policies.reputation.models import (
CACHE_KEY_IP_PREFIX, CACHE_KEY_IP_PREFIX,
CACHE_KEY_USER_PREFIX, CACHE_KEY_USER_PREFIX,
@ -19,9 +20,12 @@ class TestReputationPolicy(TestCase):
"""test reputation signals and policy""" """test reputation signals and policy"""
def setUp(self): def setUp(self):
self.test_ip = "255.255.255.255" self.request_factory = RequestFactory()
self.request = self.request_factory.get("/")
self.test_ip = "127.0.0.1"
self.test_username = "test" self.test_username = "test"
cache.delete(CACHE_KEY_IP_PREFIX + self.test_ip) cache.delete(CACHE_KEY_IP_PREFIX + self.test_ip)
cache.delete(CACHE_KEY_IP_PREFIX + DEFAULT_IP)
cache.delete(CACHE_KEY_USER_PREFIX + self.test_username) cache.delete(CACHE_KEY_USER_PREFIX + self.test_username)
# We need a user for the one-to-one in userreputation # We need a user for the one-to-one in userreputation
self.user = User.objects.create(username=self.test_username) self.user = User.objects.create(username=self.test_username)
@ -29,7 +33,9 @@ class TestReputationPolicy(TestCase):
def test_ip_reputation(self): def test_ip_reputation(self):
"""test IP reputation""" """test IP reputation"""
# Trigger negative reputation # Trigger negative reputation
authenticate(None, username=self.test_username, password=self.test_username) authenticate(
self.request, username=self.test_username, password=self.test_username
)
# Test value in cache # Test value in cache
self.assertEqual(cache.get(CACHE_KEY_IP_PREFIX + self.test_ip), -1) self.assertEqual(cache.get(CACHE_KEY_IP_PREFIX + self.test_ip), -1)
# Save cache and check db values # Save cache and check db values
@ -39,7 +45,9 @@ class TestReputationPolicy(TestCase):
def test_user_reputation(self): def test_user_reputation(self):
"""test User reputation""" """test User reputation"""
# Trigger negative reputation # Trigger negative reputation
authenticate(None, username=self.test_username, password=self.test_username) authenticate(
self.request, username=self.test_username, password=self.test_username
)
# Test value in cache # Test value in cache
self.assertEqual(cache.get(CACHE_KEY_USER_PREFIX + self.test_username), -1) self.assertEqual(cache.get(CACHE_KEY_USER_PREFIX + self.test_username), -1)
# Save cache and check db values # Save cache and check db values

View File

@ -1,7 +1,7 @@
"""ProxyProvider API Views""" """ProxyProvider API Views"""
from typing import Any from typing import Any
from drf_spectacular.utils import extend_schema_field from drf_spectacular.utils import extend_schema_field, extend_schema_serializer
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField, ListField, SerializerMethodField from rest_framework.fields import CharField, ListField, SerializerMethodField
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
@ -85,6 +85,7 @@ class ProxyProviderViewSet(UsedByMixin, ModelViewSet):
ordering = ["name"] ordering = ["name"]
@extend_schema_serializer(deprecate_fields=["forward_auth_mode"])
class ProxyOutpostConfigSerializer(ModelSerializer): class ProxyOutpostConfigSerializer(ModelSerializer):
"""Proxy provider serializer for outposts""" """Proxy provider serializer for outposts"""

View File

@ -24,6 +24,7 @@ from authentik.sources.saml.processors.constants import (
SAML_NAME_ID_FORMAT_EMAIL, SAML_NAME_ID_FORMAT_EMAIL,
SAML_NAME_ID_FORMAT_PERSISTENT, SAML_NAME_ID_FORMAT_PERSISTENT,
SAML_NAME_ID_FORMAT_TRANSIENT, SAML_NAME_ID_FORMAT_TRANSIENT,
SAML_NAME_ID_FORMAT_UNSPECIFIED,
SAML_NAME_ID_FORMAT_WINDOWS, SAML_NAME_ID_FORMAT_WINDOWS,
SAML_NAME_ID_FORMAT_X509, SAML_NAME_ID_FORMAT_X509,
SIGN_ALGORITHM_TRANSFORM_MAP, SIGN_ALGORITHM_TRANSFORM_MAP,
@ -165,7 +166,10 @@ class AssertionProcessor:
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_EMAIL: if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_EMAIL:
name_id.text = self.http_request.user.email name_id.text = self.http_request.user.email
return name_id return name_id
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_PERSISTENT: if name_id.attrib["Format"] in [
SAML_NAME_ID_FORMAT_PERSISTENT,
SAML_NAME_ID_FORMAT_UNSPECIFIED,
]:
name_id.text = persistent name_id.text = persistent
return name_id return name_id
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_X509: if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_X509:
@ -180,7 +184,7 @@ class AssertionProcessor:
return name_id return name_id
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT: if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
# Use the hash of the user's session, which changes every session # Use the hash of the user's session, which changes every session
session_key: str = self.http_request.user.session.session_key session_key: str = self.http_request.session.session_key
name_id.text = sha256(session_key.encode()).hexdigest() name_id.text = sha256(session_key.encode()).hexdigest()
return name_id return name_id
raise UnsupportedNameIDFormat( raise UnsupportedNameIDFormat(

View File

@ -120,7 +120,7 @@ class ServiceProviderMetadataParser:
) )
ctx.key = key ctx.key = key
ctx.verify(signature_node) ctx.verify(signature_node)
except xmlsec.VerificationError as exc: except xmlsec.Error as exc:
raise ValueError("Failed to verify Metadata signature") from exc raise ValueError("Failed to verify Metadata signature") from exc
def parse(self, raw_xml: str) -> ServiceProviderMetadata: def parse(self, raw_xml: str) -> ServiceProviderMetadata:

View File

@ -20,7 +20,7 @@ from authentik.sources.saml.processors.constants import (
RSA_SHA256, RSA_SHA256,
RSA_SHA384, RSA_SHA384,
RSA_SHA512, RSA_SHA512,
SAML_NAME_ID_FORMAT_EMAIL, SAML_NAME_ID_FORMAT_UNSPECIFIED,
) )
LOGGER = get_logger() LOGGER = get_logger()
@ -42,7 +42,7 @@ class AuthNRequest:
relay_state: Optional[str] = None relay_state: Optional[str] = None
name_id_policy: str = SAML_NAME_ID_FORMAT_EMAIL name_id_policy: str = SAML_NAME_ID_FORMAT_UNSPECIFIED
class AuthNRequestParser: class AuthNRequestParser:
@ -69,10 +69,12 @@ class AuthNRequestParser:
auth_n_request = AuthNRequest(id=root.attrib["ID"], relay_state=relay_state) auth_n_request = AuthNRequest(id=root.attrib["ID"], relay_state=relay_state)
# Check if AuthnRequest has a NameID Policy object # Check if AuthnRequest has a NameID Policy object
name_id_policies = root.findall(f"{{{NS_SAML_PROTOCOL}}}:NameIDPolicy") name_id_policies = root.findall(f"{{{NS_SAML_PROTOCOL}}}NameIDPolicy")
if len(name_id_policies) > 0: if len(name_id_policies) > 0:
name_id_policy = name_id_policies[0] name_id_policy = name_id_policies[0]
auth_n_request.name_id_policy = name_id_policy.attrib["Format"] auth_n_request.name_id_policy = name_id_policy.attrib.get(
"Format", SAML_NAME_ID_FORMAT_UNSPECIFIED
)
return auth_n_request return auth_n_request
@ -108,7 +110,7 @@ class AuthNRequestParser:
) )
ctx.key = key ctx.key = key
ctx.verify(signature_node) ctx.verify(signature_node)
except xmlsec.VerificationError as exc: except xmlsec.Error as exc:
raise CannotHandleAssertion(ERROR_FAILED_TO_VERIFY) from exc raise CannotHandleAssertion(ERROR_FAILED_TO_VERIFY) from exc
return self._parse_xml(decoded_xml, relay_state) return self._parse_xml(decoded_xml, relay_state)
@ -160,7 +162,7 @@ class AuthNRequestParser:
sign_algorithm_transform, sign_algorithm_transform,
b64decode(signature), b64decode(signature),
) )
except xmlsec.VerificationError as exc: except xmlsec.Error as exc:
raise CannotHandleAssertion(ERROR_FAILED_TO_VERIFY) from exc raise CannotHandleAssertion(ERROR_FAILED_TO_VERIFY) from exc
return self._parse_xml(decoded_xml, relay_state) return self._parse_xml(decoded_xml, relay_state)

View File

@ -2,18 +2,19 @@
from base64 import b64encode from base64 import b64encode
from django.contrib.sessions.middleware import SessionMiddleware from django.contrib.sessions.middleware import SessionMiddleware
from django.http.request import HttpRequest, QueryDict from django.http.request import QueryDict
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from guardian.utils import get_anonymous_user from guardian.utils import get_anonymous_user
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.flows.tests.test_planner import dummy_get_response
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.processors.assertion import AssertionProcessor from authentik.providers.saml.processors.assertion import AssertionProcessor
from authentik.providers.saml.processors.request_parser import AuthNRequestParser from authentik.providers.saml.processors.request_parser import AuthNRequestParser
from authentik.sources.saml.exceptions import MismatchedRequestID from authentik.sources.saml.exceptions import MismatchedRequestID
from authentik.sources.saml.models import SAMLSource from authentik.sources.saml.models import SAMLSource
from authentik.sources.saml.processors.constants import SAML_NAME_ID_FORMAT_EMAIL from authentik.sources.saml.processors.constants import SAML_NAME_ID_FORMAT_UNSPECIFIED
from authentik.sources.saml.processors.request import ( from authentik.sources.saml.processors.request import (
SESSION_REQUEST_ID, SESSION_REQUEST_ID,
RequestProcessor, RequestProcessor,
@ -58,11 +59,6 @@ qNAZMq1DqpibfCBg
-----END CERTIFICATE-----""" -----END CERTIFICATE-----"""
def dummy_get_response(request: HttpRequest): # pragma: no cover
"""Dummy get_response for SessionMiddleware"""
return None
class TestAuthNRequest(TestCase): class TestAuthNRequest(TestCase):
"""Test AuthN Request generator and parser""" """Test AuthN Request generator and parser"""
@ -210,5 +206,5 @@ class TestAuthNRequest(TestCase):
REDIRECT_REQUEST, REDIRECT_RELAY_STATE, REDIRECT_SIGNATURE, REDIRECT_SIG_ALG REDIRECT_REQUEST, REDIRECT_RELAY_STATE, REDIRECT_SIGNATURE, REDIRECT_SIG_ALG
) )
self.assertEqual(parsed_request.id, "_dcf55fcd27a887e60a7ef9ee6fd3adab") self.assertEqual(parsed_request.id, "_dcf55fcd27a887e60a7ef9ee6fd3adab")
self.assertEqual(parsed_request.name_id_policy, SAML_NAME_ID_FORMAT_EMAIL) self.assertEqual(parsed_request.name_id_policy, SAML_NAME_ID_FORMAT_UNSPECIFIED)
self.assertEqual(parsed_request.relay_state, REDIRECT_RELAY_STATE) self.assertEqual(parsed_request.relay_state, REDIRECT_RELAY_STATE)

View File

@ -17,6 +17,7 @@ from authentik.providers.saml.models import SAMLBindings, SAMLProvider
from authentik.providers.saml.processors.assertion import AssertionProcessor from authentik.providers.saml.processors.assertion import AssertionProcessor
from authentik.providers.saml.processors.request_parser import AuthNRequest from authentik.providers.saml.processors.request_parser import AuthNRequest
from authentik.providers.saml.utils.encoding import deflate_and_base64_encode, nice64 from authentik.providers.saml.utils.encoding import deflate_and_base64_encode, nice64
from authentik.sources.saml.exceptions import SAMLException
LOGGER = get_logger() LOGGER = get_logger()
URL_VALIDATOR = URLValidator(schemes=("http", "https")) URL_VALIDATOR = URLValidator(schemes=("http", "https"))
@ -56,22 +57,30 @@ class SAMLFlowFinalView(ChallengeStageView):
provider: SAMLProvider = get_object_or_404( provider: SAMLProvider = get_object_or_404(
SAMLProvider, pk=application.provider_id SAMLProvider, pk=application.provider_id
) )
# Log Application Authorization
Event.new(
EventAction.AUTHORIZE_APPLICATION,
authorized_application=application,
flow=self.executor.plan.flow_pk,
).from_http(self.request)
if SESSION_KEY_AUTH_N_REQUEST not in self.request.session: if SESSION_KEY_AUTH_N_REQUEST not in self.request.session:
return self.executor.stage_invalid() return self.executor.stage_invalid()
auth_n_request: AuthNRequest = self.request.session.pop( auth_n_request: AuthNRequest = self.request.session.pop(
SESSION_KEY_AUTH_N_REQUEST SESSION_KEY_AUTH_N_REQUEST
) )
try:
response = AssertionProcessor( response = AssertionProcessor(
provider, request, auth_n_request provider, request, auth_n_request
).build_response() ).build_response()
except SAMLException as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
message=f"Failed to process SAML assertion: {str(exc)}",
provider=provider,
).from_http(self.request)
return self.executor.stage_invalid()
# Log Application Authorization
Event.new(
EventAction.AUTHORIZE_APPLICATION,
authorized_application=application,
flow=self.executor.plan.flow_pk,
).from_http(self.request)
if provider.sp_binding == SAMLBindings.POST: if provider.sp_binding == SAMLBindings.POST:
form_attrs = { form_attrs = {

View File

@ -44,7 +44,7 @@ class Command(BaseCommand):
user=user, user=user,
intent=TokenIntents.INTENT_RECOVERY, intent=TokenIntents.INTENT_RECOVERY,
description=f"Recovery Token generated by {getuser()} on {_now}", description=f"Recovery Token generated by {getuser()} on {_now}",
identifier=f"ak-recovery-{user}", identifier=f"ak-recovery-{user}-{_now}",
) )
self.stdout.write( self.stdout.write(
( (

View File

@ -7,6 +7,7 @@ from django.utils.translation import gettext as _
from django.views import View from django.views import View
from authentik.core.models import Token, TokenIntents from authentik.core.models import Token, TokenIntents
from authentik.stages.password import BACKEND_DJANGO
class UseTokenView(View): class UseTokenView(View):
@ -18,7 +19,7 @@ class UseTokenView(View):
if not tokens.exists(): if not tokens.exists():
raise Http404 raise Http404
token = tokens.first() token = tokens.first()
login(request, token.user, backend="django.contrib.auth.backends.ModelBackend") login(request, token.user, backend=BACKEND_DJANGO)
token.delete() token.delete()
messages.warning(request, _("Used recovery-link to authenticate.")) messages.warning(request, _("Used recovery-link to authenticate."))
return redirect("authentik_core:if-admin") return redirect("authentik_core:if-admin")

View File

@ -375,7 +375,11 @@ if _ERROR_REPORTING:
environment=CONFIG.y("error_reporting.environment", "customer"), environment=CONFIG.y("error_reporting.environment", "customer"),
send_default_pii=CONFIG.y_bool("error_reporting.send_pii", False), send_default_pii=CONFIG.y_bool("error_reporting.send_pii", False),
) )
set_tag("authentik.build_hash", os.environ.get(ENV_GIT_HASH_KEY, "tagged")) # Default to empty string as that is what docker has
build_hash = os.environ.get(ENV_GIT_HASH_KEY, "")
if build_hash == "":
build_hash = "tagged"
set_tag("authentik.build_hash", build_hash)
set_tag( set_tag(
"authentik.env", "kubernetes" if "KUBERNETES_PORT" in os.environ else "compose" "authentik.env", "kubernetes" if "KUBERNETES_PORT" in os.environ else "compose"
) )

View File

@ -2,17 +2,21 @@
from authentik.lib.sentry import SentryIgnoredException from authentik.lib.sentry import SentryIgnoredException
class MissingSAMLResponse(SentryIgnoredException): class SAMLException(SentryIgnoredException):
"""Base SAML Exception"""
class MissingSAMLResponse(SAMLException):
"""Exception raised when request does not contain SAML Response.""" """Exception raised when request does not contain SAML Response."""
class UnsupportedNameIDFormat(SentryIgnoredException): class UnsupportedNameIDFormat(SAMLException):
"""Exception raised when SAML Response contains NameID Format not supported.""" """Exception raised when SAML Response contains NameID Format not supported."""
class MismatchedRequestID(SentryIgnoredException): class MismatchedRequestID(SAMLException):
"""Exception raised when the returned request ID doesn't match the saved ID.""" """Exception raised when the returned request ID doesn't match the saved ID."""
class InvalidSignature(SentryIgnoredException): class InvalidSignature(SAMLException):
"""Signature of XML Object is either missing or invalid""" """Signature of XML Object is either missing or invalid"""

View File

@ -15,6 +15,9 @@ NS_MAP = {
SAML_NAME_ID_FORMAT_EMAIL = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" SAML_NAME_ID_FORMAT_EMAIL = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
SAML_NAME_ID_FORMAT_PERSISTENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" SAML_NAME_ID_FORMAT_PERSISTENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
SAML_NAME_ID_FORMAT_UNSPECIFIED = (
"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
)
SAML_NAME_ID_FORMAT_X509 = "urn:oasis:names:tc:SAML:2.0:nameid-format:X509SubjectName" SAML_NAME_ID_FORMAT_X509 = "urn:oasis:names:tc:SAML:2.0:nameid-format:X509SubjectName"
SAML_NAME_ID_FORMAT_WINDOWS = ( SAML_NAME_ID_FORMAT_WINDOWS = (
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName" "urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName"

View File

@ -39,7 +39,7 @@ from authentik.sources.saml.processors.constants import (
from authentik.sources.saml.processors.request import SESSION_REQUEST_ID from authentik.sources.saml.processors.request import SESSION_REQUEST_ID
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
from authentik.stages.user_login.stage import DEFAULT_BACKEND from authentik.stages.user_login.stage import BACKEND_DJANGO
LOGGER = get_logger() LOGGER = get_logger()
if TYPE_CHECKING: if TYPE_CHECKING:
@ -141,7 +141,7 @@ class ResponseProcessor:
self._source.authentication_flow, self._source.authentication_flow,
**{ **{
PLAN_CONTEXT_PENDING_USER: user, PLAN_CONTEXT_PENDING_USER: user,
PLAN_CONTEXT_AUTHENTICATION_BACKEND: DEFAULT_BACKEND, PLAN_CONTEXT_AUTHENTICATION_BACKEND: BACKEND_DJANGO,
}, },
) )
@ -204,7 +204,7 @@ class ResponseProcessor:
self._source.authentication_flow, self._source.authentication_flow,
**{ **{
PLAN_CONTEXT_PENDING_USER: matching_users.first(), PLAN_CONTEXT_PENDING_USER: matching_users.first(),
PLAN_CONTEXT_AUTHENTICATION_BACKEND: DEFAULT_BACKEND, PLAN_CONTEXT_AUTHENTICATION_BACKEND: BACKEND_DJANGO,
PLAN_CONTEXT_REDIRECT: final_redirect, PLAN_CONTEXT_REDIRECT: final_redirect,
}, },
) )

View File

@ -2,7 +2,7 @@
from django.utils.timezone import now from django.utils.timezone import now
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.models import User from authentik.core.models import AuthenticatedSession, User
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.lib.utils.time import timedelta_from_string from authentik.lib.utils.time import timedelta_from_string
from authentik.root.celery import CELERY_APP from authentik.root.celery import CELERY_APP
@ -31,11 +31,13 @@ def clean_temporary_users(self: MonitoredTask):
continue continue
source = sources.first() source = sources.first()
source_delta = timedelta_from_string(source.temporary_user_delete_after) source_delta = timedelta_from_string(source.temporary_user_delete_after)
if _now - user.last_login >= source_delta: if (
_now - user.last_login >= source_delta
and not AuthenticatedSession.objects.filter(user=user).exists()
):
LOGGER.debug( LOGGER.debug(
"User is expired and will be deleted.", user=user, delta=source_delta "User is expired and will be deleted.", user=user, delta=source_delta
) )
# TODO: Check if user is signed in anywhere?
user.delete() user.delete()
deleted_users += 1 deleted_users += 1
messages.append(f"Successfully deleted {deleted_users} users.") messages.append(f"Successfully deleted {deleted_users} users.")

View File

@ -9,7 +9,7 @@ from rest_framework.permissions import IsAdminUser
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from authentik.api.authorization import OwnerFilter, OwnerPermissions from authentik.api.authorization import OwnerFilter, OwnerPermissions
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
@ -94,7 +94,7 @@ class DuoDeviceViewSet(
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter] filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
class DuoAdminDeviceViewSet(ReadOnlyModelViewSet): class DuoAdminDeviceViewSet(ModelViewSet):
"""Viewset for Duo authenticator devices (for admins)""" """Viewset for Duo authenticator devices (for admins)"""
permission_classes = [IsAdminUser] permission_classes = [IsAdminUser]

View File

@ -1,44 +1,6 @@
# Generated by Django 3.1.1 on 2020-09-25 14:32 # Generated by Django 3.1.1 on 2020-09-25 14:32
from django.apps.registry import Apps
from django.db import migrations from django.db import migrations
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from authentik.flows.models import FlowDesignation
def create_default_setup_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
Flow = apps.get_model("authentik_flows", "Flow")
FlowStageBinding = apps.get_model("authentik_flows", "FlowStageBinding")
AuthenticatorDuoStage = apps.get_model(
"authentik_stages_authenticator_duo", "AuthenticatorDuoStage"
)
db_alias = schema_editor.connection.alias
flow, _ = Flow.objects.using(db_alias).update_or_create(
slug="default-authenticator-duo-setup",
designation=FlowDesignation.STAGE_CONFIGURATION,
defaults={
"name": "default-authenticator-duo-setup",
"title": "Setup Duo",
},
)
stage, _ = AuthenticatorDuoStage.objects.using(db_alias).update_or_create(
name="default-authenticator-duo-setup"
)
FlowStageBinding.objects.using(db_alias).update_or_create(
target=flow, stage=stage, defaults={"order": 0}
)
for stage in AuthenticatorDuoStage.objects.using(db_alias).filter(
configure_flow=None
):
stage.configure_flow = flow
stage.save()
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -50,6 +12,4 @@ class Migration(migrations.Migration):
), ),
] ]
operations = [ operations = []
migrations.RunPython(create_default_setup_flow),
]

View File

@ -3,6 +3,7 @@ from django.http import HttpRequest, HttpResponse
from rest_framework.fields import CharField from rest_framework.fields import CharField
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.events.models import Event, EventAction
from authentik.flows.challenge import ( from authentik.flows.challenge import (
Challenge, Challenge,
ChallengeResponse, ChallengeResponse,
@ -11,6 +12,7 @@ from authentik.flows.challenge import (
) )
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import ChallengeStageView from authentik.flows.stage import ChallengeStageView
from authentik.flows.views import InvalidStageError
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
LOGGER = get_logger() LOGGER = get_logger()
@ -42,7 +44,15 @@ class AuthenticatorDuoStageView(ChallengeStageView):
def get_challenge(self, *args, **kwargs) -> Challenge: def get_challenge(self, *args, **kwargs) -> Challenge:
user = self.get_pending_user() user = self.get_pending_user()
stage: AuthenticatorDuoStage = self.executor.current_stage stage: AuthenticatorDuoStage = self.executor.current_stage
try:
enroll = stage.client.enroll(user.username) enroll = stage.client.enroll(user.username)
except RuntimeError as exc:
Event.new(
EventAction.CONFIGURATION_ERROR,
message=f"Failed to enroll user: {str(exc)}",
user=user,
).from_http(self.request, user)
raise InvalidStageError(str(exc)) from exc
user_id = enroll["user_id"] user_id = enroll["user_id"]
self.request.session[SESSION_KEY_DUO_USER_ID] = user_id self.request.session[SESSION_KEY_DUO_USER_ID] = user_id
self.request.session[SESSION_KEY_DUO_ACTIVATION_CODE] = enroll[ self.request.session[SESSION_KEY_DUO_ACTIVATION_CODE] = enroll[

View File

@ -3,4 +3,4 @@
INSTALLED_APPS = [ INSTALLED_APPS = [
"django_otp.plugins.otp_totp", "django_otp.plugins.otp_totp",
] ]
OTP_TOTP_ISSUER = "authentik" OTP_TOTP_ISSUER = "__to_replace__"

View File

@ -1,6 +1,7 @@
"""TOTP Setup stage""" """TOTP Setup stage"""
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.http.request import QueryDict from django.http.request import QueryDict
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_otp.plugins.otp_totp.models import TOTPDevice from django_otp.plugins.otp_totp.models import TOTPDevice
from rest_framework.fields import CharField, IntegerField from rest_framework.fields import CharField, IntegerField
@ -16,6 +17,7 @@ from authentik.flows.challenge import (
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import ChallengeStageView from authentik.flows.stage import ChallengeStageView
from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage
from authentik.stages.authenticator_totp.settings import OTP_TOTP_ISSUER
LOGGER = get_logger() LOGGER = get_logger()
SESSION_TOTP_DEVICE = "totp_device" SESSION_TOTP_DEVICE = "totp_device"
@ -54,7 +56,9 @@ class AuthenticatorTOTPStageView(ChallengeStageView):
return AuthenticatorTOTPChallenge( return AuthenticatorTOTPChallenge(
data={ data={
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"config_url": device.config_url, "config_url": device.config_url.replace(
OTP_TOTP_ISSUER, slugify(self.request.tenant.branding_title)
),
} }
) )

View File

@ -0,0 +1,127 @@
"""Test validator stage"""
from unittest.mock import MagicMock, patch
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase
from django.test.client import RequestFactory
from django_otp.plugins.otp_totp.models import TOTPDevice
from rest_framework.exceptions import ValidationError
from authentik.core.models import User
from authentik.flows.models import NotConfiguredAction
from authentik.flows.tests.test_planner import dummy_get_response
from authentik.providers.oauth2.generators import (
generate_client_id,
generate_client_secret,
)
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
from authentik.stages.authenticator_validate.api import (
AuthenticatorValidateStageSerializer,
)
from authentik.stages.authenticator_validate.challenge import (
get_challenge_for_device,
validate_challenge_code,
validate_challenge_duo,
validate_challenge_webauthn,
)
from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
class AuthenticatorValidateStageTests(TestCase):
"""Test validator stage"""
def setUp(self) -> None:
self.user = User.objects.get(username="akadmin")
self.request_factory = RequestFactory()
def test_stage_validation(self):
"""Test serializer validation"""
self.client.force_login(self.user)
serializer = AuthenticatorValidateStageSerializer(
data={"name": "foo", "not_configured_action": NotConfiguredAction.CONFIGURE}
)
self.assertFalse(serializer.is_valid())
self.assertIn("not_configured_action", serializer.errors)
def test_device_challenge_totp(self):
"""Test device challenge"""
request = self.request_factory.get("/")
totp_device = TOTPDevice.objects.create(
user=self.user, confirmed=True, digits=6
)
self.assertEqual(get_challenge_for_device(request, totp_device), {})
with self.assertRaises(ValidationError):
validate_challenge_code("1234", request, self.user)
def test_device_challenge_webauthn(self):
"""Test webauthn"""
request = self.request_factory.get("/")
request.user = self.user
middleware = SessionMiddleware(dummy_get_response)
middleware.process_request(request)
request.session.save()
webauthn_device = WebAuthnDevice.objects.create(
user=self.user,
public_key="qwerqwerqre",
credential_id="foobarbaz",
sign_count=0,
rp_id="foo",
)
challenge = get_challenge_for_device(request, webauthn_device)
del challenge["challenge"]
self.assertEqual(
challenge,
{
"allowCredentials": [
{
"id": "foobarbaz",
"transports": ["usb", "nfc", "ble", "internal"],
"type": "public-key",
}
],
"rpId": "foo",
"timeout": 60000,
"userVerification": "discouraged",
},
)
with self.assertRaises(ValidationError):
validate_challenge_webauthn({}, request, self.user)
def test_device_challenge_duo(self):
"""Test duo"""
request = self.request_factory.get("/")
stage = AuthenticatorDuoStage.objects.create(
name="test",
client_id=generate_client_id(),
client_secret=generate_client_secret(),
api_hostname="",
)
duo_device = DuoDevice.objects.create(
user=self.user,
stage=stage,
)
duo_mock = MagicMock(
auth=MagicMock(
return_value={
"result": "allow",
"status": "allow",
"status_msg": "Success. Logging you in...",
}
)
)
failed_duo_mock = MagicMock(auth=MagicMock(return_value={"result": "deny"}))
with patch(
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client",
duo_mock,
):
self.assertEqual(
duo_device.pk, validate_challenge_duo(duo_device.pk, request, self.user)
)
with patch(
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client",
failed_duo_mock,
):
with self.assertRaises(ValidationError):
validate_challenge_duo(duo_device.pk, request, self.user)

View File

@ -28,8 +28,6 @@ from authentik.stages.authenticator_webauthn.utils import (
get_rp_id, get_rp_id,
) )
RP_NAME = "authentik"
LOGGER = get_logger() LOGGER = get_logger()
SESSION_KEY_WEBAUTHN_AUTHENTICATED = ( SESSION_KEY_WEBAUTHN_AUTHENTICATED = (
@ -119,7 +117,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView):
user = self.get_pending_user() user = self.get_pending_user()
make_credential_options = WebAuthnMakeCredentialOptions( make_credential_options = WebAuthnMakeCredentialOptions(
challenge, challenge,
RP_NAME, self.request.tenant.branding_title,
get_rp_id(self.request), get_rp_id(self.request),
user.uid, user.uid,
user.username, user.username,

View File

@ -0,0 +1,31 @@
# Generated by Django 3.2.4 on 2021-06-14 15:32
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_identification", "0010_identificationstage_password_stage"),
]
operations = [
migrations.AlterField(
model_name="identificationstage",
name="user_fields",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
choices=[
("email", "E Mail"),
("username", "Username"),
("upn", "Upn"),
],
max_length=100,
),
blank=True,
help_text="Fields of the user object to match against. (Hold shift to select multiple options)",
size=None,
),
),
]

View File

@ -17,6 +17,7 @@ class UserFields(models.TextChoices):
E_MAIL = "email" E_MAIL = "email"
USERNAME = "username" USERNAME = "username"
UPN = "upn"
class IdentificationStage(Stage): class IdentificationStage(Stage):

View File

@ -8,19 +8,20 @@ from django.db.models import Q
from django.http import HttpResponse from django.http import HttpResponse
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from rest_framework.fields import BooleanField, CharField, ListField from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema_field
from rest_framework.fields import BooleanField, CharField, DictField, ListField
from rest_framework.serializers import ValidationError from rest_framework.serializers import ValidationError
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import Application, Source, User from authentik.core.models import Application, Source, User
from authentik.core.types import UILoginButtonSerializer
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import ( from authentik.flows.stage import (
PLAN_CONTEXT_PENDING_USER_IDENTIFIER, PLAN_CONTEXT_PENDING_USER_IDENTIFIER,
ChallengeStageView, ChallengeStageView,
) )
from authentik.flows.views import SESSION_KEY_APPLICATION_PRE from authentik.flows.views import SESSION_KEY_APPLICATION_PRE, challenge_types
from authentik.stages.identification.models import IdentificationStage from authentik.stages.identification.models import IdentificationStage
from authentik.stages.identification.signals import identification_failed from authentik.stages.identification.signals import identification_failed
from authentik.stages.password.stage import authenticate from authentik.stages.password.stage import authenticate
@ -28,6 +29,26 @@ from authentik.stages.password.stage import authenticate
LOGGER = get_logger() LOGGER = get_logger()
@extend_schema_field(
PolymorphicProxySerializer(
component_name="ChallengeTypes",
serializers=challenge_types(),
resource_type_field_name="component",
)
)
class ChallengeDictWrapper(DictField):
"""Wrapper around DictField that annotates itself as challenge proxy"""
class LoginSourceSerializer(PassiveSerializer):
"""Serializer for Login buttons of sources"""
name = CharField()
icon_url = CharField(required=False, allow_null=True)
challenge = ChallengeDictWrapper()
class IdentificationChallenge(Challenge): class IdentificationChallenge(Challenge):
"""Identification challenges with all UI elements""" """Identification challenges with all UI elements"""
@ -38,7 +59,7 @@ class IdentificationChallenge(Challenge):
enroll_url = CharField(required=False) enroll_url = CharField(required=False)
recovery_url = CharField(required=False) recovery_url = CharField(required=False)
primary_action = CharField() primary_action = CharField()
sources = UILoginButtonSerializer(many=True, required=False) sources = LoginSourceSerializer(many=True, required=False)
component = CharField(default="ak-stage-identification") component = CharField(default="ak-stage-identification")
@ -96,7 +117,11 @@ class IdentificationStageView(ChallengeStageView):
current_stage: IdentificationStage = self.executor.current_stage current_stage: IdentificationStage = self.executor.current_stage
query = Q() query = Q()
for search_field in current_stage.user_fields: for search_field in current_stage.user_fields:
model_field = search_field model_field = {
"email": "email",
"username": "username",
"upn": "attributes__upn",
}[search_field]
if current_stage.case_insensitive_matching: if current_stage.case_insensitive_matching:
model_field += "__iexact" model_field += "__iexact"
else: else:

View File

@ -9,6 +9,7 @@ from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
from authentik.providers.oauth2.generators import generate_client_secret from authentik.providers.oauth2.generators import generate_client_secret
from authentik.sources.oauth.models import OAuthSource from authentik.sources.oauth.models import OAuthSource
from authentik.stages.identification.models import IdentificationStage, UserFields from authentik.stages.identification.models import IdentificationStage, UserFields
from authentik.stages.password import BACKEND_DJANGO
from authentik.stages.password.models import PasswordStage from authentik.stages.password.models import PasswordStage
@ -70,7 +71,7 @@ class TestIdentificationStage(TestCase):
def test_valid_with_password(self): def test_valid_with_password(self):
"""Test with valid email and password in single step""" """Test with valid email and password in single step"""
pw_stage = PasswordStage.objects.create( pw_stage = PasswordStage.objects.create(
name="password", backends=["django.contrib.auth.backends.ModelBackend"] name="password", backends=[BACKEND_DJANGO]
) )
self.stage.password_stage = pw_stage self.stage.password_stage = pw_stage
self.stage.save() self.stage.save()
@ -92,7 +93,7 @@ class TestIdentificationStage(TestCase):
def test_invalid_with_password(self): def test_invalid_with_password(self):
"""Test with valid email and invalid password in single step""" """Test with valid email and invalid password in single step"""
pw_stage = PasswordStage.objects.create( pw_stage = PasswordStage.objects.create(
name="password", backends=["django.contrib.auth.backends.ModelBackend"] name="password", backends=[BACKEND_DJANGO]
) )
self.stage.password_stage = pw_stage self.stage.password_stage = pw_stage
self.stage.save() self.stage.save()

View File

@ -17,6 +17,7 @@ from authentik.flows.tests.test_views import TO_STAGE_RESPONSE_MOCK
from authentik.flows.views import SESSION_KEY_PLAN from authentik.flows.views import SESSION_KEY_PLAN
from authentik.stages.invitation.models import Invitation, InvitationStage from authentik.stages.invitation.models import Invitation, InvitationStage
from authentik.stages.invitation.stage import INVITATION_TOKEN_KEY, PLAN_CONTEXT_PROMPT from authentik.stages.invitation.stage import INVITATION_TOKEN_KEY, PLAN_CONTEXT_PROMPT
from authentik.stages.password import BACKEND_DJANGO
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
@ -46,9 +47,7 @@ class TestUserLoginStage(TestCase):
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()] flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
) )
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
plan.context[ plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_DJANGO
PLAN_CONTEXT_AUTHENTICATION_BACKEND
] = "django.contrib.auth.backends.ModelBackend"
session = self.client.session session = self.client.session
session[SESSION_KEY_PLAN] = plan session[SESSION_KEY_PLAN] = plan
session.save() session.save()
@ -79,9 +78,7 @@ class TestUserLoginStage(TestCase):
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()] flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
) )
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
plan.context[ plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_DJANGO
PLAN_CONTEXT_AUTHENTICATION_BACKEND
] = "django.contrib.auth.backends.ModelBackend"
session = self.client.session session = self.client.session
session[SESSION_KEY_PLAN] = plan session[SESSION_KEY_PLAN] = plan
session.save() session.save()

View File

@ -0,0 +1,3 @@
"""Backend paths"""
BACKEND_DJANGO = "django.contrib.auth.backends.ModelBackend"
BACKEND_LDAP = "authentik.sources.ldap.auth.LDAPBackend"

View File

@ -9,17 +9,18 @@ from rest_framework.serializers import BaseSerializer
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, Stage from authentik.flows.models import ConfigurableStage, Stage
from authentik.stages.password import BACKEND_DJANGO, BACKEND_LDAP
def get_authentication_backends(): def get_authentication_backends():
"""Return all available authentication backends as tuple set""" """Return all available authentication backends as tuple set"""
return [ return [
( (
"django.contrib.auth.backends.ModelBackend", BACKEND_DJANGO,
_("authentik-internal Userdatabase"), _("authentik-internal Userdatabase"),
), ),
( (
"authentik.sources.ldap.auth.LDAPBackend", BACKEND_LDAP,
_("authentik LDAP"), _("authentik LDAP"),
), ),
] ]

View File

@ -14,6 +14,7 @@ from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.tests.test_views import TO_STAGE_RESPONSE_MOCK from authentik.flows.tests.test_views import TO_STAGE_RESPONSE_MOCK
from authentik.flows.views import SESSION_KEY_PLAN from authentik.flows.views import SESSION_KEY_PLAN
from authentik.providers.oauth2.generators import generate_client_secret from authentik.providers.oauth2.generators import generate_client_secret
from authentik.stages.password import BACKEND_DJANGO
from authentik.stages.password.models import PasswordStage from authentik.stages.password.models import PasswordStage
MOCK_BACKEND_AUTHENTICATE = MagicMock(side_effect=PermissionDenied("test")) MOCK_BACKEND_AUTHENTICATE = MagicMock(side_effect=PermissionDenied("test"))
@ -36,7 +37,7 @@ class TestPasswordStage(TestCase):
designation=FlowDesignation.AUTHENTICATION, designation=FlowDesignation.AUTHENTICATION,
) )
self.stage = PasswordStage.objects.create( self.stage = PasswordStage.objects.create(
name="password", backends=["django.contrib.auth.backends.ModelBackend"] name="password", backends=[BACKEND_DJANGO]
) )
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2) FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)

View File

@ -8,10 +8,10 @@ from structlog.stdlib import get_logger
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import StageView from authentik.flows.stage import StageView
from authentik.lib.utils.time import timedelta_from_string from authentik.lib.utils.time import timedelta_from_string
from authentik.stages.password import BACKEND_DJANGO
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
LOGGER = get_logger() LOGGER = get_logger()
DEFAULT_BACKEND = "django.contrib.auth.backends.ModelBackend"
USER_LOGIN_AUTHENTICATED = "user_login_authenticated" USER_LOGIN_AUTHENTICATED = "user_login_authenticated"
@ -26,7 +26,7 @@ class UserLoginStageView(StageView):
LOGGER.debug(message) LOGGER.debug(message)
return self.executor.stage_invalid() return self.executor.stage_invalid()
backend = self.executor.plan.context.get( backend = self.executor.plan.context.get(
PLAN_CONTEXT_AUTHENTICATION_BACKEND, DEFAULT_BACKEND PLAN_CONTEXT_AUTHENTICATION_BACKEND, BACKEND_DJANGO
) )
login( login(
self.request, self.request,

View File

@ -9,6 +9,7 @@ from authentik.flows.markers import StageMarker
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.views import SESSION_KEY_PLAN from authentik.flows.views import SESSION_KEY_PLAN
from authentik.stages.password import BACKEND_DJANGO
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.user_logout.models import UserLogoutStage from authentik.stages.user_logout.models import UserLogoutStage
@ -35,9 +36,7 @@ class TestUserLogoutStage(TestCase):
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()] flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
) )
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
plan.context[ plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_DJANGO
PLAN_CONTEXT_AUTHENTICATION_BACKEND
] = "django.contrib.auth.backends.ModelBackend"
session = self.client.session session = self.client.session
session[SESSION_KEY_PLAN] = plan session[SESSION_KEY_PLAN] = plan
session.save() session.save()

View File

@ -9,6 +9,7 @@ from authentik.lib.config import CONFIG
from authentik.tenants.models import Tenant from authentik.tenants.models import Tenant
_q_default = Q(default=True) _q_default = Q(default=True)
DEFAULT_TENANT = Tenant(domain="fallback")
def get_tenant_for_request(request: HttpRequest) -> Tenant: def get_tenant_for_request(request: HttpRequest) -> Tenant:
@ -17,13 +18,13 @@ def get_tenant_for_request(request: HttpRequest) -> Tenant:
Q(domain__iendswith=request.get_host()) | _q_default Q(domain__iendswith=request.get_host()) | _q_default
) )
if not db_tenants.exists(): if not db_tenants.exists():
return Tenant(domain="fallback") return DEFAULT_TENANT
return db_tenants.first() return db_tenants.first()
def context_processor(request: HttpRequest) -> dict[str, Any]: def context_processor(request: HttpRequest) -> dict[str, Any]:
"""Context Processor that injects tenant object into every template""" """Context Processor that injects tenant object into every template"""
tenant = getattr(request, "tenant", Tenant(domain="fallback")) tenant = getattr(request, "tenant", DEFAULT_TENANT)
return { return {
"tenant": tenant, "tenant": tenant,
"ak_version": __version__, "ak_version": __version__,

View File

@ -148,7 +148,7 @@ stages:
inputs: inputs:
script: | script: |
pipenv run python -m scripts.generate_ci_config pipenv run python -m scripts.generate_ci_config
pipenv run ./manage.py migrate pipenv run python -m lifecycle.migrate
- job: migrations_from_previous_release - job: migrations_from_previous_release
pool: pool:
vmImage: 'ubuntu-latest' vmImage: 'ubuntu-latest'

View File

@ -21,7 +21,7 @@ services:
networks: networks:
- internal - internal
server: server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.6.1-rc3} image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.6.1}
restart: unless-stopped restart: unless-stopped
command: server command: server
environment: environment:
@ -52,7 +52,7 @@ services:
- "0.0.0.0:9000:9000" - "0.0.0.0:9000:9000"
- "0.0.0.0:9443:9443" - "0.0.0.0:9443:9443"
worker: worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.6.1-rc3} image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.6.1}
restart: unless-stopped restart: unless-stopped
command: worker command: worker
networks: networks:

View File

@ -1,3 +1,3 @@
package constants package constants
const VERSION = "2021.6.1-rc3" const VERSION = "2021.6.1"

View File

@ -3,7 +3,6 @@ package ldap
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"net" "net"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
@ -66,15 +65,15 @@ func (pi *ProviderInstance) Bind(username string, bindDN, bindPW string, conn ne
params := url.Values{} params := url.Values{}
params.Add("goauthentik.io/outpost/ldap", "true") params.Add("goauthentik.io/outpost/ldap", "true")
passed, err := pi.solveFlowChallenge(username, bindPW, apiClient, params.Encode(), 1) passed, rerr := pi.solveFlowChallenge(username, bindPW, apiClient, params.Encode(), 1)
if err != nil { if rerr != ldap.LDAPResultSuccess {
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to solve challenge") pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to solve challenge")
return ldap.LDAPResultOperationsError, nil return rerr, nil
} }
if !passed { if !passed {
return ldap.LDAPResultInvalidCredentials, nil return ldap.LDAPResultInvalidCredentials, nil
} }
p, _, err := apiClient.CoreApi.CoreApplicationsCheckAccessCreate(context.Background(), pi.appSlug).Execute() p, _, err := apiClient.CoreApi.CoreApplicationsCheckAccessRetrieve(context.Background(), pi.appSlug).Execute()
if !p.Passing { if !p.Passing {
pi.log.WithField("bindDN", bindDN).Info("Access denied for user") pi.log.WithField("bindDN", bindDN).Info("Access denied for user")
return ldap.LDAPResultInsufficientAccessRights, nil return ldap.LDAPResultInsufficientAccessRights, nil
@ -138,12 +137,12 @@ type ChallengeInt interface {
GetResponseErrors() map[string][]api.ErrorDetail GetResponseErrors() map[string][]api.ErrorDetail
} }
func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, client *api.APIClient, urlParams string, depth int) (bool, error) { func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, client *api.APIClient, urlParams string, depth int) (bool, ldap.LDAPResultCode) {
req := client.FlowsApi.FlowsExecutorGet(context.Background(), pi.flowSlug).Query(urlParams) req := client.FlowsApi.FlowsExecutorGet(context.Background(), pi.flowSlug).Query(urlParams)
challenge, _, err := req.Execute() challenge, _, err := req.Execute()
if err != nil { if err != nil {
pi.log.WithError(err).Warning("Failed to get challenge") pi.log.WithError(err).Warning("Failed to get challenge")
return false, err return false, ldap.LDAPResultOperationsError
} }
ch := challenge.GetActualInstance().(ChallengeInt) ch := challenge.GetActualInstance().(ChallengeInt)
pi.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got challenge") pi.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got challenge")
@ -162,45 +161,51 @@ func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, c
} }
} }
if deviceChallenge == nil { if deviceChallenge == nil {
return false, errors.New("got ak-stage-authenticator-validate without duo") pi.log.Warning("got ak-stage-authenticator-validate without duo")
return false, ldap.LDAPResultOperationsError
} }
devId, err := strconv.Atoi(deviceChallenge.DeviceUid) devId, err := strconv.Atoi(deviceChallenge.DeviceUid)
if err != nil { if err != nil {
return false, errors.New("failed to convert duo device id to int") pi.log.Warning("failed to convert duo device id to int")
return false, ldap.LDAPResultOperationsError
} }
devId32 := int32(devId) devId32 := int32(devId)
inner := api.NewAuthenticatorValidationChallengeResponseRequest() inner := api.NewAuthenticatorValidationChallengeResponseRequest()
inner.Duo = &devId32 inner.Duo = &devId32
responseReq = responseReq.FlowChallengeResponseRequest(api.AuthenticatorValidationChallengeResponseRequestAsFlowChallengeResponseRequest(inner)) responseReq = responseReq.FlowChallengeResponseRequest(api.AuthenticatorValidationChallengeResponseRequestAsFlowChallengeResponseRequest(inner))
case "ak-stage-access-denied": case "ak-stage-access-denied":
return false, errors.New("got ak-stage-access-denied") pi.log.Info("got ak-stage-access-denied")
return false, ldap.LDAPResultInsufficientAccessRights
default: default:
return false, fmt.Errorf("unsupported challenge type: %s", ch.GetComponent()) pi.log.WithField("component", ch.GetComponent()).Warning("unsupported challenge type")
return false, ldap.LDAPResultOperationsError
} }
response, _, err := responseReq.Execute() response, _, err := responseReq.Execute()
ch = response.GetActualInstance().(ChallengeInt) ch = response.GetActualInstance().(ChallengeInt)
pi.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response") pi.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response")
switch ch.GetComponent() { switch ch.GetComponent() {
case "ak-stage-access-denied": case "ak-stage-access-denied":
return false, errors.New("got ak-stage-access-denied") pi.log.Info("got ak-stage-access-denied")
return false, ldap.LDAPResultInsufficientAccessRights
} }
if ch.GetType() == "redirect" { if ch.GetType() == "redirect" {
return true, nil return true, ldap.LDAPResultSuccess
} }
if err != nil { if err != nil {
pi.log.WithError(err).Warning("Failed to submit challenge") pi.log.WithError(err).Warning("Failed to submit challenge")
return false, err return false, ldap.LDAPResultOperationsError
} }
if len(ch.GetResponseErrors()) > 0 { if len(ch.GetResponseErrors()) > 0 {
for key, errs := range ch.GetResponseErrors() { for key, errs := range ch.GetResponseErrors() {
for _, err := range errs { for _, err := range errs {
pi.log.WithField("key", key).WithField("code", err.Code).WithField("msg", err.String).Warning("Flow error") pi.log.WithField("key", key).WithField("code", err.Code).WithField("msg", err.String).Warning("Flow error")
return false, nil return false, ldap.LDAPResultInsufficientAccessRights
} }
} }
} }
if depth >= 10 { if depth >= 10 {
return false, errors.New("exceeded stage recursion depth") pi.log.Warning("exceeded stage recursion depth")
return false, ldap.LDAPResultOperationsError
} }
return pi.solveFlowChallenge(bindDN, password, client, urlParams, depth+1) return pi.solveFlowChallenge(bindDN, password, client, urlParams, depth+1)
} }

View File

@ -5,7 +5,7 @@ import (
"os" "os"
) )
const VERSION = "2021.6.1-rc3" const VERSION = "2021.6.1"
func BUILD() string { func BUILD() string {
build := os.Getenv("GIT_BUILD_HASH") build := os.Getenv("GIT_BUILD_HASH")

View File

@ -1,7 +1,7 @@
openapi: 3.0.3 openapi: 3.0.3
info: info:
title: authentik title: authentik
version: 2021.6.1-rc1 version: 2021.6.1-rc6
description: Making authentication simple. description: Making authentication simple.
contact: contact:
email: hello@beryju.org email: hello@beryju.org
@ -236,6 +236,37 @@ paths:
$ref: '#/components/schemas/ValidationError' $ref: '#/components/schemas/ValidationError'
'403': '403':
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
post:
operationId: authenticators_admin_duo_create
description: Viewset for Duo authenticator devices (for admins)
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DuoDeviceRequest'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/DuoDeviceRequest'
multipart/form-data:
schema:
$ref: '#/components/schemas/DuoDeviceRequest'
required: true
security:
- authentik: []
- cookieAuth: []
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/DuoDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/api/v2beta/authenticators/admin/duo/{id}/: /api/v2beta/authenticators/admin/duo/{id}/:
get: get:
operationId: authenticators_admin_duo_retrieve operationId: authenticators_admin_duo_retrieve
@ -263,6 +294,103 @@ paths:
$ref: '#/components/schemas/ValidationError' $ref: '#/components/schemas/ValidationError'
'403': '403':
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
put:
operationId: authenticators_admin_duo_update
description: Viewset for Duo authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this Duo Device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DuoDeviceRequest'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/DuoDeviceRequest'
multipart/form-data:
schema:
$ref: '#/components/schemas/DuoDeviceRequest'
required: true
security:
- authentik: []
- cookieAuth: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/DuoDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
patch:
operationId: authenticators_admin_duo_partial_update
description: Viewset for Duo authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this Duo Device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatchedDuoDeviceRequest'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/PatchedDuoDeviceRequest'
multipart/form-data:
schema:
$ref: '#/components/schemas/PatchedDuoDeviceRequest'
security:
- authentik: []
- cookieAuth: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/DuoDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
delete:
operationId: authenticators_admin_duo_destroy
description: Viewset for Duo authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this Duo Device.
required: true
tags:
- authenticators
security:
- authentik: []
- cookieAuth: []
responses:
'204':
description: No response body
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/api/v2beta/authenticators/admin/static/: /api/v2beta/authenticators/admin/static/:
get: get:
operationId: authenticators_admin_static_list operationId: authenticators_admin_static_list
@ -1504,10 +1632,14 @@ paths:
'403': '403':
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
/api/v2beta/core/applications/{slug}/check_access/: /api/v2beta/core/applications/{slug}/check_access/:
post: get:
operationId: core_applications_check_access_create operationId: core_applications_check_access_retrieve
description: Check access to a single application by slug description: Check access to a single application by slug
parameters: parameters:
- in: query
name: for_user
schema:
type: integer
- in: path - in: path
name: slug name: slug
schema: schema:
@ -1516,17 +1648,6 @@ paths:
required: true required: true
tags: tags:
- core - core
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CheckAccessRequestRequest'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/CheckAccessRequestRequest'
multipart/form-data:
schema:
$ref: '#/components/schemas/CheckAccessRequestRequest'
security: security:
- authentik: [] - authentik: []
- cookieAuth: [] - cookieAuth: []
@ -2717,14 +2838,10 @@ paths:
name: is_active name: is_active
schema: schema:
type: boolean type: boolean
title: Active
description: Designates whether this user should be treated as active. Unselect
this instead of deleting accounts.
- in: query - in: query
name: is_superuser name: is_superuser
schema: schema:
type: string type: boolean
format: uuid
- in: query - in: query
name: name name: name
schema: schema:
@ -3041,7 +3158,7 @@ paths:
- in: query - in: query
name: has_key name: has_key
schema: schema:
type: string type: boolean
description: Only return certificate-key pairs with keys description: Only return certificate-key pairs with keys
- in: query - in: query
name: name name: name
@ -3429,6 +3546,11 @@ paths:
description: A search term. description: A search term.
schema: schema:
type: string type: string
- in: query
name: tenant_name
schema:
type: string
description: Tenant name
- in: query - in: query
name: username name: username
schema: schema:
@ -3545,6 +3667,11 @@ paths:
description: A search term. description: A search term.
schema: schema:
type: string type: string
- in: query
name: tenant_name
schema:
type: string
description: Tenant name
- in: query - in: query
name: top_n name: top_n
schema: schema:
@ -4609,7 +4736,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/FlowChallengeRequest' $ref: '#/components/schemas/ChallengeTypes'
description: '' description: ''
'404': '404':
description: No Token found description: No Token found
@ -4655,7 +4782,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/FlowChallengeRequest' $ref: '#/components/schemas/ChallengeTypes'
description: '' description: ''
'400': '400':
$ref: '#/components/schemas/ValidationError' $ref: '#/components/schemas/ValidationError'
@ -18419,11 +18546,45 @@ components:
- shell - shell
- redirect - redirect
type: string type: string
CheckAccessRequestRequest: ChallengeTypes:
type: object oneOf:
properties: - $ref: '#/components/schemas/AccessDeniedChallenge'
for_user: - $ref: '#/components/schemas/AuthenticatorDuoChallenge'
type: integer - $ref: '#/components/schemas/AuthenticatorStaticChallenge'
- $ref: '#/components/schemas/AuthenticatorTOTPChallenge'
- $ref: '#/components/schemas/AuthenticatorValidationChallenge'
- $ref: '#/components/schemas/AuthenticatorWebAuthnChallenge'
- $ref: '#/components/schemas/AutosubmitChallenge'
- $ref: '#/components/schemas/CaptchaChallenge'
- $ref: '#/components/schemas/ConsentChallenge'
- $ref: '#/components/schemas/DummyChallenge'
- $ref: '#/components/schemas/EmailChallenge'
- $ref: '#/components/schemas/IdentificationChallenge'
- $ref: '#/components/schemas/PasswordChallenge'
- $ref: '#/components/schemas/PlexAuthenticationChallenge'
- $ref: '#/components/schemas/PromptChallenge'
- $ref: '#/components/schemas/RedirectChallenge'
- $ref: '#/components/schemas/ShellChallenge'
discriminator:
propertyName: component
mapping:
ak-stage-access-denied: '#/components/schemas/AccessDeniedChallenge'
ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallenge'
ak-stage-authenticator-static: '#/components/schemas/AuthenticatorStaticChallenge'
ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallenge'
ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallenge'
ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallenge'
ak-stage-autosubmit: '#/components/schemas/AutosubmitChallenge'
ak-stage-captcha: '#/components/schemas/CaptchaChallenge'
ak-stage-consent: '#/components/schemas/ConsentChallenge'
ak-stage-dummy: '#/components/schemas/DummyChallenge'
ak-stage-email: '#/components/schemas/EmailChallenge'
ak-stage-identification: '#/components/schemas/IdentificationChallenge'
ak-stage-password: '#/components/schemas/PasswordChallenge'
ak-flow-sources-plex: '#/components/schemas/PlexAuthenticationChallenge'
ak-stage-prompt: '#/components/schemas/PromptChallenge'
xak-flow-redirect: '#/components/schemas/RedirectChallenge'
xak-flow-shell: '#/components/schemas/ShellChallenge'
ClientTypeEnum: ClientTypeEnum:
enum: enum:
- confidential - confidential
@ -19097,6 +19258,9 @@ components:
expires: expires:
type: string type: string
format: date-time format: date-time
tenant:
type: object
additionalProperties: {}
required: required:
- action - action
- app - app
@ -19169,6 +19333,7 @@ components:
- property_mapping_exception - property_mapping_exception
- system_task_execution - system_task_execution
- system_task_exception - system_task_exception
- system_exception
- configuration_error - configuration_error
- model_created - model_created
- model_updated - model_updated
@ -19222,6 +19387,9 @@ components:
expires: expires:
type: string type: string
format: date-time format: date-time
tenant:
type: object
additionalProperties: {}
required: required:
- action - action
- app - app
@ -19387,45 +19555,6 @@ components:
- slug - slug
- stages - stages
- title - title
FlowChallengeRequest:
oneOf:
- $ref: '#/components/schemas/AccessDeniedChallenge'
- $ref: '#/components/schemas/AuthenticatorDuoChallenge'
- $ref: '#/components/schemas/AuthenticatorStaticChallenge'
- $ref: '#/components/schemas/AuthenticatorTOTPChallenge'
- $ref: '#/components/schemas/AuthenticatorValidationChallenge'
- $ref: '#/components/schemas/AuthenticatorWebAuthnChallenge'
- $ref: '#/components/schemas/AutosubmitChallenge'
- $ref: '#/components/schemas/CaptchaChallenge'
- $ref: '#/components/schemas/ConsentChallenge'
- $ref: '#/components/schemas/DummyChallenge'
- $ref: '#/components/schemas/EmailChallenge'
- $ref: '#/components/schemas/IdentificationChallenge'
- $ref: '#/components/schemas/PasswordChallenge'
- $ref: '#/components/schemas/PlexAuthenticationChallenge'
- $ref: '#/components/schemas/PromptChallenge'
- $ref: '#/components/schemas/RedirectChallenge'
- $ref: '#/components/schemas/ShellChallenge'
discriminator:
propertyName: component
mapping:
ak-stage-access-denied: '#/components/schemas/AccessDeniedChallenge'
ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallenge'
ak-stage-authenticator-static: '#/components/schemas/AuthenticatorStaticChallenge'
ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallenge'
ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallenge'
ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallenge'
ak-stage-autosubmit: '#/components/schemas/AutosubmitChallenge'
ak-stage-captcha: '#/components/schemas/CaptchaChallenge'
ak-stage-consent: '#/components/schemas/ConsentChallenge'
ak-stage-dummy: '#/components/schemas/DummyChallenge'
ak-stage-email: '#/components/schemas/EmailChallenge'
ak-stage-identification: '#/components/schemas/IdentificationChallenge'
ak-stage-password: '#/components/schemas/PasswordChallenge'
ak-flow-sources-plex: '#/components/schemas/PlexAuthenticationChallenge'
ak-stage-prompt: '#/components/schemas/PromptChallenge'
xak-flow-redirect: '#/components/schemas/RedirectChallenge'
xak-flow-shell: '#/components/schemas/ShellChallenge'
FlowChallengeResponseRequest: FlowChallengeResponseRequest:
oneOf: oneOf:
- $ref: '#/components/schemas/AuthenticatorDuoChallengeResponseRequest' - $ref: '#/components/schemas/AuthenticatorDuoChallengeResponseRequest'
@ -19775,7 +19904,7 @@ components:
sources: sources:
type: array type: array
items: items:
$ref: '#/components/schemas/UILoginButton' $ref: '#/components/schemas/LoginSource'
required: required:
- password_fields - password_fields
- primary_action - primary_action
@ -20471,6 +20600,20 @@ components:
required: required:
- logins_failed_per_1h - logins_failed_per_1h
- logins_per_1h - logins_per_1h
LoginSource:
type: object
description: Serializer for Login buttons of sources
properties:
name:
type: string
icon_url:
type: string
nullable: true
challenge:
$ref: '#/components/schemas/ChallengeTypes'
required:
- challenge
- name
NameIdPolicyEnum: NameIdPolicyEnum:
enum: enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
@ -26137,6 +26280,7 @@ components:
forward_auth_mode: forward_auth_mode:
type: boolean type: boolean
readOnly: true readOnly: true
deprecated: true
required: required:
- external_host - external_host
- forward_auth_mode - forward_auth_mode
@ -27524,21 +27668,6 @@ components:
- description - description
- model_name - model_name
- name - name
UILoginButton:
type: object
description: Serializer for Login buttons of sources
properties:
name:
type: string
challenge:
type: object
additionalProperties: {}
icon_url:
type: string
nullable: true
required:
- challenge
- name
UsedBy: UsedBy:
type: object type: object
description: A list of all objects referencing the queried object description: A list of all objects referencing the queried object
@ -27687,6 +27816,7 @@ components:
enum: enum:
- email - email
- username - username
- upn
type: string type: string
UserLoginStage: UserLoginStage:
type: object type: object
@ -27917,7 +28047,6 @@ components:
type: string type: string
required: required:
- component - component
- configure_url
- object_uid - object_uid
- title - title
UserWriteStage: UserWriteStage:

View File

@ -1,5 +1,4 @@
env env
helm
static static
htmlcov htmlcov
*.env.yml *.env.yml

398
web/package-lock.json generated
View File

@ -9,14 +9,14 @@
"version": "0.0.0", "version": "0.0.0",
"license": "GNU GPLv3", "license": "GNU GPLv3",
"dependencies": { "dependencies": {
"@babel/core": "^7.14.5", "@babel/core": "^7.14.6",
"@babel/plugin-proposal-decorators": "^7.14.5", "@babel/plugin-proposal-decorators": "^7.14.5",
"@babel/plugin-transform-runtime": "^7.14.5", "@babel/plugin-transform-runtime": "^7.14.5",
"@babel/preset-env": "^7.14.5", "@babel/preset-env": "^7.14.5",
"@babel/preset-typescript": "^7.14.5", "@babel/preset-typescript": "^7.14.5",
"@fortawesome/fontawesome-free": "^5.15.3", "@fortawesome/fontawesome-free": "^5.15.3",
"@lingui/cli": "^3.10.2", "@lingui/cli": "^3.10.2",
"@lingui/core": "^3.10.2", "@lingui/core": "^3.10.4",
"@lingui/macro": "^3.10.2", "@lingui/macro": "^3.10.2",
"@patternfly/patternfly": "^4.108.2", "@patternfly/patternfly": "^4.108.2",
"@polymer/iron-form": "^3.0.1", "@polymer/iron-form": "^3.0.1",
@ -24,13 +24,13 @@
"@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^2.4.2", "@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.1", "@rollup/plugin-typescript": "^8.2.1",
"@sentry/browser": "^6.5.1", "@sentry/browser": "^6.7.1",
"@sentry/tracing": "^6.5.1", "@sentry/tracing": "^6.7.1",
"@types/chart.js": "^2.9.32", "@types/chart.js": "^2.9.32",
"@types/codemirror": "5.60.0", "@types/codemirror": "5.60.0",
"@types/grecaptcha": "^3.0.2", "@types/grecaptcha": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^4.26.1", "@typescript-eslint/eslint-plugin": "^4.27.0",
"@typescript-eslint/parser": "^4.26.1", "@typescript-eslint/parser": "^4.27.0",
"@webcomponents/webcomponentsjs": "^2.5.0", "@webcomponents/webcomponentsjs": "^2.5.0",
"authentik-api": "file:api", "authentik-api": "file:api",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
@ -48,7 +48,7 @@
"lit-html": "^1.4.1", "lit-html": "^1.4.1",
"moment": "^2.29.1", "moment": "^2.29.1",
"rapidoc": "^9.0.0", "rapidoc": "^9.0.0",
"rollup": "^2.51.1", "rollup": "^2.52.1",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2", "rollup-plugin-cssimport": "^1.0.2",
@ -57,8 +57,8 @@
"rollup-plugin-sourcemaps": "^0.6.3", "rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",
"tslib": "^2.2.0", "tslib": "^2.3.0",
"typescript": "^4.3.2", "typescript": "^4.3.3",
"webcomponent-qr-code": "^1.0.5", "webcomponent-qr-code": "^1.0.5",
"yaml": "^1.10.2" "yaml": "^1.10.2"
} }
@ -110,16 +110,16 @@
} }
}, },
"node_modules/@babel/core": { "node_modules/@babel/core": {
"version": "7.14.5", "version": "7.14.6",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.6.tgz",
"integrity": "sha512-RN/AwP2DJmQTZSfiDaD+JQQ/J99KsIpOCfBE5pL+5jJSt7nI3nYGoAXZu+ffYSQ029NLs2DstZb+eR81uuARgg==", "integrity": "sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA==",
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.14.5", "@babel/code-frame": "^7.14.5",
"@babel/generator": "^7.14.5", "@babel/generator": "^7.14.5",
"@babel/helper-compilation-targets": "^7.14.5", "@babel/helper-compilation-targets": "^7.14.5",
"@babel/helper-module-transforms": "^7.14.5", "@babel/helper-module-transforms": "^7.14.5",
"@babel/helpers": "^7.14.5", "@babel/helpers": "^7.14.6",
"@babel/parser": "^7.14.5", "@babel/parser": "^7.14.6",
"@babel/template": "^7.14.5", "@babel/template": "^7.14.5",
"@babel/traverse": "^7.14.5", "@babel/traverse": "^7.14.5",
"@babel/types": "^7.14.5", "@babel/types": "^7.14.5",
@ -479,9 +479,9 @@
} }
}, },
"node_modules/@babel/helpers": { "node_modules/@babel/helpers": {
"version": "7.14.5", "version": "7.14.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.6.tgz",
"integrity": "sha512-xtcWOuN9VL6nApgVHtq3PPcQv5qFBJzoSZzJ/2c0QK/IP/gxVcoWSNQwFEGvmbQsuS9rhYqjILDGGXcTkA705Q==", "integrity": "sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA==",
"dependencies": { "dependencies": {
"@babel/template": "^7.14.5", "@babel/template": "^7.14.5",
"@babel/traverse": "^7.14.5", "@babel/traverse": "^7.14.5",
@ -505,9 +505,9 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.14.5", "version": "7.14.6",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz",
"integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ==",
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
}, },
@ -2047,9 +2047,9 @@
} }
}, },
"node_modules/@lingui/core": { "node_modules/@lingui/core": {
"version": "3.10.2", "version": "3.10.4",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.10.2.tgz", "resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.10.4.tgz",
"integrity": "sha512-RL4Bn1s4Ukd0GBlEFO/+GoxnK48xuZR2GwFslg/eQxcQmeOA4c1Jw/tbvMKDfi40XwqMBWcS0G2njpWjuHC3SA==", "integrity": "sha512-V9QKQ9PFMTPrGGz2PaeKHZcxFikQZzJbptyQbVFJdXaKhdE2RH6HhdK1PIziDHqp6ZWPthVIfVLURT3ku8eu5w==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.11.2", "@babel/runtime": "^7.11.2",
"make-plural": "^6.2.2", "make-plural": "^6.2.2",
@ -2314,13 +2314,13 @@
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="
}, },
"node_modules/@sentry/browser": { "node_modules/@sentry/browser": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.7.1.tgz",
"integrity": "sha512-iVLCdEFwsoWAzE/hNknexPQjjDpMQV7mmaq9Z1P63bD6MfhwVTx4hG4pHn8HEvC38VvCVf1wv0v/LxtoODAYXg==", "integrity": "sha512-R5PYx4TTvifcU790XkK6JVGwavKaXwycDU0MaAwfc4Vf7BLm5KCNJCsDySu1RPAap/017MVYf54p6dWvKiRviA==",
"dependencies": { "dependencies": {
"@sentry/core": "6.5.1", "@sentry/core": "6.7.1",
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"@sentry/utils": "6.5.1", "@sentry/utils": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2333,14 +2333,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/core": { "node_modules/@sentry/core": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.7.1.tgz",
"integrity": "sha512-Mh3sl/iUOT1myHmM6RlDy2ARzkUClx/g4DAt1rJ/IpQBOlDYQraplXSIW80i/hzRgQDfwhwgf4wUa5DicKBjKw==", "integrity": "sha512-VAv8OR/7INn2JfiLcuop4hfDcyC7mfL9fdPndQEhlacjmw8gRrgXjR7qyhnCTgzFLkHI7V5bcdIzA83TRPYQpA==",
"dependencies": { "dependencies": {
"@sentry/hub": "6.5.1", "@sentry/hub": "6.7.1",
"@sentry/minimal": "6.5.1", "@sentry/minimal": "6.7.1",
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"@sentry/utils": "6.5.1", "@sentry/utils": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2353,12 +2353,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/hub": { "node_modules/@sentry/hub": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.7.1.tgz",
"integrity": "sha512-lBRMBVMYP8B4PfRiM70murbtJAXiIAao/asDEMIRNGMP6pI2ArqXfJCBYDkStukhikYD0Kqb4trXq+JYF07Hbg==", "integrity": "sha512-eVCTWvvcp6xa0A5GGNHMQEWslmKPlisE5rGmsV/kjvSUv3zSrI0eIDfb51ikdnCiBjHpK2NBWP8Vy8cZOEJegg==",
"dependencies": { "dependencies": {
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"@sentry/utils": "6.5.1", "@sentry/utils": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2371,12 +2371,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/minimal": { "node_modules/@sentry/minimal": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.7.1.tgz",
"integrity": "sha512-q9Do/oreu1RP695CXCLowVDuQyk7ilE6FGdz2QLpTXAfx8247qOwk6+zy9Kea/Djk93+BoSDVQUSneNiVwl0nQ==", "integrity": "sha512-HDDPEnQRD6hC0qaHdqqKDStcdE1KhkFh0RCtJNMCDn0zpav8Dj9AteF70x6kLSlliAJ/JFwi6AmQrLz+FxPexw==",
"dependencies": { "dependencies": {
"@sentry/hub": "6.5.1", "@sentry/hub": "6.7.1",
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2389,14 +2389,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/tracing": { "node_modules/@sentry/tracing": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.7.1.tgz",
"integrity": "sha512-y1W/xFC2hAuKqSuuaovkElHY4pbli3XoXrreesg8PtO7ilX6ZbatOQbHsEsHQyoUv0F6aVA+MABOxWH2jt7tfw==", "integrity": "sha512-wyS3nWNl5mzaC1qZ2AIp1hjXnfO9EERjMIJjCihs2LWBz1r3efxrHxJHs8wXlNWvrT3KLhq/7vvF5CdU82uPeQ==",
"dependencies": { "dependencies": {
"@sentry/hub": "6.5.1", "@sentry/hub": "6.7.1",
"@sentry/minimal": "6.5.1", "@sentry/minimal": "6.7.1",
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"@sentry/utils": "6.5.1", "@sentry/utils": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2409,19 +2409,19 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/types": { "node_modules/@sentry/types": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.7.1.tgz",
"integrity": "sha512-b/7a6CMoytaeFPx4IBjfxPw3nPvsQh7ui1C8Vw0LxNNDgBwVhPLzUOWeLWbo5YZCVbGEMIWwtCUQYWxneceZSA==", "integrity": "sha512-9AO7HKoip2MBMNQJEd6+AKtjj2+q9Ze4ooWUdEvdOVSt5drg7BGpK221/p9JEOyJAZwEPEXdcMd3VAIMiOb4MA==",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/@sentry/utils": { "node_modules/@sentry/utils": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.7.1.tgz",
"integrity": "sha512-Wv86JYGQH+ZJ5XGFQX7h6ijl32667ikenoL9EyXMn8UoOYX/MLwZoQZin1P60wmKkYR9ifTNVmpaI9OoTaH+UQ==", "integrity": "sha512-Tq2otdbWlHAkctD+EWTYKkEx6BL1Qn3Z/imkO06/PvzpWvVhJWQ5qHAzz5XnwwqNHyV03KVzYB6znq1Bea9HuA==",
"dependencies": { "dependencies": {
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2579,12 +2579,12 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==" "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.27.0.tgz",
"integrity": "sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw==", "integrity": "sha512-DsLqxeUfLVNp3AO7PC3JyaddmEHTtI9qTSAs+RB6ja27QvIM0TA8Cizn1qcS6vOu+WDLFJzkwkgweiyFhssDdQ==",
"dependencies": { "dependencies": {
"@typescript-eslint/experimental-utils": "4.26.1", "@typescript-eslint/experimental-utils": "4.27.0",
"@typescript-eslint/scope-manager": "4.26.1", "@typescript-eslint/scope-manager": "4.27.0",
"debug": "^4.3.1", "debug": "^4.3.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@ -2610,14 +2610,14 @@
} }
}, },
"node_modules/@typescript-eslint/experimental-utils": { "node_modules/@typescript-eslint/experimental-utils": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.27.0.tgz",
"integrity": "sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ==", "integrity": "sha512-n5NlbnmzT2MXlyT+Y0Jf0gsmAQzCnQSWXKy4RGSXVStjDvS5we9IWbh7qRVKdGcxT0WYlgcCYUK/HRg7xFhvjQ==",
"dependencies": { "dependencies": {
"@types/json-schema": "^7.0.7", "@types/json-schema": "^7.0.7",
"@typescript-eslint/scope-manager": "4.26.1", "@typescript-eslint/scope-manager": "4.27.0",
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"@typescript-eslint/typescript-estree": "4.26.1", "@typescript-eslint/typescript-estree": "4.27.0",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0" "eslint-utils": "^3.0.0"
}, },
@ -2650,13 +2650,13 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.27.0.tgz",
"integrity": "sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ==", "integrity": "sha512-XpbxL+M+gClmJcJ5kHnUpBGmlGdgNvy6cehgR6ufyxkEJMGP25tZKCaKyC0W/JVpuhU3VU1RBn7SYUPKSMqQvQ==",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "4.26.1", "@typescript-eslint/scope-manager": "4.27.0",
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"@typescript-eslint/typescript-estree": "4.26.1", "@typescript-eslint/typescript-estree": "4.27.0",
"debug": "^4.3.1" "debug": "^4.3.1"
}, },
"engines": { "engines": {
@ -2676,12 +2676,12 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.27.0.tgz",
"integrity": "sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ==", "integrity": "sha512-DY73jK6SEH6UDdzc6maF19AHQJBFVRf6fgAXHPXCGEmpqD4vYgPEzqpFz1lf/daSbOcMpPPj9tyXXDPW2XReAw==",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"@typescript-eslint/visitor-keys": "4.26.1" "@typescript-eslint/visitor-keys": "4.27.0"
}, },
"engines": { "engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1" "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
@ -2692,9 +2692,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.27.0.tgz",
"integrity": "sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg==", "integrity": "sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA==",
"engines": { "engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1" "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
}, },
@ -2704,12 +2704,12 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.27.0.tgz",
"integrity": "sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg==", "integrity": "sha512-KH03GUsUj41sRLLEy2JHstnezgpS5VNhrJouRdmh6yNdQ+yl8w5LrSwBkExM+jWwCJa7Ct2c8yl8NdtNRyQO6g==",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"@typescript-eslint/visitor-keys": "4.26.1", "@typescript-eslint/visitor-keys": "4.27.0",
"debug": "^4.3.1", "debug": "^4.3.1",
"globby": "^11.0.3", "globby": "^11.0.3",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -2749,11 +2749,11 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.27.0.tgz",
"integrity": "sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw==", "integrity": "sha512-es0GRYNZp0ieckZ938cEANfEhsfHrzuLrePukLKtY3/KPXcq1Xd555Mno9/GOgXhKzn0QfkDLVgqWO3dGY80bg==",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
}, },
"engines": { "engines": {
@ -6771,9 +6771,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "2.51.1", "version": "2.52.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.51.1.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.1.tgz",
"integrity": "sha512-8xfDbAtBleXotb6qKEHWuo/jkn94a9dVqGc7Rwl3sqspCVlnCfbRek7ldhCARSi7h32H0xR4QThm1t9zHN+3uw==", "integrity": "sha512-/SPqz8UGnp4P1hq6wc9gdTqA2bXQXGx13TtoL03GBm6qGRI6Hm3p4Io7GeiHNLl0BsQAne1JNYY+q/apcY933w==",
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
}, },
@ -6781,7 +6781,7 @@
"node": ">=10.0.0" "node": ">=10.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"fsevents": "~2.3.1" "fsevents": "~2.3.2"
} }
}, },
"node_modules/rollup-plugin-commonjs": { "node_modules/rollup-plugin-commonjs": {
@ -7570,9 +7570,9 @@
"integrity": "sha512-zKmsCQs4dZaeSKjEA7pLFDv7FHHqAFLPd0Mr//OIJvu8M+4p4bgSFJwZSEBEg3ec9W7RzRz1vi8giiX0+mheBQ==" "integrity": "sha512-zKmsCQs4dZaeSKjEA7pLFDv7FHHqAFLPd0Mr//OIJvu8M+4p4bgSFJwZSEBEg3ec9W7RzRz1vi8giiX0+mheBQ=="
}, },
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.2.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
}, },
"node_modules/tsutils": { "node_modules/tsutils": {
"version": "3.21.0", "version": "3.21.0",
@ -7605,9 +7605,9 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "4.3.2", "version": "4.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.3.tgz",
"integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", "integrity": "sha512-rUvLW0WtF7PF2b9yenwWUi9Da9euvDRhmH7BLyBG4DCFfOJ850LGNknmRpp8Z8kXNUPObdZQEfKOiHtXuQHHKA==",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@ -8071,16 +8071,16 @@
"integrity": "sha512-kixrYn4JwfAVPa0f2yfzc2AWti6WRRyO3XjWW5PJAvtE11qhSayrrcrEnee05KAtNaPC+EwehE8Qt1UedEVB8w==" "integrity": "sha512-kixrYn4JwfAVPa0f2yfzc2AWti6WRRyO3XjWW5PJAvtE11qhSayrrcrEnee05KAtNaPC+EwehE8Qt1UedEVB8w=="
}, },
"@babel/core": { "@babel/core": {
"version": "7.14.5", "version": "7.14.6",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.6.tgz",
"integrity": "sha512-RN/AwP2DJmQTZSfiDaD+JQQ/J99KsIpOCfBE5pL+5jJSt7nI3nYGoAXZu+ffYSQ029NLs2DstZb+eR81uuARgg==", "integrity": "sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA==",
"requires": { "requires": {
"@babel/code-frame": "^7.14.5", "@babel/code-frame": "^7.14.5",
"@babel/generator": "^7.14.5", "@babel/generator": "^7.14.5",
"@babel/helper-compilation-targets": "^7.14.5", "@babel/helper-compilation-targets": "^7.14.5",
"@babel/helper-module-transforms": "^7.14.5", "@babel/helper-module-transforms": "^7.14.5",
"@babel/helpers": "^7.14.5", "@babel/helpers": "^7.14.6",
"@babel/parser": "^7.14.5", "@babel/parser": "^7.14.6",
"@babel/template": "^7.14.5", "@babel/template": "^7.14.5",
"@babel/traverse": "^7.14.5", "@babel/traverse": "^7.14.5",
"@babel/types": "^7.14.5", "@babel/types": "^7.14.5",
@ -8345,9 +8345,9 @@
} }
}, },
"@babel/helpers": { "@babel/helpers": {
"version": "7.14.5", "version": "7.14.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.6.tgz",
"integrity": "sha512-xtcWOuN9VL6nApgVHtq3PPcQv5qFBJzoSZzJ/2c0QK/IP/gxVcoWSNQwFEGvmbQsuS9rhYqjILDGGXcTkA705Q==", "integrity": "sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA==",
"requires": { "requires": {
"@babel/template": "^7.14.5", "@babel/template": "^7.14.5",
"@babel/traverse": "^7.14.5", "@babel/traverse": "^7.14.5",
@ -8365,9 +8365,9 @@
} }
}, },
"@babel/parser": { "@babel/parser": {
"version": "7.14.5", "version": "7.14.6",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.6.tgz",
"integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==" "integrity": "sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ=="
}, },
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
"version": "7.14.5", "version": "7.14.5",
@ -9431,9 +9431,9 @@
} }
}, },
"@lingui/core": { "@lingui/core": {
"version": "3.10.2", "version": "3.10.4",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.10.2.tgz", "resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.10.4.tgz",
"integrity": "sha512-RL4Bn1s4Ukd0GBlEFO/+GoxnK48xuZR2GwFslg/eQxcQmeOA4c1Jw/tbvMKDfi40XwqMBWcS0G2njpWjuHC3SA==", "integrity": "sha512-V9QKQ9PFMTPrGGz2PaeKHZcxFikQZzJbptyQbVFJdXaKhdE2RH6HhdK1PIziDHqp6ZWPthVIfVLURT3ku8eu5w==",
"requires": { "requires": {
"@babel/runtime": "^7.11.2", "@babel/runtime": "^7.11.2",
"make-plural": "^6.2.2", "make-plural": "^6.2.2",
@ -9670,13 +9670,13 @@
} }
}, },
"@sentry/browser": { "@sentry/browser": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.7.1.tgz",
"integrity": "sha512-iVLCdEFwsoWAzE/hNknexPQjjDpMQV7mmaq9Z1P63bD6MfhwVTx4hG4pHn8HEvC38VvCVf1wv0v/LxtoODAYXg==", "integrity": "sha512-R5PYx4TTvifcU790XkK6JVGwavKaXwycDU0MaAwfc4Vf7BLm5KCNJCsDySu1RPAap/017MVYf54p6dWvKiRviA==",
"requires": { "requires": {
"@sentry/core": "6.5.1", "@sentry/core": "6.7.1",
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"@sentry/utils": "6.5.1", "@sentry/utils": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -9688,14 +9688,14 @@
} }
}, },
"@sentry/core": { "@sentry/core": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.7.1.tgz",
"integrity": "sha512-Mh3sl/iUOT1myHmM6RlDy2ARzkUClx/g4DAt1rJ/IpQBOlDYQraplXSIW80i/hzRgQDfwhwgf4wUa5DicKBjKw==", "integrity": "sha512-VAv8OR/7INn2JfiLcuop4hfDcyC7mfL9fdPndQEhlacjmw8gRrgXjR7qyhnCTgzFLkHI7V5bcdIzA83TRPYQpA==",
"requires": { "requires": {
"@sentry/hub": "6.5.1", "@sentry/hub": "6.7.1",
"@sentry/minimal": "6.5.1", "@sentry/minimal": "6.7.1",
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"@sentry/utils": "6.5.1", "@sentry/utils": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -9707,12 +9707,12 @@
} }
}, },
"@sentry/hub": { "@sentry/hub": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.7.1.tgz",
"integrity": "sha512-lBRMBVMYP8B4PfRiM70murbtJAXiIAao/asDEMIRNGMP6pI2ArqXfJCBYDkStukhikYD0Kqb4trXq+JYF07Hbg==", "integrity": "sha512-eVCTWvvcp6xa0A5GGNHMQEWslmKPlisE5rGmsV/kjvSUv3zSrI0eIDfb51ikdnCiBjHpK2NBWP8Vy8cZOEJegg==",
"requires": { "requires": {
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"@sentry/utils": "6.5.1", "@sentry/utils": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -9724,12 +9724,12 @@
} }
}, },
"@sentry/minimal": { "@sentry/minimal": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.7.1.tgz",
"integrity": "sha512-q9Do/oreu1RP695CXCLowVDuQyk7ilE6FGdz2QLpTXAfx8247qOwk6+zy9Kea/Djk93+BoSDVQUSneNiVwl0nQ==", "integrity": "sha512-HDDPEnQRD6hC0qaHdqqKDStcdE1KhkFh0RCtJNMCDn0zpav8Dj9AteF70x6kLSlliAJ/JFwi6AmQrLz+FxPexw==",
"requires": { "requires": {
"@sentry/hub": "6.5.1", "@sentry/hub": "6.7.1",
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -9741,14 +9741,14 @@
} }
}, },
"@sentry/tracing": { "@sentry/tracing": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.7.1.tgz",
"integrity": "sha512-y1W/xFC2hAuKqSuuaovkElHY4pbli3XoXrreesg8PtO7ilX6ZbatOQbHsEsHQyoUv0F6aVA+MABOxWH2jt7tfw==", "integrity": "sha512-wyS3nWNl5mzaC1qZ2AIp1hjXnfO9EERjMIJjCihs2LWBz1r3efxrHxJHs8wXlNWvrT3KLhq/7vvF5CdU82uPeQ==",
"requires": { "requires": {
"@sentry/hub": "6.5.1", "@sentry/hub": "6.7.1",
"@sentry/minimal": "6.5.1", "@sentry/minimal": "6.7.1",
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"@sentry/utils": "6.5.1", "@sentry/utils": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -9760,16 +9760,16 @@
} }
}, },
"@sentry/types": { "@sentry/types": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.7.1.tgz",
"integrity": "sha512-b/7a6CMoytaeFPx4IBjfxPw3nPvsQh7ui1C8Vw0LxNNDgBwVhPLzUOWeLWbo5YZCVbGEMIWwtCUQYWxneceZSA==" "integrity": "sha512-9AO7HKoip2MBMNQJEd6+AKtjj2+q9Ze4ooWUdEvdOVSt5drg7BGpK221/p9JEOyJAZwEPEXdcMd3VAIMiOb4MA=="
}, },
"@sentry/utils": { "@sentry/utils": {
"version": "6.5.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.5.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.7.1.tgz",
"integrity": "sha512-Wv86JYGQH+ZJ5XGFQX7h6ijl32667ikenoL9EyXMn8UoOYX/MLwZoQZin1P60wmKkYR9ifTNVmpaI9OoTaH+UQ==", "integrity": "sha512-Tq2otdbWlHAkctD+EWTYKkEx6BL1Qn3Z/imkO06/PvzpWvVhJWQ5qHAzz5XnwwqNHyV03KVzYB6znq1Bea9HuA==",
"requires": { "requires": {
"@sentry/types": "6.5.1", "@sentry/types": "6.7.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -9926,12 +9926,12 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==" "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.27.0.tgz",
"integrity": "sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw==", "integrity": "sha512-DsLqxeUfLVNp3AO7PC3JyaddmEHTtI9qTSAs+RB6ja27QvIM0TA8Cizn1qcS6vOu+WDLFJzkwkgweiyFhssDdQ==",
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "4.26.1", "@typescript-eslint/experimental-utils": "4.27.0",
"@typescript-eslint/scope-manager": "4.26.1", "@typescript-eslint/scope-manager": "4.27.0",
"debug": "^4.3.1", "debug": "^4.3.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@ -9941,14 +9941,14 @@
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.27.0.tgz",
"integrity": "sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ==", "integrity": "sha512-n5NlbnmzT2MXlyT+Y0Jf0gsmAQzCnQSWXKy4RGSXVStjDvS5we9IWbh7qRVKdGcxT0WYlgcCYUK/HRg7xFhvjQ==",
"requires": { "requires": {
"@types/json-schema": "^7.0.7", "@types/json-schema": "^7.0.7",
"@typescript-eslint/scope-manager": "4.26.1", "@typescript-eslint/scope-manager": "4.27.0",
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"@typescript-eslint/typescript-estree": "4.26.1", "@typescript-eslint/typescript-estree": "4.27.0",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0" "eslint-utils": "^3.0.0"
}, },
@ -9964,37 +9964,37 @@
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.27.0.tgz",
"integrity": "sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ==", "integrity": "sha512-XpbxL+M+gClmJcJ5kHnUpBGmlGdgNvy6cehgR6ufyxkEJMGP25tZKCaKyC0W/JVpuhU3VU1RBn7SYUPKSMqQvQ==",
"requires": { "requires": {
"@typescript-eslint/scope-manager": "4.26.1", "@typescript-eslint/scope-manager": "4.27.0",
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"@typescript-eslint/typescript-estree": "4.26.1", "@typescript-eslint/typescript-estree": "4.27.0",
"debug": "^4.3.1" "debug": "^4.3.1"
} }
}, },
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.27.0.tgz",
"integrity": "sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ==", "integrity": "sha512-DY73jK6SEH6UDdzc6maF19AHQJBFVRf6fgAXHPXCGEmpqD4vYgPEzqpFz1lf/daSbOcMpPPj9tyXXDPW2XReAw==",
"requires": { "requires": {
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"@typescript-eslint/visitor-keys": "4.26.1" "@typescript-eslint/visitor-keys": "4.27.0"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.27.0.tgz",
"integrity": "sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg==" "integrity": "sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA=="
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.27.0.tgz",
"integrity": "sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg==", "integrity": "sha512-KH03GUsUj41sRLLEy2JHstnezgpS5VNhrJouRdmh6yNdQ+yl8w5LrSwBkExM+jWwCJa7Ct2c8yl8NdtNRyQO6g==",
"requires": { "requires": {
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"@typescript-eslint/visitor-keys": "4.26.1", "@typescript-eslint/visitor-keys": "4.27.0",
"debug": "^4.3.1", "debug": "^4.3.1",
"globby": "^11.0.3", "globby": "^11.0.3",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -10018,11 +10018,11 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.26.1", "version": "4.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.27.0.tgz",
"integrity": "sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw==", "integrity": "sha512-es0GRYNZp0ieckZ938cEANfEhsfHrzuLrePukLKtY3/KPXcq1Xd555Mno9/GOgXhKzn0QfkDLVgqWO3dGY80bg==",
"requires": { "requires": {
"@typescript-eslint/types": "4.26.1", "@typescript-eslint/types": "4.27.0",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
} }
}, },
@ -13202,11 +13202,11 @@
} }
}, },
"rollup": { "rollup": {
"version": "2.51.1", "version": "2.52.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.51.1.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.1.tgz",
"integrity": "sha512-8xfDbAtBleXotb6qKEHWuo/jkn94a9dVqGc7Rwl3sqspCVlnCfbRek7ldhCARSi7h32H0xR4QThm1t9zHN+3uw==", "integrity": "sha512-/SPqz8UGnp4P1hq6wc9gdTqA2bXQXGx13TtoL03GBm6qGRI6Hm3p4Io7GeiHNLl0BsQAne1JNYY+q/apcY933w==",
"requires": { "requires": {
"fsevents": "~2.3.1" "fsevents": "~2.3.2"
} }
}, },
"rollup-plugin-commonjs": { "rollup-plugin-commonjs": {
@ -13870,9 +13870,9 @@
"integrity": "sha512-zKmsCQs4dZaeSKjEA7pLFDv7FHHqAFLPd0Mr//OIJvu8M+4p4bgSFJwZSEBEg3ec9W7RzRz1vi8giiX0+mheBQ==" "integrity": "sha512-zKmsCQs4dZaeSKjEA7pLFDv7FHHqAFLPd0Mr//OIJvu8M+4p4bgSFJwZSEBEg3ec9W7RzRz1vi8giiX0+mheBQ=="
}, },
"tslib": { "tslib": {
"version": "2.2.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
}, },
"tsutils": { "tsutils": {
"version": "3.21.0", "version": "3.21.0",
@ -13898,9 +13898,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "4.3.2", "version": "4.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.3.tgz",
"integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==" "integrity": "sha512-rUvLW0WtF7PF2b9yenwWUi9Da9euvDRhmH7BLyBG4DCFfOJ850LGNknmRpp8Z8kXNUPObdZQEfKOiHtXuQHHKA=="
}, },
"uglify-js": { "uglify-js": {
"version": "3.13.0", "version": "3.13.0",

View File

@ -38,14 +38,14 @@
] ]
}, },
"dependencies": { "dependencies": {
"@babel/core": "^7.14.5", "@babel/core": "^7.14.6",
"@babel/plugin-proposal-decorators": "^7.14.5", "@babel/plugin-proposal-decorators": "^7.14.5",
"@babel/plugin-transform-runtime": "^7.14.5", "@babel/plugin-transform-runtime": "^7.14.5",
"@babel/preset-env": "^7.14.5", "@babel/preset-env": "^7.14.5",
"@babel/preset-typescript": "^7.14.5", "@babel/preset-typescript": "^7.14.5",
"@fortawesome/fontawesome-free": "^5.15.3", "@fortawesome/fontawesome-free": "^5.15.3",
"@lingui/cli": "^3.10.2", "@lingui/cli": "^3.10.2",
"@lingui/core": "^3.10.2", "@lingui/core": "^3.10.4",
"@lingui/macro": "^3.10.2", "@lingui/macro": "^3.10.2",
"@patternfly/patternfly": "^4.108.2", "@patternfly/patternfly": "^4.108.2",
"@polymer/iron-form": "^3.0.1", "@polymer/iron-form": "^3.0.1",
@ -53,13 +53,13 @@
"@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^2.4.2", "@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.1", "@rollup/plugin-typescript": "^8.2.1",
"@sentry/browser": "^6.5.1", "@sentry/browser": "^6.7.1",
"@sentry/tracing": "^6.5.1", "@sentry/tracing": "^6.7.1",
"@types/chart.js": "^2.9.32", "@types/chart.js": "^2.9.32",
"@types/codemirror": "5.60.0", "@types/codemirror": "5.60.0",
"@types/grecaptcha": "^3.0.2", "@types/grecaptcha": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^4.26.1", "@typescript-eslint/eslint-plugin": "^4.27.0",
"@typescript-eslint/parser": "^4.26.1", "@typescript-eslint/parser": "^4.27.0",
"@webcomponents/webcomponentsjs": "^2.5.0", "@webcomponents/webcomponentsjs": "^2.5.0",
"authentik-api": "file:api", "authentik-api": "file:api",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
@ -77,7 +77,7 @@
"lit-html": "^1.4.1", "lit-html": "^1.4.1",
"moment": "^2.29.1", "moment": "^2.29.1",
"rapidoc": "^9.0.0", "rapidoc": "^9.0.0",
"rollup": "^2.51.1", "rollup": "^2.52.1",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2", "rollup-plugin-cssimport": "^1.0.2",
@ -86,8 +86,8 @@
"rollup-plugin-sourcemaps": "^0.6.3", "rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",
"tslib": "^2.2.0", "tslib": "^2.3.0",
"typescript": "^4.3.2", "typescript": "^4.3.3",
"webcomponent-qr-code": "^1.0.5", "webcomponent-qr-code": "^1.0.5",
"yaml": "^1.10.2" "yaml": "^1.10.2"
}, },

View File

@ -8,10 +8,22 @@ export interface EventUser {
} }
export interface EventContext { export interface EventContext {
[key: string]: EventContext | string | number | string[]; [key: string]: EventContext | EventModel | string | number | string[];
} }
export interface EventWithContext extends Event { export interface EventWithContext extends Event {
user: EventUser; user: EventUser;
context: EventContext; context: EventContext;
} }
export interface EventModel {
pk: string;
name: string;
app: string;
model_name: string;
}
export interface EventRequest {
path: string;
method: string;
}

View File

@ -7,8 +7,9 @@ import { config } from "./Config";
import { Config } from "authentik-api"; import { Config } from "authentik-api";
export const TAG_SENTRY_COMPONENT = "authentik.component"; export const TAG_SENTRY_COMPONENT = "authentik.component";
export const TAG_SENTRY_CAPABILITIES = "authentik.capabilities";
export function configureSentry(canDoPpi: boolean = false, tags: { [key: string]: string; } = {}): Promise<Config> { export function configureSentry(canDoPpi: boolean = false): Promise<Config> {
return config().then((config) => { return config().then((config) => {
if (config.errorReportingEnabled) { if (config.errorReportingEnabled) {
Sentry.init({ Sentry.init({
@ -53,7 +54,7 @@ export function configureSentry(canDoPpi: boolean = false, tags: { [key: string]
return event; return event;
}, },
}); });
Sentry.setTags(tags); Sentry.setTag(TAG_SENTRY_CAPABILITIES, config.capabilities.join(","));
if (window.location.pathname.includes("if/")) { if (window.location.pathname.includes("if/")) {
// Get the interface name from URL // Get the interface name from URL
const intf = window.location.pathname.replace(/.+if\/(.+)\//, "$1"); const intf = window.location.pathname.replace(/.+if\/(.+)\//, "$1");

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger"; export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress"; export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current"; export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2021.6.1-rc3"; export const VERSION = "2021.6.1";
export const PAGE_SIZE = 20; export const PAGE_SIZE = 20;
export const EVENT_REFRESH = "ak-refresh"; export const EVENT_REFRESH = "ak-refresh";
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle"; export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";

View File

@ -1,27 +0,0 @@
import { customElement, CSSResult, html, LitElement, property, TemplateResult } from "lit-element";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
import AKGlobal from "../authentik.css";
@customElement("ak-banner")
export class Banner extends LitElement {
@property()
level = "pf-m-warning";
static get styles(): CSSResult[] {
return [PFBase, PFBanner, PFFlex, AKGlobal];
}
render(): TemplateResult {
return html`<div class="pf-c-banner ${this.level} pf-m-sticky">
<div class="pf-l-flex pf-m-justify-content-center pf-m-justify-content-space-between-on-lg pf-m-nowrap" style="height: 100%;">
<div class="pf-u-display-none pf-u-display-block-on-lg">
<slot></slot>
</div>
</div>
</div>`;
}
}

View File

@ -7,7 +7,6 @@ import PFFormControl from "@patternfly/patternfly/components/FormControl/form-co
import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css"; import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css"; import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css"; import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css"; import PFContent from "@patternfly/patternfly/components/Content/content.css";
@ -40,7 +39,7 @@ export class ModalButton extends LitElement {
open = false; open = false;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [PFBase, PFButton, PFModalBox, PFForm, PFTitle, PFFormControl, PFBullseye, PFBackdrop, PFPage, PFStack, PFCard, PFContent, AKGlobal, MODAL_BUTTON_STYLES]; return [PFBase, PFButton, PFModalBox, PFForm, PFTitle, PFFormControl, PFBullseye, PFBackdrop, PFPage, PFCard, PFContent, AKGlobal, MODAL_BUTTON_STYLES];
} }
constructor() { constructor() {

View File

@ -19,7 +19,6 @@ export class Sidebar extends LitElement {
css` css`
:host { :host {
z-index: 100; z-index: 100;
box-shadow: none !important;
} }
.pf-c-nav__link.pf-m-current::after, .pf-c-nav__link.pf-m-current::after,
.pf-c-nav__link.pf-m-current:hover::after, .pf-c-nav__link.pf-m-current:hover::after,

View File

@ -21,6 +21,13 @@ export class SidebarItem extends LitElement {
z-index: 100; z-index: 100;
box-shadow: none !important; box-shadow: none !important;
} }
:host([highlight]) .pf-c-nav__item {
background-color: var(--ak-accent);
margin: 16px;
}
:host([highlight]) .pf-c-nav__item .pf-c-nav__link {
padding-left: 0.5rem;
}
.pf-c-nav__link.pf-m-current::after, .pf-c-nav__link.pf-m-current::after,
.pf-c-nav__link.pf-m-current:hover::after, .pf-c-nav__link.pf-m-current:hover::after,
.pf-c-nav__item.pf-m-current:not(.pf-m-expanded) .pf-c-nav__link::after { .pf-c-nav__item.pf-m-current:not(.pf-m-expanded) .pf-c-nav__link::after {
@ -75,6 +82,12 @@ export class SidebarItem extends LitElement {
@property({ type: Boolean }) @property({ type: Boolean })
isActive = false; isActive = false;
@property({ type: Boolean })
isAbsoluteLink?: boolean;
@property({ type: Boolean })
highlight?: boolean;
parent?: SidebarItem; parent?: SidebarItem;
get childItems(): SidebarItem[] { get childItems(): SidebarItem[] {
@ -159,9 +172,15 @@ export class SidebarItem extends LitElement {
</li>`; </li>`;
} }
return html`<li class="pf-c-nav__item"> return html`<li class="pf-c-nav__item">
<a href="#${this.path}" class="pf-c-nav__link ${this.isActive ? "pf-m-current" : ""}"> ${this.path ? html`
<a href="${this.isAbsoluteLink ? "" : "#"}${this.path}" class="pf-c-nav__link ${this.isActive ? "pf-m-current" : ""}">
<slot name="label"></slot> <slot name="label"></slot>
</a> </a>
` : html`
<span class="pf-c-nav__link">
<slot name="label"></slot>
</span>
`}
</li>`; </li>`;
} }
} }

View File

@ -26,7 +26,7 @@ import "./stages/password/PasswordStage";
import "./stages/prompt/PromptStage"; import "./stages/prompt/PromptStage";
import "./sources/plex/PlexLoginInit"; import "./sources/plex/PlexLoginInit";
import { StageHost } from "./stages/base"; import { StageHost } from "./stages/base";
import { ChallengeChoices, CurrentTenant, FlowChallengeRequest, FlowChallengeResponseRequest, FlowsApi, RedirectChallenge, ShellChallenge } from "authentik-api"; import { ChallengeChoices, CurrentTenant, ChallengeTypes, FlowChallengeResponseRequest, FlowsApi, RedirectChallenge, ShellChallenge } from "authentik-api";
import { DEFAULT_CONFIG, tenant } from "../api/Config"; import { DEFAULT_CONFIG, tenant } from "../api/Config";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
@ -40,7 +40,7 @@ export class FlowExecutor extends LitElement implements StageHost {
flowSlug: string; flowSlug: string;
@property({attribute: false}) @property({attribute: false})
challenge?: FlowChallengeRequest; challenge?: ChallengeTypes;
@property({type: Boolean}) @property({type: Boolean})
loading = false; loading = false;
@ -107,8 +107,8 @@ export class FlowExecutor extends LitElement implements StageHost {
}).then((data) => { }).then((data) => {
this.challenge = data; this.challenge = data;
this.postUpdate(); this.postUpdate();
}).catch((e: Error) => { }).catch((e: Error | Response) => {
this.errorMessage(e.toString()); this.errorMessage(e);
}).finally(() => { }).finally(() => {
this.loading = false; this.loading = false;
}); });
@ -128,15 +128,21 @@ export class FlowExecutor extends LitElement implements StageHost {
this.setBackground(this.challenge.flowInfo.background); this.setBackground(this.challenge.flowInfo.background);
} }
this.postUpdate(); this.postUpdate();
}).catch((e: Error) => { }).catch((e: Error | Response) => {
// Catch JSON or Update errors // Catch JSON or Update errors
this.errorMessage(e.toString()); this.errorMessage(e);
}).finally(() => { }).finally(() => {
this.loading = false; this.loading = false;
}); });
} }
errorMessage(error: string): void { async errorMessage(error: Error | Response): Promise<void> {
let body = "";
if (error instanceof Error) {
body = error.message;
} else if (error instanceof Response) {
body = await error.text();
}
this.challenge = { this.challenge = {
type: ChallengeChoices.Shell, type: ChallengeChoices.Shell,
body: `<header class="pf-c-login__main-header"> body: `<header class="pf-c-login__main-header">
@ -146,7 +152,7 @@ export class FlowExecutor extends LitElement implements StageHost {
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">
<h3>${t`Something went wrong! Please try again later.`}</h3> <h3>${t`Something went wrong! Please try again later.`}</h3>
<pre class="ak-exception">${error}</pre> <pre class="ak-exception">${body}</pre>
</div> </div>
<footer class="pf-c-login__main-footer"> <footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"> <ul class="pf-c-login__main-footer-links">
@ -157,7 +163,7 @@ export class FlowExecutor extends LitElement implements StageHost {
</li> </li>
</ul> </ul>
</footer>` </footer>`
} as FlowChallengeRequest; } as ChallengeTypes;
} }
renderLoading(): TemplateResult { renderLoading(): TemplateResult {

View File

@ -11,7 +11,7 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import AKGlobal from "../../../authentik.css"; import AKGlobal from "../../../authentik.css";
import "../../../elements/forms/FormElement"; import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState"; import "../../../elements/EmptyState";
import { FlowChallengeRequest, IdentificationChallenge, IdentificationChallengeResponseRequest, UILoginButton } from "authentik-api"; import { IdentificationChallenge, IdentificationChallengeResponseRequest, LoginSource, UserFieldsEnum } from "authentik-api";
export const PasswordManagerPrefill: { export const PasswordManagerPrefill: {
password: string | undefined; password: string | undefined;
@ -21,6 +21,7 @@ export const PasswordManagerPrefill: {
totp: undefined, totp: undefined,
}; };
export const OR_LIST_FORMATTERS = new Intl.ListFormat("default", { style: "short", type: "disjunction" });
@customElement("ak-stage-identification") @customElement("ak-stage-identification")
export class IdentificationStage extends BaseStage<IdentificationChallenge, IdentificationChallengeResponseRequest> { export class IdentificationStage extends BaseStage<IdentificationChallenge, IdentificationChallengeResponseRequest> {
@ -109,7 +110,7 @@ export class IdentificationStage extends BaseStage<IdentificationChallenge, Iden
wrapperForm.appendChild(totp); wrapperForm.appendChild(totp);
} }
renderSource(source: UILoginButton): TemplateResult { renderSource(source: LoginSource): TemplateResult {
let icon = html`<i class="fas fas fa-share-square" title="${source.name}"></i>`; let icon = html`<i class="fas fas fa-share-square" title="${source.name}"></i>`;
if (source.iconUrl) { if (source.iconUrl) {
icon = html`<img src="${source.iconUrl}" alt="${source.name}">`; icon = html`<img src="${source.iconUrl}" alt="${source.name}">`;
@ -117,7 +118,7 @@ export class IdentificationStage extends BaseStage<IdentificationChallenge, Iden
return html`<li class="pf-c-login__main-footer-links-item"> return html`<li class="pf-c-login__main-footer-links-item">
<button type="button" @click=${() => { <button type="button" @click=${() => {
if (!this.host) return; if (!this.host) return;
this.host.challenge = source.challenge as FlowChallengeRequest; this.host.challenge = source.challenge;
}}> }}>
${icon} ${icon}
</button> </button>
@ -142,21 +143,23 @@ export class IdentificationStage extends BaseStage<IdentificationChallenge, Iden
} }
renderInput(): TemplateResult { renderInput(): TemplateResult {
let label = "";
let type = "text"; let type = "text";
if (!this.challenge?.userFields) { if (!this.challenge?.userFields) {
return html`<p> return html`<p>
${t`Select one of the sources below to login.`} ${t`Select one of the sources below to login.`}
</p>`; </p>`;
} }
if (this.challenge?.userFields === ["email"]) { const fields = (this.challenge?.userFields || []).sort();
label = t`Email`; // Check if the field should be *only* email to set the input type
if (fields.includes(UserFieldsEnum.Email) && fields.length === 1) {
type = "email"; type = "email";
} else if (this.challenge?.userFields === ["username"]) {
label = t`Username`;
} else {
label = t`Email or username`;
} }
const uiFields: { [key: string]: string } = {
[UserFieldsEnum.Username]: t`Username`,
[UserFieldsEnum.Email]: t`Email`,
[UserFieldsEnum.Upn]: t`UPN`,
};
const label = OR_LIST_FORMATTERS.format(fields.map(f => uiFields[f]));
return html`<ak-form-element return html`<ak-form-element
label=${label} label=${label}
?required="${true}" ?required="${true}"
@ -165,7 +168,7 @@ export class IdentificationStage extends BaseStage<IdentificationChallenge, Iden
<!-- @ts-ignore --> <!-- @ts-ignore -->
<input type=${type} <input type=${type}
name="uidField" name="uidField"
placeholder="Email or Username" placeholder=${label}
autofocus="" autofocus=""
autocomplete="username" autocomplete="username"
class="pf-c-form-control" class="pf-c-form-control"

7
web/src/global.d.ts vendored
View File

@ -1 +1,8 @@
declare module "*.css"; declare module "*.css";
declare namespace Intl {
class ListFormat {
constructor(locale: string, args: { [key: string]: string });
public format: (items: string[]) => string;
}
}

View File

@ -1,20 +1,102 @@
import "../elements/messages/MessageContainer"; import "../elements/messages/MessageContainer";
import { customElement, html, TemplateResult } from "lit-element"; import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { me } from "../api/Users"; import { me } from "../api/Users";
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "../elements/router/Route"; import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "../elements/router/Route";
import { Interface } from "./Interface";
import "./locale"; import "./locale";
import "../elements/sidebar/SidebarItem"; import "../elements/sidebar/SidebarItem";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
import "../elements/router/RouterOutlet";
import "../elements/messages/MessageContainer";
import "../elements/notifications/NotificationDrawer";
import "../elements/sidebar/Sidebar";
import { until } from "lit-html/directives/until";
import { EVENT_NOTIFICATION_TOGGLE, EVENT_SIDEBAR_TOGGLE, VERSION } from "../constants";
import { AdminApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../api/Config";
@customElement("ak-interface-admin") @customElement("ak-interface-admin")
export class AdminInterface extends Interface { export class AdminInterface extends LitElement {
@property({type: Boolean})
sidebarOpen = true;
@property({type: Boolean})
notificationOpen = false;
static get styles(): CSSResult[] {
return [PFBase, PFPage, PFButton, PFDrawer, css`
.pf-c-page__main, .pf-c-drawer__content, .pf-c-page__drawer {
z-index: auto !important;
}
`];
}
constructor() {
super();
this.sidebarOpen = window.innerWidth >= 1280;
window.addEventListener("resize", () => {
this.sidebarOpen = window.innerWidth >= 1280;
});
window.addEventListener(EVENT_SIDEBAR_TOGGLE, () => {
this.sidebarOpen = !this.sidebarOpen;
});
window.addEventListener(EVENT_NOTIFICATION_TOGGLE, () => {
this.notificationOpen = !this.notificationOpen;
});
}
render(): TemplateResult {
return html`
<div class="pf-c-page">
<ak-sidebar class="pf-c-page__sidebar ${this.sidebarOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
${this.renderSidebarItems()}
</ak-sidebar>
<div class="pf-c-page__drawer">
<div class="pf-c-drawer ${this.notificationOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
<div class="pf-c-drawer__main">
<div class="pf-c-drawer__content">
<div class="pf-c-drawer__body">
<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>
</main>
</div>
</div>
<ak-notification-drawer class="pf-c-drawer__panel pf-m-width-33">
</ak-notification-drawer>
</div>
</div>
</div>
</div>`;
}
renderSidebarItems(): TemplateResult { renderSidebarItems(): TemplateResult {
const superUserCondition = () => { const superUserCondition = () => {
return me().then(u => u.user.isSuperuser || false); return me().then(u => u.user.isSuperuser || false);
}; };
return html` return html`
${until(new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve().then(version => {
if (version.versionCurrent !== VERSION) {
return html`<ak-sidebar-item ?highlight=${true}>
<span slot="label">${t`A newer version of the frontend is available.`}</span>
</ak-sidebar-item>`;
}
return html``;
}))}
${until(me().then((u) => {
if (u.original) {
return html`<ak-sidebar-item ?highlight=${true} ?isAbsoluteLink=${true} path=${`/-/impersonation/end/?back=${window.location.pathname}%23${window.location.hash}`}>
<span slot="label">${t`You're currently impersonating ${u.user.username}. Click to stop.`}</span>
</ak-sidebar-item>`;
}
return html``;
}))}
<ak-sidebar-item path="/library"> <ak-sidebar-item path="/library">
<span slot="label">${t`Library`}</span> <span slot="label">${t`Library`}</span>
</ak-sidebar-item> </ak-sidebar-item>

View File

@ -1,99 +0,0 @@
import { css, CSSResult, html, LitElement, property, TemplateResult } from "lit-element";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
import "../elements/router/RouterOutlet";
import "../elements/messages/MessageContainer";
import "../elements/notifications/NotificationDrawer";
import "../elements/Banner";
import "../elements/sidebar/Sidebar";
import { until } from "lit-html/directives/until";
import { me } from "../api/Users";
import { t } from "@lingui/macro";
import { EVENT_NOTIFICATION_TOGGLE, EVENT_SIDEBAR_TOGGLE, VERSION } from "../constants";
import { AdminApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../api/Config";
export abstract class Interface extends LitElement {
@property({type: Boolean})
sidebarOpen = true;
@property({type: Boolean})
notificationOpen = false;
static get styles(): CSSResult[] {
return [PFBase, PFPage, PFButton, PFDrawer, css`
.pf-c-page__main, .pf-c-drawer__content, .pf-c-page__drawer {
z-index: auto !important;
}
`];
}
constructor() {
super();
this.sidebarOpen = window.innerWidth >= 1280;
window.addEventListener("resize", () => {
this.sidebarOpen = window.innerWidth >= 1280;
});
window.addEventListener(EVENT_SIDEBAR_TOGGLE, () => {
this.sidebarOpen = !this.sidebarOpen;
});
window.addEventListener(EVENT_NOTIFICATION_TOGGLE, () => {
this.notificationOpen = !this.notificationOpen;
});
}
renderSidebarItems(): TemplateResult {
return html``;
}
render(): TemplateResult {
return html`
${until(new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve().then(version => {
if (version.versionCurrent !== VERSION) {
return html`<ak-banner>
${t`A newer version of the frontend is available.`}
<button @click=${() => { window.location.reload(true); }}>
${t`Reload`}
</button>
</ak-banner>`;
}
return html``;
}))}
${until(me().then((u) => {
if (u.original) {
return html`<ak-banner>
${t`You're currently impersonating ${u.user.username}.`}
<a href=${`/-/impersonation/end/?back=${window.location.pathname}%23${window.location.hash}`}>
${t`Stop impersonation`}
</a>
</ak-banner>`;
}
return html``;
}))}
<div class="pf-c-page">
<ak-sidebar class="pf-c-page__sidebar ${this.sidebarOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
${this.renderSidebarItems()}
</ak-sidebar>
<div class="pf-c-page__drawer">
<div class="pf-c-drawer ${this.notificationOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
<div class="pf-c-drawer__main">
<div class="pf-c-drawer__content">
<div class="pf-c-drawer__body">
<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>
</main>
</div>
</div>
<ak-notification-drawer class="pf-c-drawer__panel pf-m-width-33">
</ak-notification-drawer>
</div>
</div>
</div>
</div>`;
}
}

View File

@ -32,7 +32,7 @@ msgstr "6 digits, widely compatible"
msgid "8 digits, not compatible with apps like Google Authenticator" msgid "8 digits, not compatible with apps like Google Authenticator"
msgstr "8 digits, not compatible with apps like Google Authenticator" msgstr "8 digits, not compatible with apps like Google Authenticator"
#: src/interfaces/Interface.ts #: src/interfaces/AdminInterface.ts
msgid "A newer version of the frontend is available." msgid "A newer version of the frontend is available."
msgstr "A newer version of the frontend is available." msgstr "A newer version of the frontend is available."
@ -674,11 +674,14 @@ msgstr "Configuration"
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts #: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts #: src/pages/stages/password/PasswordStageForm.ts
msgid "Configuration flow" msgid "Configuration flow"
msgstr "Configuration flow" msgstr "Configuration flow"
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Configuration stage"
msgstr "Configuration stage"
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts #: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
msgid "Configure WebAuthn" msgid "Configure WebAuthn"
msgstr "Configure WebAuthn" msgstr "Configure WebAuthn"
@ -1283,10 +1286,6 @@ msgstr "Email address"
msgid "Email info:" msgid "Email info:"
msgstr "Email info:" msgstr "Email info:"
#: src/flows/stages/identification/IdentificationStage.ts
msgid "Email or username"
msgstr "Email or username"
#: src/pages/stages/prompt/PromptForm.ts #: src/pages/stages/prompt/PromptForm.ts
msgid "Email: Text field with Email type." msgid "Email: Text field with Email type."
msgstr "Email: Text field with Email type." msgstr "Email: Text field with Email type."
@ -1392,6 +1391,7 @@ msgstr "Event {0}"
msgid "Events" msgid "Events"
msgstr "Events" msgstr "Events"
#: src/pages/events/EventInfo.ts
#: src/pages/events/EventInfo.ts #: src/pages/events/EventInfo.ts
#: src/pages/events/EventInfo.ts #: src/pages/events/EventInfo.ts
msgid "Exception" msgid "Exception"
@ -2498,6 +2498,10 @@ msgstr "Only send notification once, for example when sending a webhook into a c
msgid "Open application" msgid "Open application"
msgstr "Open application" msgstr "Open application"
#: src/pages/events/EventInfo.ts
msgid "Open issue on GitHub..."
msgstr "Open issue on GitHub..."
#: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts #: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts
msgid "OpenID Configuration Issuer" msgid "OpenID Configuration Issuer"
msgstr "OpenID Configuration Issuer" msgstr "OpenID Configuration Issuer"
@ -2928,10 +2932,6 @@ msgstr "Regular expressions for which authentication is not required. Each new l
msgid "Related" msgid "Related"
msgstr "Related" msgstr "Related"
#: src/interfaces/Interface.ts
msgid "Reload"
msgstr "Reload"
#: src/pages/stages/user_logout/UserLogoutStageForm.ts #: src/pages/stages/user_logout/UserLogoutStageForm.ts
msgid "Remove the user from the current session." msgid "Remove the user from the current session."
msgstr "Remove the user from the current session." msgstr "Remove the user from the current session."
@ -3409,10 +3409,6 @@ msgstr "Status: Disabled"
msgid "Status: Enabled" msgid "Status: Enabled"
msgstr "Status: Enabled" msgstr "Status: Enabled"
#: src/interfaces/Interface.ts
msgid "Stop impersonation"
msgstr "Stop impersonation"
#: src/pages/events/EventInfo.ts #: src/pages/events/EventInfo.ts
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "Subject" msgid "Subject"
@ -3772,6 +3768,7 @@ msgstr "Task finished with warnings"
msgid "Template" msgid "Template"
msgstr "Template" msgstr "Template"
#: src/pages/events/EventListPage.ts
#: src/pages/tenants/TenantListPage.ts #: src/pages/tenants/TenantListPage.ts
msgid "Tenant" msgid "Tenant"
msgstr "Tenant" msgstr "Tenant"
@ -3981,6 +3978,11 @@ msgstr "UI settings"
msgid "UID" msgid "UID"
msgstr "UID" msgstr "UID"
#: src/flows/stages/identification/IdentificationStage.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
msgid "UPN"
msgstr "UPN"
#: src/pages/sources/oauth/OAuthSourceForm.ts #: src/pages/sources/oauth/OAuthSourceForm.ts
msgid "URL settings" msgid "URL settings"
msgstr "URL settings" msgstr "URL settings"
@ -4517,9 +4519,9 @@ msgstr "Yes"
msgid "You can only select providers that match the type of the outpost." msgid "You can only select providers that match the type of the outpost."
msgstr "You can only select providers that match the type of the outpost." msgstr "You can only select providers that match the type of the outpost."
#: src/interfaces/Interface.ts #: src/interfaces/AdminInterface.ts
msgid "You're currently impersonating {0}." msgid "You're currently impersonating {0}. Click to stop."
msgstr "You're currently impersonating {0}." msgstr "You're currently impersonating {0}. Click to stop."
#: src/pages/stages/password/PasswordStageForm.ts #: src/pages/stages/password/PasswordStageForm.ts
msgid "authentik Builtin Database" msgid "authentik Builtin Database"
@ -4529,6 +4531,10 @@ msgstr "authentik Builtin Database"
msgid "authentik LDAP Backend" msgid "authentik LDAP Backend"
msgstr "authentik LDAP Backend" msgstr "authentik LDAP Backend"
#: src/elements/forms/DeleteForm.ts
msgid "connecting object will be deleted"
msgstr "connecting object will be deleted"
#: src/elements/Tabs.ts #: src/elements/Tabs.ts
msgid "no tabs defined" msgid "no tabs defined"
msgstr "no tabs defined" msgstr "no tabs defined"

View File

@ -669,10 +669,13 @@ msgstr ""
#: #:
#: #:
#: #:
#:
msgid "Configuration flow" msgid "Configuration flow"
msgstr "" msgstr ""
#:
msgid "Configuration stage"
msgstr ""
#: #:
msgid "Configure WebAuthn" msgid "Configure WebAuthn"
msgstr "" msgstr ""
@ -1275,10 +1278,6 @@ msgstr ""
msgid "Email info:" msgid "Email info:"
msgstr "" msgstr ""
#:
msgid "Email or username"
msgstr ""
#: #:
msgid "Email: Text field with Email type." msgid "Email: Text field with Email type."
msgstr "" msgstr ""
@ -1384,6 +1383,7 @@ msgstr ""
msgid "Events" msgid "Events"
msgstr "" msgstr ""
#:
#: #:
#: #:
msgid "Exception" msgid "Exception"
@ -2490,6 +2490,10 @@ msgstr ""
msgid "Open application" msgid "Open application"
msgstr "" msgstr ""
#:
msgid "Open issue on GitHub..."
msgstr ""
#: #:
msgid "OpenID Configuration Issuer" msgid "OpenID Configuration Issuer"
msgstr "" msgstr ""
@ -2920,10 +2924,6 @@ msgstr ""
msgid "Related" msgid "Related"
msgstr "" msgstr ""
#:
msgid "Reload"
msgstr ""
#: #:
msgid "Remove the user from the current session." msgid "Remove the user from the current session."
msgstr "" msgstr ""
@ -3401,10 +3401,6 @@ msgstr ""
msgid "Status: Enabled" msgid "Status: Enabled"
msgstr "" msgstr ""
#:
msgid "Stop impersonation"
msgstr ""
#: #:
#: #:
msgid "Subject" msgid "Subject"
@ -3764,6 +3760,7 @@ msgstr ""
msgid "Template" msgid "Template"
msgstr "" msgstr ""
#:
#: #:
msgid "Tenant" msgid "Tenant"
msgstr "" msgstr ""
@ -3969,6 +3966,11 @@ msgstr ""
msgid "UID" msgid "UID"
msgstr "" msgstr ""
#:
#:
msgid "UPN"
msgstr ""
#: #:
msgid "URL settings" msgid "URL settings"
msgstr "" msgstr ""
@ -4504,7 +4506,7 @@ msgid "You can only select providers that match the type of the outpost."
msgstr "" msgstr ""
#: #:
msgid "You're currently impersonating {0}." msgid "You're currently impersonating {0}. Click to stop."
msgstr "" msgstr ""
#: #:
@ -4515,6 +4517,10 @@ msgstr ""
msgid "authentik LDAP Backend" msgid "authentik LDAP Backend"
msgstr "" msgstr ""
#:
msgid "connecting object will be deleted"
msgstr ""
#: #:
msgid "no tabs defined" msgid "no tabs defined"
msgstr "" msgstr ""

View File

@ -34,7 +34,7 @@ export class UserCountStatusChart extends AKChart<UserMetrics> {
pageSize: 1 pageSize: 1
})).pagination.count; })).pagination.count;
const superusers = (await api.coreUsersList({ const superusers = (await api.coreUsersList({
isSuperuser: "true" isSuperuser: true
})).pagination.count; })).pagination.count;
this.centerText = count.toString(); this.centerText = count.toString();
return { return {

View File

@ -1,4 +1,4 @@
import { Application, CoreApi, CheckAccessRequestRequest, PolicyTestResult } from "authentik-api"; import { Application, CoreApi, PolicyTestResult } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement, property } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
@ -8,7 +8,7 @@ import { until } from "lit-html/directives/until";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
@customElement("ak-application-check-access-form") @customElement("ak-application-check-access-form")
export class ApplicationCheckAccessForm extends Form<CheckAccessRequestRequest> { export class ApplicationCheckAccessForm extends Form<number> {
@property({attribute: false}) @property({attribute: false})
application!: Application; application!: Application;
@ -17,17 +17,17 @@ export class ApplicationCheckAccessForm extends Form<CheckAccessRequestRequest>
result?: PolicyTestResult; result?: PolicyTestResult;
@property({ attribute: false}) @property({ attribute: false})
request?: CheckAccessRequestRequest; request?: number;
getSuccessMessage(): string { getSuccessMessage(): string {
return t`Successfully sent test-request.`; return t`Successfully sent test-request.`;
} }
send = (data: CheckAccessRequestRequest): Promise<PolicyTestResult> => { send = (data: number): Promise<PolicyTestResult> => {
this.request = data; this.request = data;
return new CoreApi(DEFAULT_CONFIG).coreApplicationsCheckAccessCreate({ return new CoreApi(DEFAULT_CONFIG).coreApplicationsCheckAccessRetrieve({
slug: this.application?.slug, slug: this.application?.slug,
checkAccessRequestRequest: data, forUser: data,
}).then(result => this.result = result); }).then(result => this.result = result);
}; };
@ -68,7 +68,7 @@ export class ApplicationCheckAccessForm extends Form<CheckAccessRequestRequest>
ordering: "username", ordering: "username",
}).then(users => { }).then(users => {
return users.results.map(user => { return users.results.map(user => {
return html`<option ?selected=${user.pk.toString() === this.request?.forUser?.toString()} value=${user.pk}>${user.username}</option>`; return html`<option ?selected=${user.pk.toString() === this.request?.toString()} value=${user.pk}>${user.username}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>

View File

@ -5,13 +5,14 @@ import { EventMatcherPolicyActionEnum, FlowsApi } from "authentik-api";
import "../../elements/Spinner"; import "../../elements/Spinner";
import "../../elements/Expand"; import "../../elements/Expand";
import { PFSize } from "../../elements/Spinner"; import { PFSize } from "../../elements/Spinner";
import { EventContext, EventWithContext } from "../../api/Events"; import { EventContext, EventModel, EventWithContext } from "../../api/Events";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css"; import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFList from "@patternfly/patternfly/components/List/list.css"; import PFList from "@patternfly/patternfly/components/List/list.css";
import { VERSION } from "../../constants";
@customElement("ak-event-info") @customElement("ak-event-info")
export class EventInfo extends LitElement { export class EventInfo extends LitElement {
@ -20,7 +21,7 @@ export class EventInfo extends LitElement {
event!: EventWithContext; event!: EventWithContext;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [PFBase, PFFlex, PFList, PFDescriptionList, return [PFBase, PFButton, PFFlex, PFList, PFDescriptionList,
css` css`
code { code {
display: block; display: block;
@ -40,7 +41,7 @@ export class EventInfo extends LitElement {
]; ];
} }
getModelInfo(context: EventContext): TemplateResult { getModelInfo(context: EventModel): TemplateResult {
if (context === null) { if (context === null) {
return html`<span>-</span>`; return html`<span>-</span>`;
} }
@ -50,7 +51,7 @@ export class EventInfo extends LitElement {
<span class="pf-c-description-list__text">${t`UID`}</span> <span class="pf-c-description-list__text">${t`UID`}</span>
</dt> </dt>
<dd class="pf-c-description-list__description"> <dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">${context.pk as string}</div> <div class="pf-c-description-list__text">${context.pk}</div>
</dd> </dd>
</div> </div>
<div class="pf-c-description-list__group"> <div class="pf-c-description-list__group">
@ -58,7 +59,7 @@ export class EventInfo extends LitElement {
<span class="pf-c-description-list__text">${t`Name`}</span> <span class="pf-c-description-list__text">${t`Name`}</span>
</dt> </dt>
<dd class="pf-c-description-list__description"> <dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">${context.name as string}</div> <div class="pf-c-description-list__text">${context.name}</div>
</dd> </dd>
</div> </div>
<div class="pf-c-description-list__group"> <div class="pf-c-description-list__group">
@ -66,7 +67,7 @@ export class EventInfo extends LitElement {
<span class="pf-c-description-list__text">${t`App`}</span> <span class="pf-c-description-list__text">${t`App`}</span>
</dt> </dt>
<dd class="pf-c-description-list__description"> <dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">${context.app as string}</div> <div class="pf-c-description-list__text">${context.app}</div>
</dd> </dd>
</div> </div>
<div class="pf-c-description-list__group"> <div class="pf-c-description-list__group">
@ -74,7 +75,7 @@ export class EventInfo extends LitElement {
<span class="pf-c-description-list__text">${t`Model Name`}</span> <span class="pf-c-description-list__text">${t`Model Name`}</span>
</dt> </dt>
<dd class="pf-c-description-list__description"> <dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">${context.model_name as string}</div> <div class="pf-c-description-list__text">${context.model_name}</div>
</dd> </dd>
</div> </div>
</dl>`; </dl>`;
@ -137,6 +138,52 @@ export class EventInfo extends LitElement {
</div>`; </div>`;
} }
buildGitHubIssueUrl(context: EventContext): string {
const httpRequest = this.event.context.http_request as EventContext;
let title = "";
if (httpRequest) {
title = `${httpRequest?.method} ${httpRequest?.path}`;
}
// https://docs.github.com/en/issues/tracking-your-work-with-issues/creating-issues/about-automation-for-issues-and-pull-requests-with-query-parameters
const fullBody = `
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Logs**
<details>
<summary>Stacktrace from authentik</summary>
\`\`\`
${context.message as string}
\`\`\`
</details>
**Version and Deployment (please complete the following information):**
- authentik version: ${VERSION}
- Deployment: [e.g. docker-compose, helm]
**Additional context**
Add any other context about the problem here.
`;
return `https://github.com/goauthentik/authentik/issues/
new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
&body=${encodeURIComponent(fullBody)}`.trim();
}
render(): TemplateResult { render(): TemplateResult {
if (!this.event) { if (!this.event) {
return html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`; return html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`;
@ -147,13 +194,13 @@ export class EventInfo extends LitElement {
case EventMatcherPolicyActionEnum.ModelDeleted: case EventMatcherPolicyActionEnum.ModelDeleted:
return html` return html`
<h3>${t`Affected model:`}</h3> <h3>${t`Affected model:`}</h3>
${this.getModelInfo(this.event.context?.model as EventContext)} ${this.getModelInfo(this.event.context?.model as EventModel)}
`; `;
case EventMatcherPolicyActionEnum.AuthorizeApplication: case EventMatcherPolicyActionEnum.AuthorizeApplication:
return html`<div class="pf-l-flex"> return html`<div class="pf-l-flex">
<div class="pf-l-flex__item"> <div class="pf-l-flex__item">
<h3>${t`Authorized application:`}</h3> <h3>${t`Authorized application:`}</h3>
${this.getModelInfo(this.event.context.authorized_application as EventContext)} ${this.getModelInfo(this.event.context.authorized_application as EventModel)}
</div> </div>
<div class="pf-l-flex__item"> <div class="pf-l-flex__item">
<h3>${t`Using flow`}</h3> <h3>${t`Using flow`}</h3>
@ -175,7 +222,24 @@ export class EventInfo extends LitElement {
case EventMatcherPolicyActionEnum.SecretView: case EventMatcherPolicyActionEnum.SecretView:
return html` return html`
<h3>${t`Secret:`}</h3> <h3>${t`Secret:`}</h3>
${this.getModelInfo(this.event.context.secret as EventContext)}`; ${this.getModelInfo(this.event.context.secret as EventModel)}`;
case EventMatcherPolicyActionEnum.SystemException:
return html`
<a
class="pf-c-button pf-m-primary"
target="_blank"
href=${this.buildGitHubIssueUrl(
this.event.context
)}>
${t`Open issue on GitHub...`}
</a>
<div class="pf-l-flex">
<div class="pf-l-flex__item">
<h3>${t`Exception`}</h3>
<code>${this.event.context.message}</code>
</div>
</div>
<ak-expand>${this.defaultResponse()}</ak-expand>`;
case EventMatcherPolicyActionEnum.PropertyMappingException: case EventMatcherPolicyActionEnum.PropertyMappingException:
return html`<div class="pf-l-flex"> return html`<div class="pf-l-flex">
<div class="pf-l-flex__item"> <div class="pf-l-flex__item">
@ -192,12 +256,12 @@ export class EventInfo extends LitElement {
return html`<div class="pf-l-flex"> return html`<div class="pf-l-flex">
<div class="pf-l-flex__item"> <div class="pf-l-flex__item">
<h3>${t`Binding`}</h3> <h3>${t`Binding`}</h3>
${this.getModelInfo(this.event.context.binding as EventContext)} ${this.getModelInfo(this.event.context.binding as EventModel)}
</div> </div>
<div class="pf-l-flex__item"> <div class="pf-l-flex__item">
<h3>${t`Request`}</h3> <h3>${t`Request`}</h3>
<ul class="pf-c-list"> <ul class="pf-c-list">
<li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventContext)}</li> <li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventModel)}</li>
<li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li> <li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li>
</ul> </ul>
</div> </div>
@ -211,12 +275,12 @@ export class EventInfo extends LitElement {
return html`<div class="pf-l-flex"> return html`<div class="pf-l-flex">
<div class="pf-l-flex__item"> <div class="pf-l-flex__item">
<h3>${t`Binding`}</h3> <h3>${t`Binding`}</h3>
${this.getModelInfo(this.event.context.binding as EventContext)} ${this.getModelInfo(this.event.context.binding as EventModel)}
</div> </div>
<div class="pf-l-flex__item"> <div class="pf-l-flex__item">
<h3>${t`Request`}</h3> <h3>${t`Request`}</h3>
<ul class="pf-c-list"> <ul class="pf-c-list">
<li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventContext)}</li> <li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventModel)}</li>
<li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li> <li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li>
</ul> </ul>
</div> </div>
@ -252,7 +316,7 @@ export class EventInfo extends LitElement {
return html`<div class="pf-l-flex"> return html`<div class="pf-l-flex">
<div class="pf-l-flex__item"> <div class="pf-l-flex__item">
<h3>${t`Using source`}</h3> <h3>${t`Using source`}</h3>
${this.getModelInfo(this.event.context.using_source as EventContext)} ${this.getModelInfo(this.event.context.using_source as EventModel)}
</div> </div>
</div>`; </div>`;
} }

View File

@ -44,6 +44,7 @@ export class EventListPage extends TablePage<Event> {
new TableColumn(t`User`, "user"), new TableColumn(t`User`, "user"),
new TableColumn(t`Creation Date`, "created"), new TableColumn(t`Creation Date`, "created"),
new TableColumn(t`Client IP`, "client_ip"), new TableColumn(t`Client IP`, "client_ip"),
new TableColumn(t`Tenant`, "tenant_name"),
new TableColumn(""), new TableColumn(""),
]; ];
} }
@ -62,6 +63,7 @@ export class EventListPage extends TablePage<Event> {
html`-`, html`-`,
html`<span>${item.created?.toLocaleString()}</span>`, html`<span>${item.created?.toLocaleString()}</span>`,
html`<span>${item.clientIp || "-"}</span>`, html`<span>${item.clientIp || "-"}</span>`,
html`<span>${item.tenant?.name || "-"}</span>`,
html`<a href="#/events/log/${item.pk}"> html`<a href="#/events/log/${item.pk}">
<i class="fas fas fa-share-square"></i> <i class="fas fas fa-share-square"></i>
</a>`, </a>`,

View File

@ -103,7 +103,7 @@ export class FlowListPage extends TablePage<Flow> {
}}> }}>
${t`Execute`} ${t`Execute`}
</button> </button>
<a class="pf-c-button pf-m-secondary" href=${flow.exportUrl}> <a class="pf-c-button pf-m-secondary" href=${item.exportUrl}>
${t`Export`} ${t`Export`}
</a>`, </a>`,
]; ];

View File

@ -102,7 +102,11 @@ export class OutpostForm extends ModelForm<Outpost, string> {
ordering: "pk" ordering: "pk"
}).then(scs => { }).then(scs => {
return scs.results.map(sc => { return scs.results.map(sc => {
return html`<option value=${ifDefined(sc.pk)} ?selected=${this.instance?.serviceConnection === sc.pk}> let selected = this.instance?.serviceConnection === sc.pk;
if (scs.results.length === 1 && !this.instance) {
selected = true;
}
return html`<option value=${ifDefined(sc.pk)} ?selected=${selected}>
${sc.name} (${sc.verboseName}) ${sc.name} (${sc.verboseName})
</option>`; </option>`;
}); });
@ -134,6 +138,9 @@ export class OutpostForm extends ModelForm<Outpost, string> {
return YAML.stringify(fc); return YAML.stringify(fc);
}))}"></ak-codemirror> }))}"></ak-codemirror>
<p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p> <p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p>
<p class="pf-c-form__helper-text">
See <a target="_blank" href="https://goauthentik.io/docs/outposts/outposts#configuration">documentation</a>.
</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</form>`; </form>`;
} }

View File

@ -186,7 +186,7 @@ export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
<option value="" ?selected=${this.instance?.rsaKey === undefined}>---------</option> <option value="" ?selected=${this.instance?.rsaKey === undefined}>---------</option>
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({ ${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
ordering: "pk", ordering: "pk",
hasKey: "true", hasKey: true,
}).then(keys => { }).then(keys => {
return keys.results.map(key => { return keys.results.map(key => {
let selected = this.instance?.rsaKey === key.pk; let selected = this.instance?.rsaKey === key.pk;

View File

@ -218,7 +218,7 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
<option value="" ?selected=${this.instance?.certificate === undefined}>---------</option> <option value="" ?selected=${this.instance?.certificate === undefined}>---------</option>
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({ ${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
ordering: "pk", ordering: "pk",
hasKey: "true", hasKey: true,
}).then(keys => { }).then(keys => {
return keys.results.map(key => { return keys.results.map(key => {
return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.certificate === key.pk}>${key.name}</option>`; return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.certificate === key.pk}>${key.name}</option>`;

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