Compare commits

...

63 Commits

Author SHA1 Message Date
3665e2fefa release: 2021.5.1-rc8 2021-05-12 14:52:34 +02:00
3dbe35cf9e stages/invitation: fix wrong serializer used for user model
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

# Conflicts:
#	swagger.yaml
2021-05-12 14:22:16 +02:00
c7f0ea8a4b root: update dbbackup to git version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 01:20:31 +02:00
0620324702 root: bump version of psf black
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-12 00:42:46 +02:00
5a802bcf83 web/admin: fix list of outpost status
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 22:59:45 +02:00
00c8054893 web/admin: fix border on dark mode in firefox
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 22:27:33 +02:00
dc2538f59d web/admin: fix outpost health not updating on refresh
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 21:53:19 +02:00
5a0e78c698 outposts: fix issue with duplicate outpost health
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 21:46:30 +02:00
fd4e8a59f4 web/admin: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 20:09:49 +02:00
dd1a6a81c8 outposts/proxy: improve host header detection
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 20:02:36 +02:00
84dfbcaaae providers/api: return redirect_uris for proxy provider
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 20:02:17 +02:00
e649e9fb03 core: don't use self.get_object for application permission check to prevent 404 when view permission is missing
closes #864

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 17:35:11 +02:00
266ef66a6f Merge branch 'master' into next 2021-05-11 14:57:52 +02:00
842fdb0b0c fixed session durations of more than 1 day (#863) 2021-05-11 14:57:33 +02:00
a270a84aae website/docs: update link for saml provider metadata
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#857
2021-05-11 14:23:39 +02:00
36f7cad23b Merge pull request #862 from goauthentik/form-refresh-on-save
Form refresh on save
2021-05-11 14:23:32 +02:00
e441ac1e43 web/admin: add download links for certificates
closes #861

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 14:21:48 +02:00
24f2932777 crypto: add ?download flag
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#861
2021-05-11 14:21:35 +02:00
a6c6f22221 web/admin: add button to copy saml metadata download link
closes #857

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:52:47 +02:00
abd5db8ad4 website/docs: update link for saml provider metadata
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#857
2021-05-11 13:44:51 +02:00
124ce80694 sources/plex: make plex_token readable from API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:32:28 +02:00
4352960f83 web/admin: fix error when updating oauth source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:31:33 +02:00
4e2443d60b flows: make cancel link always logout user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:13:05 +02:00
34a8408a4f Merge branch 'next' into form-refresh-on-save 2021-05-11 13:07:57 +02:00
17b65adcc5 lib: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 13:07:47 +02:00
6f8d129dea web/admin: migrate remaining forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:44:50 +02:00
59f339beda web/admin: migrate stage forms to ModelForm
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:35:53 +02:00
ce1c400022 web/admin: migrate policy forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:19:35 +02:00
c99afe0ad4 web/admin: remove unused imports
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:12:31 +02:00
ff9ff18c11 web/admin: migrate more forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 12:05:30 +02:00
4d11d82c6e web/admin: migrate more forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 11:55:25 +02:00
b4d750174f web/admin: add modelform as base, start migrating
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 11:48:34 +02:00
fd44765ff4 Merge branch 'next' into form-refresh-on-save 2021-05-11 11:47:29 +02:00
190ebb27e4 Merge branch 'master' into next 2021-05-11 11:47:10 +02:00
fb3c04d0c7 build(deps): bump postcss from 8.2.14 to 8.2.15 in /website (#858) 2021-05-11 10:46:06 +02:00
3ba8de61e0 build(deps): bump eslint-plugin-lit from 1.3.0 to 1.4.0 in /web (#859) 2021-05-11 10:45:46 +02:00
d4d2be84a3 build(deps): bump boto3 from 1.17.69 to 1.17.70 (#860) 2021-05-11 10:45:33 +02:00
96ea7ae09c root: allow configuration of s3 backup location
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 02:10:00 +02:00
172bfceb31 root: fix db backup failing when password has special chars
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 02:01:22 +02:00
932b19999e providers/proxy: missing @property for noop
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 01:26:01 +02:00
0f1cc86e71 outposts/ak: updater providers automatically every 150 seconds
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 01:07:26 +02:00
788fd00390 outposts: use noop flag in each reconciler instead of raising Disabled and force use of get_referecen_object
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 00:27:29 +02:00
f602e202b8 website/docs: use beryju.org directly for beta
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-11 00:11:42 +02:00
9b60fcb08b root: only install latest postgresql client, since they are backwards compatible
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 23:24:27 +02:00
a293a14f2a outposts: re-add _config for backwards compat
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 22:28:46 +02:00
65bfa589eb Merge branch 'master' into next 2021-05-10 20:35:11 +02:00
defca51d24 build(deps): bump @sentry/browser from 6.3.5 to 6.3.6 in /web (#855)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.3.5 to 6.3.6.
- [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.3.5...6.3.6)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:34:09 +02:00
d862028134 build(deps): bump @typescript-eslint/eslint-plugin in /web (#856)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.22.1 to 4.23.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.23.0/packages/eslint-plugin)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:33:59 +02:00
c19d7c37aa build(deps): bump @sentry/tracing from 6.3.5 to 6.3.6 in /web (#853)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.3.5 to 6.3.6.
- [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.3.5...6.3.6)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:30:55 +02:00
6fb3102d25 build(deps): bump @typescript-eslint/parser in /web (#854)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.22.1 to 4.23.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.23.0/packages/parser)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 20:30:41 +02:00
51e3453dca admin: fix linting in api tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 20:14:21 +02:00
6f58fdf158 api: add more tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:51:29 +02:00
5d4051f547 ci: test and lint at the same time
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:36:28 +02:00
219b8d1a57 outposts: allow individual components of managed outposts to be disabled
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:27:48 +02:00
c7d4e69669 root: make database port configurable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:25:15 +02:00
cd629dfbaa outposts: improve API validation for config attribute, ensure all required attributes are set
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 19:24:42 +02:00
8eaaaae2a7 outpost: add trace log level
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 18:09:52 +02:00
3d0a853449 Merge branch 'version-2021.5' into next 2021-05-10 18:07:39 +02:00
c2f8ff55cf outposts: fix outpost delete hanging thread, run cleanup in async task with info from cache with ability to retry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 17:11:31 +02:00
4b52697cfe web/elements: add refresh support to chart
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 15:57:52 +02:00
80fae44f47 release: 2021.5.1-rc7 2021-05-10 12:13:10 +02:00
afd7af557d ci: login to ghcr
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-10 12:13:03 +02:00
d4493c0ee9 web/admin: add new base form to handle refresh events
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-05-09 12:59:00 +02:00
143 changed files with 3339 additions and 3336 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 2021.5.1-rc6 current_version = 2021.5.1-rc8
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

@ -22,6 +22,12 @@ jobs:
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: prepare ts api client - name: prepare ts api client
run: | run: |
docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/api --additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0 docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/api --additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0
@ -30,9 +36,9 @@ jobs:
with: with:
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name == 'release' }}
tags: | tags: |
beryju/authentik:2021.5.1-rc6, beryju/authentik:2021.5.1-rc8,
beryju/authentik:latest, beryju/authentik:latest,
ghcr.io/goauthentik/server:2021.5.1-rc6, ghcr.io/goauthentik/server:2021.5.1-rc8,
ghcr.io/goauthentik/server:latest ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
context: . context: .
@ -58,14 +64,20 @@ jobs:
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Building Docker Image - name: Building Docker Image
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name == 'release' }}
tags: | tags: |
beryju/authentik-proxy:2021.5.1-rc6, beryju/authentik-proxy:2021.5.1-rc8,
beryju/authentik-proxy:latest, beryju/authentik-proxy:latest,
ghcr.io/goauthentik/proxy:2021.5.1-rc6, ghcr.io/goauthentik/proxy:2021.5.1-rc8,
ghcr.io/goauthentik/proxy:latest ghcr.io/goauthentik/proxy:latest
context: outpost/ context: outpost/
file: outpost/proxy.Dockerfile file: outpost/proxy.Dockerfile
@ -92,14 +104,20 @@ jobs:
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Building Docker Image - name: Building Docker Image
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name == 'release' }}
tags: | tags: |
beryju/authentik-ldap:2021.5.1-rc6, beryju/authentik-ldap:2021.5.1-rc8,
beryju/authentik-ldap:latest, beryju/authentik-ldap:latest,
ghcr.io/goauthentik/ldap:2021.5.1-rc6, ghcr.io/goauthentik/ldap:2021.5.1-rc8,
ghcr.io/goauthentik/ldap:latest ghcr.io/goauthentik/ldap:latest
context: outpost/ context: outpost/
file: outpost/ldap.Dockerfile file: outpost/ldap.Dockerfile
@ -137,5 +155,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.5.1-rc6 version: authentik@2021.5.1-rc8
environment: beryjuorg-prod environment: beryjuorg-prod

View File

@ -48,13 +48,13 @@ ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y --no-install-recommends curl ca-certificates gnupg && \ apt-get install -y --no-install-recommends curl ca-certificates gnupg git && \
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \ curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \ echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
apt-get update && \ apt-get update && \
apt-get install -y --no-install-recommends libpq-dev postgresql-client-12 postgresql-client-11 build-essential libxmlsec1-dev pkg-config libmaxminddb0 && \ apt-get install -y --no-install-recommends libpq-dev postgresql-client build-essential libxmlsec1-dev pkg-config libmaxminddb0 && \
pip install -r /requirements.txt --no-cache-dir && \ pip install -r /requirements.txt --no-cache-dir && \
apt-get remove --purge -y build-essential && \ apt-get remove --purge -y build-essential git && \
apt-get autoremove --purge -y && \ apt-get autoremove --purge -y && \
apt-get clean && \ apt-get clean && \
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \ rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \

View File

@ -1,3 +1,6 @@
.SHELLFLAGS += -x -e
PWD = $(shell pwd)
all: lint-fix lint test gen all: lint-fix lint test gen
test-integration: test-integration:
@ -24,6 +27,14 @@ lint:
gen: gen:
./manage.py generate_swagger -o swagger.yaml -f yaml ./manage.py generate_swagger -o swagger.yaml -f yaml
docker run \
--rm -v ${PWD}:/local \
openapitools/openapi-generator-cli generate \
-i /local/swagger.yaml \
-g typescript-fetch \
-o /local/web/api \
--additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0
cd web/api && npx tsc
local-stack: local-stack:
export AUTHENTIK_TAG=testing export AUTHENTIK_TAG=testing

View File

@ -11,7 +11,7 @@ channels-redis = "*"
dacite = "*" dacite = "*"
defusedxml = "*" defusedxml = "*"
django = "*" django = "*"
django-dbbackup = "*" django-dbbackup = { git = 'https://github.com/django-dbbackup/django-dbbackup.git', ref = '9d1909c30a3271c8c9c8450add30d6e0b996e145' }
django-filter = "*" django-filter = "*"
django-guardian = "*" django-guardian = "*"
django-model-utils = "*" django-model-utils = "*"
@ -50,7 +50,7 @@ python_version = "3.9"
[dev-packages] [dev-packages]
bandit = "*" bandit = "*"
black = "==20.8b1" black = "==21.5b1"
bump2version = "*" bump2version = "*"
colorama = "*" colorama = "*"
coverage = "*" coverage = "*"

258
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "17be2923cf8d281e430ec1467aea723806ac6f7c58fc6553ede92317e43f4d14" "sha256": "8a32708c1c04f8da03c817df973de28c37c97ee773f571ce0b3f3f834e1b7094"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -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,24 +122,26 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:2f0d76660d484ff4c8c2efe9171c1281b38681e6806f87cf100e822432eda11e", "sha256:bcb1b76ca5a60586181ad202b19f4c50cb7c22ac68cae20f997abe311e22bf2a",
"sha256:cbaa8df5faf81730f117bfa0e3fcda68ec3fa9449a05847aa6140a3f4c087765" "sha256:edf9b3b36e08cd575a9458bf59871852335aceb5db2d07bfc8530bae3a97d045"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.17.69" "version": "==1.17.71"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:7e94d3777763ece33d282b437e3b05b5567b9af816bd7819dbe4eb9bc6db6082", "sha256:414e1721d381095767db1cf673257fdfec639da3be9405a41d49cc859b817d68",
"sha256:f755b19ddebda0f8ab7afc75ebcb5412dd802eca0a7e670f5fff8c5e58bc88b1" "sha256:b7afebca1fd6ca1f8af79f377a445d474e3bd2cf88e704169d6713a6362a304f"
], ],
"version": "==1.20.69" "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.71"
}, },
"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": {
@ -220,6 +228,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": {
@ -227,6 +236,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": {
@ -300,6 +310,7 @@
"sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f", "sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f",
"sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393" "sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393"
], ],
"markers": "python_version >= '3.6'",
"version": "==3.0.2" "version": "==3.0.2"
}, },
"defusedxml": { "defusedxml": {
@ -319,11 +330,11 @@
"version": "==3.2.2" "version": "==3.2.2"
}, },
"django-dbbackup": { "django-dbbackup": {
"git": "https://github.com/django-dbbackup/django-dbbackup.git",
"hashes": [ "hashes": [
"sha256:bb109735cae98b64ad084e5b461b7aca2d7b39992f10c9ed9435e3ebb6fb76c8" "sha256:bb109735cae98b64ad084e5b461b7aca2d7b39992f10c9ed9435e3ebb6fb76c8"
], ],
"index": "pypi", "ref": "9d1909c30a3271c8c9c8450add30d6e0b996e145"
"version": "==3.3.0"
}, },
"django-filter": { "django-filter": {
"hashes": [ "hashes": [
@ -425,6 +436,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": {
@ -440,6 +452,7 @@
"sha256:588bdb03a41ecb4978472b847881e5518b5d9ec6153d3d679aa127a55e13b39f", "sha256:588bdb03a41ecb4978472b847881e5518b5d9ec6153d3d679aa127a55e13b39f",
"sha256:9ad25fba07f46a628ad4d0ca09f38dcb262830df2ac95b217f9b0129c9e42206" "sha256:9ad25fba07f46a628ad4d0ca09f38dcb262830df2ac95b217f9b0129c9e42206"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.30.0" "version": "==1.30.0"
}, },
"gunicorn": { "gunicorn": {
@ -455,6 +468,7 @@
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
], ],
"markers": "python_version >= '3.6'",
"version": "==0.12.0" "version": "==0.12.0"
}, },
"hiredis": { "hiredis": {
@ -501,6 +515,7 @@
"sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0", "sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0",
"sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a" "sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a"
], ],
"markers": "python_version >= '3.6'",
"version": "==2.0.0" "version": "==2.0.0"
}, },
"httptools": { "httptools": {
@ -549,6 +564,7 @@
"sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417", "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417",
"sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2" "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"
], ],
"markers": "python_version >= '3.5'",
"version": "==0.5.1" "version": "==0.5.1"
}, },
"itypes": { "itypes": {
@ -560,16 +576,18 @@
}, },
"jinja2": { "jinja2": {
"hashes": [ "hashes": [
"sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", "sha256:2f2de5285cf37f33d33ecd4a9080b75c87cd0c1994d5a9c6df17131ea1f049c6",
"sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" "sha256:ea8d7dd814ce9df6de6a761ec7f1cac98afe305b8cdc4aaae4e114b8d8ce24c5"
], ],
"version": "==2.11.3" "markers": "python_version >= '3.6'",
"version": "==3.0.0"
}, },
"jmespath": { "jmespath": {
"hashes": [ "hashes": [
"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 +602,7 @@
"sha256:6dc509178ac4269b0e66ab4881f70a2035c33d3a622e20585f965986a5182006", "sha256:6dc509178ac4269b0e66ab4881f70a2035c33d3a622e20585f965986a5182006",
"sha256:f4965fba0a4718d47d470beeb5d6446e3357a62402b16c510b6a2f251e05ac3c" "sha256:f4965fba0a4718d47d470beeb5d6446e3357a62402b16c510b6a2f251e05ac3c"
], ],
"markers": "python_version >= '3.6'",
"version": "==5.0.2" "version": "==5.0.2"
}, },
"kubernetes": { "kubernetes": {
@ -597,6 +616,9 @@
"ldap3": { "ldap3": {
"hashes": [ "hashes": [
"sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91", "sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91",
"sha256:4139c91f0eef9782df7b77c8cbc6243086affcb6a8a249b768a9658438e5da59",
"sha256:8c949edbad2be8a03e719ba48bd6779f327ec156929562814b3e84ab56889c8c",
"sha256:afc6fc0d01f02af82cd7bfabd3bbfd5dc96a6ae91e97db0a2dab8a0f1b436056",
"sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57" "sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57"
], ],
"index": "pypi", "index": "pypi",
@ -656,65 +678,49 @@
}, },
"markupsafe": { "markupsafe": {
"hashes": [ "hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "sha256:007dc055dbce5b1104876acee177dbfd18757e19d562cd440182e1f492e96b95",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "sha256:031bf79a27d1c42f69c276d6221172417b47cb4b31cdc73d362a9bf5a1889b9f",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "sha256:161d575fa49395860b75da5135162481768b11208490d5a2143ae6785123e77d",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "sha256:24bbc3507fb6dfff663af7900a631f2aca90d5a445f272db5fc84999fa5718bc",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", "sha256:2efaeb1baff547063bad2b2893a8f5e9c459c4624e1a96644bbba08910ae34e0",
"sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", "sha256:32200f562daaab472921a11cbb63780f1654552ae49518196fc361ed8e12e901",
"sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", "sha256:3261fae28155e5c8634dd7710635fe540a05b58f160cef7713c7700cb9980e66",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:3b54a9c68995ef4164567e2cd1a5e16db5dac30b2a50c39c82db8d4afaf14f63",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "sha256:3c352ff634e289061711608f5e474ec38dbaa21e3e168820d53d5f4015e5b91b",
"sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", "sha256:3fb47f97f1d338b943126e90b79cad50d4fcfa0b80637b5a9f468941dbbd9ce5",
"sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", "sha256:441ce2a8c17683d97e06447fcbccbdb057cbf587c78eb75ae43ea7858042fe2c",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:45535241baa0fc0ba2a43961a1ac7562ca3257f46c4c3e9c0de38b722be41bd1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:4aca81a687975b35e3e80bcf9aa93fe10cd57fac37bf18b2314c186095f57e05",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "sha256:4cc563836f13c57f1473bc02d1e01fc37bab70ad4ee6be297d58c1d66bc819bf",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "sha256:4fae0677f712ee090721d8b17f412f1cbceefbf0dc180fe91bab3232f38b4527",
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", "sha256:58bc9fce3e1557d463ef5cee05391a05745fd95ed660f23c1742c711712c0abb",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "sha256:664832fb88b8162268928df233f4b12a144a0c78b01d38b81bdcf0fc96668ecb",
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:70820a1c96311e02449591cbdf5cd1c6a34d5194d5b55094ab725364375c9eb2",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "sha256:79b2ae94fa991be023832e6bcc00f41dbc8e5fe9d997a02db965831402551730",
"sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", "sha256:83cf0228b2f694dcdba1374d5312f2277269d798e65f40344964f642935feac1",
"sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", "sha256:87de598edfa2230ff274c4de7fcf24c73ffd96208c8e1912d5d0fee459767d75",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:8f806bfd0f218477d7c46a11d3e52dc7f5fdfaa981b18202b7dc84bbc287463b",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:90053234a6479738fd40d155268af631c7fca33365f964f2208867da1349294b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "sha256:a00dce2d96587651ef4fa192c17e039e8cfab63087c67e7d263a5533c7dad715",
"sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", "sha256:a08cd07d3c3c17cd33d9e66ea9dee8f8fc1c48e2d11bd88fd2dc515a602c709b",
"sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", "sha256:a19d39b02a24d3082856a5b06490b714a9d4179321225bbf22809ff1e1887cc8",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:d00a669e4a5bec3ee6dbeeeedd82a405ced19f8aeefb109a012ea88a45afff96",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "sha256:dab0c685f21f4a6c95bfc2afd1e7eae0033b403dd3d8c1b6d13a652ada75b348",
"sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", "sha256:df561f65049ed3556e5b52541669310e88713fdae2934845ec3606f283337958",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:e4570d16f88c7f3032ed909dc9e905a17da14a1c4cfd92608e3fda4cb1208bbd",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:e77e4b983e2441aff0c0d07ee711110c106b625f440292dfe02a2f60c8218bd6",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "sha256:e79212d09fc0e224d20b43ad44bb0a0a3416d1e04cf6b45fed265114a5d43d20",
"sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", "sha256:f58b5ba13a5689ca8317b98439fccfbcc673acaaf8241c1869ceea40f5d585bf",
"sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", "sha256:fef86115fdad7ae774720d7103aa776144cf9b66673b4afa9bcaa7af990ed07b"
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
"sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5",
"sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c",
"sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
"sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
], ],
"version": "==1.1.1" "markers": "python_version >= '3.6'",
"version": "==2.0.0"
}, },
"maxminddb": { "maxminddb": {
"hashes": [ "hashes": [
"sha256:47e86a084dd814fac88c99ea34ba3278a74bc9de5a25f4b815b608798747c7dc" "sha256:47e86a084dd814fac88c99ea34ba3278a74bc9de5a25f4b815b608798747c7dc"
], ],
"markers": "python_version >= '3.6'",
"version": "==2.0.3" "version": "==2.0.3"
}, },
"msgpack": { "msgpack": {
@ -790,6 +796,7 @@
"sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281", "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281",
"sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80" "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"
], ],
"markers": "python_version >= '3.6'",
"version": "==5.1.0" "version": "==5.1.0"
}, },
"oauthlib": { "oauthlib": {
@ -797,6 +804,7 @@
"sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889", "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889",
"sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea" "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.1.0" "version": "==3.1.0"
}, },
"packaging": { "packaging": {
@ -812,6 +820,7 @@
"sha256:030e4f9df5f53db2292eec37c6255957eb76168c6f974e4176c711cf91ed34aa", "sha256:030e4f9df5f53db2292eec37c6255957eb76168c6f974e4176c711cf91ed34aa",
"sha256:b6c5a9643e3545bcbfd9451766cbaa5d9c67e7303c7bc32c750b6fa70ecb107d" "sha256:b6c5a9643e3545bcbfd9451766cbaa5d9c67e7303c7bc32c750b6fa70ecb107d"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.1" "version": "==0.10.1"
}, },
"prompt-toolkit": { "prompt-toolkit": {
@ -819,6 +828,7 @@
"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": {
@ -864,15 +874,37 @@
}, },
"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"
}, },
@ -881,6 +913,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": {
@ -924,6 +957,7 @@
"sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316", "sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316",
"sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29" "sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29"
], ],
"markers": "python_version >= '3.5'",
"version": "==2.0.2" "version": "==2.0.2"
}, },
"pyjwt": { "pyjwt": {
@ -946,12 +980,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": {
@ -959,6 +995,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": {
@ -1015,6 +1052,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": {
@ -1022,12 +1060,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"
@ -1045,6 +1085,7 @@
"sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28", "sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28",
"sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22" "sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22"
], ],
"markers": "python_version >= '3'",
"version": "==0.17.4" "version": "==0.17.4"
}, },
"ruamel.yaml.clib": { "ruamel.yaml.clib": {
@ -1081,7 +1122,7 @@
"sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2", "sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2",
"sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f" "sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f"
], ],
"markers": "platform_python_implementation == 'CPython' and python_version < '3.10'", "markers": "python_version < '3.10' and platform_python_implementation == 'CPython'",
"version": "==0.2.2" "version": "==0.2.2"
}, },
"s3transfer": { "s3transfer": {
@ -1112,6 +1153,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": {
@ -1119,6 +1161,7 @@
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
], ],
"markers": "python_version >= '3.5'",
"version": "==0.4.1" "version": "==0.4.1"
}, },
"structlog": { "structlog": {
@ -1174,6 +1217,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": {
@ -1189,6 +1233,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": {
@ -1233,6 +1278,7 @@
"sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30", "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30",
"sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e" "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"
], ],
"markers": "python_version >= '3.6'",
"version": "==5.0.0" "version": "==5.0.0"
}, },
"watchgod": { "watchgod": {
@ -1262,6 +1308,7 @@
"sha256:2e50d26ca593f70aba7b13a489435ef88b8fc3b5c5643c1ce8808ff9b40f0b32", "sha256:2e50d26ca593f70aba7b13a489435ef88b8fc3b5c5643c1ce8808ff9b40f0b32",
"sha256:d376bd60eace9d437ab6d7ee16f4ab4e821c9dae591e1b783c58ebd8aaf80c5c" "sha256:d376bd60eace9d437ab6d7ee16f4ab4e821c9dae591e1b783c58ebd8aaf80c5c"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.59.0" "version": "==0.59.0"
}, },
"websockets": { "websockets": {
@ -1348,6 +1395,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": {
@ -1404,6 +1452,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"
} }
}, },
@ -1420,6 +1469,7 @@
"sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e", "sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e",
"sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975" "sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"
], ],
"markers": "python_version ~= '3.6'",
"version": "==2.5.6" "version": "==2.5.6"
}, },
"attrs": { "attrs": {
@ -1427,6 +1477,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": {
@ -1439,10 +1490,11 @@
}, },
"black": { "black": {
"hashes": [ "hashes": [
"sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea" "sha256:23695358dbcb3deafe7f0a3ad89feee5999a46be5fec21f4f1d108be0bcdb3b1",
"sha256:8a60071a0043876a4ae96e6c69bd3a127dad2c1ca7c8083573eb82f92705d008"
], ],
"index": "pypi", "index": "pypi",
"version": "==20.8b1" "version": "==21.5b1"
}, },
"bump2version": { "bump2version": {
"hashes": [ "hashes": [
@ -1464,6 +1516,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": {
@ -1471,6 +1524,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": {
@ -1544,14 +1598,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:05af150f47a5cca3f4b0af289b73aef8cf3c4fe2385015b06220cbcdee48bb6e", "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b",
"sha256:a77824e516d3298b04fb36ec7845e92747df8fcfee9cacc32dd6239f9652f867" "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"
], ],
"version": "==3.1.15" "markers": "python_version >= '3.4'",
"version": "==3.1.14"
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
@ -1572,6 +1628,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": {
@ -1599,6 +1656,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": {
@ -1635,6 +1693,7 @@
"sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd", "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd",
"sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4" "sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"
], ],
"markers": "python_version >= '2.6'",
"version": "==5.6.0" "version": "==5.6.0"
}, },
"pluggy": { "pluggy": {
@ -1642,6 +1701,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": {
@ -1649,6 +1709,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": {
@ -1679,6 +1740,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": {
@ -1783,6 +1845,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": {
@ -1806,6 +1869,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": {
@ -1813,6 +1877,7 @@
"sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182", "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182",
"sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2" "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"
], ],
"markers": "python_version >= '3.5'",
"version": "==4.0.0" "version": "==4.0.0"
}, },
"stevedore": { "stevedore": {
@ -1820,6 +1885,7 @@
"sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee", "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee",
"sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a" "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"
], ],
"markers": "python_version >= '3.6'",
"version": "==3.3.0" "version": "==3.3.0"
}, },
"toml": { "toml": {
@ -1827,51 +1893,9 @@
"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"
}, },
"typed-ast": {
"hashes": [
"sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace",
"sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff",
"sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266",
"sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528",
"sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6",
"sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808",
"sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4",
"sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363",
"sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341",
"sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04",
"sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41",
"sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e",
"sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3",
"sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899",
"sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805",
"sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c",
"sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c",
"sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39",
"sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a",
"sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3",
"sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7",
"sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f",
"sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075",
"sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0",
"sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40",
"sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428",
"sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927",
"sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3",
"sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f",
"sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"
],
"version": "==1.4.3"
},
"typing-extensions": {
"hashes": [
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
],
"version": "==3.10.0.0"
},
"urllib3": { "urllib3": {
"extras": [ "extras": [
"secure" "secure"

View File

@ -1,3 +1,3 @@
"""authentik""" """authentik"""
__version__ = "2021.5.1-rc6" __version__ = "2021.5.1-rc8"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -7,6 +7,7 @@ from django.urls import reverse
from authentik import __version__ from authentik import __version__
from authentik.core.models import Group, User from authentik.core.models import Group, User
from authentik.core.tasks import clean_expired_models from authentik.core.tasks import clean_expired_models
from authentik.events.monitored_tasks import TaskResultStatus
class TestAdminAPI(TestCase): class TestAdminAPI(TestCase):
@ -30,6 +31,26 @@ class TestAdminAPI(TestCase):
any(task["task_name"] == "clean_expired_models" for task in body) any(task["task_name"] == "clean_expired_models" for task in body)
) )
def test_tasks_single(self):
"""Test Task API (read single)"""
clean_expired_models.delay()
response = self.client.get(
reverse(
"authentik_api:admin_system_tasks-detail",
kwargs={"pk": "clean_expired_models"},
)
)
self.assertEqual(response.status_code, 200)
body = loads(response.content)
self.assertEqual(body["status"], TaskResultStatus.SUCCESSFUL.name)
self.assertEqual(body["task_name"], "clean_expired_models")
response = self.client.get(
reverse(
"authentik_api:admin_system_tasks-detail", kwargs={"pk": "qwerqwer"}
)
)
self.assertEqual(response.status_code, 404)
def test_tasks_retry(self): def test_tasks_retry(self):
"""Test Task API (retry)""" """Test Task API (retry)"""
clean_expired_models.delay() clean_expired_models.delay()

View File

@ -54,4 +54,4 @@ class AuthentikTokenAuthentication(BaseAuthentication):
if not token: if not token:
return None return None
return (token.user, None) return (token.user, None) # pragma: no cover

View File

@ -22,3 +22,10 @@ class TestSwaggerGeneration(APITestCase):
reverse("authentik_api:schema-json", kwargs={"format": ".json"}), reverse("authentik_api:schema-json", kwargs={"format": ".json"}),
) )
self.assertTrue(loads(response.content.decode())) self.assertTrue(loads(response.content.decode()))
def test_browser(self):
"""Test API Browser"""
response = self.client.get(
reverse("authentik_api:swagger"),
)
self.assertEqual(response.status_code, 200)

View File

@ -4,6 +4,7 @@ from typing import Optional
from django.core.cache import cache from django.core.cache import cache
from django.db.models import QuerySet from django.db.models import QuerySet
from django.http.response import HttpResponseBadRequest from django.http.response import HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from drf_yasg import openapi from drf_yasg import openapi
from drf_yasg.utils import no_body, swagger_auto_schema from drf_yasg.utils import no_body, swagger_auto_schema
from rest_framework.decorators import action from rest_framework.decorators import action
@ -101,7 +102,9 @@ class ApplicationViewSet(ModelViewSet):
# 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"""
application = self.get_object() # Don't use self.get_object as that checks for view_application permission
# which the user might not have, even if they have access
application = get_object_or_404(Application, slug=slug)
engine = PolicyEngine(application, self.request.user, self.request) engine = PolicyEngine(application, self.request.user, self.request)
engine.build() engine.build()
if engine.passing: if engine.passing:

View File

@ -24,7 +24,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.get( response = self.client.get(
reverse( reverse(

View File

@ -21,7 +21,7 @@ class TestModels(TestCase):
self.assertTrue(token.is_expired) self.assertTrue(token.is_expired)
def test_token_expire_no_expire(self): def test_token_expire_no_expire(self):
"""Test token expiring with "expiring" set """ """Test token expiring with "expiring" set"""
token = Token.objects.create( token = Token.objects.create(
expires=now(), user=get_anonymous_user(), expiring=False expires=now(), user=get_anonymous_user(), expiring=False
) )

View File

@ -3,7 +3,9 @@ import django_filters
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import load_pem_x509_certificate from cryptography.x509 import load_pem_x509_certificate
from django.http.response import HttpResponse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.fields import ( from rest_framework.fields import (
@ -145,7 +147,16 @@ class CertificateKeyPairViewSet(ModelViewSet):
serializer = self.get_serializer(instance) serializer = self.get_serializer(instance)
return Response(serializer.data) return Response(serializer.data)
@swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)}) @swagger_auto_schema(
manual_parameters=[
openapi.Parameter(
name="download",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
],
responses={200: CertificateDataSerializer(many=False)},
)
@action(detail=True, pagination_class=None, filter_backends=[]) @action(detail=True, pagination_class=None, filter_backends=[])
# pylint: disable=invalid-name, unused-argument # pylint: disable=invalid-name, unused-argument
def view_certificate(self, request: Request, pk: str) -> Response: def view_certificate(self, request: Request, pk: str) -> Response:
@ -156,11 +167,29 @@ class CertificateKeyPairViewSet(ModelViewSet):
secret=certificate, secret=certificate,
type="certificate", type="certificate",
).from_http(request) ).from_http(request)
if "download" in request._request.GET:
# Mime type from https://pki-tutorial.readthedocs.io/en/latest/mime.html
response = HttpResponse(
certificate.certificate_data, content_type="application/x-pem-file"
)
response[
"Content-Disposition"
] = f'attachment; filename="{certificate.name}_certificate.pem"'
return response
return Response( return Response(
CertificateDataSerializer({"data": certificate.certificate_data}).data CertificateDataSerializer({"data": certificate.certificate_data}).data
) )
@swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)}) @swagger_auto_schema(
manual_parameters=[
openapi.Parameter(
name="download",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
],
responses={200: CertificateDataSerializer(many=False)},
)
@action(detail=True, pagination_class=None, filter_backends=[]) @action(detail=True, pagination_class=None, filter_backends=[])
# pylint: disable=invalid-name, unused-argument # pylint: disable=invalid-name, unused-argument
def view_private_key(self, request: Request, pk: str) -> Response: def view_private_key(self, request: Request, pk: str) -> Response:
@ -171,4 +200,13 @@ class CertificateKeyPairViewSet(ModelViewSet):
secret=certificate, secret=certificate,
type="private_key", type="private_key",
).from_http(request) ).from_http(request)
if "download" in request._request.GET:
# Mime type from https://pki-tutorial.readthedocs.io/en/latest/mime.html
response = HttpResponse(
certificate.key_data, content_type="application/x-pem-file"
)
response[
"Content-Disposition"
] = f'attachment; filename="{certificate.name}_private_key.pem"'
return response
return Response(CertificateDataSerializer({"data": certificate.key_data}).data) return Response(CertificateDataSerializer({"data": certificate.key_data}).data)

View File

@ -2,7 +2,9 @@
import datetime import datetime
from django.test import TestCase from django.test import TestCase
from django.urls import reverse
from authentik.core.models import User
from authentik.crypto.api import CertificateKeyPairSerializer from authentik.crypto.api import CertificateKeyPairSerializer
from authentik.crypto.builder import CertificateBuilder from authentik.crypto.builder import CertificateBuilder
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
@ -47,3 +49,45 @@ class TestCrypto(TestCase):
now = datetime.datetime.today() now = datetime.datetime.today()
self.assertEqual(instance.name, "test-cert") self.assertEqual(instance.name, "test-cert")
self.assertEqual((instance.certificate.not_valid_after - now).days, 2) self.assertEqual((instance.certificate.not_valid_after - now).days, 2)
def test_certificate_download(self):
"""Test certificate export (download)"""
self.client.force_login(User.objects.get(username="akadmin"))
keypair = CertificateKeyPair.objects.first()
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-certificate",
kwargs={"pk": keypair.pk},
)
)
self.assertEqual(200, response.status_code)
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-certificate",
kwargs={"pk": keypair.pk},
)
+ "?download",
)
self.assertEqual(200, response.status_code)
self.assertIn("Content-Disposition", response)
def test_private_key_download(self):
"""Test private_key export (download)"""
self.client.force_login(User.objects.get(username="akadmin"))
keypair = CertificateKeyPair.objects.first()
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-private-key",
kwargs={"pk": keypair.pk},
)
)
self.assertEqual(200, response.status_code)
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-private-key",
kwargs={"pk": keypair.pk},
)
+ "?download",
)
self.assertEqual(200, response.status_code)
self.assertIn("Content-Disposition", response)

View File

@ -298,7 +298,7 @@ class CancelView(View):
if SESSION_KEY_PLAN in request.session: if SESSION_KEY_PLAN in request.session:
del request.session[SESSION_KEY_PLAN] del request.session[SESSION_KEY_PLAN]
LOGGER.debug("Canceled current plan") LOGGER.debug("Canceled current plan")
return redirect("authentik_core:root-redirect") return redirect("authentik_core:default-invalidation")
class ToDefaultFlow(View): class ToDefaultFlow(View):

View File

@ -3,6 +3,7 @@ postgresql:
host: localhost host: localhost
name: authentik name: authentik
user: authentik user: authentik
port: 5432
password: 'env://POSTGRES_PASSWORD' password: 'env://POSTGRES_PASSWORD'
web: web:

View File

@ -1,23 +1,35 @@
"""Outpost API Views""" """Outpost API Views"""
from dacite.core import from_dict
from dacite.exceptions import DaciteError
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.fields import BooleanField, CharField, DateTimeField from rest_framework.fields import BooleanField, CharField, DateTimeField
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 JSONField, ModelSerializer from rest_framework.serializers import JSONField, ModelSerializer, ValidationError
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.core.api.providers import ProviderSerializer from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.utils import PassiveSerializer, is_dict from authentik.core.api.utils import PassiveSerializer, is_dict
from authentik.outposts.models import Outpost, default_outpost_config from authentik.outposts.models import Outpost, OutpostConfig, default_outpost_config
class OutpostSerializer(ModelSerializer): class OutpostSerializer(ModelSerializer):
"""Outpost Serializer""" """Outpost Serializer"""
_config = JSONField(validators=[is_dict]) config = JSONField(validators=[is_dict], source="_config")
# TODO: Remove _config again, this is only here for legacy with older outposts
_config = JSONField(validators=[is_dict], read_only=True)
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True) providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
def validate_config(self, config) -> dict:
"""Check that the config has all required fields"""
try:
from_dict(OutpostConfig, config)
except DaciteError as exc:
raise ValidationError(f"Failed to validate config: {str(exc)}") from exc
return config
class Meta: class Meta:
model = Outpost model = Outpost
@ -29,6 +41,7 @@ class OutpostSerializer(ModelSerializer):
"providers_obj", "providers_obj",
"service_connection", "service_connection",
"token_identifier", "token_identifier",
"config",
"_config", "_config",
] ]

View File

@ -42,6 +42,8 @@ class OutpostConsumer(AuthJsonConsumer):
outpost: Optional[Outpost] = None outpost: Optional[Outpost] = None
last_uid: Optional[str] = None
def connect(self): def connect(self):
super().connect() super().connect()
uuid = self.scope["url_route"]["kwargs"]["pk"] uuid = self.scope["url_route"]["kwargs"]["pk"]
@ -52,9 +54,7 @@ class OutpostConsumer(AuthJsonConsumer):
raise DenyConnection() raise DenyConnection()
self.accept() self.accept()
self.outpost = outpost.first() self.outpost = outpost.first()
OutpostState( self.last_uid = self.channel_name
uid=self.channel_name, last_seen=datetime.now(), _outpost=self.outpost
).save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)
LOGGER.debug( LOGGER.debug(
"added outpost instace to cache", "added outpost instace to cache",
outpost=self.outpost, outpost=self.outpost,
@ -63,18 +63,20 @@ class OutpostConsumer(AuthJsonConsumer):
# pylint: disable=unused-argument # pylint: disable=unused-argument
def disconnect(self, close_code): def disconnect(self, close_code):
if self.outpost: if self.outpost and self.last_uid:
OutpostState.for_channel(self.outpost, self.channel_name).delete() OutpostState.for_channel(self.outpost, self.last_uid).delete()
LOGGER.debug( LOGGER.debug(
"removed outpost instance from cache", "removed outpost instance from cache",
outpost=self.outpost, outpost=self.outpost,
channel_name=self.channel_name, instance_uuid=self.last_uid,
) )
def receive_json(self, content: Data): def receive_json(self, content: Data):
msg = from_dict(WebsocketMessage, content) msg = from_dict(WebsocketMessage, content)
uid = msg.args.get("uuid", self.channel_name)
self.last_uid = uid
state = OutpostState( state = OutpostState(
uid=self.channel_name, uid=uid,
last_seen=datetime.now(), last_seen=datetime.now(),
_outpost=self.outpost, _outpost=self.outpost,
) )
@ -82,8 +84,7 @@ class OutpostConsumer(AuthJsonConsumer):
state.version = msg.args.get("version", None) state.version = msg.args.get("version", None)
elif msg.instruction == WebsocketMessageInstruction.ACK: elif msg.instruction == WebsocketMessageInstruction.ACK:
return return
if state.version: state.save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)
state.save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)
response = WebsocketMessage(instruction=WebsocketMessageInstruction.ACK) response = WebsocketMessage(instruction=WebsocketMessageInstruction.ACK)
self.send_json(asdict(response)) self.send_json(asdict(response))

View File

@ -56,6 +56,12 @@ class BaseController:
"""Handler to delete everything we've created""" """Handler to delete everything we've created"""
raise NotImplementedError raise NotImplementedError
def down_with_logs(self) -> list[str]:
"""Call .down() but capture all log output and return it."""
with capture_logs() as logs:
self.down()
return [x["event"] for x in logs]
def get_static_deployment(self) -> str: def get_static_deployment(self) -> str:
"""Return a static deployment configuration""" """Return a static deployment configuration"""
raise NotImplementedError raise NotImplementedError

View File

@ -30,11 +30,6 @@ class NeedsUpdate(ReconcileTrigger):
"""Exception to trigger an update to the Kubernetes Object""" """Exception to trigger an update to the Kubernetes Object"""
class Disabled(SentryIgnoredException):
"""Exception which can be thrown in a reconciler to signal than an
object should not be created."""
class KubernetesObjectReconciler(Generic[T]): class KubernetesObjectReconciler(Generic[T]):
"""Base Kubernetes Reconciler, handles the basic logic.""" """Base Kubernetes Reconciler, handles the basic logic."""
@ -45,6 +40,11 @@ class KubernetesObjectReconciler(Generic[T]):
self.namespace = controller.outpost.config.kubernetes_namespace self.namespace = controller.outpost.config.kubernetes_namespace
self.logger = get_logger().bind(type=self.__class__.__name__) self.logger = get_logger().bind(type=self.__class__.__name__)
@property
def noop(self) -> bool:
"""Return true if this object should not be created/updated/deleted in this cluster"""
return False
@property @property
def name(self) -> str: def name(self) -> str:
"""Get the name of the object this reconciler manages""" """Get the name of the object this reconciler manages"""
@ -59,11 +59,10 @@ class KubernetesObjectReconciler(Generic[T]):
def up(self): def up(self):
"""Create object if it doesn't exist, update if needed or recreate if needed.""" """Create object if it doesn't exist, update if needed or recreate if needed."""
current = None current = None
try: if self.noop:
reference = self.get_reference_object() self.logger.debug("Object is noop")
except Disabled:
self.logger.debug("Object not required")
return return
reference = self.get_reference_object()
try: try:
try: try:
current = self.retrieve() current = self.retrieve()
@ -92,11 +91,8 @@ class KubernetesObjectReconciler(Generic[T]):
def down(self): def down(self):
"""Delete object if found""" """Delete object if found"""
# Call self.get_reference_object to check if we even need to do anything if self.noop:
try: self.logger.debug("Object is noop")
self.get_reference_object()
except Disabled:
self.logger.debug("Object not required")
return return
try: try:
current = self.retrieve() current = self.retrieve()

View File

@ -8,7 +8,7 @@ from structlog.testing import capture_logs
from yaml import dump_all from yaml import dump_all
from authentik.outposts.controllers.base import BaseController, ControllerException from authentik.outposts.controllers.base import BaseController, ControllerException
from authentik.outposts.controllers.k8s.base import Disabled, KubernetesObjectReconciler from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
from authentik.outposts.controllers.k8s.secret import SecretReconciler from authentik.outposts.controllers.k8s.secret import SecretReconciler
from authentik.outposts.controllers.k8s.service import ServiceReconciler from authentik.outposts.controllers.k8s.service import ServiceReconciler
@ -49,6 +49,9 @@ class KubernetesController(BaseController):
try: try:
all_logs = [] all_logs = []
for reconcile_key in self.reconcile_order: for reconcile_key in self.reconcile_order:
if reconcile_key in self.outpost.config.kubernetes_disabled_components:
all_logs += [f"{reconcile_key.title()}: Disabled"]
continue
with capture_logs() as logs: with capture_logs() as logs:
reconciler = self.reconcilers[reconcile_key](self) reconciler = self.reconcilers[reconcile_key](self)
reconciler.up() reconciler.up()
@ -67,14 +70,28 @@ class KubernetesController(BaseController):
except ApiException as exc: except ApiException as exc:
raise ControllerException(str(exc)) from exc raise ControllerException(str(exc)) from exc
def down_with_logs(self) -> list[str]:
try:
all_logs = []
for reconcile_key in self.reconcile_order:
if reconcile_key in self.outpost.config.kubernetes_disabled_components:
all_logs += [f"{reconcile_key.title()}: Disabled"]
continue
with capture_logs() as logs:
reconciler = self.reconcilers[reconcile_key](self)
reconciler.down()
all_logs += [f"{reconcile_key.title()}: {x['event']}" for x in logs]
return all_logs
except ApiException as exc:
raise ControllerException(str(exc)) from exc
def get_static_deployment(self) -> str: def get_static_deployment(self) -> str:
documents = [] documents = []
for reconcile_key in self.reconcile_order: for reconcile_key in self.reconcile_order:
reconciler = self.reconcilers[reconcile_key](self) reconciler = self.reconcilers[reconcile_key](self)
try: if reconciler.noop:
documents.append(reconciler.get_reference_object().to_dict())
except Disabled:
continue continue
documents.append(reconciler.get_reference_object().to_dict())
with StringIO() as _str: with StringIO() as _str:
dump_all( dump_all(

View File

@ -42,7 +42,7 @@ LOGGER = get_logger()
class ServiceConnectionInvalid(SentryIgnoredException): class ServiceConnectionInvalid(SentryIgnoredException):
""""Exception raised when a Service Connection has invalid parameters""" """Exception raised when a Service Connection has invalid parameters"""
@dataclass @dataclass
@ -64,6 +64,7 @@ class OutpostConfig:
kubernetes_ingress_annotations: dict[str, str] = field(default_factory=dict) kubernetes_ingress_annotations: dict[str, str] = field(default_factory=dict)
kubernetes_ingress_secret_name: str = field(default="authentik-outpost-tls") kubernetes_ingress_secret_name: str = field(default="authentik-outpost-tls")
kubernetes_service_type: str = field(default="ClusterIP") kubernetes_service_type: str = field(default="ClusterIP")
kubernetes_disabled_components: list[str] = field(default_factory=list)
class OutpostModel(Model): class OutpostModel(Model):

View File

@ -1,5 +1,5 @@
"""authentik outpost signals""" """authentik outpost signals"""
from django.conf import settings from django.core.cache import cache
from django.db.models import Model from django.db.models import Model
from django.db.models.signals import post_save, pre_delete, pre_save from django.db.models.signals import post_save, pre_delete, pre_save
from django.dispatch import receiver from django.dispatch import receiver
@ -8,9 +8,12 @@ from structlog.stdlib import get_logger
from authentik.core.models import Provider from authentik.core.models import Provider
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.lib.utils.reflection import class_to_path from authentik.lib.utils.reflection import class_to_path
from authentik.outposts.controllers.base import ControllerException
from authentik.outposts.models import Outpost, OutpostServiceConnection from authentik.outposts.models import Outpost, OutpostServiceConnection
from authentik.outposts.tasks import outpost_controller_down, outpost_post_save from authentik.outposts.tasks import (
CACHE_KEY_OUTPOST_DOWN,
outpost_controller,
outpost_post_save,
)
LOGGER = get_logger() LOGGER = get_logger()
UPDATE_TRIGGERING_MODELS = ( UPDATE_TRIGGERING_MODELS = (
@ -39,7 +42,8 @@ def pre_save_outpost(sender, instance: Outpost, **_):
) )
if bool(dirty): if bool(dirty):
LOGGER.info("Outpost needs re-deployment due to changes", instance=instance) LOGGER.info("Outpost needs re-deployment due to changes", instance=instance)
outpost_controller_down_wrapper(old_instance) cache.set(CACHE_KEY_OUTPOST_DOWN % instance.pk.hex, old_instance)
outpost_controller.delay(instance.pk.hex, action="down", from_cache=True)
@receiver(post_save) @receiver(post_save)
@ -63,23 +67,5 @@ def post_save_update(sender, instance: Model, **_):
def pre_delete_cleanup(sender, instance: Outpost, **_): def pre_delete_cleanup(sender, instance: Outpost, **_):
"""Ensure that Outpost's user is deleted (which will delete the token through cascade)""" """Ensure that Outpost's user is deleted (which will delete the token through cascade)"""
instance.user.delete() instance.user.delete()
outpost_controller_down_wrapper(instance) cache.set(CACHE_KEY_OUTPOST_DOWN % instance.pk.hex, instance)
outpost_controller.delay(instance.pk.hex, action="down", from_cache=True)
def outpost_controller_down_wrapper(instance: Outpost):
"""To ensure that deployment is cleaned up *consistently* we call the controller, and wait
for it to finish. We don't want to call it in this thread, as we don't have the Outpost
Service connection here"""
try:
outpost_controller_down.delay(instance.pk.hex).get()
except RuntimeError: # pragma: no cover
# In e2e/integration tests, this might run inside a thread/process and
# trigger the celery `Never call result.get() within a task` detection
if settings.TEST:
pass
else:
raise
except ControllerException as exc:
LOGGER.warning(
"failed to cleanup outpost deployment", exc=exc, instance=instance
)

View File

@ -36,6 +36,7 @@ from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesCont
from authentik.root.celery import CELERY_APP from authentik.root.celery import CELERY_APP
LOGGER = get_logger() LOGGER = get_logger()
CACHE_KEY_OUTPOST_DOWN = "outpost_teardown_%s"
def controller_for_outpost(outpost: Outpost) -> Optional[BaseController]: def controller_for_outpost(outpost: Outpost) -> Optional[BaseController]:
@ -56,13 +57,6 @@ def controller_for_outpost(outpost: Outpost) -> Optional[BaseController]:
return None return None
@CELERY_APP.task()
def outpost_controller_all():
"""Launch Controller for all Outposts which support it"""
for outpost in Outpost.objects.exclude(service_connection=None):
outpost_controller.delay(outpost.pk.hex)
@CELERY_APP.task() @CELERY_APP.task()
def outpost_service_connection_state(connection_pk: Any): def outpost_service_connection_state(connection_pk: Any):
"""Update cached state of a service connection""" """Update cached state of a service connection"""
@ -89,17 +83,29 @@ def outpost_service_connection_monitor(self: MonitoredTask):
) )
@CELERY_APP.task()
def outpost_controller_all():
"""Launch Controller for all Outposts which support it"""
for outpost in Outpost.objects.exclude(service_connection=None):
outpost_controller.delay(outpost.pk.hex, "up", from_cache=False)
@CELERY_APP.task(bind=True, base=MonitoredTask) @CELERY_APP.task(bind=True, base=MonitoredTask)
def outpost_controller(self: MonitoredTask, outpost_pk: str): def outpost_controller(
"""Create/update/monitor the deployment of an Outpost""" self: MonitoredTask, outpost_pk: str, action: str = "up", from_cache: bool = False
):
"""Create/update/monitor/delete the deployment of an Outpost"""
logs = [] logs = []
outpost: Outpost = Outpost.objects.get(pk=outpost_pk) if from_cache:
outpost: Outpost = cache.get(CACHE_KEY_OUTPOST_DOWN % outpost_pk)
else:
outpost: Outpost = Outpost.objects.get(pk=outpost_pk)
self.set_uid(slugify(outpost.name)) self.set_uid(slugify(outpost.name))
try: try:
controller = controller_for_outpost(outpost) controller = controller_for_outpost(outpost)
if not controller: if not controller:
return return
logs = controller.up_with_logs() logs = getattr(controller, f"{action}_with_logs")()
LOGGER.debug("---------------Outpost Controller logs starting----------------") LOGGER.debug("---------------Outpost Controller logs starting----------------")
for log in logs: for log in logs:
LOGGER.debug(log) LOGGER.debug(log)
@ -110,16 +116,6 @@ def outpost_controller(self: MonitoredTask, outpost_pk: str):
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, logs)) self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, logs))
@CELERY_APP.task()
def outpost_controller_down(outpost_pk: str):
"""Delete outpost objects before deleting the DB Object"""
outpost = Outpost.objects.get(pk=outpost_pk)
controller = controller_for_outpost(outpost)
if not controller:
return
controller.down()
@CELERY_APP.task(bind=True, base=MonitoredTask) @CELERY_APP.task(bind=True, base=MonitoredTask)
def outpost_token_ensurer(self: MonitoredTask): def outpost_token_ensurer(self: MonitoredTask):
"""Periodically ensure that all Outposts have valid Service Accounts """Periodically ensure that all Outposts have valid Service Accounts

View File

@ -3,6 +3,10 @@ from django.urls import reverse
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from authentik.core.models import PropertyMapping, User from authentik.core.models import PropertyMapping, User
from authentik.flows.models import Flow
from authentik.outposts.api.outposts import OutpostSerializer
from authentik.outposts.models import default_outpost_config
from authentik.providers.proxy.models import ProxyProvider
class TestOutpostServiceConnectionsAPI(APITestCase): class TestOutpostServiceConnectionsAPI(APITestCase):
@ -22,3 +26,22 @@ class TestOutpostServiceConnectionsAPI(APITestCase):
reverse("authentik_api:outpostserviceconnection-types"), reverse("authentik_api:outpostserviceconnection-types"),
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_outpost_config(self):
"""Test Outpost's config field"""
provider = ProxyProvider.objects.create(
name="test", authorization_flow=Flow.objects.first()
)
invalid = OutpostSerializer(
data={"name": "foo", "providers": [provider.pk], "config": {}}
)
self.assertFalse(invalid.is_valid())
self.assertIn("config", invalid.errors)
valid = OutpostSerializer(
data={
"name": "foo",
"providers": [provider.pk],
"config": default_outpost_config("foo"),
}
)
self.assertTrue(valid.is_valid())

View File

@ -33,6 +33,8 @@ class OpenIDConnectConfigurationSerializer(PassiveSerializer):
class ProxyProviderSerializer(ProviderSerializer): class ProxyProviderSerializer(ProviderSerializer):
"""ProxyProvider Serializer""" """ProxyProvider Serializer"""
redirect_uris = CharField(read_only=True)
def validate(self, attrs) -> dict[Any, str]: def validate(self, attrs) -> dict[Any, str]:
"""Check that internal_host is set when forward_auth_mode is disabled""" """Check that internal_host is set when forward_auth_mode is disabled"""
if ( if (
@ -67,6 +69,7 @@ class ProxyProviderSerializer(ProviderSerializer):
"basic_auth_password_attribute", "basic_auth_password_attribute",
"basic_auth_user_attribute", "basic_auth_user_attribute",
"forward_auth_mode", "forward_auth_mode",
"redirect_uris",
] ]

View File

@ -17,7 +17,6 @@ from kubernetes.client.models.networking_v1beta1_ingress_rule import (
from authentik.outposts.controllers.base import FIELD_MANAGER from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import ( from authentik.outposts.controllers.k8s.base import (
Disabled,
KubernetesObjectReconciler, KubernetesObjectReconciler,
NeedsUpdate, NeedsUpdate,
) )
@ -137,9 +136,6 @@ class IngressReconciler(KubernetesObjectReconciler[NetworkingV1beta1Ingress]):
), ),
) )
rules.append(rule) rules.append(rule)
if not rules:
self.logger.debug("No providers use proxying, no ingress needed")
raise Disabled()
tls_config = None tls_config = None
if tls_hosts: if tls_hosts:
tls_config = NetworkingV1beta1IngressTLS( tls_config = NetworkingV1beta1IngressTLS(

View File

@ -7,7 +7,6 @@ from kubernetes.client import ApiextensionsV1Api, CustomObjectsApi
from authentik.outposts.controllers.base import FIELD_MANAGER from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import ( from authentik.outposts.controllers.k8s.base import (
Disabled,
KubernetesObjectReconciler, KubernetesObjectReconciler,
NeedsUpdate, NeedsUpdate,
) )
@ -70,6 +69,19 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware])
self.api_ex = ApiextensionsV1Api(controller.client) self.api_ex = ApiextensionsV1Api(controller.client)
self.api = CustomObjectsApi(controller.client) self.api = CustomObjectsApi(controller.client)
@property
def noop(self) -> bool:
if not ProxyProvider.objects.filter(
outpost__in=[self.controller.outpost],
forward_auth_mode=True,
).exists():
self.logger.debug("No providers with forward auth enabled.")
return True
if not self._crd_exists():
self.logger.debug("CRD doesn't exist")
return True
return False
def _crd_exists(self) -> bool: def _crd_exists(self) -> bool:
"""Check if the traefik middleware exists""" """Check if the traefik middleware exists"""
return bool( return bool(
@ -87,15 +99,6 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware])
def get_reference_object(self) -> TraefikMiddleware: def get_reference_object(self) -> TraefikMiddleware:
"""Get deployment object for outpost""" """Get deployment object for outpost"""
if not ProxyProvider.objects.filter(
outpost__in=[self.controller.outpost],
forward_auth_mode=True,
).exists():
self.logger.debug("No providers with forward auth enabled.")
raise Disabled()
if not self._crd_exists():
self.logger.debug("CRD doesn't exist")
raise Disabled()
return TraefikMiddleware( return TraefikMiddleware(
apiVersion=f"{CRD_GROUP}/{CRD_VERSION}", apiVersion=f"{CRD_GROUP}/{CRD_VERSION}",
kind="Middleware", kind="Middleware",

View File

@ -5,6 +5,7 @@ from defusedxml.ElementTree import fromstring
from django.http.response import HttpResponse from django.http.response import HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.fields import CharField, FileField, ReadOnlyField from rest_framework.fields import CharField, FileField, ReadOnlyField
@ -83,7 +84,14 @@ class SAMLProviderViewSet(ModelViewSet):
responses={ responses={
200: SAMLMetadataSerializer(many=False), 200: SAMLMetadataSerializer(many=False),
404: "Provider has no application assigned", 404: "Provider has no application assigned",
} },
manual_parameters=[
openapi.Parameter(
name="download",
in_=openapi.IN_QUERY,
type=openapi.TYPE_BOOLEAN,
)
],
) )
@action(methods=["GET"], detail=True, permission_classes=[AllowAny]) @action(methods=["GET"], detail=True, permission_classes=[AllowAny])
# pylint: disable=invalid-name, unused-argument # pylint: disable=invalid-name, unused-argument

View File

@ -23,7 +23,7 @@ def deflate_and_base64_encode(inflated: str, encoding="utf-8"):
def nice64(src: str) -> str: def nice64(src: str) -> str:
"""Returns src base64-encoded and formatted nicely for our XML. """ """Returns src base64-encoded and formatted nicely for our XML."""
return base64.b64encode(src.encode()).decode("utf-8").replace("\n", "") return base64.b64encode(src.encode()).decode("utf-8").replace("\n", "")

View File

@ -248,6 +248,7 @@ DATABASES = {
"NAME": CONFIG.y("postgresql.name"), "NAME": CONFIG.y("postgresql.name"),
"USER": CONFIG.y("postgresql.user"), "USER": CONFIG.y("postgresql.user"),
"PASSWORD": CONFIG.y("postgresql.password"), "PASSWORD": CONFIG.y("postgresql.password"),
"PORT": int(CONFIG.y("postgresql.port")),
} }
} }
@ -319,9 +320,6 @@ CELERY_RESULT_BACKEND = (
# Database backup # Database backup
DBBACKUP_STORAGE = "django.core.files.storage.FileSystemStorage" DBBACKUP_STORAGE = "django.core.files.storage.FileSystemStorage"
DBBACKUP_STORAGE_OPTIONS = {"location": "./backups" if DEBUG else "/backups"} DBBACKUP_STORAGE_OPTIONS = {"location": "./backups" if DEBUG else "/backups"}
DBBACKUP_CONNECTOR_MAPPING = {
"django_prometheus.db.backends.postgresql": "dbbackup.db.postgresql.PgDumpConnector"
}
if CONFIG.y("postgresql.s3_backup"): if CONFIG.y("postgresql.s3_backup"):
DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage" DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
DBBACKUP_STORAGE_OPTIONS = { DBBACKUP_STORAGE_OPTIONS = {
@ -331,9 +329,10 @@ if CONFIG.y("postgresql.s3_backup"):
"region_name": CONFIG.y("postgresql.s3_backup.region", "eu-central-1"), "region_name": CONFIG.y("postgresql.s3_backup.region", "eu-central-1"),
"default_acl": "private", "default_acl": "private",
"endpoint_url": CONFIG.y("postgresql.s3_backup.host"), "endpoint_url": CONFIG.y("postgresql.s3_backup.host"),
"location": CONFIG.y("postgresql.s3_backup.location", ""),
} }
j_print( j_print(
"Database backup to S3 is configured.", "Database backup to S3 is configured",
host=CONFIG.y("postgresql.s3_backup.host"), host=CONFIG.y("postgresql.s3_backup.host"),
) )
@ -355,7 +354,7 @@ if _ERROR_REPORTING:
send_default_pii=CONFIG.y_bool("error_reporting.send_pii", False), send_default_pii=CONFIG.y_bool("error_reporting.send_pii", False),
) )
j_print( j_print(
"Error reporting is enabled.", "Error reporting is enabled",
env=CONFIG.y("error_reporting.environment", "customer"), env=CONFIG.y("error_reporting.environment", "customer"),
) )

View File

@ -34,7 +34,6 @@ class PlexSourceSerializer(SourceSerializer):
"allow_friends", "allow_friends",
"plex_token", "plex_token",
] ]
extra_kwargs = {"plex_token": {"write_only": True}}
class PlexTokenRedeemSerializer(PassiveSerializer): class PlexTokenRedeemSerializer(PassiveSerializer):

View File

@ -3,6 +3,7 @@ from rest_framework.fields import JSONField
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import is_dict from authentik.core.api.utils import is_dict
from authentik.flows.api.stages import StageSerializer from authentik.flows.api.stages import StageSerializer
from authentik.stages.invitation.models import Invitation, InvitationStage from authentik.stages.invitation.models import Invitation, InvitationStage
@ -29,6 +30,7 @@ class InvitationStageViewSet(ModelViewSet):
class InvitationSerializer(ModelSerializer): class InvitationSerializer(ModelSerializer):
"""Invitation Serializer""" """Invitation Serializer"""
created_by = UserSerializer(read_only=True)
fixed_data = JSONField(validators=[is_dict], required=False) fixed_data = JSONField(validators=[is_dict], required=False)
class Meta: class Meta:
@ -41,7 +43,6 @@ class InvitationSerializer(ModelSerializer):
"created_by", "created_by",
"single_use", "single_use",
] ]
depth = 2
class InvitationViewSet(ModelViewSet): class InvitationViewSet(ModelViewSet):

View File

@ -33,7 +33,7 @@ class UserLoginStageView(StageView):
backend=backend, backend=backend,
) )
delta = timedelta_from_string(self.executor.current_stage.session_duration) delta = timedelta_from_string(self.executor.current_stage.session_duration)
if delta.seconds == 0: if delta.total_seconds() == 0:
self.request.session.set_expiry(0) self.request.session.set_expiry(0)
else: else:
self.request.session.set_expiry(delta) self.request.session.set_expiry(delta)

View File

@ -19,7 +19,7 @@ variables:
branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }} branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }}
stages: stages:
- stage: Lint - stage: Lint_and_test
jobs: jobs:
- job: pylint - job: pylint
pool: pool:
@ -118,8 +118,6 @@ stages:
- task: CmdLine@2 - task: CmdLine@2
inputs: inputs:
script: pipenv run pyright e2e lifecycle script: pipenv run pyright e2e lifecycle
- stage: Test
jobs:
- job: migrations - job: migrations
pool: pool:
vmImage: 'ubuntu-latest' vmImage: 'ubuntu-latest'

View File

@ -21,7 +21,7 @@ services:
networks: networks:
- internal - internal
server: server:
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc6} image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc8}
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:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc6} image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc8}
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.5.1-rc6" const VERSION = "2021.5.1-rc8"

View File

@ -17,6 +17,7 @@ require (
github.com/go-redis/redis/v7 v7.4.0 // indirect github.com/go-redis/redis/v7 v7.4.0 // indirect
github.com/go-swagger/go-swagger v0.27.0 // indirect github.com/go-swagger/go-swagger v0.27.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/justinas/alice v1.2.0 github.com/justinas/alice v1.2.0

View File

@ -352,6 +352,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=

View File

@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/go-openapi/runtime" "github.com/go-openapi/runtime"
"github.com/google/uuid"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/recws-org/recws" "github.com/recws-org/recws"
"goauthentik.io/outpost/pkg" "goauthentik.io/outpost/pkg"
@ -35,7 +36,8 @@ type APIController struct {
reloadOffset time.Duration reloadOffset time.Duration
wsConn *recws.RecConn wsConn *recws.RecConn
instanceUUID uuid.UUID
} }
// NewAPIController initialise new API Controller instance from URL and API token // NewAPIController initialise new API Controller instance from URL and API token
@ -70,6 +72,7 @@ func NewAPIController(akURL url.URL, token string) *APIController {
logger: log, logger: log,
reloadOffset: time.Duration(rand.Intn(10)) * time.Second, reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
instanceUUID: uuid.New(),
} }
ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset) ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset)
ac.initWS(akURL, outpost.Pk) ac.initWS(akURL, outpost.Pk)
@ -90,6 +93,10 @@ func (a *APIController) Start() error {
a.logger.Debug("Starting WS Health notifier...") a.logger.Debug("Starting WS Health notifier...")
a.startWSHealth() a.startWSHealth()
}() }()
go func() {
a.logger.Debug("Starting Interval updater...")
a.startIntervalUpdater()
}()
go func() { go func() {
err := a.Server.Start() err := a.Server.Start()
if err != nil { if err != nil {

View File

@ -47,6 +47,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
Instruction: WebsocketInstructionHello, Instruction: WebsocketInstructionHello,
Args: map[string]interface{}{ Args: map[string]interface{}{
"version": pkg.VERSION, "version": pkg.VERSION,
"uuid": ac.instanceUUID.String(),
}, },
} }
err := ws.WriteJSON(msg) err := ws.WriteJSON(msg)
@ -100,6 +101,7 @@ func (ac *APIController) startWSHealth() {
Instruction: WebsocketInstructionHello, Instruction: WebsocketInstructionHello,
Args: map[string]interface{}{ Args: map[string]interface{}{
"version": pkg.VERSION, "version": pkg.VERSION,
"uuid": ac.instanceUUID.String(),
}, },
} }
err := ac.wsConn.WriteJSON(aliveMsg) err := ac.wsConn.WriteJSON(aliveMsg)
@ -111,3 +113,14 @@ func (ac *APIController) startWSHealth() {
} }
} }
} }
func (ac *APIController) startIntervalUpdater() {
logger := ac.logger.WithField("loop", "interval-updater")
ticker := time.NewTicker(time.Second * 150)
for ; true; <-ticker.C {
err := ac.Server.Refresh()
if err != nil {
logger.WithError(err).Debug("Failed to update")
}
}
}

View File

@ -20,6 +20,8 @@ func doGlobalSetup(config map[string]interface{}) {
}, },
}) })
switch config[ConfigLogLevel].(string) { switch config[ConfigLogLevel].(string) {
case "trace":
log.SetLevel(log.TraceLevel)
case "debug": case "debug":
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
case "info": case "info":

View File

@ -57,7 +57,7 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
for k := range s.Handlers { for k := range s.Handlers {
hostKeys = append(hostKeys, k) hostKeys = append(hostKeys, k)
} }
s.logger.WithField("host", host).WithField("known-hosts", strings.Join(hostKeys, ", ")).Debug("Host header does not match any we know of") s.logger.WithField("host", host).WithField("known-hosts", strings.Join(hostKeys, ",")).Debug("Host header does not match any we know of")
w.WriteHeader(404) w.WriteHeader(404)
return return
} }

View File

@ -1,12 +1,20 @@
package proxy package proxy
import "net/http" import (
"net"
"net/http"
)
var xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host") var xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host")
func getHost(req *http.Request) string { func getHost(req *http.Request) string {
host := req.Host
if req.Header.Get(xForwardedHost) != "" { if req.Header.Get(xForwardedHost) != "" {
return req.Header.Get(xForwardedHost) host = req.Header.Get(xForwardedHost)
} }
return req.Host hostOnly, _, err := net.SplitHostPort(host)
if err != nil {
return host
}
return hostOnly
} }

View File

@ -1,3 +1,3 @@
package pkg package pkg
const VERSION = "2021.5.1-rc6" const VERSION = "2021.5.1-rc8"

View File

@ -531,11 +531,6 @@ paths:
description: '' description: ''
required: false required: false
type: string type: string
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search - name: search
in: query in: query
description: A search term. description: A search term.
@ -2532,7 +2527,10 @@ paths:
get: get:
operationId: crypto_certificatekeypairs_view_certificate operationId: crypto_certificatekeypairs_view_certificate
description: Return certificate-key pairs certificate and log access description: Return certificate-key pairs certificate and log access
parameters: [] parameters:
- name: download
in: query
type: boolean
responses: responses:
'200': '200':
description: '' description: ''
@ -2560,7 +2558,10 @@ paths:
get: get:
operationId: crypto_certificatekeypairs_view_private_key operationId: crypto_certificatekeypairs_view_private_key
description: Return certificate-key pairs private key and log access description: Return certificate-key pairs private key and log access
parameters: [] parameters:
- name: download
in: query
type: boolean
responses: responses:
'200': '200':
description: '' description: ''
@ -9701,7 +9702,10 @@ paths:
get: get:
operationId: providers_saml_metadata operationId: providers_saml_metadata
description: Return metadata as XML string description: Return metadata as XML string
parameters: [] parameters:
- name: download
in: query
type: boolean
responses: responses:
'200': '200':
description: '' description: ''
@ -16203,7 +16207,7 @@ definitions:
required: required:
- name - name
- providers - providers
- _config - config
type: object type: object
properties: properties:
pk: pk:
@ -16242,8 +16246,8 @@ definitions:
title: Token identifier title: Token identifier
type: string type: string
readOnly: true readOnly: true
_config: config:
title: config title: Config
type: object type: object
OutpostDefaultConfig: OutpostDefaultConfig:
type: object type: object
@ -17494,6 +17498,11 @@ definitions:
description: Enable support for forwardAuth in traefik and nginx auth_request. description: Enable support for forwardAuth in traefik and nginx auth_request.
Exclusive with internal_host. Exclusive with internal_host.
type: boolean type: boolean
redirect_uris:
title: Redirect uris
type: string
readOnly: true
minLength: 1
SAMLProvider: SAMLProvider:
required: required:
- name - name
@ -18878,237 +18887,7 @@ definitions:
title: Fixed data title: Fixed data
type: object type: object
created_by: created_by:
required: $ref: '#/definitions/User'
- password
- username
- name
type: object
properties:
id:
title: ID
type: integer
readOnly: true
password:
title: Password
type: string
maxLength: 128
minLength: 1
last_login:
title: Last login
type: string
format: date-time
x-nullable: true
username:
title: Username
description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
only.
type: string
pattern: ^[\w.@+-]+$
maxLength: 150
minLength: 1
first_name:
title: First name
type: string
maxLength: 150
last_name:
title: Last name
type: string
maxLength: 150
email:
title: Email address
type: string
format: email
maxLength: 254
is_active:
title: Active
description: Designates whether this user should be treated as active.
Unselect this instead of deleting accounts.
type: boolean
date_joined:
title: Date joined
type: string
format: date-time
uuid:
title: Uuid
type: string
format: uuid
readOnly: true
name:
title: Name
description: User's display name.
type: string
minLength: 1
password_change_date:
title: Password change date
type: string
format: date-time
readOnly: true
attributes:
title: Attributes
type: object
groups:
type: array
items:
required:
- name
type: object
properties:
id:
title: ID
type: integer
readOnly: true
name:
title: Name
type: string
maxLength: 150
minLength: 1
permissions:
type: array
items:
type: integer
uniqueItems: true
readOnly: true
user_permissions:
type: array
items:
required:
- name
- codename
- content_type
type: object
properties:
id:
title: ID
type: integer
readOnly: true
name:
title: Name
type: string
maxLength: 255
minLength: 1
codename:
title: Codename
type: string
maxLength: 100
minLength: 1
content_type:
title: Content type
type: integer
readOnly: true
sources:
type: array
items:
required:
- name
- slug
type: object
properties:
pbm_uuid:
title: Pbm uuid
type: string
format: uuid
readOnly: true
policy_engine_mode:
title: Policy engine mode
type: string
enum:
- all
- any
managed:
title: Managed by authentik
description: Objects which are managed by authentik. These objects
are created and updated automatically. This is flag only indicates
that an object can be overwritten by migrations. You can still
modify the objects via the API, but expect changes to be overwritten
in a later update.
type: string
minLength: 1
x-nullable: true
name:
title: Name
description: Source's display Name.
type: string
minLength: 1
slug:
title: Slug
description: Internal source name, used in URLs.
type: string
format: slug
pattern: ^[-a-zA-Z0-9_]+$
maxLength: 50
minLength: 1
enabled:
title: Enabled
type: boolean
user_matching_mode:
title: User matching mode
description: How the source determines if an existing user should
be authenticated or a new user enrolled.
type: string
enum:
- identifier
- email_link
- email_deny
- username_link
- username_deny
authentication_flow:
title: Authentication flow
description: Flow to use when authenticating existing users.
type: string
format: uuid
x-nullable: true
enrollment_flow:
title: Enrollment flow
description: Flow to use when enrolling new users.
type: string
format: uuid
x-nullable: true
policies:
type: array
items:
type: string
format: uuid
readOnly: true
uniqueItems: true
property_mappings:
type: array
items:
type: string
format: uuid
uniqueItems: true
readOnly: true
ak_groups:
type: array
items:
required:
- name
- parent
type: object
properties:
group_uuid:
title: Group uuid
type: string
format: uuid
readOnly: true
name:
title: Name
type: string
maxLength: 80
minLength: 1
is_superuser:
title: Is superuser
description: Users added to this group will be superusers.
type: boolean
attributes:
title: Attributes
type: object
parent:
title: Parent
type: string
format: uuid
x-nullable: true
readOnly: true
readOnly: true
single_use: single_use:
title: Single use title: Single use
description: When enabled, the invitation will be deleted after usage. description: When enabled, the invitation will be deleted after usage.

View File

@ -81,7 +81,7 @@ http {
location /static/ { location /static/ {
expires 31d; expires 31d;
add_header Cache-Control "public, no-transform"; add_header Cache-Control "public, no-transform";
add_header X-authentik-version "2021.5.1-rc6"; add_header X-authentik-version "2021.5.1-rc8";
add_header Vary X-authentik-version; add_header Vary X-authentik-version;
} }

423
web/package-lock.json generated
View File

@ -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.3.5", "@sentry/browser": "^6.3.6",
"@sentry/tracing": "^6.3.5", "@sentry/tracing": "^6.3.6",
"@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.22.1", "@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.22.1", "@typescript-eslint/parser": "^4.23.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",
@ -42,7 +42,7 @@
"eslint": "^7.26.0", "eslint": "^7.26.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.2", "eslint-plugin-custom-elements": "0.0.2",
"eslint-plugin-lit": "^1.3.0", "eslint-plugin-lit": "^1.4.0",
"flowchart.js": "^1.15.0", "flowchart.js": "^1.15.0",
"lit-element": "^2.5.1", "lit-element": "^2.5.1",
"lit-html": "^1.4.1", "lit-html": "^1.4.1",
@ -2338,33 +2338,13 @@
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="
}, },
"node_modules/@sentry/browser": { "node_modules/@sentry/browser": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.6.tgz",
"integrity": "sha512-fjkhPR5gLCGVWhbWjEoN64hnmTvfTLRCgWmYTc9SiGchWFoFEmLqZyF2uJFyt27+qamLQ9fN58nnv4Ly2yyxqg==", "integrity": "sha512-l4323jxuBOArki6Wf+EHes39IEyJ2Zj/CIUaTY7GWh7CntpfHQAfFmZWQw3Ozq+ka1u8lVp25RPhb4Wng3azNA==",
"dependencies": { "dependencies": {
"@sentry/core": "6.3.5", "@sentry/core": "6.3.6",
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"@sentry/utils": "6.3.5", "@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/browser/node_modules/@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/browser/node_modules/@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"dependencies": {
"@sentry/types": "6.3.5",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2377,60 +2357,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/core": { "node_modules/@sentry/core": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.6.tgz",
"integrity": "sha512-VR2ibDy33mryD0mT6d9fGhKjdNzS2FSwwZPe9GvmNOjkyjly/oV91BKVoYJneCqOeq8fyj2lvkJGKuupdJNDqg==", "integrity": "sha512-w6BRizAqh7BaiM9oeKzO6aACXwRijUPacYaVLX/OfhqCSueF9uDxpMRT7+4D/eCeDVqgJYhBJ4Vsu2NSstkk4A==",
"dependencies": { "dependencies": {
"@sentry/hub": "6.3.5", "@sentry/hub": "6.3.6",
"@sentry/minimal": "6.3.5", "@sentry/minimal": "6.3.6",
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"@sentry/utils": "6.3.5", "@sentry/utils": "6.3.6",
"tslib": "^1.9.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/core/node_modules/@sentry/hub": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.5.tgz",
"integrity": "sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ==",
"dependencies": {
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"tslib": "^1.9.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/core/node_modules/@sentry/minimal": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.5.tgz",
"integrity": "sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA==",
"dependencies": {
"@sentry/hub": "6.3.5",
"@sentry/types": "6.3.5",
"tslib": "^1.9.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/core/node_modules/@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/core/node_modules/@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"dependencies": {
"@sentry/types": "6.3.5",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2443,12 +2377,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/hub": { "node_modules/@sentry/hub": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.6.tgz",
"integrity": "sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ==", "integrity": "sha512-foBZ3ilMnm9Gf9OolrAxYHK8jrA6IF72faDdJ3Al+1H27qcpnBaMdrdEp2/jzwu/dgmwuLmbBaMjEPXaGH/0JQ==",
"dependencies": { "dependencies": {
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"@sentry/utils": "6.3.5", "@sentry/utils": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2461,12 +2395,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/minimal": { "node_modules/@sentry/minimal": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.6.tgz",
"integrity": "sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA==", "integrity": "sha512-uM2/dH0a6zfvI5f+vg+/mST+uTBdN6Jgpm585ipH84ckCYQwIIDRg6daqsen4S1sy/xgg1P1YyC3zdEC4G6b1Q==",
"dependencies": { "dependencies": {
"@sentry/hub": "6.3.5", "@sentry/hub": "6.3.6",
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2479,14 +2413,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/tracing": { "node_modules/@sentry/tracing": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.6.tgz",
"integrity": "sha512-TNKAST1ge2g24BlTfVxNp4gP5t3drbi0OVCh8h8ah+J7UjHSfdiqhd9W2h5qv1GO61gGlpWeN/TyioyQmOxu0Q==", "integrity": "sha512-dfyYY2eESJGt5Qbigmfmb2U9ntqbwPhLNAOcjKaVg9WQRV5q2RkHCVctPoYk7TEAvfNeNRXCD8SnuFOZhttt8g==",
"dependencies": { "dependencies": {
"@sentry/hub": "6.3.5", "@sentry/hub": "6.3.6",
"@sentry/minimal": "6.3.5", "@sentry/minimal": "6.3.6",
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"@sentry/utils": "6.3.5", "@sentry/utils": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2499,19 +2433,19 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/types": { "node_modules/@sentry/types": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.6.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ==", "integrity": "sha512-93cFJdJkWyCfyZeWFARSU11qnoHVOS/R2h5WIsEf+jbQmkqG2C+TXVz/19s6nHVsfDrwpvYpwALPv4/nrxfU7g==",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/@sentry/utils": { "node_modules/@sentry/utils": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.6.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==", "integrity": "sha512-HnYlDBf8Dq8MEv7AulH7B6R1D/2LAooVclGdjg48tSrr9g+31kmtj+SAj2WWVHP9+bp29BWaC7i5nkfKrOibWw==",
"dependencies": { "dependencies": {
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2669,12 +2603,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.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz",
"integrity": "sha512-kVTAghWDDhsvQ602tHBc6WmQkdaYbkcTwZu+7l24jtJiYvm9l+/y/b2BZANEezxPDiX5MK2ZecE+9BFi/YJryw==", "integrity": "sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw==",
"dependencies": { "dependencies": {
"@typescript-eslint/experimental-utils": "4.22.1", "@typescript-eslint/experimental-utils": "4.23.0",
"@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/scope-manager": "4.23.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15", "lodash": "^4.17.15",
@ -2700,14 +2634,14 @@
} }
}, },
"node_modules/@typescript-eslint/experimental-utils": { "node_modules/@typescript-eslint/experimental-utils": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz",
"integrity": "sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ==", "integrity": "sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA==",
"dependencies": { "dependencies": {
"@types/json-schema": "^7.0.3", "@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/scope-manager": "4.23.0",
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"@typescript-eslint/typescript-estree": "4.22.1", "@typescript-eslint/typescript-estree": "4.23.0",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0" "eslint-utils": "^2.0.0"
}, },
@ -2723,13 +2657,13 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.23.0.tgz",
"integrity": "sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw==", "integrity": "sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug==",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/scope-manager": "4.23.0",
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"@typescript-eslint/typescript-estree": "4.22.1", "@typescript-eslint/typescript-estree": "4.23.0",
"debug": "^4.1.1" "debug": "^4.1.1"
}, },
"engines": { "engines": {
@ -2749,12 +2683,12 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz",
"integrity": "sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g==", "integrity": "sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w==",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"@typescript-eslint/visitor-keys": "4.22.1" "@typescript-eslint/visitor-keys": "4.23.0"
}, },
"engines": { "engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1" "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
@ -2765,9 +2699,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.23.0.tgz",
"integrity": "sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw==", "integrity": "sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw==",
"engines": { "engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1" "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
}, },
@ -2777,12 +2711,12 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz",
"integrity": "sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A==", "integrity": "sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw==",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"@typescript-eslint/visitor-keys": "4.22.1", "@typescript-eslint/visitor-keys": "4.23.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"globby": "^11.0.1", "globby": "^11.0.1",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -2822,11 +2756,11 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz",
"integrity": "sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ==", "integrity": "sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg==",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
}, },
"engines": { "engines": {
@ -3983,13 +3917,16 @@
} }
}, },
"node_modules/eslint-plugin-lit": { "node_modules/eslint-plugin-lit": {
"version": "1.3.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.3.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.4.0.tgz",
"integrity": "sha512-fy6Lr5vYI3kvCYaDXA20lwyKAp1keS9UjR5ntj8U2TeV+1yUta3S7xxXe+rABKRPbcNzi1ZvQLE1LmNKc9yr4Q==", "integrity": "sha512-3PJCC1p4pvDBKtFmg1g2cGzAgJF4IDqhb9NJUh95nYc+QXExa/O/0fILF4WB6X7qdNQKm+gW6nYtSKTyYPHtXw==",
"dependencies": { "dependencies": {
"parse5": "^6.0.1", "parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1", "parse5-htmlparser2-tree-adapter": "^6.0.1",
"requireindex": "^1.2.0" "requireindex": "^1.2.0"
},
"peerDependencies": {
"eslint": ">= 5"
} }
}, },
"node_modules/eslint-rule-documentation": { "node_modules/eslint-rule-documentation": {
@ -10160,30 +10097,16 @@
} }
}, },
"@sentry/browser": { "@sentry/browser": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.3.6.tgz",
"integrity": "sha512-fjkhPR5gLCGVWhbWjEoN64hnmTvfTLRCgWmYTc9SiGchWFoFEmLqZyF2uJFyt27+qamLQ9fN58nnv4Ly2yyxqg==", "integrity": "sha512-l4323jxuBOArki6Wf+EHes39IEyJ2Zj/CIUaTY7GWh7CntpfHQAfFmZWQw3Ozq+ka1u8lVp25RPhb4Wng3azNA==",
"requires": { "requires": {
"@sentry/core": "6.3.5", "@sentry/core": "6.3.6",
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"@sentry/utils": "6.3.5", "@sentry/utils": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
"@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ=="
},
"@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"requires": {
"@sentry/types": "6.3.5",
"tslib": "^1.9.3"
}
},
"tslib": { "tslib": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -10192,51 +10115,17 @@
} }
}, },
"@sentry/core": { "@sentry/core": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.6.tgz",
"integrity": "sha512-VR2ibDy33mryD0mT6d9fGhKjdNzS2FSwwZPe9GvmNOjkyjly/oV91BKVoYJneCqOeq8fyj2lvkJGKuupdJNDqg==", "integrity": "sha512-w6BRizAqh7BaiM9oeKzO6aACXwRijUPacYaVLX/OfhqCSueF9uDxpMRT7+4D/eCeDVqgJYhBJ4Vsu2NSstkk4A==",
"requires": { "requires": {
"@sentry/hub": "6.3.5", "@sentry/hub": "6.3.6",
"@sentry/minimal": "6.3.5", "@sentry/minimal": "6.3.6",
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"@sentry/utils": "6.3.5", "@sentry/utils": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
"@sentry/hub": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.5.tgz",
"integrity": "sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ==",
"requires": {
"@sentry/types": "6.3.5",
"@sentry/utils": "6.3.5",
"tslib": "^1.9.3"
}
},
"@sentry/minimal": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.5.tgz",
"integrity": "sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA==",
"requires": {
"@sentry/hub": "6.3.5",
"@sentry/types": "6.3.5",
"tslib": "^1.9.3"
}
},
"@sentry/types": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ=="
},
"@sentry/utils": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==",
"requires": {
"@sentry/types": "6.3.5",
"tslib": "^1.9.3"
}
},
"tslib": { "tslib": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -10245,12 +10134,12 @@
} }
}, },
"@sentry/hub": { "@sentry/hub": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.6.tgz",
"integrity": "sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ==", "integrity": "sha512-foBZ3ilMnm9Gf9OolrAxYHK8jrA6IF72faDdJ3Al+1H27qcpnBaMdrdEp2/jzwu/dgmwuLmbBaMjEPXaGH/0JQ==",
"requires": { "requires": {
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"@sentry/utils": "6.3.5", "@sentry/utils": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10262,12 +10151,12 @@
} }
}, },
"@sentry/minimal": { "@sentry/minimal": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.6.tgz",
"integrity": "sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA==", "integrity": "sha512-uM2/dH0a6zfvI5f+vg+/mST+uTBdN6Jgpm585ipH84ckCYQwIIDRg6daqsen4S1sy/xgg1P1YyC3zdEC4G6b1Q==",
"requires": { "requires": {
"@sentry/hub": "6.3.5", "@sentry/hub": "6.3.6",
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10279,14 +10168,14 @@
} }
}, },
"@sentry/tracing": { "@sentry/tracing": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.6.tgz",
"integrity": "sha512-TNKAST1ge2g24BlTfVxNp4gP5t3drbi0OVCh8h8ah+J7UjHSfdiqhd9W2h5qv1GO61gGlpWeN/TyioyQmOxu0Q==", "integrity": "sha512-dfyYY2eESJGt5Qbigmfmb2U9ntqbwPhLNAOcjKaVg9WQRV5q2RkHCVctPoYk7TEAvfNeNRXCD8SnuFOZhttt8g==",
"requires": { "requires": {
"@sentry/hub": "6.3.5", "@sentry/hub": "6.3.6",
"@sentry/minimal": "6.3.5", "@sentry/minimal": "6.3.6",
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"@sentry/utils": "6.3.5", "@sentry/utils": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10298,16 +10187,16 @@
} }
}, },
"@sentry/types": { "@sentry/types": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.6.tgz",
"integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ==" "integrity": "sha512-93cFJdJkWyCfyZeWFARSU11qnoHVOS/R2h5WIsEf+jbQmkqG2C+TXVz/19s6nHVsfDrwpvYpwALPv4/nrxfU7g=="
}, },
"@sentry/utils": { "@sentry/utils": {
"version": "6.3.5", "version": "6.3.6",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.6.tgz",
"integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==", "integrity": "sha512-HnYlDBf8Dq8MEv7AulH7B6R1D/2LAooVclGdjg48tSrr9g+31kmtj+SAj2WWVHP9+bp29BWaC7i5nkfKrOibWw==",
"requires": { "requires": {
"@sentry/types": "6.3.5", "@sentry/types": "6.3.6",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10464,12 +10353,12 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==" "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz",
"integrity": "sha512-kVTAghWDDhsvQ602tHBc6WmQkdaYbkcTwZu+7l24jtJiYvm9l+/y/b2BZANEezxPDiX5MK2ZecE+9BFi/YJryw==", "integrity": "sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw==",
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "4.22.1", "@typescript-eslint/experimental-utils": "4.23.0",
"@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/scope-manager": "4.23.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15", "lodash": "^4.17.15",
@ -10479,50 +10368,50 @@
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz",
"integrity": "sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ==", "integrity": "sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA==",
"requires": { "requires": {
"@types/json-schema": "^7.0.3", "@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/scope-manager": "4.23.0",
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"@typescript-eslint/typescript-estree": "4.22.1", "@typescript-eslint/typescript-estree": "4.23.0",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0" "eslint-utils": "^2.0.0"
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.23.0.tgz",
"integrity": "sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw==", "integrity": "sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug==",
"requires": { "requires": {
"@typescript-eslint/scope-manager": "4.22.1", "@typescript-eslint/scope-manager": "4.23.0",
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"@typescript-eslint/typescript-estree": "4.22.1", "@typescript-eslint/typescript-estree": "4.23.0",
"debug": "^4.1.1" "debug": "^4.1.1"
} }
}, },
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz",
"integrity": "sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g==", "integrity": "sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w==",
"requires": { "requires": {
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"@typescript-eslint/visitor-keys": "4.22.1" "@typescript-eslint/visitor-keys": "4.23.0"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.23.0.tgz",
"integrity": "sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw==" "integrity": "sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw=="
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz",
"integrity": "sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A==", "integrity": "sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw==",
"requires": { "requires": {
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"@typescript-eslint/visitor-keys": "4.22.1", "@typescript-eslint/visitor-keys": "4.23.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"globby": "^11.0.1", "globby": "^11.0.1",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -10546,11 +10435,11 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.22.1", "version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz",
"integrity": "sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ==", "integrity": "sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg==",
"requires": { "requires": {
"@typescript-eslint/types": "4.22.1", "@typescript-eslint/types": "4.23.0",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
} }
}, },
@ -11549,9 +11438,9 @@
} }
}, },
"eslint-plugin-lit": { "eslint-plugin-lit": {
"version": "1.3.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.3.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.4.0.tgz",
"integrity": "sha512-fy6Lr5vYI3kvCYaDXA20lwyKAp1keS9UjR5ntj8U2TeV+1yUta3S7xxXe+rABKRPbcNzi1ZvQLE1LmNKc9yr4Q==", "integrity": "sha512-3PJCC1p4pvDBKtFmg1g2cGzAgJF4IDqhb9NJUh95nYc+QXExa/O/0fILF4WB6X7qdNQKm+gW6nYtSKTyYPHtXw==",
"requires": { "requires": {
"parse5": "^6.0.1", "parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1", "parse5-htmlparser2-tree-adapter": "^6.0.1",

View File

@ -50,13 +50,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.3.5", "@sentry/browser": "^6.3.6",
"@sentry/tracing": "^6.3.5", "@sentry/tracing": "^6.3.6",
"@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.22.1", "@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.22.1", "@typescript-eslint/parser": "^4.23.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",
@ -68,7 +68,7 @@
"eslint": "^7.26.0", "eslint": "^7.26.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.2", "eslint-plugin-custom-elements": "0.0.2",
"eslint-plugin-lit": "^1.3.0", "eslint-plugin-lit": "^1.4.0",
"flowchart.js": "^1.15.0", "flowchart.js": "^1.15.0",
"lit-element": "^2.5.1", "lit-element": "^2.5.1",
"lit-html": "^1.4.1", "lit-html": "^1.4.1",

View File

@ -204,6 +204,9 @@ body {
.pf-c-form__field-group-header-title-text { .pf-c-form__field-group-header-title-text {
color: var(--ak-dark-foreground); color: var(--ak-dark-foreground);
} }
.pf-c-form__field-group {
border-bottom: 0;
}
/* inputs */ /* inputs */
optgroup, option { optgroup, option {
color: var(--ak-dark-foreground); color: var(--ak-dark-foreground);

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.5.1-rc6"; export const VERSION = "2021.5.1-rc8";
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

@ -5,11 +5,6 @@ import { MessageLevel } from "../messages/Message";
@customElement("ak-action-button") @customElement("ak-action-button")
export class ActionButton extends SpinnerButton { export class ActionButton extends SpinnerButton {
@property()
url = "";
@property()
method = "POST";
@property({attribute: false}) @property({attribute: false})
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -6,6 +6,7 @@ import { ArcElement, BarElement } from "chart.js";
import { TimeScale, LinearScale } from "chart.js"; import { TimeScale, LinearScale } from "chart.js";
import "chartjs-adapter-moment"; import "chartjs-adapter-moment";
import { FONT_COLOUR_DARK_MODE, FONT_COLOUR_LIGHT_MODE } from "../../pages/flows/FlowDiagram"; import { FONT_COLOUR_DARK_MODE, FONT_COLOUR_LIGHT_MODE } from "../../pages/flows/FlowDiagram";
import {EVENT_REFRESH} from "../../constants";
Chart.register(Legend, Tooltip); Chart.register(Legend, Tooltip);
Chart.register(LineController, BarController, DoughnutController); Chart.register(LineController, BarController, DoughnutController);
@ -43,6 +44,13 @@ export abstract class AKChart<T> extends LitElement {
this.chart.resize(); this.chart.resize();
} }
}); });
window.addEventListener(EVENT_REFRESH, () => {
this.apiRequest().then((r: T) => {
if (!this.chart) return;
this.chart.data = this.getChartData(r);
this.chart.update();
});
});
const matcher = window.matchMedia("(prefers-color-scheme: light)"); const matcher = window.matchMedia("(prefers-color-scheme: light)");
const handler = (ev?: MediaQueryListEvent) => { const handler = (ev?: MediaQueryListEvent) => {
if (ev?.matches || matcher.matches) { if (ev?.matches || matcher.matches) {
@ -56,6 +64,22 @@ export abstract class AKChart<T> extends LitElement {
handler(); handler();
} }
firstUpdated(): void {
this.apiRequest().then((r) => {
const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas");
if (!canvas) {
console.warn("Failed to get canvas element");
return false;
}
const ctx = canvas.getContext("2d");
if (!ctx) {
console.warn("failed to get 2d context");
return false;
}
this.chart = this.configureChart(r, ctx);
});
}
getChartType(): string { getChartType(): string {
return "bar"; return "bar";
} }
@ -129,23 +153,6 @@ export abstract class AKChart<T> extends LitElement {
return new Chart(ctx, config as ChartConfiguration); return new Chart(ctx, config as ChartConfiguration);
} }
firstUpdated(): void {
this.apiRequest().then((r) => {
const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas");
if (!canvas) {
console.warn("Failed to get canvas element");
return false;
}
const ctx = canvas.getContext("2d");
if (!ctx) {
console.warn("failed to get 2d context");
return false;
}
this.chart = this.configureChart(r, ctx);
});
}
render(): TemplateResult { render(): TemplateResult {
return html` return html`
<div class="container"> <div class="container">

View File

@ -15,6 +15,7 @@ import { MessageLevel } from "../messages/Message";
import { IronFormElement } from "@polymer/iron-form/iron-form"; import { IronFormElement } from "@polymer/iron-form/iron-form";
import { camelToSnake, convertToSlug } from "../../utils"; import { camelToSnake, convertToSlug } from "../../utils";
import { ValidationError } from "authentik-api/src"; import { ValidationError } from "authentik-api/src";
import { EVENT_REFRESH } from "../../constants";
export class APIError extends Error { export class APIError extends Error {
@ -140,6 +141,12 @@ export class Form<T> extends LitElement {
level: MessageLevel.success, level: MessageLevel.success,
message: this.getSuccessMessage() message: this.getSuccessMessage()
}); });
this.dispatchEvent(
new CustomEvent(EVENT_REFRESH, {
bubbles: true,
composed: true,
})
);
return r; return r;
}).catch((ex: Response) => { }).catch((ex: Response) => {
if (ex.status > 399 && ex.status < 500) { if (ex.status > 399 && ex.status < 500) {

View File

@ -0,0 +1,36 @@
import { property } from "lit-element";
import { EVENT_REFRESH } from "../../constants";
import { Form } from "./Form";
export abstract class ModelForm<T, PKT extends string | number> extends Form<T> {
abstract loadInstance(pk: PKT): Promise<T>;
@property({attribute: false})
set instancePk(value: PKT) {
this._instancePk = value;
this.loadInstance(value).then(instance => {
this.instance = instance;
});
}
private _instancePk?: PKT;
@property({ attribute: false })
instance?: T = this.defaultInstance;
get defaultInstance(): T | undefined {
return undefined;
}
constructor() {
super();
this.addEventListener(EVENT_REFRESH, () => {
if (!this._instancePk) return;
this.loadInstance(this._instancePk).then(instance => {
this.instance = instance;
});
});
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@ import { t } from "@lingui/macro";
import { CSSResult, customElement, property } from "lit-element"; import { CSSResult, customElement, property } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/buttons/Dropdown"; import "../../elements/buttons/Dropdown";
@ -13,18 +12,22 @@ import "../../elements/forms/ModalForm";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import "../../elements/forms/FormGroup"; import "../../elements/forms/FormGroup";
import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css"; import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-application-form") @customElement("ak-application-form")
export class ApplicationForm extends Form<Application> { export class ApplicationForm extends ModelForm<Application, string> {
@property({ attribute: false }) loadInstance(pk: string): Promise<Application> {
application?: Application; return new CoreApi(DEFAULT_CONFIG).coreApplicationsRead({
slug: pk
});
}
@property({ attribute: false }) @property({ attribute: false })
provider?: number; provider?: number;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.application) { if (this.instance) {
return t`Successfully updated application.`; return t`Successfully updated application.`;
} else { } else {
return t`Successfully created application.`; return t`Successfully created application.`;
@ -37,9 +40,9 @@ export class ApplicationForm extends Form<Application> {
send = (data: Application): Promise<Application | void> => { send = (data: Application): Promise<Application | void> => {
let writeOp: Promise<Application>; let writeOp: Promise<Application>;
if (this.application) { if (this.instance) {
writeOp = new CoreApi(DEFAULT_CONFIG).coreApplicationsUpdate({ writeOp = new CoreApi(DEFAULT_CONFIG).coreApplicationsUpdate({
slug: this.application.slug, slug: this.instance.slug,
data: data data: data
}); });
} else { } else {
@ -72,7 +75,7 @@ export class ApplicationForm extends Form<Application> {
${Array.from(m).map(([group, providers]) => { ${Array.from(m).map(([group, providers]) => {
return html`<optgroup label=${group}> return html`<optgroup label=${group}>
${providers.map(p => { ${providers.map(p => {
const selected = (this.application?.provider === p.pk) || (this.provider === p.pk); const selected = (this.instance?.provider === p.pk) || (this.provider === p.pk);
return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>${p.name}</option>`; return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>${p.name}</option>`;
})} })}
</optgroup>`; </optgroup>`;
@ -86,21 +89,21 @@ export class ApplicationForm extends Form<Application> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.application?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Application's display Name.`}</p> <p class="pf-c-form__helper-text">${t`Application's display Name.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Slug`} label=${t`Slug`}
?required=${true} ?required=${true}
name="slug"> name="slug">
<input type="text" value="${ifDefined(this.application?.slug)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Internal application name, used in URLs.`}</p> <p class="pf-c-form__helper-text">${t`Internal application name, used in URLs.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Provider`} label=${t`Provider`}
name="provider"> name="provider">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.application?.provider === undefined}>---------</option> <option value="" ?selected=${this.instance?.provider === undefined}>---------</option>
${until(new ProvidersApi(DEFAULT_CONFIG).providersAllList({}).then(providers => { ${until(new ProvidersApi(DEFAULT_CONFIG).providersAllList({}).then(providers => {
return this.groupProviders(providers.results); return this.groupProviders(providers.results);
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
@ -142,10 +145,10 @@ export class ApplicationForm extends Form<Application> {
?required=${true} ?required=${true}
name="policyEngineMode"> name="policyEngineMode">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value=${ApplicationPolicyEngineModeEnum.Any} ?selected=${this.application?.policyEngineMode === ApplicationPolicyEngineModeEnum.Any}> <option value=${ApplicationPolicyEngineModeEnum.Any} ?selected=${this.instance?.policyEngineMode === ApplicationPolicyEngineModeEnum.Any}>
${t`ANY, any policy must match to grant access.`} ${t`ANY, any policy must match to grant access.`}
</option> </option>
<option value=${ApplicationPolicyEngineModeEnum.All} ?selected=${this.application?.policyEngineMode === ApplicationPolicyEngineModeEnum.All}> <option value=${ApplicationPolicyEngineModeEnum.All} ?selected=${this.instance?.policyEngineMode === ApplicationPolicyEngineModeEnum.All}>
${t`ALL, all policies must match to grant access.`} ${t`ALL, all policies must match to grant access.`}
</option> </option>
</select> </select>
@ -158,23 +161,23 @@ export class ApplicationForm extends Form<Application> {
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Launch URL`} label=${t`Launch URL`}
name="metaLaunchUrl"> name="metaLaunchUrl">
<input type="text" value="${ifDefined(this.application?.metaLaunchUrl)}" class="pf-c-form-control"> <input type="text" value="${ifDefined(this.instance?.metaLaunchUrl)}" class="pf-c-form-control">
<p class="pf-c-form__helper-text">${t`If left empty, authentik will try to extract the launch URL based on the selected provider.`}</p> <p class="pf-c-form__helper-text">${t`If left empty, authentik will try to extract the launch URL based on the selected provider.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Icon`} label=${t`Icon`}
name="metaIcon"> name="metaIcon">
<input type="file" value="${ifDefined(this.application?.metaIcon)}" class="pf-c-form-control"> <input type="file" value="${ifDefined(this.instance?.metaIcon)}" class="pf-c-form-control">
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Description`} label=${t`Description`}
name="metaDescription"> name="metaDescription">
<textarea class="pf-c-form-control">${ifDefined(this.application?.metaDescription)}</textarea> <textarea class="pf-c-form-control">${ifDefined(this.instance?.metaDescription)}</textarea>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Publisher`} label=${t`Publisher`}
name="metaPublisher"> name="metaPublisher">
<input type="text" value="${ifDefined(this.application?.metaPublisher)}" class="pf-c-form-control"> <input type="text" value="${ifDefined(this.instance?.metaPublisher)}" class="pf-c-form-control">
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>
</ak-form-group> </ak-form-group>

View File

@ -89,7 +89,7 @@ export class ApplicationListPage extends TablePage<Application> {
<span slot="header"> <span slot="header">
${t`Update Application`} ${t`Update Application`}
</span> </span>
<ak-application-form slot="form" .application=${item}> <ak-application-form slot="form" .instancePk=${item.slug}>
</ak-application-form> </ak-application-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`} ${t`Edit`}

View File

@ -102,7 +102,7 @@ export class ApplicationViewPage extends LitElement {
<span slot="header"> <span slot="header">
${t`Update Application`} ${t`Update Application`}
</span> </span>
<ak-application-form slot="form" .application=${this.application}> <ak-application-form slot="form" .instancePk=${this.application.slug}>
</ak-application-form> </ak-application-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`} ${t`Edit`}

View File

@ -1,21 +1,24 @@
import { CertificateKeyPair, CryptoApi } from "authentik-api"; import { CertificateKeyPair, CryptoApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import "../../elements/CodeMirror"; import "../../elements/CodeMirror";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-crypto-certificate-form") @customElement("ak-crypto-certificate-form")
export class CertificateKeyPairForm extends Form<CertificateKeyPair> { export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string> {
@property({attribute: false}) loadInstance(pk: string): Promise<CertificateKeyPair> {
keyPair?: CertificateKeyPair; return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsRead({
kpUuid: pk,
});
}
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.keyPair) { if (this.instance) {
return t`Successfully updated certificate-key pair.`; return t`Successfully updated certificate-key pair.`;
} else { } else {
return t`Successfully created certificate-key pair.`; return t`Successfully created certificate-key pair.`;
@ -23,9 +26,9 @@ export class CertificateKeyPairForm extends Form<CertificateKeyPair> {
} }
send = (data: CertificateKeyPair): Promise<CertificateKeyPair> => { send = (data: CertificateKeyPair): Promise<CertificateKeyPair> => {
if (this.keyPair) { if (this.instance) {
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsPartialUpdate({ return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsPartialUpdate({
kpUuid: this.keyPair.pk || "", kpUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -41,21 +44,21 @@ export class CertificateKeyPairForm extends Form<CertificateKeyPair> {
label=${t`Name`} label=${t`Name`}
name="name" name="name"
?required=${true}> ?required=${true}>
<input type="text" value="${ifDefined(this.keyPair?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Certificate`} label=${t`Certificate`}
name="certificateData" name="certificateData"
?writeOnly=${this.keyPair !== undefined} ?writeOnly=${this.instance !== undefined}
?required=${true}> ?required=${true}>
<textarea class="pf-c-form-control" required>${ifDefined(this.keyPair?.certificateData)}</textarea> <textarea class="pf-c-form-control" required>${ifDefined(this.instance?.certificateData)}</textarea>
<p class="pf-c-form__helper-text">${t`PEM-encoded Certificate data.`}</p> <p class="pf-c-form__helper-text">${t`PEM-encoded Certificate data.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
name="keyData" name="keyData"
?writeOnly=${this.keyPair !== undefined} ?writeOnly=${this.instance !== undefined}
label=${t`Private Key`}> label=${t`Private Key`}>
<textarea class="pf-c-form-control" >${ifDefined(this.keyPair?.keyData)}</textarea> <textarea class="pf-c-form-control" >${ifDefined(this.instance?.keyData)}</textarea>
<p class="pf-c-form__helper-text">${t`Optional Private Key. If this is set, you can use this keypair for encryption.`}</p> <p class="pf-c-form__helper-text">${t`Optional Private Key. If this is set, you can use this keypair for encryption.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</form>`; </form>`;

View File

@ -70,7 +70,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
<span slot="header"> <span slot="header">
${t`Update Certificate-Key Pair`} ${t`Update Certificate-Key Pair`}
</span> </span>
<ak-crypto-certificate-form slot="form" .keyPair=${item}> <ak-crypto-certificate-form slot="form" .instancePk=${item.pk}>
</ak-crypto-certificate-form> </ak-crypto-certificate-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`} ${t`Edit`}
@ -112,6 +112,23 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
<div class="pf-c-description-list__text">${item.certSubject}</div> <div class="pf-c-description-list__text">${item.certSubject}</div>
</dd> </dd>
</div> </div>
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${t`Download`}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<a class="pf-c-button pf-m-secondary" target="_blank"
href="/api/v2beta/crypto/certificatekeypairs/${item.pk}/view_certificate/?download">
${t`Download Certificate`}
</a>
${item.privateKeyAvailable ? html`<a class="pf-c-button pf-m-secondary" target="_blank"
href="/api/v2beta/crypto/certificatekeypairs/${item.pk}/view_private_key/?download">
${t`Download Private key`}
</a>` : html``}
</div>
</dd>
</div>
</dl> </dl>
</div> </div>
</td> </td>

View File

@ -1,21 +1,24 @@
import { CoreApi, EventsApi, NotificationRule, NotificationRuleSeverityEnum } from "authentik-api"; import { CoreApi, EventsApi, NotificationRule, NotificationRuleSeverityEnum } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-event-rule-form") @customElement("ak-event-rule-form")
export class RuleForm extends Form<NotificationRule> { export class RuleForm extends ModelForm<NotificationRule, string> {
@property({attribute: false}) loadInstance(pk: string): Promise<NotificationRule> {
rule?: NotificationRule; return new EventsApi(DEFAULT_CONFIG).eventsRulesRead({
pbmUuid: pk,
});
}
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.rule) { if (this.instance) {
return t`Successfully updated rule.`; return t`Successfully updated rule.`;
} else { } else {
return t`Successfully created rule.`; return t`Successfully created rule.`;
@ -23,9 +26,9 @@ export class RuleForm extends Form<NotificationRule> {
} }
send = (data: NotificationRule): Promise<NotificationRule> => { send = (data: NotificationRule): Promise<NotificationRule> => {
if (this.rule) { if (this.instance) {
return new EventsApi(DEFAULT_CONFIG).eventsRulesUpdate({ return new EventsApi(DEFAULT_CONFIG).eventsRulesUpdate({
pbmUuid: this.rule.pk || "", pbmUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -37,13 +40,13 @@ export class RuleForm extends Form<NotificationRule> {
renderSeverity(): TemplateResult { renderSeverity(): TemplateResult {
return html` return html`
<option value=${NotificationRuleSeverityEnum.Alert} ?selected=${this.rule?.severity === NotificationRuleSeverityEnum.Alert}> <option value=${NotificationRuleSeverityEnum.Alert} ?selected=${this.instance?.severity === NotificationRuleSeverityEnum.Alert}>
${t`Alert`} ${t`Alert`}
</option> </option>
<option value=${NotificationRuleSeverityEnum.Warning} ?selected=${this.rule?.severity === NotificationRuleSeverityEnum.Warning}> <option value=${NotificationRuleSeverityEnum.Warning} ?selected=${this.instance?.severity === NotificationRuleSeverityEnum.Warning}>
${t`Warning`} ${t`Warning`}
</option> </option>
<option value=${NotificationRuleSeverityEnum.Notice} ?selected=${this.rule?.severity === NotificationRuleSeverityEnum.Notice}> <option value=${NotificationRuleSeverityEnum.Notice} ?selected=${this.instance?.severity === NotificationRuleSeverityEnum.Notice}>
${t`Notice`} ${t`Notice`}
</option> </option>
`; `;
@ -55,16 +58,16 @@ export class RuleForm extends Form<NotificationRule> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.rule?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Group`} label=${t`Group`}
name="group"> name="group">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.rule?.group === undefined}>---------</option> <option value="" ?selected=${this.instance?.group === undefined}>---------</option>
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => { ${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
return groups.results.map(group => { return groups.results.map(group => {
return html`<option value=${ifDefined(group.pk)} ?selected=${this.rule?.group?.groupUuid === group.pk}>${group.name}</option>`; return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.group?.groupUuid === group.pk}>${group.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -76,7 +79,7 @@ export class RuleForm extends Form<NotificationRule> {
<select name="users" class="pf-c-form-control" multiple> <select name="users" class="pf-c-form-control" multiple>
${until(new EventsApi(DEFAULT_CONFIG).eventsTransportsList({}).then(transports => { ${until(new EventsApi(DEFAULT_CONFIG).eventsTransportsList({}).then(transports => {
return transports.results.map(transport => { return transports.results.map(transport => {
const selected = Array.from(this.rule?.transports || []).some(su => { const selected = Array.from(this.instance?.transports || []).some(su => {
return su.uuid == transport.pk; return su.uuid == transport.pk;
}); });
return html`<option value=${ifDefined(transport.pk)} ?selected=${selected}>${transport.name}</option>`; return html`<option value=${ifDefined(transport.pk)} ?selected=${selected}>${transport.name}</option>`;

View File

@ -64,7 +64,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
<span slot="header"> <span slot="header">
${t`Update Notification Rule`} ${t`Update Notification Rule`}
</span> </span>
<ak-event-rule-form slot="form" .rule=${item}> <ak-event-rule-form slot="form" .instancePk=${item.pk}>
</ak-event-rule-form> </ak-event-rule-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`} ${t`Edit`}

View File

@ -3,22 +3,25 @@ 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";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import { first } from "../../utils"; import { first } from "../../utils";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-event-transport-form") @customElement("ak-event-transport-form")
export class TransportForm extends Form<NotificationTransport> { export class TransportForm extends ModelForm<NotificationTransport, string> {
@property({attribute: false}) loadInstance(pk: string): Promise<NotificationTransport> {
transport?: NotificationTransport; return new EventsApi(DEFAULT_CONFIG).eventsTransportsRead({
uuid: pk,
});
}
@property({type: Boolean}) @property({type: Boolean})
showWebhook = false; showWebhook = false;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.transport) { if (this.instance) {
return t`Successfully updated transport.`; return t`Successfully updated transport.`;
} else { } else {
return t`Successfully created transport.`; return t`Successfully created transport.`;
@ -26,9 +29,9 @@ export class TransportForm extends Form<NotificationTransport> {
} }
send = (data: NotificationTransport): Promise<NotificationTransport> => { send = (data: NotificationTransport): Promise<NotificationTransport> => {
if (this.transport) { if (this.instance) {
return new EventsApi(DEFAULT_CONFIG).eventsTransportsUpdate({ return new EventsApi(DEFAULT_CONFIG).eventsTransportsUpdate({
uuid: this.transport.pk || "", uuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -40,21 +43,21 @@ export class TransportForm extends Form<NotificationTransport> {
renderTransportModes(): TemplateResult { renderTransportModes(): TemplateResult {
return html` return html`
<option value=${NotificationTransportModeEnum.Email} ?selected=${this.transport?.mode === NotificationTransportModeEnum.Email}> <option value=${NotificationTransportModeEnum.Email} ?selected=${this.instance?.mode === NotificationTransportModeEnum.Email}>
${t`Email`} ${t`Email`}
</option> </option>
<option value=${NotificationTransportModeEnum.Webhook} ?selected=${this.transport?.mode === NotificationTransportModeEnum.Webhook}> <option value=${NotificationTransportModeEnum.Webhook} ?selected=${this.instance?.mode === NotificationTransportModeEnum.Webhook}>
${t`Webhook (generic)`} ${t`Webhook (generic)`}
</option> </option>
<option value=${NotificationTransportModeEnum.WebhookSlack} ?selected=${this.transport?.mode === NotificationTransportModeEnum.WebhookSlack}> <option value=${NotificationTransportModeEnum.WebhookSlack} ?selected=${this.instance?.mode === NotificationTransportModeEnum.WebhookSlack}>
${t`Webhook (Slack/Discord)`} ${t`Webhook (Slack/Discord)`}
</option> </option>
`; `;
} }
firstUpdated(): void { firstUpdated(): void {
if (this.transport) { if (this.instance) {
this.onModeChange(this.transport.mode); this.onModeChange(this.instance.mode);
} }
} }
@ -72,7 +75,7 @@ export class TransportForm extends Form<NotificationTransport> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.transport?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Mode`} label=${t`Mode`}
@ -89,11 +92,11 @@ export class TransportForm extends Form<NotificationTransport> {
?hidden=${!this.showWebhook} ?hidden=${!this.showWebhook}
label=${t`Webhook URL`} label=${t`Webhook URL`}
name="webhookUrl"> name="webhookUrl">
<input type="text" value="${ifDefined(this.transport?.webhookUrl)}" class="pf-c-form-control"> <input type="text" value="${ifDefined(this.instance?.webhookUrl)}" class="pf-c-form-control">
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="sendOnce"> <ak-form-element-horizontal name="sendOnce">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.transport?.sendOnce, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.sendOnce, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Send once`} ${t`Send once`}
</label> </label>

View File

@ -68,7 +68,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
<span slot="header"> <span slot="header">
${t`Update Notification Transport`} ${t`Update Notification Transport`}
</span> </span>
<ak-event-transport-form slot="form" .transport=${item}> <ak-event-transport-form slot="form" .instancePk=${item.pk}>
</ak-event-transport-form> </ak-event-transport-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`} ${t`Edit`}

View File

@ -58,7 +58,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
<ak-proxy-form <ak-proxy-form
slot="form" slot="form"
.args=${{ .args=${{
"stageUUID": item.stage "instancePk": item.stage
}} }}
type=${ifDefined(item.stageObj?.component)}> type=${ifDefined(item.stageObj?.component)}>
</ak-proxy-form> </ak-proxy-form>
@ -73,7 +73,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
<span slot="header"> <span slot="header">
${t`Update Stage binding`} ${t`Update Stage binding`}
</span> </span>
<ak-stage-binding-form slot="form" .fsb=${item}> <ak-stage-binding-form slot="form" .instancePk=${item.pk}>
</ak-stage-binding-form> </ak-stage-binding-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit Binding`} ${t`Edit Binding`}

View File

@ -1,20 +1,23 @@
import { Flow, FlowDesignationEnum, FlowPolicyEngineModeEnum, FlowsApi } from "authentik-api"; import { Flow, FlowDesignationEnum, FlowPolicyEngineModeEnum, FlowsApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-flow-form") @customElement("ak-flow-form")
export class FlowForm extends Form<Flow> { export class FlowForm extends ModelForm<Flow, string> {
@property({attribute: false}) loadInstance(pk: string): Promise<Flow> {
flow?: Flow; return new FlowsApi(DEFAULT_CONFIG).flowsInstancesRead({
slug: pk,
});
}
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.flow) { if (this.instance) {
return t`Successfully updated flow.`; return t`Successfully updated flow.`;
} else { } else {
return t`Successfully created flow.`; return t`Successfully created flow.`;
@ -23,9 +26,9 @@ export class FlowForm extends Form<Flow> {
send = (data: Flow): Promise<void | Flow> => { send = (data: Flow): Promise<void | Flow> => {
let writeOp: Promise<Flow>; let writeOp: Promise<Flow>;
if (this.flow) { if (this.instance) {
writeOp = new FlowsApi(DEFAULT_CONFIG).flowsInstancesUpdate({ writeOp = new FlowsApi(DEFAULT_CONFIG).flowsInstancesUpdate({
slug: this.flow.slug, slug: this.instance.slug,
data: data data: data
}); });
} else { } else {
@ -47,25 +50,25 @@ export class FlowForm extends Form<Flow> {
renderDesignations(): TemplateResult { renderDesignations(): TemplateResult {
return html` return html`
<option value=${FlowDesignationEnum.Authentication} ?selected=${this.flow?.designation === FlowDesignationEnum.Authentication}> <option value=${FlowDesignationEnum.Authentication} ?selected=${this.instance?.designation === FlowDesignationEnum.Authentication}>
${t`Authentication`} ${t`Authentication`}
</option> </option>
<option value=${FlowDesignationEnum.Authorization} ?selected=${this.flow?.designation === FlowDesignationEnum.Authorization}> <option value=${FlowDesignationEnum.Authorization} ?selected=${this.instance?.designation === FlowDesignationEnum.Authorization}>
${t`Authorization`} ${t`Authorization`}
</option> </option>
<option value=${FlowDesignationEnum.Enrollment} ?selected=${this.flow?.designation === FlowDesignationEnum.Enrollment}> <option value=${FlowDesignationEnum.Enrollment} ?selected=${this.instance?.designation === FlowDesignationEnum.Enrollment}>
${t`Enrollment`} ${t`Enrollment`}
</option> </option>
<option value=${FlowDesignationEnum.Invalidation} ?selected=${this.flow?.designation === FlowDesignationEnum.Invalidation}> <option value=${FlowDesignationEnum.Invalidation} ?selected=${this.instance?.designation === FlowDesignationEnum.Invalidation}>
${t`Invalidation`} ${t`Invalidation`}
</option> </option>
<option value=${FlowDesignationEnum.Recovery} ?selected=${this.flow?.designation === FlowDesignationEnum.Recovery}> <option value=${FlowDesignationEnum.Recovery} ?selected=${this.instance?.designation === FlowDesignationEnum.Recovery}>
${t`Recovery`} ${t`Recovery`}
</option> </option>
<option value=${FlowDesignationEnum.StageConfiguration} ?selected=${this.flow?.designation === FlowDesignationEnum.StageConfiguration}> <option value=${FlowDesignationEnum.StageConfiguration} ?selected=${this.instance?.designation === FlowDesignationEnum.StageConfiguration}>
${t`Stage Configuration`} ${t`Stage Configuration`}
</option> </option>
<option value=${FlowDesignationEnum.Unenrollment} ?selected=${this.flow?.designation === FlowDesignationEnum.Unenrollment}> <option value=${FlowDesignationEnum.Unenrollment} ?selected=${this.instance?.designation === FlowDesignationEnum.Unenrollment}>
${t`Unenrollment`} ${t`Unenrollment`}
</option> </option>
`; `;
@ -77,20 +80,20 @@ export class FlowForm extends Form<Flow> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.flow?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Title`} label=${t`Title`}
?required=${true} ?required=${true}
name="title"> name="title">
<input type="text" value="${ifDefined(this.flow?.title)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.title)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Shown as the Title in Flow pages.`}</p> <p class="pf-c-form__helper-text">${t`Shown as the Title in Flow pages.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Slug`} label=${t`Slug`}
?required=${true} ?required=${true}
name="slug"> name="slug">
<input type="text" value="${ifDefined(this.flow?.slug)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Visible in the URL.`}</p> <p class="pf-c-form__helper-text">${t`Visible in the URL.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
@ -98,10 +101,10 @@ export class FlowForm extends Form<Flow> {
?required=${true} ?required=${true}
name="policyEngineMode"> name="policyEngineMode">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value=${FlowPolicyEngineModeEnum.Any} ?selected=${this.flow?.policyEngineMode === FlowPolicyEngineModeEnum.Any}> <option value=${FlowPolicyEngineModeEnum.Any} ?selected=${this.instance?.policyEngineMode === FlowPolicyEngineModeEnum.Any}>
${t`ANY, any policy must match to grant access.`} ${t`ANY, any policy must match to grant access.`}
</option> </option>
<option value=${FlowPolicyEngineModeEnum.All} ?selected=${this.flow?.policyEngineMode === FlowPolicyEngineModeEnum.All}> <option value=${FlowPolicyEngineModeEnum.All} ?selected=${this.instance?.policyEngineMode === FlowPolicyEngineModeEnum.All}>
${t`ALL, all policies must match to grant access.`} ${t`ALL, all policies must match to grant access.`}
</option> </option>
</select> </select>
@ -111,7 +114,7 @@ export class FlowForm extends Form<Flow> {
?required=${true} ?required=${true}
name="designation"> name="designation">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.flow?.designation === undefined}>---------</option> <option value="" ?selected=${this.instance?.designation === undefined}>---------</option>
${this.renderDesignations()} ${this.renderDesignations()}
</select> </select>
<p class="pf-c-form__helper-text">${t`Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik.`}</p> <p class="pf-c-form__helper-text">${t`Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik.`}</p>
@ -119,7 +122,7 @@ export class FlowForm extends Form<Flow> {
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Background`} label=${t`Background`}
name="background"> name="background">
<input type="file" value="${ifDefined(this.flow?.background)}" class="pf-c-form-control"> <input type="file" value="${ifDefined(this.instance?.background)}" class="pf-c-form-control">
<p class="pf-c-form__helper-text">${t`Background shown during execution.`}</p> <p class="pf-c-form__helper-text">${t`Background shown during execution.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</form>`; </form>`;

View File

@ -68,7 +68,7 @@ export class FlowListPage extends TablePage<Flow> {
<span slot="header"> <span slot="header">
${t`Update Flow`} ${t`Update Flow`}
</span> </span>
<ak-flow-form slot="form" .flow=${item}> <ak-flow-form slot="form" .instancePk=${item.pk}>
</ak-flow-form> </ak-flow-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`} ${t`Edit`}

View File

@ -3,23 +3,26 @@ 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";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import { first, groupBy } from "../../utils"; import { first, groupBy } from "../../utils";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-stage-binding-form") @customElement("ak-stage-binding-form")
export class StageBindingForm extends Form<FlowStageBinding> { export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
@property({attribute: false}) loadInstance(pk: string): Promise<FlowStageBinding> {
fsb?: FlowStageBinding; return new FlowsApi(DEFAULT_CONFIG).flowsBindingsRead({
fsbUuid: pk,
});
}
@property() @property()
targetPk?: string; targetPk?: string;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.fsb) { if (this.instance) {
return t`Successfully updated binding.`; return t`Successfully updated binding.`;
} else { } else {
return t`Successfully created binding.`; return t`Successfully created binding.`;
@ -27,9 +30,9 @@ export class StageBindingForm extends Form<FlowStageBinding> {
} }
send = (data: FlowStageBinding): Promise<FlowStageBinding> => { send = (data: FlowStageBinding): Promise<FlowStageBinding> => {
if (this.fsb) { if (this.instance) {
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsUpdate({ return new FlowsApi(DEFAULT_CONFIG).flowsBindingsUpdate({
fsbUuid: this.fsb.pk || "", fsbUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -45,7 +48,7 @@ export class StageBindingForm extends Form<FlowStageBinding> {
${groupBy<Stage>(stages, (s => s.verboseName || "")).map(([group, stages]) => { ${groupBy<Stage>(stages, (s => s.verboseName || "")).map(([group, stages]) => {
return html`<optgroup label=${group}> return html`<optgroup label=${group}>
${stages.map(stage => { ${stages.map(stage => {
const selected = (this.fsb?.stage === stage.pk); const selected = (this.instance?.stage === stage.pk);
return html`<option ?selected=${selected} value=${ifDefined(stage.pk)}>${stage.name}</option>`; return html`<option ?selected=${selected} value=${ifDefined(stage.pk)}>${stage.name}</option>`;
})} })}
</optgroup>`; </optgroup>`;
@ -54,8 +57,8 @@ export class StageBindingForm extends Form<FlowStageBinding> {
} }
getOrder(): Promise<number> { getOrder(): Promise<number> {
if (this.fsb) { if (this.instance) {
return Promise.resolve(this.fsb.order); return Promise.resolve(this.instance.order);
} }
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({ return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({
target: this.targetPk || "", target: this.targetPk || "",
@ -69,9 +72,9 @@ export class StageBindingForm extends Form<FlowStageBinding> {
} }
renderTarget(): TemplateResult { renderTarget(): TemplateResult {
if (this.fsb?.target || this.targetPk) { if (this.instance?.target || this.targetPk) {
return html` return html`
<input required name="target" type="hidden" value=${ifDefined(this.fsb?.target || this.targetPk)}> <input required name="target" type="hidden" value=${ifDefined(this.instance?.target || this.targetPk)}>
`; `;
} }
return html`<ak-form-element-horizontal return html`<ak-form-element-horizontal
@ -114,7 +117,7 @@ export class StageBindingForm extends Form<FlowStageBinding> {
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="evaluateOnPlan"> <ak-form-element-horizontal name="evaluateOnPlan">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.fsb?.evaluateOnPlan, true)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.evaluateOnPlan, true)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Evaluate on plan`} ${t`Evaluate on plan`}
</label> </label>
@ -125,7 +128,7 @@ export class StageBindingForm extends Form<FlowStageBinding> {
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="reEvaluatePolicies"> <ak-form-element-horizontal name="reEvaluatePolicies">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.fsb?.reEvaluatePolicies, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.reEvaluatePolicies, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Re-evaluate policies`} ${t`Re-evaluate policies`}
</label> </label>
@ -137,10 +140,10 @@ export class StageBindingForm extends Form<FlowStageBinding> {
?required=${true} ?required=${true}
name="policyEngineMode"> name="policyEngineMode">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value=${FlowStageBindingPolicyEngineModeEnum.Any} ?selected=${this.fsb?.policyEngineMode === FlowStageBindingPolicyEngineModeEnum.Any}> <option value=${FlowStageBindingPolicyEngineModeEnum.Any} ?selected=${this.instance?.policyEngineMode === FlowStageBindingPolicyEngineModeEnum.Any}>
${t`ANY, any policy must match to include this stage access.`} ${t`ANY, any policy must match to include this stage access.`}
</option> </option>
<option value=${FlowStageBindingPolicyEngineModeEnum.All} ?selected=${this.fsb?.policyEngineMode === FlowStageBindingPolicyEngineModeEnum.All}> <option value=${FlowStageBindingPolicyEngineModeEnum.All} ?selected=${this.instance?.policyEngineMode === FlowStageBindingPolicyEngineModeEnum.All}>
${t`ALL, all policies must match to include this stage access.`} ${t`ALL, all policies must match to include this stage access.`}
</option> </option>
</select> </select>

View File

@ -1,9 +1,8 @@
import { CoreApi, Group, User } from "authentik-api"; import { CoreApi, Group, User } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
@ -13,15 +12,19 @@ import "../../elements/chips/Chip";
import "./MemberSelectModal"; import "./MemberSelectModal";
import YAML from "yaml"; import YAML from "yaml";
import { first } from "../../utils"; import { first } from "../../utils";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-group-form") @customElement("ak-group-form")
export class GroupForm extends Form<Group> { export class GroupForm extends ModelForm<Group, string> {
@property({attribute: false}) loadInstance(pk: string): Promise<Group> {
group?: Group; return new CoreApi(DEFAULT_CONFIG).coreGroupsRead({
groupUuid: pk
});
}
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.group) { if (this.instance) {
return t`Successfully updated group.`; return t`Successfully updated group.`;
} else { } else {
return t`Successfully created group.`; return t`Successfully created group.`;
@ -29,13 +32,13 @@ export class GroupForm extends Form<Group> {
} }
send = (data: Group): Promise<Group> => { send = (data: Group): Promise<Group> => {
if (this.group?.pk) { if (this.instance?.pk) {
return new CoreApi(DEFAULT_CONFIG).coreGroupsUpdate({ return new CoreApi(DEFAULT_CONFIG).coreGroupsUpdate({
groupUuid: this.group.pk || "", groupUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
data.users = Array.from(this.group?.users || []) as unknown as Set<number>; data.users = Array.from(this.instance?.users || []) as unknown as Set<number>;
return new CoreApi(DEFAULT_CONFIG).coreGroupsCreate({ return new CoreApi(DEFAULT_CONFIG).coreGroupsCreate({
data: data data: data
}); });
@ -48,11 +51,11 @@ export class GroupForm extends Form<Group> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.group?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="isSuperuser"> <ak-form-element-horizontal name="isSuperuser">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.group?.isSuperuser, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.isSuperuser, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Is superuser`} ${t`Is superuser`}
</label> </label>
@ -63,10 +66,10 @@ export class GroupForm extends Form<Group> {
label=${t`Parent`} label=${t`Parent`}
name="parent"> name="parent">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.group?.parent === undefined}>---------</option> <option value="" ?selected=${this.instance?.parent === undefined}>---------</option>
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => { ${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
return groups.results.map(group => { return groups.results.map(group => {
return html`<option value=${ifDefined(group.pk)} ?selected=${this.group?.parent === group.pk}>${group.name}</option>`; return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.parent === group.pk}>${group.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -79,8 +82,8 @@ export class GroupForm extends Form<Group> {
.confirm=${(items: User[]) => { .confirm=${(items: User[]) => {
// Because the model only has the IDs, map the user list to IDs // Because the model only has the IDs, map the user list to IDs
const ids = items.map(u => u.pk || 0); const ids = items.map(u => u.pk || 0);
if (!this.group) this.group = {} as Group; if (!this.instance) this.instance = {} as Group;
this.group.users = new Set(Array.from(this.group?.users || []).concat(ids)); this.instance.users = new Set(Array.from(this.instance?.users || []).concat(ids));
this.requestUpdate(); this.requestUpdate();
return Promise.resolve(); return Promise.resolve();
}}> }}>
@ -94,7 +97,7 @@ export class GroupForm extends Form<Group> {
ordering: "username", ordering: "username",
}).then(users => { }).then(users => {
return users.results.map(user => { return users.results.map(user => {
const selected = Array.from(this.group?.users || []).some(su => { const selected = Array.from(this.instance?.users || []).some(su => {
return su == user.pk; return su == user.pk;
}); });
if (!selected) return; if (!selected) return;
@ -102,11 +105,11 @@ export class GroupForm extends Form<Group> {
.removable=${true} .removable=${true}
value=${ifDefined(user.pk)} value=${ifDefined(user.pk)}
@remove=${() => { @remove=${() => {
if (!this.group) return; if (!this.instance) return;
const users = Array.from(this.group?.users || []); const users = Array.from(this.instance?.users || []);
const idx = users.indexOf(user.pk || 0); const idx = users.indexOf(user.pk || 0);
users.splice(idx, 1); users.splice(idx, 1);
this.group.users = new Set(users); this.instance.users = new Set(users);
this.requestUpdate(); this.requestUpdate();
}}> }}>
${user.username} ${user.username}
@ -122,7 +125,7 @@ export class GroupForm extends Form<Group> {
label=${t`Attributes`} label=${t`Attributes`}
?required=${true} ?required=${true}
name="attributes"> name="attributes">
<ak-codemirror mode="yaml" value="${YAML.stringify(first(this.group?.attributes, {}))}"> <ak-codemirror mode="yaml" value="${YAML.stringify(first(this.instance?.attributes, {}))}">
</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>
</ak-form-element-horizontal> </ak-form-element-horizontal>

View File

@ -63,7 +63,7 @@ export class GroupListPage extends TablePage<Group> {
<span slot="header"> <span slot="header">
${t`Update Group`} ${t`Update Group`}
</span> </span>
<ak-group-form slot="form" .group=${item}> <ak-group-form slot="form" .instancePk=${item.pk}>
</ak-group-form> </ak-group-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`} ${t`Edit`}

View File

@ -1,23 +1,26 @@
import { Outpost, OutpostsApi, OutpostTypeEnum, ProvidersApi } from "authentik-api"; import { Outpost, OutpostsApi, OutpostTypeEnum, ProvidersApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import "../../elements/CodeMirror"; import "../../elements/CodeMirror";
import YAML from "yaml"; import YAML from "yaml";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-outpost-form") @customElement("ak-outpost-form")
export class OutpostForm extends Form<Outpost> { export class OutpostForm extends ModelForm<Outpost, string> {
@property({attribute: false}) loadInstance(pk: string): Promise<Outpost> {
outpost?: Outpost; return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesRead({
uuid: pk
});
}
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.outpost) { if (this.instance) {
return t`Successfully updated outpost.`; return t`Successfully updated outpost.`;
} else { } else {
return t`Successfully created outpost.`; return t`Successfully created outpost.`;
@ -25,9 +28,9 @@ export class OutpostForm extends Form<Outpost> {
} }
send = (data: Outpost): Promise<Outpost> => { send = (data: Outpost): Promise<Outpost> => {
if (this.outpost) { if (this.instance) {
return new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsUpdate({ return new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsUpdate({
uuid: this.outpost.pk || "", uuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -43,27 +46,27 @@ export class OutpostForm extends Form<Outpost> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.outpost?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Type`} label=${t`Type`}
?required=${true} ?required=${true}
name="type"> name="type">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value=${OutpostTypeEnum.Proxy} ?selected=${this.outpost?.type === OutpostTypeEnum.Proxy}>${t`Proxy`}</option> <option value=${OutpostTypeEnum.Proxy} ?selected=${this.instance?.type === OutpostTypeEnum.Proxy}>${t`Proxy`}</option>
<option value=${OutpostTypeEnum.Ldap} ?selected=${this.outpost?.type === OutpostTypeEnum.Ldap}>${t`LDAP (Technical preview)`}</option> <option value=${OutpostTypeEnum.Ldap} ?selected=${this.instance?.type === OutpostTypeEnum.Ldap}>${t`LDAP (Technical preview)`}</option>
</select> </select>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Service connection`} label=${t`Service connection`}
name="serviceConnection"> name="serviceConnection">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.outpost?.serviceConnection === undefined}>---------</option> <option value="" ?selected=${this.instance?.serviceConnection === undefined}>---------</option>
${until(new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList({ ${until(new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList({
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.outpost?.serviceConnection === sc.pk}> return html`<option value=${ifDefined(sc.pk)} ?selected=${this.instance?.serviceConnection === sc.pk}>
${sc.name} (${sc.verboseName}) ${sc.name} (${sc.verboseName})
</option>`; </option>`;
}); });
@ -83,7 +86,7 @@ export class OutpostForm extends Form<Outpost> {
ordering: "pk" ordering: "pk"
}).then(providers => { }).then(providers => {
return providers.results.map(provider => { return providers.results.map(provider => {
const selected = Array.from(this.outpost?.providers || []).some(sp => { const selected = Array.from(this.instance?.providers || []).some(sp => {
return sp == provider.pk; return sp == provider.pk;
}); });
return html`<option value=${ifDefined(provider.pk)} ?selected=${selected}>${provider.verboseName} ${provider.name}</option>`; return html`<option value=${ifDefined(provider.pk)} ?selected=${selected}>${provider.verboseName} ${provider.name}</option>`;
@ -93,7 +96,7 @@ export class OutpostForm extends Form<Outpost> {
ordering: "pk" ordering: "pk"
}).then(providers => { }).then(providers => {
return providers.results.map(provider => { return providers.results.map(provider => {
const selected = Array.from(this.outpost?.providers || []).some(sp => { const selected = Array.from(this.instance?.providers || []).some(sp => {
return sp == provider.pk; return sp == provider.pk;
}); });
return html`<option value=${ifDefined(provider.pk)} ?selected=${selected}>${provider.verboseName} ${provider.name}</option>`; return html`<option value=${ifDefined(provider.pk)} ?selected=${selected}>${provider.verboseName} ${provider.name}</option>`;
@ -102,18 +105,18 @@ export class OutpostForm extends Form<Outpost> {
</select> </select>
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p> <p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
${until(new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsDefaultSettings({}).then(config => { <ak-form-element-horizontal
let fc = config.config; label=${t`Configuration`}
if (this.outpost) { name="config">
fc = this.outpost.config; <ak-codemirror mode="yaml" value="${until(new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsDefaultSettings({}).then(config => {
} let fc = config.config;
return html`<ak-form-element-horizontal if (this.instance) {
label=${t`Configuration`} fc = this.instance.config;
name="config"> }
<ak-codemirror mode="yaml" value="${YAML.stringify(fc)}"></ak-codemirror> return YAML.stringify(fc);
<p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p> }))}"></ak-codemirror>
</ak-form-element-horizontal>`; <p class="pf-c-form__helper-text">${t`Set custom attributes using YAML or JSON.`}</p>
}))} </ak-form-element-horizontal>
</form>`; </form>`;
} }

View File

@ -1,55 +1,70 @@
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { until } from "lit-html/directives/until"; import { OutpostHealth, OutpostsApi } from "authentik-api";
import { OutpostsApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css";
import "../../elements/Spinner"; import "../../elements/Spinner";
import AKGlobal from "../../authentik.css"; import AKGlobal from "../../authentik.css";
import { PFColor } from "../../elements/Label"; import { PFColor } from "../../elements/Label";
import { EVENT_REFRESH } from "../../constants";
@customElement("ak-outpost-health") @customElement("ak-outpost-health")
export class OutpostHealth extends LitElement { export class OutpostHealthElement extends LitElement {
@property() @property()
outpostId?: string; outpostId?: string;
@property({attribute: false})
outpostHealth: OutpostHealth[] = [];
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [PFBase, AKGlobal]; return [PFBase, AKGlobal];
} }
constructor() {
super();
this.addEventListener(EVENT_REFRESH, () => {
this.firstUpdated();
});
}
firstUpdated(): void {
if (!this.outpostId) return;
new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsHealth({
uuid: this.outpostId
}).then(health => {
this.outpostHealth = health;
});
}
render(): TemplateResult { render(): TemplateResult {
if (!this.outpostId) { if (!this.outpostId) {
return html`<ak-spinner></ak-spinner>`; return html`<ak-spinner></ak-spinner>`;
} }
return html`<ul>${until(new OutpostsApi(DEFAULT_CONFIG).outpostsOutpostsHealth({ if (this.outpostHealth.length === 0) {
uuid: this.outpostId return html`<li>
}).then((oh) => { <ul>
if (oh.length === 0) { <li role="cell">
return html`<li> <ak-label color=${PFColor.Grey} text=${t`Not available`}></ak-label>
<ul> </li>
<li role="cell"> </ul>
<ak-label color=${PFColor.Grey} text=${t`Not available`}></ak-label> </li>`;
</li> }
</ul> return html`<ul>${this.outpostHealth.map((h) => {
</li>`; return html`<li>
} <ul>
return oh.map((h) => { <li role="cell">
return html`<li> <ak-label color=${PFColor.Green} text=${t`Last seen: ${h.lastSeen?.toLocaleTimeString()}`}></ak-label>
<ul> </li>
<li role="cell"> <li role="cell">
<ak-label color=${PFColor.Green} text=${t`Last seen: ${h.lastSeen?.toLocaleTimeString()}`}></ak-label> ${h.versionOutdated ?
</li> html`<ak-label color=${PFColor.Red}
<li role="cell"> text=${t`${h.version}, should be ${h.versionShould}`}></ak-label>` :
${h.versionOutdated ? html`<ak-label color=${PFColor.Green} text=${t`Version: ${h.version || ""}`}></ak-label>`}
html`<ak-label color=${PFColor.Red} </li>
text=${t`${h.version}, should be ${h.versionShould}`}></ak-label>` : </ul>
html`<ak-label color=${PFColor.Green} text=${t`Version: ${h.version || ""}`}></ak-label>`} </li>`;
</li> })}</ul>`;
</ul>
</li>`;
});
}), html`<ak-spinner></ak-spinner>`)}</ul>`;
} }
} }

View File

@ -66,7 +66,7 @@ export class OutpostListPage extends TablePage<Outpost> {
<span slot="header"> <span slot="header">
${t`Update Outpost`} ${t`Update Outpost`}
</span> </span>
<ak-outpost-form slot="form" .outpost=${item}> <ak-outpost-form slot="form" .instancePk=${item.pk}>
</ak-outpost-form> </ak-outpost-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit`} ${t`Edit`}

View File

@ -1,30 +1,25 @@
import { CryptoApi, DockerServiceConnection, OutpostsApi } from "authentik-api"; import { CryptoApi, DockerServiceConnection, OutpostsApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import { first } from "../../utils"; import { first } from "../../utils";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-service-connection-docker-form") @customElement("ak-service-connection-docker-form")
export class ServiceConnectionDockerForm extends Form<DockerServiceConnection> { export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnection, string> {
set scUUID(value: string) { loadInstance(pk: string): Promise<DockerServiceConnection> {
new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerRead({ return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerRead({
uuid: value, uuid: pk,
}).then(sc => {
this.sc = sc;
}); });
} }
@property({attribute: false})
sc?: DockerServiceConnection;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.sc) { if (this.instance) {
return t`Successfully updated service-connection.`; return t`Successfully updated service-connection.`;
} else { } else {
return t`Successfully created service-connection.`; return t`Successfully created service-connection.`;
@ -32,9 +27,9 @@ export class ServiceConnectionDockerForm extends Form<DockerServiceConnection> {
} }
send = (data: DockerServiceConnection): Promise<DockerServiceConnection> => { send = (data: DockerServiceConnection): Promise<DockerServiceConnection> => {
if (this.sc) { if (this.instance) {
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerUpdate({ return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerUpdate({
uuid: this.sc.pk || "", uuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -50,11 +45,11 @@ export class ServiceConnectionDockerForm extends Form<DockerServiceConnection> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.sc?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="local"> <ak-form-element-horizontal name="local">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.sc?.local, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.local, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Local`} ${t`Local`}
</label> </label>
@ -65,19 +60,19 @@ export class ServiceConnectionDockerForm extends Form<DockerServiceConnection> {
label=${t`Docker URL`} label=${t`Docker URL`}
?required=${true} ?required=${true}
name="url"> name="url">
<input type="text" value="${ifDefined(this.sc?.url)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.url)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system.`}</p> <p class="pf-c-form__helper-text">${t`Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`TLS Verification Certificate`} label=${t`TLS Verification Certificate`}
name="tlsVerification"> name="tlsVerification">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.sc?.tlsVerification === undefined}>---------</option> <option value="" ?selected=${this.instance?.tlsVerification === undefined}>---------</option>
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({ ${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
ordering: "pk" ordering: "pk"
}).then(certs => { }).then(certs => {
return certs.results.map(cert => { return certs.results.map(cert => {
return html`<option value=${ifDefined(cert.pk)} ?selected=${this.sc?.tlsVerification === cert.pk}>${cert.name}</option>`; return html`<option value=${ifDefined(cert.pk)} ?selected=${this.instance?.tlsVerification === cert.pk}>${cert.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -87,12 +82,12 @@ export class ServiceConnectionDockerForm extends Form<DockerServiceConnection> {
label=${t`TLS Authentication Certificate`} label=${t`TLS Authentication Certificate`}
name="tlsAuthentication"> name="tlsAuthentication">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.sc?.tlsAuthentication === undefined}>---------</option> <option value="" ?selected=${this.instance?.tlsAuthentication === undefined}>---------</option>
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({ ${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
ordering: "pk" ordering: "pk"
}).then(certs => { }).then(certs => {
return certs.results.map(cert => { return certs.results.map(cert => {
return html`<option value=${ifDefined(cert.pk)} ?selected=${this.sc?.tlsAuthentication === cert.pk}>${cert.name}</option>`; return html`<option value=${ifDefined(cert.pk)} ?selected=${this.instance?.tlsAuthentication === cert.pk}>${cert.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>

View File

@ -1,31 +1,26 @@
import { KubernetesServiceConnection, OutpostsApi } from "authentik-api"; import { KubernetesServiceConnection, OutpostsApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import "../../elements/CodeMirror"; import "../../elements/CodeMirror";
import YAML from "yaml"; import YAML from "yaml";
import { first } from "../../utils"; import { first } from "../../utils";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-service-connection-kubernetes-form") @customElement("ak-service-connection-kubernetes-form")
export class ServiceConnectionKubernetesForm extends Form<KubernetesServiceConnection> { export class ServiceConnectionKubernetesForm extends ModelForm<KubernetesServiceConnection, string> {
set scUUID(value: string) { loadInstance(pk: string): Promise<KubernetesServiceConnection> {
new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesRead({ return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesRead({
uuid: value, uuid: pk,
}).then(sc => {
this.sc = sc;
}); });
} }
@property({attribute: false})
sc?: KubernetesServiceConnection;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.sc) { if (this.instance) {
return t`Successfully updated service-connection.`; return t`Successfully updated service-connection.`;
} else { } else {
return t`Successfully created service-connection.`; return t`Successfully created service-connection.`;
@ -33,9 +28,9 @@ export class ServiceConnectionKubernetesForm extends Form<KubernetesServiceConne
} }
send = (data: KubernetesServiceConnection): Promise<KubernetesServiceConnection> => { send = (data: KubernetesServiceConnection): Promise<KubernetesServiceConnection> => {
if (this.sc) { if (this.instance) {
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesUpdate({ return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesUpdate({
uuid: this.sc.pk || "", uuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -51,11 +46,11 @@ export class ServiceConnectionKubernetesForm extends Form<KubernetesServiceConne
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.sc?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="local"> <ak-form-element-horizontal name="local">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.sc?.local, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.local, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Local`} ${t`Local`}
</label> </label>
@ -65,7 +60,7 @@ export class ServiceConnectionKubernetesForm extends Form<KubernetesServiceConne
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Kubeconfig`} label=${t`Kubeconfig`}
name="kubeconfig"> name="kubeconfig">
<ak-codemirror mode="yaml" value="${YAML.stringify(first(this.sc?.kubeconfig, {}))}"> <ak-codemirror mode="yaml" value="${YAML.stringify(first(this.instance?.kubeconfig, {}))}">
</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>
</ak-form-element-horizontal> </ak-form-element-horizontal>

View File

@ -82,7 +82,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
<ak-proxy-form <ak-proxy-form
slot="form" slot="form"
.args=${{ .args=${{
"scUUID": item.pk "instancePk": item.pk
}} }}
type=${ifDefined(item.component)}> type=${ifDefined(item.component)}>
</ak-proxy-form> </ak-proxy-form>

View File

@ -88,7 +88,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
<span slot="header"> <span slot="header">
${t`Update Group`} ${t`Update Group`}
</span> </span>
<ak-group-form slot="form" .group=${item.groupObj}> <ak-group-form slot="form" .instancePk=${item.groupObj?.pk}>
</ak-group-form> </ak-group-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit Group`} ${t`Edit Group`}
@ -102,7 +102,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
<span slot="header"> <span slot="header">
${t`Update User`} ${t`Update User`}
</span> </span>
<ak-user-form slot="form" .user=${item.userObj}> <ak-user-form slot="form" .instancePk=${item.userObj?.pk}>
</ak-user-form> </ak-user-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit User`} ${t`Edit User`}
@ -128,7 +128,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
<span slot="header"> <span slot="header">
${t`Update Binding`} ${t`Update Binding`}
</span> </span>
<ak-policy-binding-form slot="form" .binding=${item} targetPk=${ifDefined(this.target)} ?policyOnly=${this.policyOnly}> <ak-policy-binding-form slot="form" .instancePk=${item.pk} targetPk=${ifDefined(this.target)} ?policyOnly=${this.policyOnly}>
</ak-policy-binding-form> </ak-policy-binding-form>
<button slot="trigger" class="pf-c-button pf-m-secondary"> <button slot="trigger" class="pf-c-button pf-m-secondary">
${t`Edit Binding`} ${t`Edit Binding`}

View File

@ -3,41 +3,38 @@ import { t } from "@lingui/macro";
import { css, CSSResult, customElement, property } from "lit-element"; import { css, CSSResult, customElement, property } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import { first, groupBy } from "../../utils"; import { first, groupBy } from "../../utils";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css"; import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css"; import PFContent from "@patternfly/patternfly/components/Content/content.css";
import { ModelForm } from "../../elements/forms/ModelForm";
enum target { enum target {
policy, group, user policy, group, user
} }
@customElement("ak-policy-binding-form") @customElement("ak-policy-binding-form")
export class PolicyBindingForm extends Form<PolicyBinding> { export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
@property({attribute: false}) loadInstance(pk: string): Promise<PolicyBinding> {
set binding(value: PolicyBinding | undefined) { return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsRead({
this._binding = value; policyBindingUuid: pk
if (value?.policyObj) { }).then(binding => {
this.policyGroupUser = target.policy; if (binding?.policyObj) {
} this.policyGroupUser = target.policy;
if (value?.groupObj) { }
this.policyGroupUser = target.group; if (binding?.groupObj) {
} this.policyGroupUser = target.group;
if (value?.userObj) { }
this.policyGroupUser = target.user; if (binding?.userObj) {
} this.policyGroupUser = target.user;
}
return binding;
});
} }
get binding(): PolicyBinding | undefined {
return this._binding;
}
_binding?: PolicyBinding;
@property() @property()
targetPk?: string; targetPk?: string;
@ -48,7 +45,7 @@ export class PolicyBindingForm extends Form<PolicyBinding> {
policyOnly = false; policyOnly = false;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.binding) { if (this.instance) {
return t`Successfully updated binding.`; return t`Successfully updated binding.`;
} else { } else {
return t`Successfully created binding.`; return t`Successfully created binding.`;
@ -64,9 +61,9 @@ export class PolicyBindingForm extends Form<PolicyBinding> {
} }
send = (data: PolicyBinding): Promise<PolicyBinding> => { send = (data: PolicyBinding): Promise<PolicyBinding> => {
if (this.binding) { if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsUpdate({ return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsUpdate({
policyBindingUuid: this.binding.pk || "", policyBindingUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -81,7 +78,7 @@ export class PolicyBindingForm extends Form<PolicyBinding> {
${groupBy<Policy>(policies, (p => p.verboseName || "")).map(([group, policies]) => { ${groupBy<Policy>(policies, (p => p.verboseName || "")).map(([group, policies]) => {
return html`<optgroup label=${group}> return html`<optgroup label=${group}>
${policies.map(p => { ${policies.map(p => {
const selected = (this.binding?.policy === p.pk); const selected = (this.instance?.policy === p.pk);
return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>${p.name}</option>`; return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>${p.name}</option>`;
})} })}
</optgroup>`; </optgroup>`;
@ -90,8 +87,8 @@ export class PolicyBindingForm extends Form<PolicyBinding> {
} }
getOrder(): Promise<number> { getOrder(): Promise<number> {
if (this.binding) { if (this.instance) {
return Promise.resolve(this.binding.order); return Promise.resolve(this.instance.order);
} }
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsList({ return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsList({
target: this.targetPk || "", target: this.targetPk || "",
@ -154,7 +151,7 @@ export class PolicyBindingForm extends Form<PolicyBinding> {
name="policy" name="policy"
?hidden=${this.policyGroupUser !== target.policy}> ?hidden=${this.policyGroupUser !== target.policy}>
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.binding?.policy === undefined}>---------</option> <option value="" ?selected=${this.instance?.policy === undefined}>---------</option>
${until(new PoliciesApi(DEFAULT_CONFIG).policiesAllList({ ${until(new PoliciesApi(DEFAULT_CONFIG).policiesAllList({
ordering: "pk" ordering: "pk"
}).then(policies => { }).then(policies => {
@ -167,12 +164,12 @@ export class PolicyBindingForm extends Form<PolicyBinding> {
name="group" name="group"
?hidden=${this.policyGroupUser !== target.group}> ?hidden=${this.policyGroupUser !== target.group}>
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.binding?.group === undefined}>---------</option> <option value="" ?selected=${this.instance?.group === undefined}>---------</option>
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({ ${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({
ordering: "pk" ordering: "pk"
}).then(groups => { }).then(groups => {
return groups.results.map(group => { return groups.results.map(group => {
return html`<option value=${ifDefined(group.pk)} ?selected=${group.pk === this.binding?.group}>${group.name}</option>`; return html`<option value=${ifDefined(group.pk)} ?selected=${group.pk === this.instance?.group}>${group.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -182,22 +179,22 @@ export class PolicyBindingForm extends Form<PolicyBinding> {
name="user" name="user"
?hidden=${this.policyGroupUser !== target.user}> ?hidden=${this.policyGroupUser !== target.user}>
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.binding?.user === undefined}>---------</option> <option value="" ?selected=${this.instance?.user === undefined}>---------</option>
${until(new CoreApi(DEFAULT_CONFIG).coreUsersList({ ${until(new CoreApi(DEFAULT_CONFIG).coreUsersList({
ordering: "pk" ordering: "pk"
}).then(users => { }).then(users => {
return users.results.map(user => { return users.results.map(user => {
return html`<option value=${ifDefined(user.pk)} ?selected=${user.pk === this.binding?.user}>${user.name}</option>`; return html`<option value=${ifDefined(user.pk)} ?selected=${user.pk === this.instance?.user}>${user.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>
</div> </div>
<input required name="target" type="hidden" value=${ifDefined(this.binding?.target || this.targetPk)}> <input required name="target" type="hidden" value=${ifDefined(this.instance?.target || this.targetPk)}>
<ak-form-element-horizontal name="enabled"> <ak-form-element-horizontal name="enabled">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.binding?.enabled, true)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.enabled, true)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Enabled`} ${t`Enabled`}
</label> </label>
@ -213,7 +210,7 @@ export class PolicyBindingForm extends Form<PolicyBinding> {
label=${t`Timeout`} label=${t`Timeout`}
?required=${true} ?required=${true}
name="timeout"> name="timeout">
<input type="number" value="${first(this.binding?.timeout, 30)}" class="pf-c-form-control" required> <input type="number" value="${first(this.instance?.timeout, 30)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</form>`; </form>`;
} }

View File

@ -82,7 +82,7 @@ export class PolicyListPage extends TablePage<Policy> {
<ak-proxy-form <ak-proxy-form
slot="form" slot="form"
.args=${{ .args=${{
"policyUUID": item.pk "instancePk": item.pk
}} }}
type=${ifDefined(item.component)}> type=${ifDefined(item.component)}>
</ak-proxy-form> </ak-proxy-form>

View File

@ -1,30 +1,25 @@
import { DummyPolicy, PoliciesApi } from "authentik-api"; import { DummyPolicy, PoliciesApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { ModelForm } from "../../../elements/forms/ModelForm";
@customElement("ak-policy-dummy-form") @customElement("ak-policy-dummy-form")
export class DummyPolicyForm extends Form<DummyPolicy> { export class DummyPolicyForm extends ModelForm<DummyPolicy, string> {
set policyUUID(value: string) { loadInstance(pk: string): Promise<DummyPolicy> {
new PoliciesApi(DEFAULT_CONFIG).policiesDummyRead({ return new PoliciesApi(DEFAULT_CONFIG).policiesDummyRead({
policyUuid: value, policyUuid: pk,
}).then(policy => {
this.policy = policy;
}); });
} }
@property({attribute: false})
policy?: DummyPolicy;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.policy) { if (this.instance) {
return t`Successfully updated policy.`; return t`Successfully updated policy.`;
} else { } else {
return t`Successfully created policy.`; return t`Successfully created policy.`;
@ -32,9 +27,9 @@ export class DummyPolicyForm extends Form<DummyPolicy> {
} }
send = (data: DummyPolicy): Promise<DummyPolicy> => { send = (data: DummyPolicy): Promise<DummyPolicy> => {
if (this.policy) { if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesDummyUpdate({ return new PoliciesApi(DEFAULT_CONFIG).policiesDummyUpdate({
policyUuid: this.policy.pk || "", policyUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -53,11 +48,11 @@ export class DummyPolicyForm extends Form<DummyPolicy> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.policy?.name || "")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="executionLogging"> <ak-form-element-horizontal name="executionLogging">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.executionLogging, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Execution logging`} ${t`Execution logging`}
</label> </label>
@ -73,7 +68,7 @@ export class DummyPolicyForm extends Form<DummyPolicy> {
<div slot="body" class="pf-c-form"> <div slot="body" class="pf-c-form">
<ak-form-element-horizontal name="result"> <ak-form-element-horizontal name="result">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.result, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.result, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Pass policy?`} ${t`Pass policy?`}
</label> </label>
@ -83,14 +78,14 @@ export class DummyPolicyForm extends Form<DummyPolicy> {
label=${t`Wait (min)`} label=${t`Wait (min)`}
?required=${true} ?required=${true}
name="waitMin"> name="waitMin">
<input type="number" value="${first(this.policy?.waitMin, 1)}" class="pf-c-form-control" required> <input type="number" value="${first(this.instance?.waitMin, 1)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`The policy takes a random time to execute. This controls the minimum time it will take.`}</p> <p class="pf-c-form__helper-text">${t`The policy takes a random time to execute. This controls the minimum time it will take.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Wait (max)`} label=${t`Wait (max)`}
?required=${true} ?required=${true}
name="waitMax"> name="waitMax">
<input type="number" value="${first(this.policy?.waitMax, 5)}" class="pf-c-form-control" required> <input type="number" value="${first(this.instance?.waitMax, 5)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>
</ak-form-group> </ak-form-group>

View File

@ -1,31 +1,26 @@
import { AdminApi, EventMatcherPolicy, EventsApi, PoliciesApi } from "authentik-api"; import { AdminApi, EventMatcherPolicy, EventsApi, PoliciesApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { ModelForm } from "../../../elements/forms/ModelForm";
@customElement("ak-policy-event-matcher-form") @customElement("ak-policy-event-matcher-form")
export class EventMatcherPolicyForm extends Form<EventMatcherPolicy> { export class EventMatcherPolicyForm extends ModelForm<EventMatcherPolicy, string> {
set policyUUID(value: string) { loadInstance(pk: string): Promise<EventMatcherPolicy> {
new PoliciesApi(DEFAULT_CONFIG).policiesEventMatcherRead({ return new PoliciesApi(DEFAULT_CONFIG).policiesEventMatcherRead({
policyUuid: value, policyUuid: pk,
}).then(policy => {
this.policy = policy;
}); });
} }
@property({attribute: false})
policy?: EventMatcherPolicy;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.policy) { if (this.instance) {
return t`Successfully updated policy.`; return t`Successfully updated policy.`;
} else { } else {
return t`Successfully created policy.`; return t`Successfully created policy.`;
@ -33,9 +28,9 @@ export class EventMatcherPolicyForm extends Form<EventMatcherPolicy> {
} }
send = (data: EventMatcherPolicy): Promise<EventMatcherPolicy> => { send = (data: EventMatcherPolicy): Promise<EventMatcherPolicy> => {
if (this.policy) { if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesEventMatcherUpdate({ return new PoliciesApi(DEFAULT_CONFIG).policiesEventMatcherUpdate({
policyUuid: this.policy.pk || "", policyUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -54,11 +49,11 @@ export class EventMatcherPolicyForm extends Form<EventMatcherPolicy> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.policy?.name || "")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="executionLogging"> <ak-form-element-horizontal name="executionLogging">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.executionLogging, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Execution logging`} ${t`Execution logging`}
</label> </label>
@ -76,10 +71,10 @@ export class EventMatcherPolicyForm extends Form<EventMatcherPolicy> {
label=${t`Action`} label=${t`Action`}
name="action"> name="action">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.policy?.action === undefined}>---------</option> <option value="" ?selected=${this.instance?.action === undefined}>---------</option>
${until(new EventsApi(DEFAULT_CONFIG).eventsEventsActions().then(actions => { ${until(new EventsApi(DEFAULT_CONFIG).eventsEventsActions().then(actions => {
return actions.map(action => { return actions.map(action => {
return html`<option value=${action.component} ?selected=${this.policy?.action === action.component}>${action.name}</option>`; return html`<option value=${action.component} ?selected=${this.instance?.action === action.component}>${action.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -88,17 +83,17 @@ export class EventMatcherPolicyForm extends Form<EventMatcherPolicy> {
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Client IP`} label=${t`Client IP`}
name="clientIp"> name="clientIp">
<input type="text" value="${ifDefined(this.policy?.clientIp || "")}" class="pf-c-form-control"> <input type="text" value="${ifDefined(this.instance?.clientIp || "")}" class="pf-c-form-control">
<p class="pf-c-form__helper-text">${t`Matches Event's Client IP (strict matching, for network matching use an Expression Policy.`}</p> <p class="pf-c-form__helper-text">${t`Matches Event's Client IP (strict matching, for network matching use an Expression Policy.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`App`} label=${t`App`}
name="app"> name="app">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.policy?.app === undefined}>---------</option> <option value="" ?selected=${this.instance?.app === undefined}>---------</option>
${until(new AdminApi(DEFAULT_CONFIG).adminAppsList().then(apps => { ${until(new AdminApi(DEFAULT_CONFIG).adminAppsList().then(apps => {
return apps.map(app => { return apps.map(app => {
return html`<option value=${app.name} ?selected=${this.policy?.app === app.name}>${app.label}</option>`; return html`<option value=${app.name} ?selected=${this.instance?.app === app.name}>${app.label}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>

View File

@ -1,30 +1,25 @@
import { PasswordExpiryPolicy, PoliciesApi } from "authentik-api"; import { PasswordExpiryPolicy, PoliciesApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { ModelForm } from "../../../elements/forms/ModelForm";
@customElement("ak-policy-password-expiry-form") @customElement("ak-policy-password-expiry-form")
export class PasswordExpiryPolicyForm extends Form<PasswordExpiryPolicy> { export class PasswordExpiryPolicyForm extends ModelForm<PasswordExpiryPolicy, string> {
set policyUUID(value: string) { loadInstance(pk: string): Promise<PasswordExpiryPolicy> {
new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryRead({ return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryRead({
policyUuid: value, policyUuid: pk,
}).then(policy => {
this.policy = policy;
}); });
} }
@property({attribute: false})
policy?: PasswordExpiryPolicy;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.policy) { if (this.instance) {
return t`Successfully updated policy.`; return t`Successfully updated policy.`;
} else { } else {
return t`Successfully created policy.`; return t`Successfully created policy.`;
@ -32,9 +27,9 @@ export class PasswordExpiryPolicyForm extends Form<PasswordExpiryPolicy> {
} }
send = (data: PasswordExpiryPolicy): Promise<PasswordExpiryPolicy> => { send = (data: PasswordExpiryPolicy): Promise<PasswordExpiryPolicy> => {
if (this.policy) { if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryUpdate({ return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryUpdate({
policyUuid: this.policy.pk || "", policyUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -53,11 +48,11 @@ export class PasswordExpiryPolicyForm extends Form<PasswordExpiryPolicy> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.policy?.name || "")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="executionLogging"> <ak-form-element-horizontal name="executionLogging">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.executionLogging, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Execution logging`} ${t`Execution logging`}
</label> </label>
@ -75,11 +70,11 @@ export class PasswordExpiryPolicyForm extends Form<PasswordExpiryPolicy> {
label=${t`Maximum age (in days)`} label=${t`Maximum age (in days)`}
?required=${true} ?required=${true}
name="days"> name="days">
<input type="number" value="${ifDefined(this.policy?.days || "")}" class="pf-c-form-control" required> <input type="number" value="${ifDefined(this.instance?.days || "")}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="denyOnly"> <ak-form-element-horizontal name="denyOnly">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.denyOnly, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.denyOnly, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Only fail the policy, don't invalidate user's password.`} ${t`Only fail the policy, don't invalidate user's password.`}
</label> </label>

View File

@ -1,31 +1,26 @@
import { ExpressionPolicy, PoliciesApi } from "authentik-api"; import { ExpressionPolicy, PoliciesApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
import "../../../elements/CodeMirror"; import "../../../elements/CodeMirror";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { ModelForm } from "../../../elements/forms/ModelForm";
@customElement("ak-policy-expression-form") @customElement("ak-policy-expression-form")
export class ExpressionPolicyForm extends Form<ExpressionPolicy> { export class ExpressionPolicyForm extends ModelForm<ExpressionPolicy, string> {
set policyUUID(value: string) { loadInstance(pk: string): Promise<ExpressionPolicy> {
new PoliciesApi(DEFAULT_CONFIG).policiesExpressionRead({ return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionRead({
policyUuid: value, policyUuid: pk,
}).then(policy => {
this.policy = policy;
}); });
} }
@property({attribute: false})
policy?: ExpressionPolicy;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.policy) { if (this.instance) {
return t`Successfully updated policy.`; return t`Successfully updated policy.`;
} else { } else {
return t`Successfully created policy.`; return t`Successfully created policy.`;
@ -33,9 +28,9 @@ export class ExpressionPolicyForm extends Form<ExpressionPolicy> {
} }
send = (data: ExpressionPolicy): Promise<ExpressionPolicy> => { send = (data: ExpressionPolicy): Promise<ExpressionPolicy> => {
if (this.policy) { if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionUpdate({ return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionUpdate({
policyUuid: this.policy.pk || "", policyUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -54,11 +49,11 @@ export class ExpressionPolicyForm extends Form<ExpressionPolicy> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.policy?.name || "")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="executionLogging"> <ak-form-element-horizontal name="executionLogging">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.executionLogging, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Execution logging`} ${t`Execution logging`}
</label> </label>
@ -76,7 +71,7 @@ export class ExpressionPolicyForm extends Form<ExpressionPolicy> {
label=${t`Expression`} label=${t`Expression`}
?required=${true} ?required=${true}
name="expression"> name="expression">
<ak-codemirror mode="python" value="${ifDefined(this.policy?.expression)}"> <ak-codemirror mode="python" value="${ifDefined(this.instance?.expression)}">
</ak-codemirror> </ak-codemirror>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Expression using Python.`} ${t`Expression using Python.`}

View File

@ -1,30 +1,25 @@
import { HaveIBeenPwendPolicy, PoliciesApi } from "authentik-api"; import { HaveIBeenPwendPolicy, PoliciesApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { ModelForm } from "../../../elements/forms/ModelForm";
@customElement("ak-policy-hibp-form") @customElement("ak-policy-hibp-form")
export class HaveIBeenPwnedPolicyForm extends Form<HaveIBeenPwendPolicy> { export class HaveIBeenPwnedPolicyForm extends ModelForm<HaveIBeenPwendPolicy, string> {
set policyUUID(value: string) { loadInstance(pk: string): Promise<HaveIBeenPwendPolicy> {
new PoliciesApi(DEFAULT_CONFIG).policiesHaveibeenpwnedRead({ return new PoliciesApi(DEFAULT_CONFIG).policiesHaveibeenpwnedRead({
policyUuid: value, policyUuid: pk,
}).then(policy => {
this.policy = policy;
}); });
} }
@property({attribute: false})
policy?: HaveIBeenPwendPolicy;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.policy) { if (this.instance) {
return t`Successfully updated policy.`; return t`Successfully updated policy.`;
} else { } else {
return t`Successfully created policy.`; return t`Successfully created policy.`;
@ -32,9 +27,9 @@ export class HaveIBeenPwnedPolicyForm extends Form<HaveIBeenPwendPolicy> {
} }
send = (data: HaveIBeenPwendPolicy): Promise<HaveIBeenPwendPolicy> => { send = (data: HaveIBeenPwendPolicy): Promise<HaveIBeenPwendPolicy> => {
if (this.policy) { if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesHaveibeenpwnedUpdate({ return new PoliciesApi(DEFAULT_CONFIG).policiesHaveibeenpwnedUpdate({
policyUuid: this.policy.pk || "", policyUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -54,11 +49,11 @@ export class HaveIBeenPwnedPolicyForm extends Form<HaveIBeenPwendPolicy> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.policy?.name || "")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="executionLogging"> <ak-form-element-horizontal name="executionLogging">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.executionLogging, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Execution logging`} ${t`Execution logging`}
</label> </label>
@ -76,14 +71,14 @@ export class HaveIBeenPwnedPolicyForm extends Form<HaveIBeenPwendPolicy> {
label=${t`Password field`} label=${t`Password field`}
?required=${true} ?required=${true}
name="passwordField"> name="passwordField">
<input type="text" value="${ifDefined(this.policy?.passwordField || "password")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.passwordField || "password")}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Field key to check, field keys defined in Prompt stages are available.`}</p> <p class="pf-c-form__helper-text">${t`Field key to check, field keys defined in Prompt stages are available.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Allowed count`} label=${t`Allowed count`}
?required=${true} ?required=${true}
name="allowedCount"> name="allowedCount">
<input type="number" value="${first(this.policy?.allowedCount, 0)}" class="pf-c-form-control" required> <input type="number" value="${first(this.instance?.allowedCount, 0)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Allow up to N occurrences in the HIBP database.`}</p> <p class="pf-c-form__helper-text">${t`Allow up to N occurrences in the HIBP database.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>

View File

@ -1,30 +1,25 @@
import { PasswordPolicy, PoliciesApi } from "authentik-api"; import { PasswordPolicy, PoliciesApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { ModelForm } from "../../../elements/forms/ModelForm";
@customElement("ak-policy-password-form") @customElement("ak-policy-password-form")
export class PasswordPolicyForm extends Form<PasswordPolicy> { export class PasswordPolicyForm extends ModelForm<PasswordPolicy, string> {
set policyUUID(value: string) { loadInstance(pk: string): Promise<PasswordPolicy> {
new PoliciesApi(DEFAULT_CONFIG).policiesPasswordRead({ return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordRead({
policyUuid: value, policyUuid: pk,
}).then(policy => {
this.policy = policy;
}); });
} }
@property({attribute: false})
policy?: PasswordPolicy;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.policy) { if (this.instance) {
return t`Successfully updated policy.`; return t`Successfully updated policy.`;
} else { } else {
return t`Successfully created policy.`; return t`Successfully created policy.`;
@ -32,9 +27,9 @@ export class PasswordPolicyForm extends Form<PasswordPolicy> {
} }
send = (data: PasswordPolicy): Promise<PasswordPolicy> => { send = (data: PasswordPolicy): Promise<PasswordPolicy> => {
if (this.policy) { if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordUpdate({ return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordUpdate({
policyUuid: this.policy.pk || "", policyUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -53,11 +48,11 @@ export class PasswordPolicyForm extends Form<PasswordPolicy> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.policy?.name || "")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="executionLogging"> <ak-form-element-horizontal name="executionLogging">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.executionLogging, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Execution logging`} ${t`Execution logging`}
</label> </label>
@ -75,7 +70,7 @@ export class PasswordPolicyForm extends Form<PasswordPolicy> {
label=${t`Password field`} label=${t`Password field`}
?required=${true} ?required=${true}
name="passwordField"> name="passwordField">
<input type="text" value="${ifDefined(this.policy?.passwordField || "password")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.passwordField || "password")}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Field key to check, field keys defined in Prompt stages are available.`}</p> <p class="pf-c-form__helper-text">${t`Field key to check, field keys defined in Prompt stages are available.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
@ -83,31 +78,31 @@ export class PasswordPolicyForm extends Form<PasswordPolicy> {
label=${t`Minimum length`} label=${t`Minimum length`}
?required=${true} ?required=${true}
name="lengthMin"> name="lengthMin">
<input type="number" value="${first(this.policy?.lengthMin, 10)}" class="pf-c-form-control" required> <input type="number" value="${first(this.instance?.lengthMin, 10)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Minimum amount of Uppercase Characters`} label=${t`Minimum amount of Uppercase Characters`}
?required=${true} ?required=${true}
name="amountUppercase"> name="amountUppercase">
<input type="number" value="${first(this.policy?.amountUppercase, 2)}" class="pf-c-form-control" required> <input type="number" value="${first(this.instance?.amountUppercase, 2)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Minimum amount of Lowercase Characters`} label=${t`Minimum amount of Lowercase Characters`}
?required=${true} ?required=${true}
name="amountLowercase"> name="amountLowercase">
<input type="number" value="${first(this.policy?.amountLowercase, 2)}" class="pf-c-form-control" required> <input type="number" value="${first(this.instance?.amountLowercase, 2)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Minimum amount of Symbols Characters`} label=${t`Minimum amount of Symbols Characters`}
?required=${true} ?required=${true}
name="amountSymbols"> name="amountSymbols">
<input type="number" value="${first(this.policy?.amountSymbols, 2)}" class="pf-c-form-control" required> <input type="number" value="${first(this.instance?.amountSymbols, 2)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Error message`} label=${t`Error message`}
?required=${true} ?required=${true}
name="errorMessage"> name="errorMessage">
<input type="text" value="${ifDefined(this.policy?.errorMessage)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.errorMessage)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>
</ak-form-group> </ak-form-group>
@ -120,7 +115,7 @@ export class PasswordPolicyForm extends Form<PasswordPolicy> {
label=${t`Symbol charset`} label=${t`Symbol charset`}
?required=${true} ?required=${true}
name="symbolCharset"> name="symbolCharset">
<input type="text" value="${ifDefined(this.policy?.symbolCharset || "!\\\"#$%&'()*+,-./:;<=>?@[]^_`{|}~ ")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.symbolCharset || "!\\\"#$%&'()*+,-./:;<=>?@[]^_`{|}~ ")}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Characters which are considered as symbols.`}</p> <p class="pf-c-form__helper-text">${t`Characters which are considered as symbols.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>

View File

@ -1,30 +1,25 @@
import { ReputationPolicy, PoliciesApi } from "authentik-api"; import { ReputationPolicy, PoliciesApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { ModelForm } from "../../../elements/forms/ModelForm";
@customElement("ak-policy-reputation-form") @customElement("ak-policy-reputation-form")
export class ReputationPolicyForm extends Form<ReputationPolicy> { export class ReputationPolicyForm extends ModelForm<ReputationPolicy, string> {
set policyUUID(value: string) { loadInstance(pk: string): Promise<ReputationPolicy> {
new PoliciesApi(DEFAULT_CONFIG).policiesReputationRead({ return new PoliciesApi(DEFAULT_CONFIG).policiesReputationRead({
policyUuid: value, policyUuid: pk,
}).then(policy => {
this.policy = policy;
}); });
} }
@property({attribute: false})
policy?: ReputationPolicy;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.policy) { if (this.instance) {
return t`Successfully updated policy.`; return t`Successfully updated policy.`;
} else { } else {
return t`Successfully created policy.`; return t`Successfully created policy.`;
@ -32,9 +27,9 @@ export class ReputationPolicyForm extends Form<ReputationPolicy> {
} }
send = (data: ReputationPolicy): Promise<ReputationPolicy> => { send = (data: ReputationPolicy): Promise<ReputationPolicy> => {
if (this.policy) { if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUpdate({ return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUpdate({
policyUuid: this.policy.pk || "", policyUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -53,11 +48,11 @@ export class ReputationPolicyForm extends Form<ReputationPolicy> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.policy?.name || "")}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="executionLogging"> <ak-form-element-horizontal name="executionLogging">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.executionLogging, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.executionLogging, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Execution logging`} ${t`Execution logging`}
</label> </label>
@ -73,7 +68,7 @@ export class ReputationPolicyForm extends Form<ReputationPolicy> {
<div slot="body" class="pf-c-form"> <div slot="body" class="pf-c-form">
<ak-form-element-horizontal name="checkIp"> <ak-form-element-horizontal name="checkIp">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.checkIp, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.checkIp, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Check IP`} ${t`Check IP`}
</label> </label>
@ -81,7 +76,7 @@ export class ReputationPolicyForm extends Form<ReputationPolicy> {
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="checkUsername"> <ak-form-element-horizontal name="checkUsername">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.policy?.checkUsername, false)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.checkUsername, false)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Check Username`} ${t`Check Username`}
</label> </label>
@ -91,7 +86,7 @@ export class ReputationPolicyForm extends Form<ReputationPolicy> {
label=${t`Threshold`} label=${t`Threshold`}
?required=${true} ?required=${true}
name="threshold"> name="threshold">
<input type="number" value="${ifDefined(this.policy?.threshold || -5)}" class="pf-c-form-control" required> <input type="number" value="${ifDefined(this.instance?.threshold || -5)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>
</ak-form-group> </ak-form-group>

View File

@ -1,29 +1,24 @@
import { LDAPPropertyMapping, PropertymappingsApi } from "authentik-api"; import { LDAPPropertyMapping, PropertymappingsApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import "../../elements/CodeMirror"; import "../../elements/CodeMirror";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-property-mapping-ldap-form") @customElement("ak-property-mapping-ldap-form")
export class PropertyMappingLDAPForm extends Form<LDAPPropertyMapping> { export class PropertyMappingLDAPForm extends ModelForm<LDAPPropertyMapping, string> {
set mappingUUID(value: string) { loadInstance(pk: string): Promise<LDAPPropertyMapping> {
new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapRead({ return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapRead({
pmUuid: value, pmUuid: pk,
}).then(mapping => {
this.mapping = mapping;
}); });
} }
@property({attribute: false})
mapping?: LDAPPropertyMapping;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.mapping) { if (this.instance) {
return t`Successfully updated mapping.`; return t`Successfully updated mapping.`;
} else { } else {
return t`Successfully created mapping.`; return t`Successfully created mapping.`;
@ -31,9 +26,9 @@ export class PropertyMappingLDAPForm extends Form<LDAPPropertyMapping> {
} }
send = (data: LDAPPropertyMapping): Promise<LDAPPropertyMapping> => { send = (data: LDAPPropertyMapping): Promise<LDAPPropertyMapping> => {
if (this.mapping) { if (this.instance) {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapUpdate({ return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapUpdate({
pmUuid: this.mapping.pk || "", pmUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -49,20 +44,20 @@ export class PropertyMappingLDAPForm extends Form<LDAPPropertyMapping> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.mapping?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Object field`} label=${t`Object field`}
?required=${true} ?required=${true}
name="objectField"> name="objectField">
<input type="text" value="${ifDefined(this.mapping?.objectField)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.objectField)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Field of the user object this value is written to.`}</p> <p class="pf-c-form__helper-text">${t`Field of the user object this value is written to.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Expression`} label=${t`Expression`}
?required=${true} ?required=${true}
name="expression"> name="expression">
<ak-codemirror mode="python" value="${ifDefined(this.mapping?.expression)}"> <ak-codemirror mode="python" value="${ifDefined(this.instance?.expression)}">
</ak-codemirror> </ak-codemirror>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Expression using Python.`} ${t`Expression using Python.`}

View File

@ -1,29 +1,23 @@
import { SAMLPropertyMapping, PropertymappingsApi } from "authentik-api"; import { SAMLPropertyMapping, PropertymappingsApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import "../../elements/CodeMirror"; import "../../elements/CodeMirror";
import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-property-mapping-saml-form") @customElement("ak-property-mapping-saml-form")
export class PropertyMappingLDAPForm extends Form<SAMLPropertyMapping> { export class PropertyMappingLDAPForm extends ModelForm<SAMLPropertyMapping, string> {
loadInstance(pk: string): Promise<SAMLPropertyMapping> {
set mappingUUID(value: string) { return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlRead({
new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlRead({ pmUuid: pk,
pmUuid: value,
}).then(mapping => {
this.mapping = mapping;
}); });
} }
@property({attribute: false})
mapping?: SAMLPropertyMapping;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.mapping) { if (this.instance) {
return t`Successfully updated mapping.`; return t`Successfully updated mapping.`;
} else { } else {
return t`Successfully created mapping.`; return t`Successfully created mapping.`;
@ -31,9 +25,9 @@ export class PropertyMappingLDAPForm extends Form<SAMLPropertyMapping> {
} }
send = (data: SAMLPropertyMapping): Promise<SAMLPropertyMapping> => { send = (data: SAMLPropertyMapping): Promise<SAMLPropertyMapping> => {
if (this.mapping) { if (this.instance) {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlUpdate({ return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlUpdate({
pmUuid: this.mapping.pk || "", pmUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -49,13 +43,13 @@ export class PropertyMappingLDAPForm extends Form<SAMLPropertyMapping> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.mapping?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`SAML Attribute Name`} label=${t`SAML Attribute Name`}
?required=${true} ?required=${true}
name="samlName"> name="samlName">
<input type="text" value="${ifDefined(this.mapping?.samlName)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.samlName)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Attribute name used for SAML Assertions. Can be a URN OID, a schema reference, or a any other string. If this property mapping is used for NameID Property, this field is discarded.`} ${t`Attribute name used for SAML Assertions. Can be a URN OID, a schema reference, or a any other string. If this property mapping is used for NameID Property, this field is discarded.`}
</p> </p>
@ -63,7 +57,7 @@ export class PropertyMappingLDAPForm extends Form<SAMLPropertyMapping> {
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Friendly Name`} label=${t`Friendly Name`}
name="friendlyName"> name="friendlyName">
<input type="text" value="${ifDefined(this.mapping?.friendlyName || "")}" class="pf-c-form-control"> <input type="text" value="${ifDefined(this.instance?.friendlyName || "")}" class="pf-c-form-control">
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Optionally set the 'FriendlyName' value of the Assertion attribute.`} ${t`Optionally set the 'FriendlyName' value of the Assertion attribute.`}
</p> </p>
@ -72,7 +66,7 @@ export class PropertyMappingLDAPForm extends Form<SAMLPropertyMapping> {
label=${t`Expression`} label=${t`Expression`}
?required=${true} ?required=${true}
name="expression"> name="expression">
<ak-codemirror mode="python" value="${ifDefined(this.mapping?.expression)}"> <ak-codemirror mode="python" value="${ifDefined(this.instance?.expression)}">
</ak-codemirror> </ak-codemirror>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Expression using Python.`} ${t`Expression using Python.`}

View File

@ -1,29 +1,24 @@
import { ScopeMapping, PropertymappingsApi } from "authentik-api"; import { ScopeMapping, PropertymappingsApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { Form } from "../../elements/forms/Form"; import { ModelForm } from "../../elements/forms/ModelForm";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../elements/forms/HorizontalFormElement"; import "../../elements/forms/HorizontalFormElement";
import "../../elements/CodeMirror"; import "../../elements/CodeMirror";
@customElement("ak-property-mapping-scope-form") @customElement("ak-property-mapping-scope-form")
export class PropertyMappingScopeForm extends Form<ScopeMapping> { export class PropertyMappingScopeForm extends ModelForm<ScopeMapping, string> {
set mappingUUID(value: string) { loadInstance(pk: string): Promise<ScopeMapping> {
new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeRead({ return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeRead({
pmUuid: value, pmUuid: pk,
}).then(mapping => {
this.mapping = mapping;
}); });
} }
@property({attribute: false})
mapping?: ScopeMapping;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.mapping) { if (this.instance) {
return t`Successfully updated mapping.`; return t`Successfully updated mapping.`;
} else { } else {
return t`Successfully created mapping.`; return t`Successfully created mapping.`;
@ -31,9 +26,9 @@ export class PropertyMappingScopeForm extends Form<ScopeMapping> {
} }
send = (data: ScopeMapping): Promise<ScopeMapping> => { send = (data: ScopeMapping): Promise<ScopeMapping> => {
if (this.mapping) { if (this.instance) {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeUpdate({ return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeUpdate({
pmUuid: this.mapping.pk || "", pmUuid: this.instance.pk || "",
data: data data: data
}); });
} else { } else {
@ -49,27 +44,27 @@ export class PropertyMappingScopeForm extends Form<ScopeMapping> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.mapping?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Scope name`} label=${t`Scope name`}
?required=${true} ?required=${true}
name="scopeName"> name="scopeName">
<input type="text" value="${ifDefined(this.mapping?.scopeName)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.scopeName)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Scope which the client can specify to access these properties.`}</p> <p class="pf-c-form__helper-text">${t`Scope which the client can specify to access these properties.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Description`} label=${t`Description`}
?required=${true} ?required=${true}
name="description"> name="description">
<input type="text" value="${ifDefined(this.mapping?.description)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.description)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Description shown to the user when consenting. If left empty, the user won't be informed.`}</p> <p class="pf-c-form__helper-text">${t`Description shown to the user when consenting. If left empty, the user won't be informed.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Expression`} label=${t`Expression`}
?required=${true} ?required=${true}
name="expression"> name="expression">
<ak-codemirror mode="python" value="${ifDefined(this.mapping?.expression)}"> <ak-codemirror mode="python" value="${ifDefined(this.instance?.expression)}">
</ak-codemirror> </ak-codemirror>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Expression using Python.`} ${t`Expression using Python.`}

View File

@ -79,7 +79,7 @@ export class ProviderListPage extends TablePage<Provider> {
<ak-proxy-form <ak-proxy-form
slot="form" slot="form"
.args=${{ .args=${{
"providerUUID": item.pk "instancePk": item.pk
}} }}
type=${ifDefined(item.component)}> type=${ifDefined(item.component)}>
</ak-proxy-form> </ak-proxy-form>

View File

@ -1,9 +1,9 @@
import { FlowDesignationEnum, FlowsApi, ProvidersApi, LDAPProvider, CoreApi } from "authentik-api"; import { FlowDesignationEnum, FlowsApi, ProvidersApi, LDAPProvider, CoreApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form"; import { ModelForm } from "../../../elements/forms/ModelForm";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
@ -11,21 +11,16 @@ import "../../../elements/forms/FormGroup";
import { first } from "../../../utils"; import { first } from "../../../utils";
@customElement("ak-provider-ldap-form") @customElement("ak-provider-ldap-form")
export class LDAPProviderFormPage extends Form<LDAPProvider> { export class LDAPProviderFormPage extends ModelForm<LDAPProvider, number> {
set providerUUID(value: number) { loadInstance(pk: number): Promise<LDAPProvider> {
new ProvidersApi(DEFAULT_CONFIG).providersLdapRead({ return new ProvidersApi(DEFAULT_CONFIG).providersLdapRead({
id: value, id: pk,
}).then(provider => {
this.provider = provider;
}); });
} }
@property({attribute: false})
provider?: LDAPProvider;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.provider) { if (this.instance) {
return t`Successfully updated provider.`; return t`Successfully updated provider.`;
} else { } else {
return t`Successfully created provider.`; return t`Successfully created provider.`;
@ -33,9 +28,9 @@ export class LDAPProviderFormPage extends Form<LDAPProvider> {
} }
send = (data: LDAPProvider): Promise<LDAPProvider> => { send = (data: LDAPProvider): Promise<LDAPProvider> => {
if (this.provider) { if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersLdapUpdate({ return new ProvidersApi(DEFAULT_CONFIG).providersLdapUpdate({
id: this.provider.pk || 0, id: this.instance.pk || 0,
data: data data: data
}); });
} else { } else {
@ -51,7 +46,7 @@ export class LDAPProviderFormPage extends Form<LDAPProvider> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.provider?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Bind flow`} label=${t`Bind flow`}
@ -63,7 +58,7 @@ export class LDAPProviderFormPage extends Form<LDAPProvider> {
designation: FlowDesignationEnum.Authentication, designation: FlowDesignationEnum.Authentication,
}).then(flows => { }).then(flows => {
return flows.results.map(flow => { return flows.results.map(flow => {
return html`<option value=${ifDefined(flow.pk)} ?selected=${this.provider?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`; return html`<option value=${ifDefined(flow.pk)} ?selected=${this.instance?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -73,10 +68,10 @@ export class LDAPProviderFormPage extends Form<LDAPProvider> {
label=${t`Group`} label=${t`Group`}
name="searchGroup"> name="searchGroup">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.provider?.searchGroup === undefined}>---------</option> <option value="" ?selected=${this.instance?.searchGroup === undefined}>---------</option>
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => { ${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
return groups.results.map(group => { return groups.results.map(group => {
return html`<option value=${ifDefined(group.pk)} ?selected=${this.provider?.searchGroup === group.pk}>${group.name}</option>`; return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.searchGroup === group.pk}>${group.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -92,7 +87,7 @@ export class LDAPProviderFormPage extends Form<LDAPProvider> {
label=${t`Base DN`} label=${t`Base DN`}
?required=${true} ?required=${true}
name="baseDn"> name="baseDn">
<input type="text" value="${first(this.provider?.baseDn, "DC=ldap,DC=goauthentik,DC=io")}" class="pf-c-form-control" required> <input type="text" value="${first(this.instance?.baseDn, "DC=ldap,DC=goauthentik,DC=io")}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`LDAP DN under which bind requests and search requests can be made.`}</p> <p class="pf-c-form__helper-text">${t`LDAP DN under which bind requests and search requests can be made.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>

View File

@ -102,7 +102,7 @@ export class LDAPProviderViewPage extends LitElement {
</span> </span>
<ak-provider-ldap-form <ak-provider-ldap-form
slot="form" slot="form"
.providerUUID=${this.provider.pk || 0}> .instancePk=${this.provider.pk || 0}>
</ak-provider-ldap-form> </ak-provider-ldap-form>
<button slot="trigger" class="pf-c-button pf-m-primary"> <button slot="trigger" class="pf-c-button pf-m-primary">
${t`Edit`} ${t`Edit`}

View File

@ -3,7 +3,7 @@ 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";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form"; import { ModelForm } from "../../../elements/forms/ModelForm";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
@ -11,25 +11,22 @@ import "../../../elements/forms/FormGroup";
import { first, randomString } from "../../../utils"; import { first, randomString } from "../../../utils";
@customElement("ak-provider-oauth2-form") @customElement("ak-provider-oauth2-form")
export class OAuth2ProviderFormPage extends Form<OAuth2Provider> { export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
set providerUUID(value: number) { loadInstance(pk: number): Promise<OAuth2Provider> {
new ProvidersApi(DEFAULT_CONFIG).providersOauth2Read({ return new ProvidersApi(DEFAULT_CONFIG).providersOauth2Read({
id: value, id: pk,
}).then(provider => { }).then(provider => {
this.provider = provider;
this.showClientSecret = provider.clientType === OAuth2ProviderClientTypeEnum.Confidential; this.showClientSecret = provider.clientType === OAuth2ProviderClientTypeEnum.Confidential;
return provider;
}); });
} }
@property({attribute: false})
provider?: OAuth2Provider;
@property({type: Boolean}) @property({type: Boolean})
showClientSecret = true; showClientSecret = true;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.provider) { if (this.instance) {
return t`Successfully updated provider.`; return t`Successfully updated provider.`;
} else { } else {
return t`Successfully created provider.`; return t`Successfully created provider.`;
@ -37,9 +34,9 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
} }
send = (data: OAuth2Provider): Promise<OAuth2Provider> => { send = (data: OAuth2Provider): Promise<OAuth2Provider> => {
if (this.provider) { if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersOauth2Update({ return new ProvidersApi(DEFAULT_CONFIG).providersOauth2Update({
id: this.provider.pk || 0, id: this.instance.pk || 0,
data: data data: data
}); });
} else { } else {
@ -55,7 +52,7 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.provider?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Authorization flow`} label=${t`Authorization flow`}
@ -67,7 +64,7 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
designation: FlowDesignationEnum.Authorization, designation: FlowDesignationEnum.Authorization,
}).then(flows => { }).then(flows => {
return flows.results.map(flow => { return flows.results.map(flow => {
return html`<option value=${ifDefined(flow.pk)} ?selected=${this.provider?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`; return html`<option value=${ifDefined(flow.pk)} ?selected=${this.instance?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -91,10 +88,10 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
this.showClientSecret = true; this.showClientSecret = true;
} }
}}> }}>
<option value=${OAuth2ProviderClientTypeEnum.Confidential} ?selected=${this.provider?.clientType === OAuth2ProviderClientTypeEnum.Confidential}> <option value=${OAuth2ProviderClientTypeEnum.Confidential} ?selected=${this.instance?.clientType === OAuth2ProviderClientTypeEnum.Confidential}>
${t`Confidential`} ${t`Confidential`}
</option> </option>
<option value=${OAuth2ProviderClientTypeEnum.Public} ?selected=${this.provider?.clientType === OAuth2ProviderClientTypeEnum.Public}> <option value=${OAuth2ProviderClientTypeEnum.Public} ?selected=${this.instance?.clientType === OAuth2ProviderClientTypeEnum.Public}>
${t`Public`} ${t`Public`}
</option> </option>
</select> </select>
@ -104,19 +101,19 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
label=${t`Client ID`} label=${t`Client ID`}
?required=${true} ?required=${true}
name="clientId"> name="clientId">
<input type="text" value="${first(this.provider?.clientId, randomString(40))}" class="pf-c-form-control" required> <input type="text" value="${first(this.instance?.clientId, randomString(40))}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
?hidden=${!this.showClientSecret} ?hidden=${!this.showClientSecret}
label=${t`Client Secret`} label=${t`Client Secret`}
name="clientSecret"> name="clientSecret">
<input type="text" value="${first(this.provider?.clientSecret, randomString(128))}" class="pf-c-form-control"> <input type="text" value="${first(this.instance?.clientSecret, randomString(128))}" class="pf-c-form-control">
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Redirect URIs/Origins`} label=${t`Redirect URIs/Origins`}
?required=${true} ?required=${true}
name="redirectUris"> name="redirectUris">
<textarea class="pf-c-form-control" required>${this.provider?.redirectUris}</textarea> <textarea class="pf-c-form-control" required>${this.instance?.redirectUris}</textarea>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`Valid redirect URLs after a successful authorization flow. Also specify any origins here for Implicit flows.`} ${t`Valid redirect URLs after a successful authorization flow. Also specify any origins here for Implicit flows.`}
</p> </p>
@ -133,7 +130,7 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
label=${t`Access code validity`} label=${t`Access code validity`}
?required=${true} ?required=${true}
name="accessCodeValidity"> name="accessCodeValidity">
<input type="text" value="${first(this.provider?.accessCodeValidity, "minutes=1")}" class="pf-c-form-control" required> <input type="text" value="${first(this.instance?.accessCodeValidity, "minutes=1")}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Configure how long access codes are valid for.`}</p> <p class="pf-c-form__helper-text">${t`Configure how long access codes are valid for.`}</p>
<p class="pf-c-form__helper-text">${t`(Format: hours=-1;minutes=-2;seconds=-3).`}</p> <p class="pf-c-form__helper-text">${t`(Format: hours=-1;minutes=-2;seconds=-3).`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
@ -141,7 +138,7 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
label=${t`Token validity`} label=${t`Token validity`}
?required=${true} ?required=${true}
name="tokenValidity"> name="tokenValidity">
<input type="text" value="${first(this.provider?.tokenValidity, "minutes=10")}" class="pf-c-form-control" required> <input type="text" value="${first(this.instance?.tokenValidity, "minutes=10")}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Configure how long refresh tokens and their id_tokens are valid for.`}</p> <p class="pf-c-form__helper-text">${t`Configure how long refresh tokens and their id_tokens are valid for.`}</p>
<p class="pf-c-form__helper-text">${t`(Format: hours=-1;minutes=-2;seconds=-3).`}</p> <p class="pf-c-form__helper-text">${t`(Format: hours=-1;minutes=-2;seconds=-3).`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
@ -150,10 +147,10 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
?required=${true} ?required=${true}
name="jwtAlg"> name="jwtAlg">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value=${OAuth2ProviderJwtAlgEnum.Rs256} ?selected=${this.provider?.jwtAlg === OAuth2ProviderJwtAlgEnum.Rs256}> <option value=${OAuth2ProviderJwtAlgEnum.Rs256} ?selected=${this.instance?.jwtAlg === OAuth2ProviderJwtAlgEnum.Rs256}>
${t`RS256 (Asymmetric Encryption)`} ${t`RS256 (Asymmetric Encryption)`}
</option> </option>
<option value=${OAuth2ProviderJwtAlgEnum.Hs256} ?selected=${this.provider?.jwtAlg === OAuth2ProviderJwtAlgEnum.Hs256}> <option value=${OAuth2ProviderJwtAlgEnum.Hs256} ?selected=${this.instance?.jwtAlg === OAuth2ProviderJwtAlgEnum.Hs256}>
${t`HS256 (Symmetric Encryption)`} ${t`HS256 (Symmetric Encryption)`}
</option> </option>
</select> </select>
@ -168,10 +165,10 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
}).then(scopes => { }).then(scopes => {
return scopes.results.map(scope => { return scopes.results.map(scope => {
let selected = false; let selected = false;
if (!this.provider?.propertyMappings) { if (!this.instance?.propertyMappings) {
selected = scope.managed?.startsWith("goauthentik.io/providers/oauth2/scope-") || false; selected = scope.managed?.startsWith("goauthentik.io/providers/oauth2/scope-") || false;
} else { } else {
selected = Array.from(this.provider?.propertyMappings).some(su => { selected = Array.from(this.instance?.propertyMappings).some(su => {
return su == scope.pk; return su == scope.pk;
}); });
} }
@ -186,13 +183,13 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
label=${t`RSA Key`} label=${t`RSA Key`}
name="rsaKey"> name="rsaKey">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.provider?.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.provider?.rsaKey === key.pk; let selected = this.instance?.rsaKey === key.pk;
if (keys.results.length === 1) { if (keys.results.length === 1) {
selected = true; selected = true;
} }
@ -207,16 +204,16 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
?required=${true} ?required=${true}
name="subMode"> name="subMode">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="${OAuth2ProviderSubModeEnum.HashedUserId}" ?selected=${this.provider?.subMode === OAuth2ProviderSubModeEnum.HashedUserId}> <option value="${OAuth2ProviderSubModeEnum.HashedUserId}" ?selected=${this.instance?.subMode === OAuth2ProviderSubModeEnum.HashedUserId}>
${t`Based on the Hashed User ID`} ${t`Based on the Hashed User ID`}
</option> </option>
<option value="${OAuth2ProviderSubModeEnum.UserUsername}" ?selected=${this.provider?.subMode === OAuth2ProviderSubModeEnum.UserUsername}> <option value="${OAuth2ProviderSubModeEnum.UserUsername}" ?selected=${this.instance?.subMode === OAuth2ProviderSubModeEnum.UserUsername}>
${t`Based on the username`} ${t`Based on the username`}
</option> </option>
<option value="${OAuth2ProviderSubModeEnum.UserEmail}" ?selected=${this.provider?.subMode === OAuth2ProviderSubModeEnum.UserEmail}> <option value="${OAuth2ProviderSubModeEnum.UserEmail}" ?selected=${this.instance?.subMode === OAuth2ProviderSubModeEnum.UserEmail}>
${t`Based on the User's Email. This is recommended over the UPN method.`} ${t`Based on the User's Email. This is recommended over the UPN method.`}
</option> </option>
<option value="${OAuth2ProviderSubModeEnum.UserUpn}" ?selected=${this.provider?.subMode === OAuth2ProviderSubModeEnum.UserUpn}> <option value="${OAuth2ProviderSubModeEnum.UserUpn}" ?selected=${this.instance?.subMode === OAuth2ProviderSubModeEnum.UserUpn}>
${t`Based on the User's UPN, only works if user has a 'upn' attribute set. Use this method only if you have different UPN and Mail domains.`} ${t`Based on the User's UPN, only works if user has a 'upn' attribute set. Use this method only if you have different UPN and Mail domains.`}
</option> </option>
</select> </select>
@ -226,7 +223,7 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="includeClaimsInIdToken"> <ak-form-element-horizontal name="includeClaimsInIdToken">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.provider?.includeClaimsInIdToken, true)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.includeClaimsInIdToken, true)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Include claims in id_token`} ${t`Include claims in id_token`}
</label> </label>
@ -238,10 +235,10 @@ export class OAuth2ProviderFormPage extends Form<OAuth2Provider> {
?required=${true} ?required=${true}
name="issuerMode"> name="issuerMode">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="${OAuth2ProviderIssuerModeEnum.PerProvider}" ?selected=${this.provider?.issuerMode === OAuth2ProviderIssuerModeEnum.PerProvider}> <option value="${OAuth2ProviderIssuerModeEnum.PerProvider}" ?selected=${this.instance?.issuerMode === OAuth2ProviderIssuerModeEnum.PerProvider}>
${t`Each provider has a different issuer, based on the application slug.`} ${t`Each provider has a different issuer, based on the application slug.`}
</option> </option>
<option value="${OAuth2ProviderIssuerModeEnum.Global}" ?selected=${this.provider?.issuerMode === OAuth2ProviderIssuerModeEnum.Global}> <option value="${OAuth2ProviderIssuerModeEnum.Global}" ?selected=${this.instance?.issuerMode === OAuth2ProviderIssuerModeEnum.Global}>
${t`Same identifier is used for all providers`} ${t`Same identifier is used for all providers`}
</option> </option>
</select> </select>

View File

@ -127,7 +127,7 @@ export class OAuth2ProviderViewPage extends LitElement {
</span> </span>
<ak-provider-oauth2-form <ak-provider-oauth2-form
slot="form" slot="form"
.providerUUID=${this.provider.pk || 0}> .instancePk=${this.provider.pk || 0}>
</ak-provider-oauth2-form> </ak-provider-oauth2-form>
<button slot="trigger" class="pf-c-button pf-m-primary"> <button slot="trigger" class="pf-c-button pf-m-primary">
${t`Edit`} ${t`Edit`}

View File

@ -3,7 +3,7 @@ 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";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form"; import { ModelForm } from "../../../elements/forms/ModelForm";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
@ -11,21 +11,18 @@ import "../../../elements/forms/FormGroup";
import { first } from "../../../utils"; import { first } from "../../../utils";
@customElement("ak-provider-proxy-form") @customElement("ak-provider-proxy-form")
export class ProxyProviderFormPage extends Form<ProxyProvider> { export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
set providerUUID(value: number) { loadInstance(pk: number): Promise<ProxyProvider> {
new ProvidersApi(DEFAULT_CONFIG).providersProxyRead({ return new ProvidersApi(DEFAULT_CONFIG).providersProxyRead({
id: value, id: pk,
}).then(provider => { }).then(provider => {
this.provider = provider;
this.showHttpBasic = first(provider.basicAuthEnabled, true); this.showHttpBasic = first(provider.basicAuthEnabled, true);
this.showInternalServer = first(!provider.forwardAuthMode, true); this.showInternalServer = first(!provider.forwardAuthMode, true);
return provider;
}); });
} }
@property({attribute: false})
provider?: ProxyProvider;
@property({type: Boolean}) @property({type: Boolean})
showHttpBasic = true; showHttpBasic = true;
@ -33,7 +30,7 @@ export class ProxyProviderFormPage extends Form<ProxyProvider> {
showInternalServer = true; showInternalServer = true;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.provider) { if (this.instance) {
return t`Successfully updated provider.`; return t`Successfully updated provider.`;
} else { } else {
return t`Successfully created provider.`; return t`Successfully created provider.`;
@ -41,9 +38,9 @@ export class ProxyProviderFormPage extends Form<ProxyProvider> {
} }
send = (data: ProxyProvider): Promise<ProxyProvider> => { send = (data: ProxyProvider): Promise<ProxyProvider> => {
if (this.provider) { if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersProxyUpdate({ return new ProvidersApi(DEFAULT_CONFIG).providersProxyUpdate({
id: this.provider.pk || 0, id: this.instance.pk || 0,
data: data data: data
}); });
} else { } else {
@ -60,13 +57,13 @@ export class ProxyProviderFormPage extends Form<ProxyProvider> {
return html`<ak-form-element-horizontal return html`<ak-form-element-horizontal
label=${t`HTTP-Basic Username Key`} label=${t`HTTP-Basic Username Key`}
name="basicAuthUserAttribute"> name="basicAuthUserAttribute">
<input type="text" value="${ifDefined(this.provider?.basicAuthUserAttribute)}" class="pf-c-form-control"> <input type="text" value="${ifDefined(this.instance?.basicAuthUserAttribute)}" class="pf-c-form-control">
<p class="pf-c-form__helper-text">${t`User/Group Attribute used for the user part of the HTTP-Basic Header. If not set, the user's Email address is used.`}</p> <p class="pf-c-form__helper-text">${t`User/Group Attribute used for the user part of the HTTP-Basic Header. If not set, the user's Email address is used.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`HTTP-Basic Password Key`} label=${t`HTTP-Basic Password Key`}
name="basicAuthPasswordAttribute"> name="basicAuthPasswordAttribute">
<input type="text" value="${ifDefined(this.provider?.basicAuthPasswordAttribute)}" class="pf-c-form-control"> <input type="text" value="${ifDefined(this.instance?.basicAuthPasswordAttribute)}" class="pf-c-form-control">
<p class="pf-c-form__helper-text">${t`User/Group Attribute used for the password part of the HTTP-Basic Header.`}</p> <p class="pf-c-form__helper-text">${t`User/Group Attribute used for the password part of the HTTP-Basic Header.`}</p>
</ak-form-element-horizontal>`; </ak-form-element-horizontal>`;
} }
@ -79,12 +76,12 @@ export class ProxyProviderFormPage extends Form<ProxyProvider> {
label=${t`Internal host`} label=${t`Internal host`}
?required=${true} ?required=${true}
name="internalHost"> name="internalHost">
<input type="text" value="${ifDefined(this.provider?.internalHost)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.internalHost)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Upstream host that the requests are forwarded to.`}</p> <p class="pf-c-form__helper-text">${t`Upstream host that the requests are forwarded to.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="internalHostSslValidation"> <ak-form-element-horizontal name="internalHostSslValidation">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.provider?.internalHostSslValidation, true)}> <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.internalHostSslValidation, true)}>
<label class="pf-c-check__label"> <label class="pf-c-check__label">
${t`Internal host SSL Validation`} ${t`Internal host SSL Validation`}
</label> </label>
@ -99,7 +96,7 @@ export class ProxyProviderFormPage extends Form<ProxyProvider> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.provider?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Authorization flow`} label=${t`Authorization flow`}
@ -111,7 +108,7 @@ export class ProxyProviderFormPage extends Form<ProxyProvider> {
designation: FlowDesignationEnum.Authorization, designation: FlowDesignationEnum.Authorization,
}).then(flows => { }).then(flows => {
return flows.results.map(flow => { return flows.results.map(flow => {
return html`<option value=${ifDefined(flow.pk)} ?selected=${this.provider?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`; return html`<option value=${ifDefined(flow.pk)} ?selected=${this.instance?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -127,12 +124,12 @@ export class ProxyProviderFormPage extends Form<ProxyProvider> {
label=${t`External host`} label=${t`External host`}
?required=${true} ?required=${true}
name="externalHost"> name="externalHost">
<input type="text" value="${ifDefined(this.provider?.externalHost)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.externalHost)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`The external URL you'll access the outpost at.`}</p> <p class="pf-c-form__helper-text">${t`The external URL you'll access the application at`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="forwardAuthMode"> <ak-form-element-horizontal name="forwardAuthMode">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.provider?.forwardAuthMode, false)} @change=${(ev: Event) => { <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.forwardAuthMode, false)} @change=${(ev: Event) => {
const el = ev.target as HTMLInputElement; const el = ev.target as HTMLInputElement;
this.showInternalServer = !el.checked; this.showInternalServer = !el.checked;
}}> }}>
@ -162,7 +159,7 @@ export class ProxyProviderFormPage extends Form<ProxyProvider> {
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.provider?.certificate === key.pk}>${key.name}</option>`; return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.certificate === key.pk}>${key.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -171,13 +168,13 @@ export class ProxyProviderFormPage extends Form<ProxyProvider> {
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Skip path regex`} label=${t`Skip path regex`}
name="skipPathRegex"> name="skipPathRegex">
<textarea class="pf-c-form-control">${this.provider?.skipPathRegex}</textarea> <textarea class="pf-c-form-control">${this.instance?.skipPathRegex}</textarea>
<p class="pf-c-form__helper-text">${t`Regular expressions for which authentication is not required. Each new line is interpreted as a new Regular Expression.`}</p> <p class="pf-c-form__helper-text">${t`Regular expressions for which authentication is not required. Each new line is interpreted as a new Regular Expression.`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="basicAuthEnabled"> <ak-form-element-horizontal name="basicAuthEnabled">
<div class="pf-c-check"> <div class="pf-c-check">
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.provider?.basicAuthEnabled, false)} @change=${(ev: Event) => { <input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.basicAuthEnabled, false)} @change=${(ev: Event) => {
const el = ev.target as HTMLInputElement; const el = ev.target as HTMLInputElement;
this.showHttpBasic = el.checked; this.showHttpBasic = el.checked;
}}> }}>

View File

@ -6,9 +6,7 @@ import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css"; import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
import PFFlex from "@patternfly/patternfly/utilities/Flex/flex.css";
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css";
import AKGlobal from "../../../authentik.css"; import AKGlobal from "../../../authentik.css";
@ -42,7 +40,7 @@ export class ProxyProviderViewPage extends LitElement {
provider?: ProxyProvider; provider?: ProxyProvider;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [PFBase, PFButton, PFPage, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, AKGlobal]; return [PFBase, PFButton, PFPage, PFGrid, PFGallery, PFContent, PFCard, PFDescriptionList, AKGlobal];
} }
constructor() { constructor() {
@ -59,8 +57,8 @@ export class ProxyProviderViewPage extends LitElement {
} }
return html`<ak-tabs> return html`<ak-tabs>
<section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile"> <section slot="page-overview" data-tab-title="${t`Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-u-display-flex pf-u-justify-content-center"> <div class="pf-l-grid pf-m-gutter">
<div class="pf-u-w-75"> <div class="pf-l-grid__item pf-m-6-col">
<div class="pf-c-card"> <div class="pf-c-card">
<div class="pf-c-card__body"> <div class="pf-c-card__body">
<dl class="pf-c-description-list pf-m-3-col-on-lg"> <dl class="pf-c-description-list pf-m-3-col-on-lg">
@ -115,6 +113,23 @@ export class ProxyProviderViewPage extends LitElement {
</div> </div>
</dd> </dd>
</div> </div>
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${t`Forward auth`}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
${this.provider.forwardAuthMode ?
html`<span class="pf-c-button__icon pf-m-start">
<i class="fas fa-check-circle" aria-hidden="true"></i>&nbsp;
</span>${t`Yes`}`:
html`<span class="pf-c-button__icon pf-m-start">
<i class="fas fa-times-circle" aria-hidden="true"></i>&nbsp;
</span>${t`No`}`
}
</div>
</dd>
</div>
</dl> </dl>
</div> </div>
<div class="pf-c-card__footer"> <div class="pf-c-card__footer">
@ -127,7 +142,7 @@ export class ProxyProviderViewPage extends LitElement {
</span> </span>
<ak-provider-proxy-form <ak-provider-proxy-form
slot="form" slot="form"
.providerUUID=${this.provider.pk || 0}> .instancePk=${this.provider.pk || 0}>
</ak-provider-proxy-form> </ak-provider-proxy-form>
<button slot="trigger" class="pf-c-button pf-m-primary"> <button slot="trigger" class="pf-c-button pf-m-primary">
${t`Edit`} ${t`Edit`}
@ -136,6 +151,27 @@ export class ProxyProviderViewPage extends LitElement {
</div> </div>
</div> </div>
</div> </div>
<div class="pf-l-grid__item pf-m-6-col">
<div class="pf-c-card">
<div class="pf-c-card__title">
${t`Protocol Settings`}
</div>
<div class="pf-c-card__body">
<dl class="pf-c-description-list pf-m-3-col-on-lg">
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${t`Allowed Redirect URIs`}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
${this.provider.redirectUris}
</div>
</dd>
</div>
</dl>
</div>
</div>
</div>
</div> </div>
</section> </section>
<section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile"> <section slot="page-changelog" data-tab-title="${t`Changelog`}" class="pf-c-page__main-section pf-m-no-padding-mobile">

View File

@ -1,30 +1,26 @@
import { CryptoApi, FlowDesignationEnum, FlowsApi, SAMLProvider, ProvidersApi, PropertymappingsApi, SAMLProviderSpBindingEnum, SAMLProviderDigestAlgorithmEnum, SAMLProviderSignatureAlgorithmEnum } from "authentik-api"; import { CryptoApi, FlowDesignationEnum, FlowsApi, SAMLProvider, ProvidersApi, PropertymappingsApi, SAMLProviderSpBindingEnum, SAMLProviderDigestAlgorithmEnum, SAMLProviderSignatureAlgorithmEnum } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { Form } from "../../../elements/forms/Form"; import { ModelForm } from "../../../elements/forms/ModelForm";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
@customElement("ak-provider-saml-form") @customElement("ak-provider-saml-form")
export class SAMLProviderFormPage extends Form<SAMLProvider> { export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
set providerUUID(value: number) { loadInstance(pk: number): Promise<SAMLProvider> {
new ProvidersApi(DEFAULT_CONFIG).providersSamlRead({ console.log("reading saml provider");
id: value, return new ProvidersApi(DEFAULT_CONFIG).providersSamlRead({
}).then(provider => { id: pk,
this.provider = provider;
}); });
} }
@property({attribute: false})
provider?: SAMLProvider;
getSuccessMessage(): string { getSuccessMessage(): string {
if (this.provider) { if (this.instance) {
return t`Successfully updated provider.`; return t`Successfully updated provider.`;
} else { } else {
return t`Successfully created provider.`; return t`Successfully created provider.`;
@ -32,9 +28,9 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
} }
send = (data: SAMLProvider): Promise<SAMLProvider> => { send = (data: SAMLProvider): Promise<SAMLProvider> => {
if (this.provider) { if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersSamlUpdate({ return new ProvidersApi(DEFAULT_CONFIG).providersSamlUpdate({
id: this.provider.pk || 0, id: this.instance.pk || 0,
data: data data: data
}); });
} else { } else {
@ -50,7 +46,7 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
label=${t`Name`} label=${t`Name`}
?required=${true} ?required=${true}
name="name"> name="name">
<input type="text" value="${ifDefined(this.provider?.name)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.name)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Authorization flow`} label=${t`Authorization flow`}
@ -62,7 +58,7 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
designation: FlowDesignationEnum.Authorization, designation: FlowDesignationEnum.Authorization,
}).then(flows => { }).then(flows => {
return flows.results.map(flow => { return flows.results.map(flow => {
return html`<option value=${ifDefined(flow.pk)} ?selected=${this.provider?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`; return html`<option value=${ifDefined(flow.pk)} ?selected=${this.instance?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -78,23 +74,23 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
label=${t`ACS URL`} label=${t`ACS URL`}
?required=${true} ?required=${true}
name="acsUrl"> name="acsUrl">
<input type="text" value="${ifDefined(this.provider?.acsUrl)}" class="pf-c-form-control" required> <input type="text" value="${ifDefined(this.instance?.acsUrl)}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Issuer`} label=${t`Issuer`}
?required=${true} ?required=${true}
name="issuer"> name="issuer">
<input type="text" value="${this.provider?.issuer || "authentik"}" class="pf-c-form-control" required> <input type="text" value="${this.instance?.issuer || "authentik"}" class="pf-c-form-control" required>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Service Provider Binding`} label=${t`Service Provider Binding`}
?required=${true} ?required=${true}
name="spBinding"> name="spBinding">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value=${SAMLProviderSpBindingEnum.Redirect} ?selected=${this.provider?.spBinding === SAMLProviderSpBindingEnum.Redirect}> <option value=${SAMLProviderSpBindingEnum.Redirect} ?selected=${this.instance?.spBinding === SAMLProviderSpBindingEnum.Redirect}>
${t`Redirect`} ${t`Redirect`}
</option> </option>
<option value=${SAMLProviderSpBindingEnum.Post} ?selected=${this.provider?.spBinding === SAMLProviderSpBindingEnum.Post}> <option value=${SAMLProviderSpBindingEnum.Post} ?selected=${this.instance?.spBinding === SAMLProviderSpBindingEnum.Post}>
${t`Post`} ${t`Post`}
</option> </option>
</select> </select>
@ -103,7 +99,7 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Audience`} label=${t`Audience`}
name="audience"> name="audience">
<input type="text" value="${ifDefined(this.provider?.audience)}" class="pf-c-form-control"> <input type="text" value="${ifDefined(this.instance?.audience)}" class="pf-c-form-control">
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>
</ak-form-group> </ak-form-group>
@ -117,13 +113,13 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
label=${t`Signing Certificate`} label=${t`Signing Certificate`}
name="signingKp"> name="signingKp">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.provider?.signingKp === undefined}>---------</option> <option value="" ?selected=${this.instance?.signingKp === 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.provider?.signingKp === key.pk}>${key.name}</option>`; return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.signingKp === key.pk}>${key.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -133,12 +129,12 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
label=${t`Verification Certificate`} label=${t`Verification Certificate`}
name="verificationKp"> name="verificationKp">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.provider?.verificationKp === undefined}>---------</option> <option value="" ?selected=${this.instance?.verificationKp === undefined}>---------</option>
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({ ${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
ordering: "pk", ordering: "pk",
}).then(keys => { }).then(keys => {
return keys.results.map(key => { return keys.results.map(key => {
return html`<option value=${ifDefined(key.pk)} ?selected=${this.provider?.verificationKp === key.pk}>${key.name}</option>`; return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.verificationKp === key.pk}>${key.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -155,10 +151,10 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
}).then(mappings => { }).then(mappings => {
return mappings.results.map(mapping => { return mappings.results.map(mapping => {
let selected = false; let selected = false;
if (!this.provider?.propertyMappings) { if (!this.instance?.propertyMappings) {
selected = mapping.managed?.startsWith("goauthentik.io/providers/saml") || false; selected = mapping.managed?.startsWith("goauthentik.io/providers/saml") || false;
} else { } else {
selected = Array.from(this.provider?.propertyMappings).some(su => { selected = Array.from(this.instance?.propertyMappings).some(su => {
return su == mapping.pk; return su == mapping.pk;
}); });
} }
@ -172,12 +168,12 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
label=${t`NameID Property Mapping`} label=${t`NameID Property Mapping`}
name="nameIdMapping"> name="nameIdMapping">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value="" ?selected=${this.provider?.nameIdMapping === undefined}>---------</option> <option value="" ?selected=${this.instance?.nameIdMapping === undefined}>---------</option>
${until(new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlList({ ${until(new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlList({
ordering: "saml_name" ordering: "saml_name"
}).then(mappings => { }).then(mappings => {
return mappings.results.map(mapping => { return mappings.results.map(mapping => {
return html`<option value=${ifDefined(mapping.pk)} ?selected=${this.provider?.nameIdMapping === mapping.pk}>${mapping.name}</option>`; return html`<option value=${ifDefined(mapping.pk)} ?selected=${this.instance?.nameIdMapping === mapping.pk}>${mapping.name}</option>`;
}); });
}), html`<option>${t`Loading...`}</option>`)} }), html`<option>${t`Loading...`}</option>`)}
</select> </select>
@ -188,7 +184,7 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
label=${t`Assertion valid not before`} label=${t`Assertion valid not before`}
?required=${true} ?required=${true}
name="assertionValidNotBefore"> name="assertionValidNotBefore">
<input type="text" value="${this.provider?.assertionValidNotBefore || "minutes=-5"}" class="pf-c-form-control" required> <input type="text" value="${this.instance?.assertionValidNotBefore || "minutes=-5"}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Configure the maximum allowed time drift for an asseration.`}</p> <p class="pf-c-form__helper-text">${t`Configure the maximum allowed time drift for an asseration.`}</p>
<p class="pf-c-form__helper-text">${t`(Format: hours=-1;minutes=-2;seconds=-3).`}</p> <p class="pf-c-form__helper-text">${t`(Format: hours=-1;minutes=-2;seconds=-3).`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
@ -196,14 +192,14 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
label=${t`Assertion valid not on or after`} label=${t`Assertion valid not on or after`}
?required=${true} ?required=${true}
name="assertionValidNotOnOrAfter"> name="assertionValidNotOnOrAfter">
<input type="text" value="${this.provider?.assertionValidNotOnOrAfter || "minutes=5"}" class="pf-c-form-control" required> <input type="text" value="${this.instance?.assertionValidNotOnOrAfter || "minutes=5"}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}</p> <p class="pf-c-form__helper-text">${t`Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Session valid not on or after`} label=${t`Session valid not on or after`}
?required=${true} ?required=${true}
name="sessionValidNotOnOrAfter"> name="sessionValidNotOnOrAfter">
<input type="text" value="${this.provider?.sessionValidNotOnOrAfter || "minutes=86400"}" class="pf-c-form-control" required> <input type="text" value="${this.instance?.sessionValidNotOnOrAfter || "minutes=86400"}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}</p> <p class="pf-c-form__helper-text">${t`Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
@ -212,16 +208,16 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
?required=${true} ?required=${true}
name="digestAlgorithm"> name="digestAlgorithm">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value=${SAMLProviderDigestAlgorithmEnum._200009Xmldsigsha1} ?selected=${this.provider?.digestAlgorithm === SAMLProviderDigestAlgorithmEnum._200009Xmldsigsha1}> <option value=${SAMLProviderDigestAlgorithmEnum._200009Xmldsigsha1} ?selected=${this.instance?.digestAlgorithm === SAMLProviderDigestAlgorithmEnum._200009Xmldsigsha1}>
${t`SHA1`} ${t`SHA1`}
</option> </option>
<option value=${SAMLProviderDigestAlgorithmEnum._200104Xmlencsha256} ?selected=${this.provider?.digestAlgorithm === SAMLProviderDigestAlgorithmEnum._200104Xmlencsha256 || this.provider?.digestAlgorithm === undefined}> <option value=${SAMLProviderDigestAlgorithmEnum._200104Xmlencsha256} ?selected=${this.instance?.digestAlgorithm === SAMLProviderDigestAlgorithmEnum._200104Xmlencsha256 || this.instance?.digestAlgorithm === undefined}>
${t`SHA256`} ${t`SHA256`}
</option> </option>
<option value=${SAMLProviderDigestAlgorithmEnum._200104XmldsigMoresha384} ?selected=${this.provider?.digestAlgorithm === SAMLProviderDigestAlgorithmEnum._200104XmldsigMoresha384}> <option value=${SAMLProviderDigestAlgorithmEnum._200104XmldsigMoresha384} ?selected=${this.instance?.digestAlgorithm === SAMLProviderDigestAlgorithmEnum._200104XmldsigMoresha384}>
${t`SHA384`} ${t`SHA384`}
</option> </option>
<option value=${SAMLProviderDigestAlgorithmEnum._200104Xmlencsha512} ?selected=${this.provider?.digestAlgorithm === SAMLProviderDigestAlgorithmEnum._200104Xmlencsha512}> <option value=${SAMLProviderDigestAlgorithmEnum._200104Xmlencsha512} ?selected=${this.instance?.digestAlgorithm === SAMLProviderDigestAlgorithmEnum._200104Xmlencsha512}>
${t`SHA512`} ${t`SHA512`}
</option> </option>
</select> </select>
@ -231,19 +227,19 @@ export class SAMLProviderFormPage extends Form<SAMLProvider> {
?required=${true} ?required=${true}
name="signatureAlgorithm"> name="signatureAlgorithm">
<select class="pf-c-form-control"> <select class="pf-c-form-control">
<option value=${SAMLProviderSignatureAlgorithmEnum._200009XmldsigrsaSha1} ?selected=${this.provider?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200009XmldsigrsaSha1}> <option value=${SAMLProviderSignatureAlgorithmEnum._200009XmldsigrsaSha1} ?selected=${this.instance?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200009XmldsigrsaSha1}>
${t`RSA-SHA1`} ${t`RSA-SHA1`}
</option> </option>
<option value=${SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha256} ?selected=${this.provider?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha256 || this.provider?.signatureAlgorithm === undefined}> <option value=${SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha256} ?selected=${this.instance?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha256 || this.instance?.signatureAlgorithm === undefined}>
${t`RSA-SHA256`} ${t`RSA-SHA256`}
</option> </option>
<option value=${SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha384} ?selected=${this.provider?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha384}> <option value=${SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha384} ?selected=${this.instance?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha384}>
${t`RSA-SHA384`} ${t`RSA-SHA384`}
</option> </option>
<option value=${SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha512} ?selected=${this.provider?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha512}> <option value=${SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha512} ?selected=${this.instance?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200104XmldsigMorersaSha512}>
${t`RSA-SHA512`} ${t`RSA-SHA512`}
</option> </option>
<option value=${SAMLProviderSignatureAlgorithmEnum._200009XmldsigdsaSha1} ?selected=${this.provider?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200009XmldsigdsaSha1}> <option value=${SAMLProviderSignatureAlgorithmEnum._200009XmldsigdsaSha1} ?selected=${this.instance?.signatureAlgorithm === SAMLProviderSignatureAlgorithmEnum._200009XmldsigdsaSha1}>
${t`DSA-SHA1`} ${t`DSA-SHA1`}
</option> </option>
</select> </select>

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