Compare commits
	
		
			63 Commits
		
	
	
		
			version/20
			...
			version/20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 3665e2fefa | |||
| 3dbe35cf9e | |||
| c7f0ea8a4b | |||
| 0620324702 | |||
| 5a802bcf83 | |||
| 00c8054893 | |||
| dc2538f59d | |||
| 5a0e78c698 | |||
| fd4e8a59f4 | |||
| dd1a6a81c8 | |||
| 84dfbcaaae | |||
| e649e9fb03 | |||
| 266ef66a6f | |||
| 842fdb0b0c | |||
| a270a84aae | |||
| 36f7cad23b | |||
| e441ac1e43 | |||
| 24f2932777 | |||
| a6c6f22221 | |||
| abd5db8ad4 | |||
| 124ce80694 | |||
| 4352960f83 | |||
| 4e2443d60b | |||
| 34a8408a4f | |||
| 17b65adcc5 | |||
| 6f8d129dea | |||
| 59f339beda | |||
| ce1c400022 | |||
| c99afe0ad4 | |||
| ff9ff18c11 | |||
| 4d11d82c6e | |||
| b4d750174f | |||
| fd44765ff4 | |||
| 190ebb27e4 | |||
| fb3c04d0c7 | |||
| 3ba8de61e0 | |||
| d4d2be84a3 | |||
| 96ea7ae09c | |||
| 172bfceb31 | |||
| 932b19999e | |||
| 0f1cc86e71 | |||
| 788fd00390 | |||
| f602e202b8 | |||
| 9b60fcb08b | |||
| a293a14f2a | |||
| 65bfa589eb | |||
| defca51d24 | |||
| d862028134 | |||
| c19d7c37aa | |||
| 6fb3102d25 | |||
| 51e3453dca | |||
| 6f58fdf158 | |||
| 5d4051f547 | |||
| 219b8d1a57 | |||
| c7d4e69669 | |||
| cd629dfbaa | |||
| 8eaaaae2a7 | |||
| 3d0a853449 | |||
| c2f8ff55cf | |||
| 4b52697cfe | |||
| 80fae44f47 | |||
| afd7af557d | |||
| d4493c0ee9 | 
| @ -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>.*) | ||||||
|  | |||||||
							
								
								
									
										32
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @ -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 | ||||||
|  | |||||||
| @ -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/ && \ | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Makefile
									
									
									
									
									
								
							| @ -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 | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								Pipfile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Pipfile
									
									
									
									
									
								
							| @ -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
									
									
									
								
							
							
						
						
									
										258
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							| @ -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" | ||||||
|  | |||||||
| @ -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" | ||||||
|  | |||||||
| @ -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() | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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) | ||||||
|  | |||||||
| @ -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: | ||||||
|  | |||||||
| @ -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( | ||||||
|  | |||||||
| @ -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 | ||||||
|         ) |         ) | ||||||
|  | |||||||
| @ -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) | ||||||
|  | |||||||
| @ -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) | ||||||
|  | |||||||
| @ -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): | ||||||
|  | |||||||
| @ -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: | ||||||
|  | |||||||
| @ -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", | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  | |||||||
| @ -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)) | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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() | ||||||
|  | |||||||
| @ -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( | ||||||
|  | |||||||
| @ -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): | ||||||
|  | |||||||
| @ -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 |  | ||||||
|         ) |  | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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()) | ||||||
|  | |||||||
| @ -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", | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -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( | ||||||
|  | |||||||
| @ -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", | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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", "") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -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"), | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  | |||||||
| @ -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): | ||||||
|  | |||||||
| @ -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): | ||||||
|  | |||||||
| @ -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) | ||||||
|  | |||||||
| @ -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' | ||||||
|  | |||||||
| @ -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: | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| package constants | package constants | ||||||
|  |  | ||||||
| const VERSION = "2021.5.1-rc6" | const VERSION = "2021.5.1-rc8" | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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= | ||||||
|  | |||||||
| @ -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 { | ||||||
|  | |||||||
| @ -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") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
| @ -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": | ||||||
|  | |||||||
| @ -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 | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -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 | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| package pkg | package pkg | ||||||
|  |  | ||||||
| const VERSION = "2021.5.1-rc6" | const VERSION = "2021.5.1-rc8" | ||||||
|  | |||||||
							
								
								
									
										263
									
								
								swagger.yaml
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								swagger.yaml
									
									
									
									
									
								
							| @ -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. | ||||||
|  | |||||||
| @ -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
									
									
									
								
							
							
						
						
									
										423
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -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", | ||||||
|  | |||||||
| @ -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", | ||||||
|  | |||||||
| @ -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); | ||||||
|  | |||||||
| @ -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"; | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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"> | ||||||
|  | |||||||
| @ -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) { | ||||||
|  | |||||||
							
								
								
									
										36
									
								
								web/src/elements/forms/ModelForm.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web/src/elements/forms/ModelForm.ts
									
									
									
									
									
										Normal 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
											
										
									
								
							| @ -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> | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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>`; | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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>`; | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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>`; | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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>`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | |||||||
| @ -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>`; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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>`; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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.`} | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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.`} | ||||||
|  | |||||||
| @ -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.`} | ||||||
|  | |||||||
| @ -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.`} | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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`} | ||||||
|  | |||||||
| @ -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; | ||||||
|                             }}> |                             }}> | ||||||
|  | |||||||
| @ -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>  | ||||||
|  |                                                             </span>${t`Yes`}`: | ||||||
|  |                                                         html`<span class="pf-c-button__icon pf-m-start"> | ||||||
|  |                                                             <i class="fas fa-times-circle" aria-hidden="true"></i>  | ||||||
|  |                                                             </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"> | ||||||
|  | |||||||
| @ -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
		Reference in New Issue
	
	Block a user
	