Compare commits
	
		
			90 Commits
		
	
	
		
			version/20
			...
			version-20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4b33971155 | |||
| 9e71287c25 | |||
| 9784c6c828 | |||
| 732b6a3556 | |||
| dc1e17ba0c | |||
| f05d5973af | |||
| deb48487f3 | |||
| 78f3abc64f | |||
| e45bc3834a | |||
| 0d9db1b6f2 | |||
| ce555aa5e9 | |||
| 07ca82e599 | |||
| a9339589bb | |||
| c8ed650f1c | |||
| cd78d8d3fa | |||
| 7fdc935fb9 | |||
| c8069325b3 | |||
| 9d08e02fe1 | |||
| a11ea598a2 | |||
| 2713b05e8c | |||
| fef5a5ca52 | |||
| 9d339d8b11 | |||
| 4e86aa3f59 | |||
| 221e4b665c | |||
| e67f235a9f | |||
| 741ebbacca | |||
| b63b789f77 | |||
| a63702ef90 | |||
| a4a4550753 | |||
| fd864655f6 | |||
| c1da09507a | |||
| ed2ea220bf | |||
| 7738cbe751 | |||
| bf16ea3607 | |||
| d6f44e069c | |||
| 899cf392f4 | |||
| d99451b45c | |||
| 5b31f8edf6 | |||
| 00235e039b | |||
| 2dfaef4220 | |||
| 13fceacfe4 | |||
| f8dc32b387 | |||
| 828f2f8b92 | |||
| 734399755d | |||
| d8f106b976 | |||
| 9a524dd671 | |||
| 0775296003 | |||
| 390534c14e | |||
| 2a644f64ad | |||
| e0298141cf | |||
| df7119bb22 | |||
| 1d5bba831e | |||
| 0b4be70c00 | |||
| 786737650b | |||
| 54c80a2e1f | |||
| b376211a0e | |||
| 1990a3063e | |||
| 5abf22ad8a | |||
| b7b87d87fc | |||
| 20184424ab | |||
| d5de12b69e | |||
| e0b84c71a7 | |||
| 132b990f10 | |||
| 34a3d81eff | |||
| 43a4217497 | |||
| e0ec5826ca | |||
| 5413a01360 | |||
| d9c3a29404 | |||
| bcce91476c | |||
| 56f0f454d0 | |||
| 25e63edf77 | |||
| d150851ff5 | |||
| 2e2840c71e | |||
| ff276fcc58 | |||
| 2852fa3c5e | |||
| 1c6d498621 | |||
| 3f0e4bb654 | |||
| a59d78a7c7 | |||
| 0a24202f1e | |||
| cbc86d674d | |||
| 082628771b | |||
| 93b50e7d6e | |||
| c6de4e47d7 | |||
| 0e9e378bdf | |||
| de4b3d6290 | |||
| 56f75aecc7 | |||
| 0fe009d37c | |||
| 49db283e71 | |||
| 7058366623 | |||
| ced45513b8 | 
| @ -1,5 +1,5 @@ | |||||||
| [bumpversion] | [bumpversion] | ||||||
| current_version = 2021.3.3 | current_version = 2021.3.4 | ||||||
| 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>.*) | ||||||
| @ -36,3 +36,7 @@ values = | |||||||
| [bumpversion:file:outpost/pkg/version.go] | [bumpversion:file:outpost/pkg/version.go] | ||||||
|  |  | ||||||
| [bumpversion:file:web/src/constants.ts] | [bumpversion:file:web/src/constants.ts] | ||||||
|  |  | ||||||
|  | [bumpversion:file:website/docs/outposts/manual-deploy-docker-compose.md] | ||||||
|  |  | ||||||
|  | [bumpversion:file:website/docs/outposts/manual-deploy-kubernetes.md] | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @ -18,11 +18,11 @@ jobs: | |||||||
|       - name: Building Docker Image |       - name: Building Docker Image | ||||||
|         run: docker build |         run: docker build | ||||||
|           --no-cache |           --no-cache | ||||||
|           -t beryju/authentik:2021.3.3 |           -t beryju/authentik:2021.3.4 | ||||||
|           -t beryju/authentik:latest |           -t beryju/authentik:latest | ||||||
|           -f Dockerfile . |           -f Dockerfile . | ||||||
|       - name: Push Docker Container to Registry (versioned) |       - name: Push Docker Container to Registry (versioned) | ||||||
|         run: docker push beryju/authentik:2021.3.3 |         run: docker push beryju/authentik:2021.3.4 | ||||||
|       - name: Push Docker Container to Registry (latest) |       - name: Push Docker Container to Registry (latest) | ||||||
|         run: docker push beryju/authentik:latest |         run: docker push beryju/authentik:latest | ||||||
|   build-proxy: |   build-proxy: | ||||||
| @ -48,17 +48,20 @@ jobs: | |||||||
|           cd outpost/ |           cd outpost/ | ||||||
|           docker build \ |           docker build \ | ||||||
|           --no-cache \ |           --no-cache \ | ||||||
|           -t beryju/authentik-proxy:2021.3.3 \ |           -t beryju/authentik-proxy:2021.3.4 \ | ||||||
|           -t beryju/authentik-proxy:latest \ |           -t beryju/authentik-proxy:latest \ | ||||||
|           -f proxy.Dockerfile . |           -f proxy.Dockerfile . | ||||||
|       - name: Push Docker Container to Registry (versioned) |       - name: Push Docker Container to Registry (versioned) | ||||||
|         run: docker push beryju/authentik-proxy:2021.3.3 |         run: docker push beryju/authentik-proxy:2021.3.4 | ||||||
|       - name: Push Docker Container to Registry (latest) |       - name: Push Docker Container to Registry (latest) | ||||||
|         run: docker push beryju/authentik-proxy:latest |         run: docker push beryju/authentik-proxy:latest | ||||||
|   build-static: |   build-static: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v1 |       - uses: actions/checkout@v1 | ||||||
|  |       - name: prepare ts api client | ||||||
|  |         run: | | ||||||
|  |           docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/src/api --additional-properties=typescriptThreePlus=true | ||||||
|       - name: Docker Login Registry |       - name: Docker Login Registry | ||||||
|         env: |         env: | ||||||
|           DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} |           DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | ||||||
| @ -69,11 +72,11 @@ jobs: | |||||||
|           cd web/ |           cd web/ | ||||||
|           docker build \ |           docker build \ | ||||||
|           --no-cache \ |           --no-cache \ | ||||||
|           -t beryju/authentik-static:2021.3.3 \ |           -t beryju/authentik-static:2021.3.4 \ | ||||||
|           -t beryju/authentik-static:latest \ |           -t beryju/authentik-static:latest \ | ||||||
|           -f Dockerfile . |           -f Dockerfile . | ||||||
|       - name: Push Docker Container to Registry (versioned) |       - name: Push Docker Container to Registry (versioned) | ||||||
|         run: docker push beryju/authentik-static:2021.3.3 |         run: docker push beryju/authentik-static:2021.3.4 | ||||||
|       - name: Push Docker Container to Registry (latest) |       - name: Push Docker Container to Registry (latest) | ||||||
|         run: docker push beryju/authentik-static:latest |         run: docker push beryju/authentik-static:latest | ||||||
|   test-release: |   test-release: | ||||||
| @ -107,5 +110,5 @@ jobs: | |||||||
|           SENTRY_PROJECT: authentik |           SENTRY_PROJECT: authentik | ||||||
|           SENTRY_URL: https://sentry.beryju.org |           SENTRY_URL: https://sentry.beryju.org | ||||||
|         with: |         with: | ||||||
|           tagName: 2021.3.3 |           tagName: 2021.3.4 | ||||||
|           environment: beryjuorg-prod |           environment: beryjuorg-prod | ||||||
|  | |||||||
| @ -15,6 +15,9 @@ WORKDIR / | |||||||
| COPY --from=locker /app/requirements.txt / | COPY --from=locker /app/requirements.txt / | ||||||
| COPY --from=locker /app/requirements-dev.txt / | COPY --from=locker /app/requirements-dev.txt / | ||||||
|  |  | ||||||
|  | ARG 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 && \ | ||||||
|     curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \ |     curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \ | ||||||
| @ -45,4 +48,5 @@ COPY ./lifecycle/ /lifecycle | |||||||
| USER authentik | USER authentik | ||||||
| STOPSIGNAL SIGINT | STOPSIGNAL SIGINT | ||||||
| ENV TMPDIR /dev/shm/ | ENV TMPDIR /dev/shm/ | ||||||
|  | ENV PYTHONUBUFFERED 1 | ||||||
| ENTRYPOINT [ "/lifecycle/bootstrap.sh" ] | ENTRYPOINT [ "/lifecycle/bootstrap.sh" ] | ||||||
|  | |||||||
							
								
								
									
										174
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										174
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							| @ -18,45 +18,45 @@ | |||||||
|     "default": { |     "default": { | ||||||
|         "aiohttp": { |         "aiohttp": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:119feb2bd551e58d83d1b38bfa4cb921af8ddedec9fad7183132db334c3133e0", |                 "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe", | ||||||
|                 "sha256:16d0683ef8a6d803207f02b899c928223eb219111bd52420ef3d7a8aa76227b6", |                 "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe", | ||||||
|                 "sha256:2eb3efe243e0f4ecbb654b08444ae6ffab37ac0ef8f69d3a2ffb958905379daf", |                 "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5", | ||||||
|                 "sha256:2ffea7904e70350da429568113ae422c88d2234ae776519549513c8f217f58a9", |                 "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8", | ||||||
|                 "sha256:40bd1b101b71a18a528ffce812cc14ff77d4a2a1272dfb8b11b200967489ef3e", |                 "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd", | ||||||
|                 "sha256:418597633b5cd9639e514b1d748f358832c08cd5d9ef0870026535bd5eaefdd0", |                 "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb", | ||||||
|                 "sha256:481d4b96969fbfdcc3ff35eea5305d8565a8300410d3d269ccac69e7256b1329", |                 "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c", | ||||||
|                 "sha256:4c1bdbfdd231a20eee3e56bd0ac1cd88c4ff41b64ab679ed65b75c9c74b6c5c2", |                 "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87", | ||||||
|                 "sha256:5563ad7fde451b1986d42b9bb9140e2599ecf4f8e42241f6da0d3d624b776f40", |                 "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0", | ||||||
|                 "sha256:58c62152c4c8731a3152e7e650b29ace18304d086cb5552d317a54ff2749d32a", |                 "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290", | ||||||
|                 "sha256:5b50e0b9460100fe05d7472264d1975f21ac007b35dcd6fd50279b72925a27f4", |                 "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5", | ||||||
|                 "sha256:5d84ecc73141d0a0d61ece0742bb7ff5751b0657dab8405f899d3ceb104cc7de", |                 "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287", | ||||||
|                 "sha256:5dde6d24bacac480be03f4f864e9a67faac5032e28841b00533cd168ab39cad9", |                 "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde", | ||||||
|                 "sha256:5e91e927003d1ed9283dee9abcb989334fc8e72cf89ebe94dc3e07e3ff0b11e9", |                 "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf", | ||||||
|                 "sha256:62bc216eafac3204877241569209d9ba6226185aa6d561c19159f2e1cbb6abfb", |                 "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8", | ||||||
|                 "sha256:6c8200abc9dc5f27203986100579fc19ccad7a832c07d2bc151ce4ff17190076", |                 "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16", | ||||||
|                 "sha256:6ca56bdfaf825f4439e9e3673775e1032d8b6ea63b8953d3812c71bd6a8b81de", |                 "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf", | ||||||
|                 "sha256:71680321a8a7176a58dfbc230789790639db78dad61a6e120b39f314f43f1907", |                 "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809", | ||||||
|                 "sha256:7c7820099e8b3171e54e7eedc33e9450afe7cd08172632d32128bd527f8cb77d", |                 "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213", | ||||||
|                 "sha256:7dbd087ff2f4046b9b37ba28ed73f15fd0bc9f4fdc8ef6781913da7f808d9536", |                 "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f", | ||||||
|                 "sha256:822bd4fd21abaa7b28d65fc9871ecabaddc42767884a626317ef5b75c20e8a2d", |                 "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013", | ||||||
|                 "sha256:8ec1a38074f68d66ccb467ed9a673a726bb397142c273f90d4ba954666e87d54", |                 "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b", | ||||||
|                 "sha256:950b7ef08b2afdab2488ee2edaff92a03ca500a48f1e1aaa5900e73d6cf992bc", |                 "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9", | ||||||
|                 "sha256:99c5a5bf7135607959441b7d720d96c8e5c46a1f96e9d6d4c9498be8d5f24212", |                 "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5", | ||||||
|                 "sha256:b84ad94868e1e6a5e30d30ec419956042815dfaea1b1df1cef623e4564c374d9", |                 "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb", | ||||||
|                 "sha256:bc3d14bf71a3fb94e5acf5bbf67331ab335467129af6416a437bd6024e4f743d", |                 "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df", | ||||||
|                 "sha256:c2a80fd9a8d7e41b4e38ea9fe149deed0d6aaede255c497e66b8213274d6d61b", |                 "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4", | ||||||
|                 "sha256:c44d3c82a933c6cbc21039326767e778eface44fca55c65719921c4b9661a3f7", |                 "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439", | ||||||
|                 "sha256:cc31e906be1cc121ee201adbdf844522ea3349600dd0a40366611ca18cd40e81", |                 "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f", | ||||||
|                 "sha256:d5d102e945ecca93bcd9801a7bb2fa703e37ad188a2f81b1e65e4abe4b51b00c", |                 "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22", | ||||||
|                 "sha256:dd7936f2a6daa861143e376b3a1fb56e9b802f4980923594edd9ca5670974895", |                 "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f", | ||||||
|                 "sha256:dee68ec462ff10c1d836c0ea2642116aba6151c6880b688e56b4c0246770f297", |                 "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5", | ||||||
|                 "sha256:e76e78863a4eaec3aee5722d85d04dcbd9844bc6cd3bfa6aa880ff46ad16bfcb", |                 "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970", | ||||||
|                 "sha256:eab51036cac2da8a50d7ff0ea30be47750547c9aa1aa2cf1a1b710a1827e7dbe", |                 "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009", | ||||||
|                 "sha256:f4496d8d04da2e98cc9133e238ccebf6a13ef39a93da2e87146c8c8ac9768242", |                 "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc", | ||||||
|                 "sha256:fbd3b5e18d34683decc00d9a360179ac1e7a320a5fee10ab8053ffd6deab76e0", |                 "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a", | ||||||
|                 "sha256:feb24ff1226beeb056e247cf2e24bba5232519efb5645121c4aea5b6ad74c1f2" |                 "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95" | ||||||
|             ], |             ], | ||||||
|             "version": "==3.7.4" |             "version": "==3.7.4.post0" | ||||||
|         }, |         }, | ||||||
|         "aioredis": { |         "aioredis": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -116,18 +116,17 @@ | |||||||
|         }, |         }, | ||||||
|         "boto3": { |         "boto3": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:2219f1ebe88d266afa5516f993983eba8742b957fa4fd6854f3c73aa3030e931", |                 "sha256:64a8900b3a110e2d6ff4d87f4d8cd56f0c8527361d9fc9385fcb50efe7a4975a", | ||||||
|                 "sha256:c0d51f344b71656c2d395d2168600d91bea252a64fb5d503a955ea96426cde8b" |                 "sha256:8e9ff8006c41889ed8a11831dee62adf922e071f14d54c52946d1f7855ae7a8e" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==1.17.20" |             "version": "==1.17.26" | ||||||
|         }, |         }, | ||||||
|         "botocore": { |         "botocore": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:80c32a81fb1ee8bdfa074a79bfb885bb2006e8a9782f2353c0c9f6392704e13a", |                 "sha256:4a785847a351e59f2329627fc9a19cf50f07644ea68996a1595d5a20487a423f" | ||||||
|                 "sha256:e9e724b59278ebf5caf032be1e32bde0990d79e8052e3bbbb97b6c1d32feba28" |  | ||||||
|             ], |             ], | ||||||
|             "version": "==1.20.20" |             "version": "==1.20.26" | ||||||
|         }, |         }, | ||||||
|         "cachetools": { |         "cachetools": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -217,10 +216,10 @@ | |||||||
|         }, |         }, | ||||||
|         "chardet": { |         "chardet": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", |                 "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", | ||||||
|                 "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" |                 "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" | ||||||
|             ], |             ], | ||||||
|             "version": "==3.0.4" |             "version": "==4.0.0" | ||||||
|         }, |         }, | ||||||
|         "click": { |         "click": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -304,11 +303,11 @@ | |||||||
|         }, |         }, | ||||||
|         "defusedxml": { |         "defusedxml": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93", |                 "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", | ||||||
|                 "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5" |                 "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==0.6.0" |             "version": "==0.7.1" | ||||||
|         }, |         }, | ||||||
|         "django": { |         "django": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -445,10 +444,10 @@ | |||||||
|         }, |         }, | ||||||
|         "google-auth": { |         "google-auth": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:d3640ea61ee025d5af00e3ffd82ba0a06dd99724adaf50bdd52f49daf29f3f65", |                 "sha256:63a5636d7eacfe6ef5b7e36e112b3149fa1c5b5ad77dd6df54910459bcd6b89f", | ||||||
|                 "sha256:da5218cbf33b8461d7661d6b4ad91c12c0107e2767904d5e3ae6408031d5463e" |                 "sha256:d8958af6968e4ecd599f82357ebcfeb126f826ed0656126ad68416f810f7531e" | ||||||
|             ], |             ], | ||||||
|             "version": "==1.27.0" |             "version": "==1.27.1" | ||||||
|         }, |         }, | ||||||
|         "gunicorn": { |         "gunicorn": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -817,10 +816,10 @@ | |||||||
|         }, |         }, | ||||||
|         "prompt-toolkit": { |         "prompt-toolkit": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:0fa02fa80363844a4ab4b8d6891f62dd0645ba672723130423ca4037b80c1974", |                 "sha256:4cea7d09e46723885cb8bc54678175453e5071e9449821dce6f017b1d1fbfc1a", | ||||||
|                 "sha256:62c811e46bd09130fb11ab759012a4ae385ce4fb2073442d1898867a824183bd" |                 "sha256:9397a7162cf45449147ad6042fa37983a081b8a73363a5253dd4072666333137" | ||||||
|             ], |             ], | ||||||
|             "version": "==3.0.16" |             "version": "==3.0.17" | ||||||
|         }, |         }, | ||||||
|         "psycopg2-binary": { |         "psycopg2-binary": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -1024,15 +1023,23 @@ | |||||||
|                 "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", |                 "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", | ||||||
|                 "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", |                 "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", | ||||||
|                 "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", |                 "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", | ||||||
|  |                 "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", | ||||||
|                 "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", |                 "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", | ||||||
|  |                 "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", | ||||||
|                 "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", |                 "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", | ||||||
|                 "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", |                 "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", | ||||||
|  |                 "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", | ||||||
|                 "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", |                 "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", | ||||||
|  |                 "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", | ||||||
|                 "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", |                 "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", | ||||||
|  |                 "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", | ||||||
|                 "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", |                 "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", | ||||||
|                 "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", |                 "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", | ||||||
|                 "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", |                 "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", | ||||||
|                 "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc" |                 "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", | ||||||
|  |                 "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", | ||||||
|  |                 "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", | ||||||
|  |                 "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==5.4.1" |             "version": "==5.4.1" | ||||||
| @ -1069,10 +1076,47 @@ | |||||||
|         }, |         }, | ||||||
|         "ruamel.yaml": { |         "ruamel.yaml": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:012b9470a0ea06e4e44e99e7920277edf6b46eee0232a04487ea73a7386340a5", |                 "sha256:64b06e7873eb8e1125525ecef7345447d786368cadca92a7cd9b59eae62e95a3", | ||||||
|                 "sha256:076cc0bc34f1966d920a49f18b52b6ad559fbe656a0748e3535cf7b3f29ebf9e" |                 "sha256:bb48c514222702878759a05af96f4b7ecdba9b33cd4efcf25c86b882cef3a942" | ||||||
|             ], |             ], | ||||||
|             "version": "==0.16.12" |             "version": "==0.16.13" | ||||||
|  |         }, | ||||||
|  |         "ruamel.yaml.clib": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b", | ||||||
|  |                 "sha256:1236df55e0f73cd138c0eca074ee086136c3f16a97c2ac719032c050f7e0622f", | ||||||
|  |                 "sha256:1f8c0a4577c0e6c99d208de5c4d3fd8aceed9574bb154d7a2b21c16bb924154c", | ||||||
|  |                 "sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91", | ||||||
|  |                 "sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc", | ||||||
|  |                 "sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7", | ||||||
|  |                 "sha256:2fd336a5c6415c82e2deb40d08c222087febe0aebe520f4d21910629018ab0f3", | ||||||
|  |                 "sha256:30dca9bbcbb1cc858717438218d11eafb78666759e5094dd767468c0d577a7e7", | ||||||
|  |                 "sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6", | ||||||
|  |                 "sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6", | ||||||
|  |                 "sha256:46d6d20815064e8bb023ea8628cfb7402c0f0e83de2c2227a88097e239a7dffd", | ||||||
|  |                 "sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0", | ||||||
|  |                 "sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62", | ||||||
|  |                 "sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99", | ||||||
|  |                 "sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5", | ||||||
|  |                 "sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026", | ||||||
|  |                 "sha256:6c0a5dc52fc74eb87c67374a4e554d4761fd42a4d01390b7e868b30d21f4b8bb", | ||||||
|  |                 "sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2", | ||||||
|  |                 "sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1", | ||||||
|  |                 "sha256:75f0ee6839532e52a3a53f80ce64925ed4aed697dd3fa890c4c918f3304bd4f4", | ||||||
|  |                 "sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b", | ||||||
|  |                 "sha256:8be05be57dc5c7b4a0b24edcaa2f7275866d9c907725226cdde46da09367d923", | ||||||
|  |                 "sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e", | ||||||
|  |                 "sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c", | ||||||
|  |                 "sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988", | ||||||
|  |                 "sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f", | ||||||
|  |                 "sha256:b4b0d31f2052b3f9f9b5327024dc629a253a83d8649d4734ca7f35b60ec3e9e5", | ||||||
|  |                 "sha256:c6ac7e45367b1317e56f1461719c853fd6825226f45b835df7436bb04031fd8a", | ||||||
|  |                 "sha256:daf21aa33ee9b351f66deed30a3d450ab55c14242cfdfcd377798e2c0d25c9f1", | ||||||
|  |                 "sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2", | ||||||
|  |                 "sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f" | ||||||
|  |             ], | ||||||
|  |             "markers": "platform_python_implementation == 'CPython' and python_version < '3.10'", | ||||||
|  |             "version": "==0.2.2" | ||||||
|         }, |         }, | ||||||
|         "s3transfer": { |         "s3transfer": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
| @ -1765,15 +1809,23 @@ | |||||||
|                 "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", |                 "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", | ||||||
|                 "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", |                 "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", | ||||||
|                 "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", |                 "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", | ||||||
|  |                 "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", | ||||||
|                 "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", |                 "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", | ||||||
|  |                 "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", | ||||||
|                 "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", |                 "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", | ||||||
|                 "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", |                 "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", | ||||||
|  |                 "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", | ||||||
|                 "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", |                 "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", | ||||||
|  |                 "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", | ||||||
|                 "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", |                 "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", | ||||||
|  |                 "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", | ||||||
|                 "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", |                 "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", | ||||||
|                 "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", |                 "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", | ||||||
|                 "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", |                 "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", | ||||||
|                 "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc" |                 "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", | ||||||
|  |                 "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", | ||||||
|  |                 "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", | ||||||
|  |                 "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==5.4.1" |             "version": "==5.4.1" | ||||||
|  | |||||||
| @ -1,2 +1,3 @@ | |||||||
| """authentik""" | """authentik""" | ||||||
| __version__ = "2021.3.3" | __version__ = "2021.3.4" | ||||||
|  | ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" | ||||||
|  | |||||||
| @ -7,8 +7,8 @@ from django.db.models import Count, ExpressionWrapper, F, Model | |||||||
| from django.db.models.fields import DurationField | from django.db.models.fields import DurationField | ||||||
| from django.db.models.functions import ExtractHour | from django.db.models.functions import ExtractHour | ||||||
| from django.utils.timezone import now | from django.utils.timezone import now | ||||||
| from drf_yasg2.utils import swagger_auto_schema | from drf_yasg2.utils import swagger_auto_schema, swagger_serializer_method | ||||||
| from rest_framework.fields import SerializerMethodField | from rest_framework.fields import IntegerField, SerializerMethodField | ||||||
| from rest_framework.permissions import IsAdminUser | from rest_framework.permissions import IsAdminUser | ||||||
| from rest_framework.request import Request | from rest_framework.request import Request | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| @ -37,23 +37,39 @@ def get_events_per_1h(**filter_kwargs) -> list[dict[str, int]]: | |||||||
|     for hour in range(0, -24, -1): |     for hour in range(0, -24, -1): | ||||||
|         results.append( |         results.append( | ||||||
|             { |             { | ||||||
|                 "x": time.mktime((_now + timedelta(hours=hour)).timetuple()) * 1000, |                 "x_cord": time.mktime((_now + timedelta(hours=hour)).timetuple()) | ||||||
|                 "y": data[hour * -1], |                 * 1000, | ||||||
|  |                 "y_cord": data[hour * -1], | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|     return results |     return results | ||||||
|  |  | ||||||
|  |  | ||||||
| class AdministrationMetricsSerializer(Serializer): | class CoordinateSerializer(Serializer): | ||||||
|  |     """Coordinates for diagrams""" | ||||||
|  |  | ||||||
|  |     x_cord = IntegerField(read_only=True) | ||||||
|  |     y_cord = IntegerField(read_only=True) | ||||||
|  |  | ||||||
|  |     def create(self, validated_data: dict) -> Model: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |     def update(self, instance: Model, validated_data: dict) -> Model: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LoginMetricsSerializer(Serializer): | ||||||
|     """Login Metrics per 1h""" |     """Login Metrics per 1h""" | ||||||
|  |  | ||||||
|     logins_per_1h = SerializerMethodField() |     logins_per_1h = SerializerMethodField() | ||||||
|     logins_failed_per_1h = SerializerMethodField() |     logins_failed_per_1h = SerializerMethodField() | ||||||
|  |  | ||||||
|  |     @swagger_serializer_method(serializer_or_field=CoordinateSerializer(many=True)) | ||||||
|     def get_logins_per_1h(self, _): |     def get_logins_per_1h(self, _): | ||||||
|         """Get successful logins per hour for the last 24 hours""" |         """Get successful logins per hour for the last 24 hours""" | ||||||
|         return get_events_per_1h(action=EventAction.LOGIN) |         return get_events_per_1h(action=EventAction.LOGIN) | ||||||
|  |  | ||||||
|  |     @swagger_serializer_method(serializer_or_field=CoordinateSerializer(many=True)) | ||||||
|     def get_logins_failed_per_1h(self, _): |     def get_logins_failed_per_1h(self, _): | ||||||
|         """Get failed logins per hour for the last 24 hours""" |         """Get failed logins per hour for the last 24 hours""" | ||||||
|         return get_events_per_1h(action=EventAction.LOGIN_FAILED) |         return get_events_per_1h(action=EventAction.LOGIN_FAILED) | ||||||
| @ -70,8 +86,8 @@ class AdministrationMetricsViewSet(ViewSet): | |||||||
|  |  | ||||||
|     permission_classes = [IsAdminUser] |     permission_classes = [IsAdminUser] | ||||||
|  |  | ||||||
|     @swagger_auto_schema(responses={200: AdministrationMetricsSerializer(many=True)}) |     @swagger_auto_schema(responses={200: LoginMetricsSerializer(many=False)}) | ||||||
|     def list(self, request: Request) -> Response: |     def list(self, request: Request) -> Response: | ||||||
|         """Login Metrics per 1h""" |         """Login Metrics per 1h""" | ||||||
|         serializer = AdministrationMetricsSerializer(True) |         serializer = LoginMetricsSerializer(True) | ||||||
|         return Response(serializer.data) |         return Response(serializer.data) | ||||||
|  | |||||||
| @ -25,8 +25,8 @@ class TaskSerializer(Serializer): | |||||||
|     task_finish_timestamp = DateTimeField(source="finish_timestamp") |     task_finish_timestamp = DateTimeField(source="finish_timestamp") | ||||||
|  |  | ||||||
|     status = ChoiceField( |     status = ChoiceField( | ||||||
|         source="result.status.value", |         source="result.status.name", | ||||||
|         choices=[(x.value, x.name) for x in TaskResultStatus], |         choices=[(x.name, x.name) for x in TaskResultStatus], | ||||||
|     ) |     ) | ||||||
|     messages = ListField(source="result.messages") |     messages = ListField(source="result.messages") | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,4 +1,6 @@ | |||||||
| """authentik administration overview""" | """authentik administration overview""" | ||||||
|  | from os import environ | ||||||
|  |  | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.db.models import Model | from django.db.models import Model | ||||||
| from drf_yasg2.utils import swagger_auto_schema | from drf_yasg2.utils import swagger_auto_schema | ||||||
| @ -11,7 +13,7 @@ from rest_framework.response import Response | |||||||
| from rest_framework.serializers import Serializer | from rest_framework.serializers import Serializer | ||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework.viewsets import GenericViewSet | ||||||
|  |  | ||||||
| from authentik import __version__ | from authentik import ENV_GIT_HASH_KEY, __version__ | ||||||
| from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version | from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -20,8 +22,13 @@ class VersionSerializer(Serializer): | |||||||
|  |  | ||||||
|     version_current = SerializerMethodField() |     version_current = SerializerMethodField() | ||||||
|     version_latest = SerializerMethodField() |     version_latest = SerializerMethodField() | ||||||
|  |     build_hash = SerializerMethodField() | ||||||
|     outdated = SerializerMethodField() |     outdated = SerializerMethodField() | ||||||
|  |  | ||||||
|  |     def get_build_hash(self, _) -> str: | ||||||
|  |         """Get build hash, if version is not latest or released""" | ||||||
|  |         return environ.get(ENV_GIT_HASH_KEY, "") | ||||||
|  |  | ||||||
|     def get_version_current(self, _) -> str: |     def get_version_current(self, _) -> str: | ||||||
|         """Get current version""" |         """Get current version""" | ||||||
|         return __version__ |         return __version__ | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| """api v2 urls""" | """api v2 urls""" | ||||||
|  | from django.conf import settings | ||||||
| from django.urls import path, re_path | from django.urls import path, re_path | ||||||
| from drf_yasg2 import openapi | from drf_yasg2 import openapi | ||||||
| from drf_yasg2.views import get_schema_view | from drf_yasg2.views import get_schema_view | ||||||
| @ -54,12 +55,24 @@ from authentik.providers.saml.api import SAMLPropertyMappingViewSet, SAMLProvide | |||||||
| from authentik.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet | from authentik.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet | ||||||
| from authentik.sources.oauth.api import OAuthSourceViewSet | from authentik.sources.oauth.api import OAuthSourceViewSet | ||||||
| from authentik.sources.saml.api import SAMLSourceViewSet | from authentik.sources.saml.api import SAMLSourceViewSet | ||||||
| from authentik.stages.authenticator_static.api import AuthenticatorStaticStageViewSet | from authentik.stages.authenticator_static.api import ( | ||||||
| from authentik.stages.authenticator_totp.api import AuthenticatorTOTPStageViewSet |     AuthenticatorStaticStageViewSet, | ||||||
|  |     StaticAdminDeviceViewSet, | ||||||
|  |     StaticDeviceViewSet, | ||||||
|  | ) | ||||||
|  | from authentik.stages.authenticator_totp.api import ( | ||||||
|  |     AuthenticatorTOTPStageViewSet, | ||||||
|  |     TOTPAdminDeviceViewSet, | ||||||
|  |     TOTPDeviceViewSet, | ||||||
|  | ) | ||||||
| from authentik.stages.authenticator_validate.api import ( | from authentik.stages.authenticator_validate.api import ( | ||||||
|     AuthenticatorValidateStageViewSet, |     AuthenticatorValidateStageViewSet, | ||||||
| ) | ) | ||||||
| from authentik.stages.authenticator_webauthn.api import AuthenticateWebAuthnStageViewSet | from authentik.stages.authenticator_webauthn.api import ( | ||||||
|  |     AuthenticateWebAuthnStageViewSet, | ||||||
|  |     WebAuthnAdminDeviceViewSet, | ||||||
|  |     WebAuthnDeviceViewSet, | ||||||
|  | ) | ||||||
| from authentik.stages.captcha.api import CaptchaStageViewSet | from authentik.stages.captcha.api import CaptchaStageViewSet | ||||||
| from authentik.stages.consent.api import ConsentStageViewSet | from authentik.stages.consent.api import ConsentStageViewSet | ||||||
| from authentik.stages.deny.api import DenyStageViewSet | from authentik.stages.deny.api import DenyStageViewSet | ||||||
| @ -133,6 +146,13 @@ router.register("propertymappings/ldap", LDAPPropertyMappingViewSet) | |||||||
| router.register("propertymappings/saml", SAMLPropertyMappingViewSet) | router.register("propertymappings/saml", SAMLPropertyMappingViewSet) | ||||||
| router.register("propertymappings/scope", ScopeMappingViewSet) | router.register("propertymappings/scope", ScopeMappingViewSet) | ||||||
|  |  | ||||||
|  | router.register("authenticators/static", StaticDeviceViewSet) | ||||||
|  | router.register("authenticators/totp", TOTPDeviceViewSet) | ||||||
|  | router.register("authenticators/webauthn", WebAuthnDeviceViewSet) | ||||||
|  | router.register("authenticators/admin/static", StaticAdminDeviceViewSet) | ||||||
|  | router.register("authenticators/admin/totp", TOTPAdminDeviceViewSet) | ||||||
|  | router.register("authenticators/admin/webauthn", WebAuthnAdminDeviceViewSet) | ||||||
|  |  | ||||||
| router.register("stages/all", StageViewSet) | router.register("stages/all", StageViewSet) | ||||||
| router.register("stages/authenticator/static", AuthenticatorStaticStageViewSet) | router.register("stages/authenticator/static", AuthenticatorStaticStageViewSet) | ||||||
| router.register("stages/authenticator/totp", AuthenticatorTOTPStageViewSet) | router.register("stages/authenticator/totp", AuthenticatorTOTPStageViewSet) | ||||||
| @ -164,27 +184,26 @@ info = openapi.Info( | |||||||
|         name="GNU GPLv3", url="https://github.com/BeryJu/authentik/blob/master/LICENSE" |         name="GNU GPLv3", url="https://github.com/BeryJu/authentik/blob/master/LICENSE" | ||||||
|     ), |     ), | ||||||
| ) | ) | ||||||
| SchemaView = get_schema_view( | SchemaView = get_schema_view(info, public=True, permission_classes=(AllowAny,)) | ||||||
|     info, |  | ||||||
|     public=True, |  | ||||||
|     permission_classes=(AllowAny,), |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| urlpatterns = [ | urlpatterns = router.urls + [ | ||||||
|     re_path( |  | ||||||
|         r"^swagger(?P<format>\.json|\.yaml)$", |  | ||||||
|         SchemaView.without_ui(cache_timeout=0), |  | ||||||
|         name="schema-json", |  | ||||||
|     ), |  | ||||||
|     path( |  | ||||||
|         "swagger/", |  | ||||||
|         SchemaView.with_ui("swagger", cache_timeout=0), |  | ||||||
|         name="schema-swagger-ui", |  | ||||||
|     ), |  | ||||||
|     path("redoc/", SchemaView.with_ui("redoc", cache_timeout=0), name="schema-redoc"), |  | ||||||
|     path( |     path( | ||||||
|         "flows/executor/<slug:flow_slug>/", |         "flows/executor/<slug:flow_slug>/", | ||||||
|         FlowExecutorView.as_view(), |         FlowExecutorView.as_view(), | ||||||
|         name="flow-executor", |         name="flow-executor", | ||||||
|     ), |     ), | ||||||
| ] + router.urls |     re_path( | ||||||
|  |         r"^swagger(?P<format>\.json|\.yaml)$", | ||||||
|  |         SchemaView.without_ui(cache_timeout=0), | ||||||
|  |         name="schema-json", | ||||||
|  |     ), | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | if settings.DEBUG: | ||||||
|  |     urlpatterns = urlpatterns + [ | ||||||
|  |         path( | ||||||
|  |             "swagger/", | ||||||
|  |             SchemaView.with_ui("swagger", cache_timeout=0), | ||||||
|  |             name="schema-swagger-ui", | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| 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 Http404 | from django.http.response import Http404 | ||||||
|  | from drf_yasg2.utils import swagger_auto_schema | ||||||
| from guardian.shortcuts import get_objects_for_user | from guardian.shortcuts import get_objects_for_user | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.fields import SerializerMethodField | from rest_framework.fields import SerializerMethodField | ||||||
| @ -13,7 +14,7 @@ from rest_framework.viewsets import ModelViewSet | |||||||
| from rest_framework_guardian.filters import ObjectPermissionsFilter | from rest_framework_guardian.filters import ObjectPermissionsFilter | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.admin.api.metrics import get_events_per_1h | from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h | ||||||
| from authentik.core.api.providers import ProviderSerializer | from authentik.core.api.providers import ProviderSerializer | ||||||
| from authentik.core.models import Application | from authentik.core.models import Application | ||||||
| from authentik.events.models import EventAction | from authentik.events.models import EventAction | ||||||
| @ -109,6 +110,7 @@ class ApplicationViewSet(ModelViewSet): | |||||||
|         serializer = self.get_serializer(allowed_applications, many=True) |         serializer = self.get_serializer(allowed_applications, many=True) | ||||||
|         return self.get_paginated_response(serializer.data) |         return self.get_paginated_response(serializer.data) | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema(responses={200: CoordinateSerializer(many=True)}) | ||||||
|     @action(detail=True) |     @action(detail=True) | ||||||
|     def metrics(self, request: Request, slug: str): |     def metrics(self, request: Request, slug: str): | ||||||
|         """Metrics for application logins""" |         """Metrics for application logins""" | ||||||
|  | |||||||
| @ -21,3 +21,4 @@ class GroupViewSet(ModelViewSet): | |||||||
|     serializer_class = GroupSerializer |     serializer_class = GroupSerializer | ||||||
|     search_fields = ["name", "is_superuser"] |     search_fields = ["name", "is_superuser"] | ||||||
|     filterset_fields = ["name", "is_superuser"] |     filterset_fields = ["name", "is_superuser"] | ||||||
|  |     ordering = ["name"] | ||||||
|  | |||||||
| @ -28,9 +28,9 @@ class MetaNameSerializer(Serializer): | |||||||
| class TypeCreateSerializer(Serializer): | class TypeCreateSerializer(Serializer): | ||||||
|     """Types of an object that can be created""" |     """Types of an object that can be created""" | ||||||
|  |  | ||||||
|     name = CharField(read_only=True) |     name = CharField(required=True) | ||||||
|     description = CharField(read_only=True) |     description = CharField(required=True) | ||||||
|     link = CharField(read_only=True) |     link = CharField(required=True) | ||||||
|  |  | ||||||
|     def create(self, validated_data: dict) -> Model: |     def create(self, validated_data: dict) -> Model: | ||||||
|         raise NotImplementedError |         raise NotImplementedError | ||||||
|  | |||||||
| @ -11,9 +11,7 @@ | |||||||
|         <title>{% block title %}{% trans title|default:config.authentik.branding.title %}{% endblock %}</title> |         <title>{% block title %}{% trans title|default:config.authentik.branding.title %}{% endblock %}</title> | ||||||
|         <link rel="icon" type="image/png" href="{% static 'dist/assets/icons/icon.png' %}?v={{ ak_version }}"> |         <link rel="icon" type="image/png" href="{% static 'dist/assets/icons/icon.png' %}?v={{ ak_version }}"> | ||||||
|         <link rel="shortcut icon" type="image/png" href="{% static 'dist/assets/icons/icon.png' %}?v={{ ak_version }}"> |         <link rel="shortcut icon" type="image/png" href="{% static 'dist/assets/icons/icon.png' %}?v={{ ak_version }}"> | ||||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.css' %}?v={{ ak_version }}"> |         <link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly-base.css' %}?v={{ ak_version }}"> | ||||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly-addons.css' %}?v={{ ak_version }}"> |  | ||||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/fontawesome.min.css' %}?v={{ ak_version }}"> |  | ||||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}?v={{ ak_version }}"> |         <link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}?v={{ ak_version }}"> | ||||||
|         <script src="{% url 'javascript-catalog' %}?v={{ ak_version }}"></script> |         <script src="{% url 'javascript-catalog' %}?v={{ ak_version }}"></script> | ||||||
|         {% block head %} |         {% block head %} | ||||||
|  | |||||||
| @ -1,9 +1,6 @@ | |||||||
| {% extends container_template|default:"administration/base.html" %} |  | ||||||
|  |  | ||||||
| {% load i18n %} | {% load i18n %} | ||||||
| {% load authentik_utils %} | {% load authentik_utils %} | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <section class="pf-c-page__main-section pf-m-light"> | <section class="pf-c-page__main-section pf-m-light"> | ||||||
|     <div class="pf-c-content"> |     <div class="pf-c-content"> | ||||||
|         {% block above_form %} |         {% block above_form %} | ||||||
| @ -38,4 +35,3 @@ | |||||||
|     <input class="pf-c-button pf-m-danger" type="submit" form="delete-form" value="{% trans 'Delete' %}" /> |     <input class="pf-c-button pf-m-danger" type="submit" form="delete-form" value="{% trans 'Delete' %}" /> | ||||||
|     <a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Back" %}</a> |     <a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Back" %}</a> | ||||||
| </footer> | </footer> | ||||||
| {% endblock %} |  | ||||||
|  | |||||||
| @ -16,6 +16,17 @@ | |||||||
|  |  | ||||||
| {% block body %} | {% block body %} | ||||||
| <div class="pf-c-background-image"> | <div class="pf-c-background-image"> | ||||||
|  |     <svg xmlns="http://www.w3.org/2000/svg" class="pf-c-background-image__filter" width="0" height="0"> | ||||||
|  |         <filter id="image_overlay"> | ||||||
|  |             <feColorMatrix in="SourceGraphic" type="matrix" values="1.3 0 0 0 0 0 1.3 0 0 0 0 0 1.3 0 0 0 0 0 1 0" /> | ||||||
|  |             <feComponentTransfer color-interpolation-filters="sRGB" result="duotone"> | ||||||
|  |                 <feFuncR type="table" tableValues="0.086274509803922 0.43921568627451"></feFuncR> | ||||||
|  |                 <feFuncG type="table" tableValues="0.086274509803922 0.43921568627451"></feFuncG> | ||||||
|  |                 <feFuncB type="table" tableValues="0.086274509803922 0.43921568627451"></feFuncB> | ||||||
|  |                 <feFuncA type="table" tableValues="0 1"></feFuncA> | ||||||
|  |             </feComponentTransfer> | ||||||
|  |         </filter> | ||||||
|  |     </svg> | ||||||
| </div> | </div> | ||||||
| <ak-message-container></ak-message-container> | <ak-message-container></ak-message-container> | ||||||
| <div class="pf-c-login"> | <div class="pf-c-login"> | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ from django.contrib.auth.mixins import ( | |||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.http.response import HttpResponse | from django.http.response import HttpResponse | ||||||
|  | from django.urls import reverse_lazy | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import UpdateView | from django.views.generic import UpdateView | ||||||
| from django.views.generic.base import TemplateView | from django.views.generic.base import TemplateView | ||||||
| @ -34,7 +35,7 @@ class UserDetailsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView): | |||||||
|     form_class = UserDetailForm |     form_class = UserDetailForm | ||||||
|  |  | ||||||
|     success_message = _("Successfully updated user.") |     success_message = _("Successfully updated user.") | ||||||
|     success_url = "/" |     success_url = reverse_lazy("authentik_core:user-details") | ||||||
|  |  | ||||||
|     def get_object(self): |     def get_object(self): | ||||||
|         return self.request.user |         return self.request.user | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ class NotificationSerializer(ModelSerializer): | |||||||
|  |  | ||||||
|     body = ReadOnlyField() |     body = ReadOnlyField() | ||||||
|     severity = ReadOnlyField() |     severity = ReadOnlyField() | ||||||
|     event = EventSerializer() |     event = EventSerializer(required=False) | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,11 +1,12 @@ | |||||||
| """NotificationTransport API Views""" | """NotificationTransport API Views""" | ||||||
| from django.http.response import Http404 | from django.http.response import Http404 | ||||||
|  | from drf_yasg2.utils import no_body, swagger_auto_schema | ||||||
| from guardian.shortcuts import get_objects_for_user | from guardian.shortcuts import get_objects_for_user | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.fields import SerializerMethodField | from rest_framework.fields import CharField, ListField, SerializerMethodField | ||||||
| from rest_framework.request import Request | from rest_framework.request import Request | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| from rest_framework.serializers import ModelSerializer | from rest_framework.serializers import ModelSerializer, Serializer | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.events.models import ( | from authentik.events.models import ( | ||||||
| @ -38,12 +39,28 @@ class NotificationTransportSerializer(ModelSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class NotificationTransportTestSerializer(Serializer): | ||||||
|  |     """Notification test serializer""" | ||||||
|  |  | ||||||
|  |     messages = ListField(child=CharField()) | ||||||
|  |  | ||||||
|  |     def create(self, request: Request) -> Response: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |     def update(self, request: Request) -> Response: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |  | ||||||
| class NotificationTransportViewSet(ModelViewSet): | class NotificationTransportViewSet(ModelViewSet): | ||||||
|     """NotificationTransport Viewset""" |     """NotificationTransport Viewset""" | ||||||
|  |  | ||||||
|     queryset = NotificationTransport.objects.all() |     queryset = NotificationTransport.objects.all() | ||||||
|     serializer_class = NotificationTransportSerializer |     serializer_class = NotificationTransportSerializer | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema( | ||||||
|  |         responses={200: NotificationTransportTestSerializer(many=False)}, | ||||||
|  |         request_body=no_body, | ||||||
|  |     ) | ||||||
|     @action(detail=True, methods=["post"]) |     @action(detail=True, methods=["post"]) | ||||||
|     # pylint: disable=invalid-name |     # pylint: disable=invalid-name | ||||||
|     def test(self, request: Request, pk=None) -> Response: |     def test(self, request: Request, pk=None) -> Response: | ||||||
| @ -61,6 +78,10 @@ class NotificationTransportViewSet(ModelViewSet): | |||||||
|             user=request.user, |             user=request.user, | ||||||
|         ) |         ) | ||||||
|         try: |         try: | ||||||
|             return Response(transport.send(notification)) |             response = NotificationTransportTestSerializer( | ||||||
|  |                 data={"messages": transport.send(notification)} | ||||||
|  |             ) | ||||||
|  |             response.is_valid() | ||||||
|  |             return Response(response.data) | ||||||
|         except NotificationTransportError as exc: |         except NotificationTransportError as exc: | ||||||
|             return Response(str(exc.__cause__ or None), status=503) |             return Response(str(exc.__cause__ or None), status=503) | ||||||
|  | |||||||
| @ -12,7 +12,10 @@ def get_geoip_reader() -> Optional[Reader]: | |||||||
|     path = CONFIG.y("authentik.geoip") |     path = CONFIG.y("authentik.geoip") | ||||||
|     if path == "" or not path: |     if path == "" or not path: | ||||||
|         return None |         return None | ||||||
|     return Reader(path) |     try: | ||||||
|  |         return Reader(path) | ||||||
|  |     except OSError: | ||||||
|  |         return None | ||||||
|  |  | ||||||
|  |  | ||||||
| GEOIP_READER = get_geoip_reader() | GEOIP_READER = get_geoip_reader() | ||||||
|  | |||||||
| @ -38,7 +38,9 @@ class Challenge(Serializer): | |||||||
|     """Challenge that gets sent to the client based on which stage |     """Challenge that gets sent to the client based on which stage | ||||||
|     is currently active""" |     is currently active""" | ||||||
|  |  | ||||||
|     type = ChoiceField(choices=list(ChallengeTypes)) |     type = ChoiceField( | ||||||
|  |         choices=[(x.name, x.name) for x in ChallengeTypes], | ||||||
|  |     ) | ||||||
|     component = CharField(required=False) |     component = CharField(required=False) | ||||||
|     title = CharField(required=False) |     title = CharField(required=False) | ||||||
|  |  | ||||||
| @ -90,7 +92,7 @@ class ChallengeResponse(Serializer): | |||||||
|  |  | ||||||
|     stage: Optional["StageView"] |     stage: Optional["StageView"] | ||||||
|  |  | ||||||
|     def __init__(self, instance, data, **kwargs): |     def __init__(self, instance=None, data=None, **kwargs): | ||||||
|         self.stage = kwargs.pop("stage", None) |         self.stage = kwargs.pop("stage", None) | ||||||
|         super().__init__(instance=instance, data=data, **kwargs) |         super().__init__(instance=instance, data=data, **kwargs) | ||||||
|  |  | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ from django.http import HttpRequest | |||||||
| from django.http.request import QueryDict | from django.http.request import QueryDict | ||||||
| from django.http.response import HttpResponse | from django.http.response import HttpResponse | ||||||
| from django.views.generic.base import View | from django.views.generic.base import View | ||||||
|  | from rest_framework.request import Request | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.core.models import DEFAULT_AVATAR, User | from authentik.core.models import DEFAULT_AVATAR, User | ||||||
| @ -67,9 +68,9 @@ class ChallengeStageView(StageView): | |||||||
|         return HttpChallengeResponse(challenge) |         return HttpChallengeResponse(challenge) | ||||||
|  |  | ||||||
|     # pylint: disable=unused-argument |     # pylint: disable=unused-argument | ||||||
|     def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: |     def post(self, request: Request, *args, **kwargs) -> HttpResponse: | ||||||
|         """Handle challenge response""" |         """Handle challenge response""" | ||||||
|         challenge: ChallengeResponse = self.get_response_instance(data=request.POST) |         challenge: ChallengeResponse = self.get_response_instance(data=request.data) | ||||||
|         if not challenge.is_valid(): |         if not challenge.is_valid(): | ||||||
|             return self.challenge_invalid(challenge) |             return self.challenge_invalid(challenge) | ||||||
|         return self.challenge_valid(challenge) |         return self.challenge_valid(challenge) | ||||||
|  | |||||||
| @ -9,11 +9,16 @@ from django.template.response import TemplateResponse | |||||||
| from django.utils.decorators import method_decorator | from django.utils.decorators import method_decorator | ||||||
| from django.views.decorators.clickjacking import xframe_options_sameorigin | from django.views.decorators.clickjacking import xframe_options_sameorigin | ||||||
| from django.views.generic import TemplateView, View | from django.views.generic import TemplateView, View | ||||||
|  | from drf_yasg2.utils import no_body, swagger_auto_schema | ||||||
|  | from rest_framework.permissions import AllowAny | ||||||
|  | from rest_framework.views import APIView | ||||||
| from structlog.stdlib import BoundLogger, get_logger | from structlog.stdlib import BoundLogger, get_logger | ||||||
|  |  | ||||||
| from authentik.core.models import USER_ATTRIBUTE_DEBUG | from authentik.core.models import USER_ATTRIBUTE_DEBUG | ||||||
| from authentik.events.models import cleanse_dict | from authentik.events.models import cleanse_dict | ||||||
| from authentik.flows.challenge import ( | from authentik.flows.challenge import ( | ||||||
|  |     Challenge, | ||||||
|  |     ChallengeResponse, | ||||||
|     ChallengeTypes, |     ChallengeTypes, | ||||||
|     HttpChallengeResponse, |     HttpChallengeResponse, | ||||||
|     RedirectChallenge, |     RedirectChallenge, | ||||||
| @ -40,9 +45,11 @@ SESSION_KEY_GET = "authentik_flows_get" | |||||||
|  |  | ||||||
|  |  | ||||||
| @method_decorator(xframe_options_sameorigin, name="dispatch") | @method_decorator(xframe_options_sameorigin, name="dispatch") | ||||||
| class FlowExecutorView(View): | class FlowExecutorView(APIView): | ||||||
|     """Stage 1 Flow executor, passing requests to Stage Views""" |     """Stage 1 Flow executor, passing requests to Stage Views""" | ||||||
|  |  | ||||||
|  |     permission_classes = [AllowAny] | ||||||
|  |  | ||||||
|     flow: Flow |     flow: Flow | ||||||
|  |  | ||||||
|     plan: Optional[FlowPlan] = None |     plan: Optional[FlowPlan] = None | ||||||
| @ -113,8 +120,13 @@ class FlowExecutorView(View): | |||||||
|         self.current_stage_view.request = request |         self.current_stage_view.request = request | ||||||
|         return super().dispatch(request) |         return super().dispatch(request) | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema( | ||||||
|  |         responses={200: Challenge()}, | ||||||
|  |         request_body=no_body, | ||||||
|  |         operation_id="flows_executor_get", | ||||||
|  |     ) | ||||||
|     def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: |     def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: | ||||||
|         """pass get request to current stage""" |         """Get the next pending challenge from the currently active flow.""" | ||||||
|         self._logger.debug( |         self._logger.debug( | ||||||
|             "f(exec): Passing GET", |             "f(exec): Passing GET", | ||||||
|             view_class=class_to_path(self.current_stage_view.__class__), |             view_class=class_to_path(self.current_stage_view.__class__), | ||||||
| @ -127,8 +139,13 @@ class FlowExecutorView(View): | |||||||
|             self._logger.exception(exc) |             self._logger.exception(exc) | ||||||
|             return to_stage_response(request, FlowErrorResponse(request, exc)) |             return to_stage_response(request, FlowErrorResponse(request, exc)) | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema( | ||||||
|  |         responses={200: Challenge()}, | ||||||
|  |         request_body=ChallengeResponse(), | ||||||
|  |         operation_id="flows_executor_solve", | ||||||
|  |     ) | ||||||
|     def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: |     def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: | ||||||
|         """pass post request to current stage""" |         """Solve the previously retrieved challenge and advanced to the next stage.""" | ||||||
|         self._logger.debug( |         self._logger.debug( | ||||||
|             "f(exec): Passing POST", |             "f(exec): Passing POST", | ||||||
|             view_class=class_to_path(self.current_stage_view.__class__), |             view_class=class_to_path(self.current_stage_view.__class__), | ||||||
| @ -175,8 +192,10 @@ class FlowExecutorView(View): | |||||||
|                 "f(exec): Continuing with next stage", |                 "f(exec): Continuing with next stage", | ||||||
|                 reamining=len(self.plan.stages), |                 reamining=len(self.plan.stages), | ||||||
|             ) |             ) | ||||||
|  |             kwargs = self.kwargs | ||||||
|  |             kwargs.update({"flow_slug": self.flow.slug}) | ||||||
|             return redirect_with_qs( |             return redirect_with_qs( | ||||||
|                 "authentik_api:flow-executor", self.request.GET, **self.kwargs |                 "authentik_api:flow-executor", self.request.GET, **kwargs | ||||||
|             ) |             ) | ||||||
|         # User passed all stages |         # User passed all stages | ||||||
|         self._logger.debug( |         self._logger.debug( | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ redis: | |||||||
|   ws_db: 2 |   ws_db: 2 | ||||||
|  |  | ||||||
| debug: false | debug: false | ||||||
|  |  | ||||||
| log_level: info | log_level: info | ||||||
|  |  | ||||||
| # Error reporting, sends stacktrace to sentry.beryju.org | # Error reporting, sends stacktrace to sentry.beryju.org | ||||||
|  | |||||||
| @ -55,13 +55,21 @@ class OutpostConsumer(AuthJsonConsumer): | |||||||
|         OutpostState( |         OutpostState( | ||||||
|             uid=self.channel_name, last_seen=datetime.now(), _outpost=self.outpost |             uid=self.channel_name, last_seen=datetime.now(), _outpost=self.outpost | ||||||
|         ).save(timeout=OUTPOST_HELLO_INTERVAL * 1.5) |         ).save(timeout=OUTPOST_HELLO_INTERVAL * 1.5) | ||||||
|         LOGGER.debug("added channel to cache", channel_name=self.channel_name) |         LOGGER.debug( | ||||||
|  |             "added outpost instace to cache", | ||||||
|  |             outpost=self.outpost, | ||||||
|  |             channel_name=self.channel_name, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     # pylint: disable=unused-argument |     # pylint: disable=unused-argument | ||||||
|     def disconnect(self, close_code): |     def disconnect(self, close_code): | ||||||
|         if self.outpost: |         if self.outpost: | ||||||
|             OutpostState.for_channel(self.outpost, self.channel_name).delete() |             OutpostState.for_channel(self.outpost, self.channel_name).delete() | ||||||
|         LOGGER.debug("removed channel from cache", channel_name=self.channel_name) |         LOGGER.debug( | ||||||
|  |             "removed outpost instance from cache", | ||||||
|  |             outpost=self.outpost, | ||||||
|  |             channel_name=self.channel_name, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     def receive_json(self, content: Data): |     def receive_json(self, content: Data): | ||||||
|         msg = from_dict(WebsocketMessage, content) |         msg = from_dict(WebsocketMessage, content) | ||||||
|  | |||||||
| @ -26,5 +26,5 @@ def invalidate_policy_cache(sender, instance, **_): | |||||||
|             cache.delete_many(keys) |             cache.delete_many(keys) | ||||||
|         LOGGER.debug("Invalidating policy cache", policy=instance, keys=total) |         LOGGER.debug("Invalidating policy cache", policy=instance, keys=total) | ||||||
|     # Also delete user application cache |     # Also delete user application cache | ||||||
|     keys = cache.keys(user_app_cache_key("*")) |     keys = cache.keys(user_app_cache_key("*")) or [] | ||||||
|     cache.delete_many(keys) |     cache.delete_many(keys) | ||||||
|  | |||||||
| @ -74,7 +74,7 @@ class SAMLFlowFinalView(ChallengeStageView): | |||||||
|             return super().get( |             return super().get( | ||||||
|                 self.request, |                 self.request, | ||||||
|                 **{ |                 **{ | ||||||
|                     "type": ChallengeTypes.native, |                     "type": ChallengeTypes.native.value, | ||||||
|                     "component": "ak-stage-autosubmit", |                     "component": "ak-stage-autosubmit", | ||||||
|                     "title": "Redirecting to %(app)s..." % {"app": application.name}, |                     "title": "Redirecting to %(app)s..." % {"app": application.name}, | ||||||
|                     "url": provider.acs_url, |                     "url": provider.acs_url, | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ from sentry_sdk.integrations.celery import CeleryIntegration | |||||||
| from sentry_sdk.integrations.django import DjangoIntegration | from sentry_sdk.integrations.django import DjangoIntegration | ||||||
| from sentry_sdk.integrations.redis import RedisIntegration | from sentry_sdk.integrations.redis import RedisIntegration | ||||||
|  |  | ||||||
| from authentik import __version__ | from authentik import ENV_GIT_HASH_KEY, __version__ | ||||||
| from authentik.core.middleware import structlog_add_request_id | from authentik.core.middleware import structlog_add_request_id | ||||||
| from authentik.lib.config import CONFIG | from authentik.lib.config import CONFIG | ||||||
| from authentik.lib.logging import add_process_id | from authentik.lib.logging import add_process_id | ||||||
| @ -150,7 +150,6 @@ SWAGGER_SETTINGS = { | |||||||
| REST_FRAMEWORK = { | REST_FRAMEWORK = { | ||||||
|     "DEFAULT_PAGINATION_CLASS": "authentik.api.pagination.Pagination", |     "DEFAULT_PAGINATION_CLASS": "authentik.api.pagination.Pagination", | ||||||
|     "PAGE_SIZE": 100, |     "PAGE_SIZE": 100, | ||||||
|     "DATETIME_FORMAT": "%s", |  | ||||||
|     "DEFAULT_FILTER_BACKENDS": [ |     "DEFAULT_FILTER_BACKENDS": [ | ||||||
|         "rest_framework_guardian.filters.ObjectPermissionsFilter", |         "rest_framework_guardian.filters.ObjectPermissionsFilter", | ||||||
|         "django_filters.rest_framework.DjangoFilterBackend", |         "django_filters.rest_framework.DjangoFilterBackend", | ||||||
| @ -475,6 +474,7 @@ for _app in INSTALLED_APPS: | |||||||
|  |  | ||||||
| if DEBUG: | if DEBUG: | ||||||
|     CELERY_TASK_ALWAYS_EAGER = True |     CELERY_TASK_ALWAYS_EAGER = True | ||||||
|  |     os.environ[ENV_GIT_HASH_KEY] = "dev" | ||||||
|  |  | ||||||
| INSTALLED_APPS.append("authentik.core.apps.AuthentikCoreConfig") | INSTALLED_APPS.append("authentik.core.apps.AuthentikCoreConfig") | ||||||
|  |  | ||||||
|  | |||||||
| @ -15,9 +15,11 @@ class OAuthSourceForm(forms.ModelForm): | |||||||
|         self.fields["authentication_flow"].queryset = Flow.objects.filter( |         self.fields["authentication_flow"].queryset = Flow.objects.filter( | ||||||
|             designation=FlowDesignation.AUTHENTICATION |             designation=FlowDesignation.AUTHENTICATION | ||||||
|         ) |         ) | ||||||
|  |         self.fields["authentication_flow"].required = True | ||||||
|         self.fields["enrollment_flow"].queryset = Flow.objects.filter( |         self.fields["enrollment_flow"].queryset = Flow.objects.filter( | ||||||
|             designation=FlowDesignation.ENROLLMENT |             designation=FlowDesignation.ENROLLMENT | ||||||
|         ) |         ) | ||||||
|  |         self.fields["enrollment_flow"].required = True | ||||||
|         if hasattr(self.Meta, "overrides"): |         if hasattr(self.Meta, "overrides"): | ||||||
|             for overide_field, overide_value in getattr(self.Meta, "overrides").items(): |             for overide_field, overide_value in getattr(self.Meta, "overrides").items(): | ||||||
|                 self.fields[overide_field].initial = overide_value |                 self.fields[overide_field].initial = overide_value | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ from typing import Any, Optional | |||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.contrib import messages | from django.contrib import messages | ||||||
| from django.http import Http404, HttpRequest, HttpResponse | from django.http import Http404, HttpRequest, HttpResponse | ||||||
|  | from django.http.response import HttpResponseBadRequest | ||||||
| from django.shortcuts import redirect | from django.shortcuts import redirect | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| @ -151,6 +152,8 @@ class OAuthCallback(OAuthClientMixin, View): | |||||||
|                 PLAN_CONTEXT_REDIRECT: final_redirect, |                 PLAN_CONTEXT_REDIRECT: final_redirect, | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|  |         if not flow: | ||||||
|  |             return HttpResponseBadRequest() | ||||||
|         # We run the Flow planner here so we can pass the Pending user in the context |         # We run the Flow planner here so we can pass the Pending user in the context | ||||||
|         planner = FlowPlanner(flow) |         planner = FlowPlanner(flow) | ||||||
|         plan = planner.plan(self.request, kwargs) |         plan = planner.plan(self.request, kwargs) | ||||||
| @ -233,6 +236,9 @@ class OAuthCallback(OAuthClientMixin, View): | |||||||
|             PLAN_CONTEXT_SOURCES_OAUTH_ACCESS: access, |             PLAN_CONTEXT_SOURCES_OAUTH_ACCESS: access, | ||||||
|         } |         } | ||||||
|         # We run the Flow planner here so we can pass the Pending user in the context |         # We run the Flow planner here so we can pass the Pending user in the context | ||||||
|  |         if not source.enrollment_flow: | ||||||
|  |             LOGGER.warning("source has no enrollment flow", source=source) | ||||||
|  |             return HttpResponseBadRequest() | ||||||
|         planner = FlowPlanner(source.enrollment_flow) |         planner = FlowPlanner(source.enrollment_flow) | ||||||
|         plan = planner.plan(self.request, context) |         plan = planner.plan(self.request, context) | ||||||
|         plan.append(in_memory_stage(PostUserEnrollmentStage)) |         plan.append(in_memory_stage(PostUserEnrollmentStage)) | ||||||
|  | |||||||
| @ -1,5 +1,8 @@ | |||||||
| """AuthenticatorStaticStage API Views""" | """AuthenticatorStaticStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from django_otp.plugins.otp_static.models import StaticDevice | ||||||
|  | from rest_framework.permissions import IsAdminUser | ||||||
|  | from rest_framework.serializers import ModelSerializer | ||||||
|  | from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_static.models import AuthenticatorStaticStage | from authentik.stages.authenticator_static.models import AuthenticatorStaticStage | ||||||
| @ -19,3 +22,39 @@ class AuthenticatorStaticStageViewSet(ModelViewSet): | |||||||
|  |  | ||||||
|     queryset = AuthenticatorStaticStage.objects.all() |     queryset = AuthenticatorStaticStage.objects.all() | ||||||
|     serializer_class = AuthenticatorStaticStageSerializer |     serializer_class = AuthenticatorStaticStageSerializer | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StaticDeviceSerializer(ModelSerializer): | ||||||
|  |     """Serializer for static authenticator devices""" | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         model = StaticDevice | ||||||
|  |         fields = ["name", "token_set"] | ||||||
|  |         depth = 2 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StaticDeviceViewSet(ModelViewSet): | ||||||
|  |     """Viewset for static authenticator devices""" | ||||||
|  |  | ||||||
|  |     queryset = StaticDevice.objects.none() | ||||||
|  |     serializer_class = StaticDeviceSerializer | ||||||
|  |     search_fields = ["name"] | ||||||
|  |     filterset_fields = ["name"] | ||||||
|  |     ordering = ["name"] | ||||||
|  |  | ||||||
|  |     def get_queryset(self): | ||||||
|  |         if not self.request: | ||||||
|  |             return super().get_queryset() | ||||||
|  |         return StaticDevice.objects.filter(user=self.request.user) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StaticAdminDeviceViewSet(ReadOnlyModelViewSet): | ||||||
|  |     """Viewset for static authenticator devices (for admins)""" | ||||||
|  |  | ||||||
|  |     permission_classes = [IsAdminUser] | ||||||
|  |     queryset = StaticDevice.objects.all() | ||||||
|  |     serializer_class = StaticDeviceSerializer | ||||||
|  |     search_fields = ["name"] | ||||||
|  |     filterset_fields = ["name"] | ||||||
|  |     ordering = ["name"] | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ class AuthenticatorStaticStageView(ChallengeStageView): | |||||||
|         tokens: list[StaticToken] = self.request.session[SESSION_STATIC_TOKENS] |         tokens: list[StaticToken] = self.request.session[SESSION_STATIC_TOKENS] | ||||||
|         return AuthenticatorStaticChallenge( |         return AuthenticatorStaticChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-authenticator-static", |                 "component": "ak-stage-authenticator-static", | ||||||
|                 "codes": [token.token for token in tokens], |                 "codes": [token.token for token in tokens], | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -1,5 +1,8 @@ | |||||||
| """AuthenticatorTOTPStage API Views""" | """AuthenticatorTOTPStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from django_otp.plugins.otp_totp.models import TOTPDevice | ||||||
|  | from rest_framework.permissions import IsAdminUser | ||||||
|  | from rest_framework.serializers import ModelSerializer | ||||||
|  | from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage | from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage | ||||||
| @ -19,3 +22,41 @@ class AuthenticatorTOTPStageViewSet(ModelViewSet): | |||||||
|  |  | ||||||
|     queryset = AuthenticatorTOTPStage.objects.all() |     queryset = AuthenticatorTOTPStage.objects.all() | ||||||
|     serializer_class = AuthenticatorTOTPStageSerializer |     serializer_class = AuthenticatorTOTPStageSerializer | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TOTPDeviceSerializer(ModelSerializer): | ||||||
|  |     """Serializer for totp authenticator devices""" | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         model = TOTPDevice | ||||||
|  |         fields = [ | ||||||
|  |             "name", | ||||||
|  |         ] | ||||||
|  |         depth = 2 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TOTPDeviceViewSet(ModelViewSet): | ||||||
|  |     """Viewset for totp authenticator devices""" | ||||||
|  |  | ||||||
|  |     queryset = TOTPDevice.objects.none() | ||||||
|  |     serializer_class = TOTPDeviceSerializer | ||||||
|  |     search_fields = ["name"] | ||||||
|  |     filterset_fields = ["name"] | ||||||
|  |     ordering = ["name"] | ||||||
|  |  | ||||||
|  |     def get_queryset(self): | ||||||
|  |         if not self.request: | ||||||
|  |             return super().get_queryset() | ||||||
|  |         return TOTPDevice.objects.filter(user=self.request.user) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TOTPAdminDeviceViewSet(ReadOnlyModelViewSet): | ||||||
|  |     """Viewset for totp authenticator devices (for admins)""" | ||||||
|  |  | ||||||
|  |     permission_classes = [IsAdminUser] | ||||||
|  |     queryset = TOTPDevice.objects.all() | ||||||
|  |     serializer_class = TOTPDeviceSerializer | ||||||
|  |     search_fields = ["name"] | ||||||
|  |     filterset_fields = ["name"] | ||||||
|  |     ordering = ["name"] | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ class AuthenticatorTOTPStageView(ChallengeStageView): | |||||||
|         device: TOTPDevice = self.request.session[SESSION_TOTP_DEVICE] |         device: TOTPDevice = self.request.session[SESSION_TOTP_DEVICE] | ||||||
|         return AuthenticatorTOTPChallenge( |         return AuthenticatorTOTPChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-authenticator-totp", |                 "component": "ak-stage-authenticator-totp", | ||||||
|                 "config_url": device.config_url, |                 "config_url": device.config_url, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -145,7 +145,7 @@ class AuthenticatorValidateStageView(ChallengeStageView): | |||||||
|         challenges = self.request.session["device_challenges"] |         challenges = self.request.session["device_challenges"] | ||||||
|         return AuthenticatorChallenge( |         return AuthenticatorChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-authenticator-validate", |                 "component": "ak-stage-authenticator-validate", | ||||||
|                 "device_challenges": challenges, |                 "device_challenges": challenges, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -1,8 +1,13 @@ | |||||||
| """AuthenticateWebAuthnStage API Views""" | """AuthenticateWebAuthnStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.permissions import IsAdminUser | ||||||
|  | from rest_framework.serializers import ModelSerializer | ||||||
|  | from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage | from authentik.stages.authenticator_webauthn.models import ( | ||||||
|  |     AuthenticateWebAuthnStage, | ||||||
|  |     WebAuthnDevice, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class AuthenticateWebAuthnStageSerializer(StageSerializer): | class AuthenticateWebAuthnStageSerializer(StageSerializer): | ||||||
| @ -19,3 +24,41 @@ class AuthenticateWebAuthnStageViewSet(ModelViewSet): | |||||||
|  |  | ||||||
|     queryset = AuthenticateWebAuthnStage.objects.all() |     queryset = AuthenticateWebAuthnStage.objects.all() | ||||||
|     serializer_class = AuthenticateWebAuthnStageSerializer |     serializer_class = AuthenticateWebAuthnStageSerializer | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class WebAuthnDeviceSerializer(ModelSerializer): | ||||||
|  |     """Serializer for WebAuthn authenticator devices""" | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         model = WebAuthnDevice | ||||||
|  |         fields = [ | ||||||
|  |             "name", | ||||||
|  |         ] | ||||||
|  |         depth = 2 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class WebAuthnDeviceViewSet(ModelViewSet): | ||||||
|  |     """Viewset for WebAuthn authenticator devices""" | ||||||
|  |  | ||||||
|  |     queryset = WebAuthnDevice.objects.none() | ||||||
|  |     serializer_class = WebAuthnDeviceSerializer | ||||||
|  |     search_fields = ["name"] | ||||||
|  |     filterset_fields = ["name"] | ||||||
|  |     ordering = ["name"] | ||||||
|  |  | ||||||
|  |     def get_queryset(self): | ||||||
|  |         if not self.request: | ||||||
|  |             return super().get_queryset() | ||||||
|  |         return WebAuthnDevice.objects.filter(user=self.request.user) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class WebAuthnAdminDeviceViewSet(ReadOnlyModelViewSet): | ||||||
|  |     """Viewset for WebAuthn authenticator devices (for admins)""" | ||||||
|  |  | ||||||
|  |     permission_classes = [IsAdminUser] | ||||||
|  |     queryset = WebAuthnDevice.objects.all() | ||||||
|  |     serializer_class = WebAuthnDeviceSerializer | ||||||
|  |     search_fields = ["name"] | ||||||
|  |     filterset_fields = ["name"] | ||||||
|  |     ordering = ["name"] | ||||||
|  | |||||||
| @ -122,7 +122,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView): | |||||||
|  |  | ||||||
|         return AuthenticatorWebAuthnChallenge( |         return AuthenticatorWebAuthnChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-authenticator-webauthn", |                 "component": "ak-stage-authenticator-webauthn", | ||||||
|                 "registration": make_credential_options.registration_dict, |                 "registration": make_credential_options.registration_dict, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -63,7 +63,7 @@ class CaptchaStageView(ChallengeStageView): | |||||||
|     def get_challenge(self, *args, **kwargs) -> Challenge: |     def get_challenge(self, *args, **kwargs) -> Challenge: | ||||||
|         return CaptchaChallenge( |         return CaptchaChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-captcha", |                 "component": "ak-stage-captcha", | ||||||
|                 "site_key": self.executor.current_stage.public_key, |                 "site_key": self.executor.current_stage.public_key, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ class ConsentStageView(ChallengeStageView): | |||||||
|     def get_challenge(self) -> Challenge: |     def get_challenge(self) -> Challenge: | ||||||
|         challenge = ConsentChallenge( |         challenge = ConsentChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-consent", |                 "component": "ak-stage-consent", | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ class DummyStageView(ChallengeStageView): | |||||||
|     def get_challenge(self, *args, **kwargs) -> Challenge: |     def get_challenge(self, *args, **kwargs) -> Challenge: | ||||||
|         return DummyChallenge( |         return DummyChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "", |                 "component": "", | ||||||
|                 "title": self.executor.current_stage.name, |                 "title": self.executor.current_stage.name, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -94,7 +94,7 @@ class EmailStageView(ChallengeStageView): | |||||||
|  |  | ||||||
|     def get_challenge(self) -> Challenge: |     def get_challenge(self) -> Challenge: | ||||||
|         challenge = EmailChallenge( |         challenge = EmailChallenge( | ||||||
|             data={"type": ChallengeTypes.native, "component": "ak-stage-email"} |             data={"type": ChallengeTypes.native.value, "component": "ak-stage-email"} | ||||||
|         ) |         ) | ||||||
|         return challenge |         return challenge | ||||||
|  |  | ||||||
|  | |||||||
| @ -78,7 +78,7 @@ class IdentificationStageView(ChallengeStageView): | |||||||
|         current_stage: IdentificationStage = self.executor.current_stage |         current_stage: IdentificationStage = self.executor.current_stage | ||||||
|         challenge = IdentificationChallenge( |         challenge = IdentificationChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-identification", |                 "component": "ak-stage-identification", | ||||||
|                 "primary_action": _("Log in"), |                 "primary_action": _("Log in"), | ||||||
|                 "input_type": "text", |                 "input_type": "text", | ||||||
|  | |||||||
| @ -78,7 +78,7 @@ class PasswordStageView(ChallengeStageView): | |||||||
|     def get_challenge(self) -> Challenge: |     def get_challenge(self) -> Challenge: | ||||||
|         challenge = PasswordChallenge( |         challenge = PasswordChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-password", |                 "component": "ak-stage-password", | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|  | |||||||
| @ -164,7 +164,7 @@ class PromptStageView(ChallengeStageView): | |||||||
|         fields = list(self.executor.current_stage.fields.all().order_by("order")) |         fields = list(self.executor.current_stage.fields.all().order_by("order")) | ||||||
|         challenge = PromptChallenge( |         challenge = PromptChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-prompt", |                 "component": "ak-stage-prompt", | ||||||
|                 "fields": [PromptSerializer(field).data for field in fields], |                 "fields": [PromptSerializer(field).data for field in fields], | ||||||
|             }, |             }, | ||||||
|  | |||||||
| @ -279,6 +279,7 @@ stages: | |||||||
|             displayName: Build static files for e2e |             displayName: Build static files for e2e | ||||||
|             inputs: |             inputs: | ||||||
|               script: | |               script: | | ||||||
|  |                 docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/src/api --additional-properties=typescriptThreePlus=true | ||||||
|                 cd web |                 cd web | ||||||
|                 npm i |                 npm i | ||||||
|                 npm run build |                 npm run build | ||||||
| @ -380,6 +381,13 @@ stages: | |||||||
|           inputs: |           inputs: | ||||||
|             containerRegistry: 'beryjuorg-harbor' |             containerRegistry: 'beryjuorg-harbor' | ||||||
|             repository: 'authentik/server' |             repository: 'authentik/server' | ||||||
|             command: 'buildAndPush' |             command: 'build' | ||||||
|             Dockerfile: 'Dockerfile' |             Dockerfile: 'Dockerfile' | ||||||
|             tags: "gh-$(branchName)" |             tags: 'gh-$(branchName)' | ||||||
|  |             arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)' | ||||||
|  |         - task: Docker@2 | ||||||
|  |           inputs: | ||||||
|  |             containerRegistry: 'beryjuorg-harbor' | ||||||
|  |             repository: 'authentik/server' | ||||||
|  |             command: 'push' | ||||||
|  |             tags: 'gh-$(branchName)' | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ services: | |||||||
|     networks: |     networks: | ||||||
|       - internal |       - internal | ||||||
|   server: |   server: | ||||||
|     image: beryju/authentik:${AUTHENTIK_TAG:-2021.3.3} |     image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.3.4} | ||||||
|     command: server |     command: server | ||||||
|     environment: |     environment: | ||||||
|       AUTHENTIK_REDIS__HOST: redis |       AUTHENTIK_REDIS__HOST: redis | ||||||
| @ -27,9 +27,11 @@ services: | |||||||
|       AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} |       AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} | ||||||
|       AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} |       AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} | ||||||
|       AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} |       AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} | ||||||
|  |       # AUTHENTIK_ERROR_REPORTING__ENABLED: true | ||||||
|     volumes: |     volumes: | ||||||
|       - ./media:/media |       - ./media:/media | ||||||
|       - ./custom-templates:/templates |       - ./custom-templates:/templates | ||||||
|  |       - geoip:/geoip | ||||||
|     ports: |     ports: | ||||||
|       - 8000 |       - 8000 | ||||||
|     networks: |     networks: | ||||||
| @ -45,7 +47,7 @@ services: | |||||||
|     env_file: |     env_file: | ||||||
|       - .env |       - .env | ||||||
|   worker: |   worker: | ||||||
|     image: beryju/authentik:${AUTHENTIK_TAG:-2021.3.3} |     image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.3.4} | ||||||
|     command: worker |     command: worker | ||||||
|     networks: |     networks: | ||||||
|       - internal |       - internal | ||||||
| @ -55,14 +57,16 @@ services: | |||||||
|       AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} |       AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} | ||||||
|       AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} |       AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} | ||||||
|       AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} |       AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} | ||||||
|  |       # AUTHENTIK_ERROR_REPORTING__ENABLED: true | ||||||
|     volumes: |     volumes: | ||||||
|       - ./backups:/backups |       - ./backups:/backups | ||||||
|       - /var/run/docker.sock:/var/run/docker.sock |       - /var/run/docker.sock:/var/run/docker.sock | ||||||
|       - ./custom-templates:/templates |       - ./custom-templates:/templates | ||||||
|  |       - geoip:/geoip | ||||||
|     env_file: |     env_file: | ||||||
|       - .env |       - .env | ||||||
|   static: |   static: | ||||||
|     image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.3.3} |     image: ${AUTHENTIK_IMAGE_STATIC:-beryju/authentik-static}:${AUTHENTIK_TAG:-2021.3.4} | ||||||
|     networks: |     networks: | ||||||
|       - internal |       - internal | ||||||
|     labels: |     labels: | ||||||
| @ -91,10 +95,21 @@ services: | |||||||
|       - "127.0.0.1:8080:8080" |       - "127.0.0.1:8080:8080" | ||||||
|     networks: |     networks: | ||||||
|       - internal |       - internal | ||||||
|  |   geoipupdate: | ||||||
|  |     image: "maxmindinc/geoipupdate:latest" | ||||||
|  |     volumes: | ||||||
|  |       - "geoip:/usr/share/GeoIP" | ||||||
|  |     environment: | ||||||
|  |       GEOIPUPDATE_EDITION_IDS: "GeoLite2-City" | ||||||
|  |       GEOIPUPDATE_FREQUENCY: "8" | ||||||
|  |     env_file: | ||||||
|  |       - .env | ||||||
|  |  | ||||||
| volumes: | volumes: | ||||||
|   database: |   database: | ||||||
|     driver: local |     driver: local | ||||||
|  |   geoip: | ||||||
|  |     driver: local | ||||||
|  |  | ||||||
| networks: | networks: | ||||||
|   internal: {} |   internal: {} | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ name: authentik | |||||||
| home: https://goauthentik.io | home: https://goauthentik.io | ||||||
| sources: | sources: | ||||||
|   - https://github.com/BeryJu/authentik |   - https://github.com/BeryJu/authentik | ||||||
| version: "2021.3.3" | version: "2021.3.4" | ||||||
| icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg | icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg | ||||||
| dependencies: | dependencies: | ||||||
|   - name: postgresql |   - name: postgresql | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
| |-----------------------------------|-------------------------|-------------| | |-----------------------------------|-------------------------|-------------| | ||||||
| | image.name                        | beryju/authentik        | Image used to run the authentik server and worker | | | image.name                        | beryju/authentik        | Image used to run the authentik server and worker | | ||||||
| | image.name_static                 | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) | | | image.name_static                 | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) | | ||||||
| | image.tag                         | 2021.3.3           | Image tag | | | image.tag                         | 2021.3.4                | Image tag | | ||||||
| | image.pullPolicy                  | IfNotPresent            | Image Pull Policy used for all deployments | | | image.pullPolicy                  | IfNotPresent            | Image Pull Policy used for all deployments | | ||||||
| | serverReplicas                    | 1                       | Replicas for the Server deployment | | | serverReplicas                    | 1                       | Replicas for the Server deployment | | ||||||
| | workerReplicas                    | 1                       | Replicas for the Worker deployment | | | workerReplicas                    | 1                       | Replicas for the Worker deployment | | ||||||
| @ -22,6 +22,10 @@ | |||||||
| | config.email.use_ssl              | false                   | Enable SSL | | | config.email.use_ssl              | false                   | Enable SSL | | ||||||
| | config.email.timeout              | 10                      | SMTP Timeout | | | config.email.timeout              | 10                      | SMTP Timeout | | ||||||
| | config.email.from                 | authentik@localhost     | Email address authentik will send from, should have a correct @domain | | | config.email.from                 | authentik@localhost     | Email address authentik will send from, should have a correct @domain | | ||||||
|  | | geoip.enabled                     | false                   | Optionally enable GeoIP | | ||||||
|  | | geoip.accountId                   |                         | GeoIP MaxMind Account ID | | ||||||
|  | | geoip.licenseKey                  |                         | GeoIP MaxMind License key | | ||||||
|  | | geoip.image                       | maxmindinc/geoipupdate:latest  | GeoIP Updater image | | ||||||
| | backup.accessKey                  |                         | Optionally enable S3 Backup, Access Key | | | backup.accessKey                  |                         | Optionally enable S3 Backup, Access Key | | ||||||
| | backup.secretKey                  |                         | Optionally enable S3 Backup, Secret Key | | | backup.secretKey                  |                         | Optionally enable S3 Backup, Secret Key | | ||||||
| | backup.bucket                     |                         | Optionally enable S3 Backup, Bucket | | | backup.bucket                     |                         | Optionally enable S3 Backup, Bucket | | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								helm/templates/geoip-configmap.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								helm/templates/geoip-configmap.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | {{- if .Values.geoip.enabled -}} | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: ConfigMap | ||||||
|  | metadata: | ||||||
|  |   name: {{ include "authentik.fullname" . }}-geoip-config | ||||||
|  | data: | ||||||
|  |   GEOIPUPDATE_ACCOUNT_ID: "{{ .Values.geoip.accountId }}" | ||||||
|  |   GEOIPUPDATE_LICENSE_KEY: "{{ .Values.geoip.licenseKey }}" | ||||||
|  |   GEOIPUPDATE_EDITION_IDS: "GeoLite2-City" | ||||||
|  |   GEOIPUPDATE_FREQUENCY: "8" | ||||||
|  | {{- end }} | ||||||
							
								
								
									
										39
									
								
								helm/templates/geoip-deployment.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								helm/templates/geoip-deployment.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | {{- if .Values.geoip.enabled -}} | ||||||
|  | apiVersion: apps/v1 | ||||||
|  | kind: Deployment | ||||||
|  | metadata: | ||||||
|  |   name: {{ include "authentik.fullname" . }}-geoip | ||||||
|  |   labels: | ||||||
|  |     app.kubernetes.io/name: {{ include "authentik.name" . }} | ||||||
|  |     helm.sh/chart: {{ include "authentik.chart" . }} | ||||||
|  |     app.kubernetes.io/instance: {{ .Release.Name }} | ||||||
|  |     app.kubernetes.io/managed-by: {{ .Release.Service }} | ||||||
|  |     k8s.goauthentik.io/component: geoip | ||||||
|  | spec: | ||||||
|  |   replicas: 1 | ||||||
|  |   selector: | ||||||
|  |     matchLabels: | ||||||
|  |       app.kubernetes.io/name: {{ include "authentik.name" . }} | ||||||
|  |       app.kubernetes.io/instance: {{ .Release.Name }} | ||||||
|  |       k8s.goauthentik.io/component: geoip | ||||||
|  |   template: | ||||||
|  |     metadata: | ||||||
|  |       labels: | ||||||
|  |         app.kubernetes.io/name: {{ include "authentik.name" . }} | ||||||
|  |         app.kubernetes.io/instance: {{ .Release.Name }} | ||||||
|  |         k8s.goauthentik.io/component: geoip | ||||||
|  |     spec: | ||||||
|  |       containers: | ||||||
|  |         - name: geoip | ||||||
|  |           image: "{{ .Values.geoip.image }}" | ||||||
|  |           envFrom: | ||||||
|  |             - configMapRef: | ||||||
|  |                 name: {{ include "authentik.fullname" . }}-geoip-config | ||||||
|  |           volumeMounts: | ||||||
|  |             - name: geoip | ||||||
|  |               mountPath: /usr/share/GeoIP | ||||||
|  |       volumes: | ||||||
|  |         - name: geoip | ||||||
|  |           persistentVolumeClaim: | ||||||
|  |             claimName: {{ include "authentik.fullname" . }}-geoip | ||||||
|  | {{- end }} | ||||||
							
								
								
									
										17
									
								
								helm/templates/geoip-pvc.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								helm/templates/geoip-pvc.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | {{- if .Values.geoip.enabled -}} | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: PersistentVolumeClaim | ||||||
|  | metadata: | ||||||
|  |   name: {{ include "authentik.fullname" . }}-geoip | ||||||
|  |   labels: | ||||||
|  |     app.kubernetes.io/name: {{ include "authentik.name" . }} | ||||||
|  |     helm.sh/chart: {{ include "authentik.chart" . }} | ||||||
|  |     app.kubernetes.io/instance: {{ .Release.Name }} | ||||||
|  |     app.kubernetes.io/managed-by: {{ .Release.Service }} | ||||||
|  | spec: | ||||||
|  |   accessModes: | ||||||
|  |   - ReadWriteMany | ||||||
|  |   resources: | ||||||
|  |     requests: | ||||||
|  |       storage: 1Gi | ||||||
|  | {{- end }} | ||||||
							
								
								
									
										121
									
								
								helm/templates/prom-rules.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								helm/templates/prom-rules.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | |||||||
|  | {{- if .Values.monitoring.enabled -}} | ||||||
|  | --- | ||||||
|  | apiVersion: monitoring.coreos.com/v1 | ||||||
|  | kind: PrometheusRule | ||||||
|  | metadata: | ||||||
|  |   name: {{ include "authentik.fullname" . }}-static-rules | ||||||
|  |   labels: | ||||||
|  |     app.kubernetes.io/name: {{ include "authentik.name" . }} | ||||||
|  |     helm.sh/chart: {{ include "authentik.chart" . }} | ||||||
|  |     app.kubernetes.io/instance: {{ .Release.Name }} | ||||||
|  |     app.kubernetes.io/managed-by: {{ .Release.Service }} | ||||||
|  | spec: | ||||||
|  |   groups: | ||||||
|  |   - name: Aggregate request counters | ||||||
|  |     rules: | ||||||
|  |       - record: job:django_http_requests_before_middlewares_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_requests_before_middlewares_total[30s])) by (job) | ||||||
|  |       - record: job:django_http_requests_unknown_latency_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_requests_unknown_latency_total[30s])) by (job) | ||||||
|  |       - record: job:django_http_ajax_requests_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_ajax_requests_total[30s])) by (job) | ||||||
|  |       - record: job:django_http_responses_before_middlewares_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_responses_before_middlewares_total[30s])) by (job) | ||||||
|  |       - record: job:django_http_requests_unknown_latency_including_middlewares_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_requests_unknown_latency_including_middlewares_total[30s])) by (job) | ||||||
|  |       - record: job:django_http_requests_body_total_bytes:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_requests_body_total_bytes[30s])) by (job) | ||||||
|  |       - record: job:django_http_responses_streaming_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_responses_streaming_total[30s])) by (job) | ||||||
|  |       - record: job:django_http_responses_body_total_bytes:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_responses_body_total_bytes[30s])) by (job) | ||||||
|  |       - record: job:django_http_requests_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_requests_total_by_method[30s])) by (job) | ||||||
|  |       - record: job:django_http_requests_total_by_method:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_requests_total_by_method[30s])) by (job,method) | ||||||
|  |       - record: job:django_http_requests_total_by_transport:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_requests_total_by_transport[30s])) by (job,transport) | ||||||
|  |       - record: job:django_http_requests_total_by_view:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) by (job,view) | ||||||
|  |       - record: job:django_http_requests_total_by_view_transport_method:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) by (job,view,transport,method) | ||||||
|  |       - record: job:django_http_responses_total_by_templatename:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_responses_total_by_templatename[30s])) by (job,templatename) | ||||||
|  |       - record: job:django_http_responses_total_by_status:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_responses_total_by_status[30s])) by (job,status) | ||||||
|  |       - record: job:django_http_responses_total_by_status_name_method:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_responses_total_by_status_name_method[30s])) by (job,status,name,method) | ||||||
|  |       - record: job:django_http_responses_total_by_charset:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_responses_total_by_charset[30s])) by (job,charset) | ||||||
|  |       - record: job:django_http_exceptions_total_by_type:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_exceptions_total_by_type[30s])) by (job,type) | ||||||
|  |       - record: job:django_http_exceptions_total_by_view:sum_rate30s | ||||||
|  |         expr: sum(rate(django_http_exceptions_total_by_view[30s])) by (job,view) | ||||||
|  |   - name: Aggregate latency histograms | ||||||
|  |     rules: | ||||||
|  |       - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s | ||||||
|  |         expr: histogram_quantile(0.50, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le)) | ||||||
|  |         labels: | ||||||
|  |           quantile: "50" | ||||||
|  |       - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s | ||||||
|  |         expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le)) | ||||||
|  |         labels: | ||||||
|  |           quantile: "95" | ||||||
|  |       - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s | ||||||
|  |         expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le)) | ||||||
|  |         labels: | ||||||
|  |           quantile: "99" | ||||||
|  |       - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s | ||||||
|  |         expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le)) | ||||||
|  |         labels: | ||||||
|  |           quantile: "99.9" | ||||||
|  |       - record: job:django_http_requests_latency_seconds:quantile_rate30s | ||||||
|  |         expr: histogram_quantile(0.50, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le)) | ||||||
|  |         labels: | ||||||
|  |           quantile: "50" | ||||||
|  |       - record: job:django_http_requests_latency_seconds:quantile_rate30s | ||||||
|  |         expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le)) | ||||||
|  |         labels: | ||||||
|  |           quantile: "95" | ||||||
|  |       - record: job:django_http_requests_latency_seconds:quantile_rate30s | ||||||
|  |         expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le)) | ||||||
|  |         labels: | ||||||
|  |           quantile: "99" | ||||||
|  |       - record: job:django_http_requests_latency_seconds:quantile_rate30s | ||||||
|  |         expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le)) | ||||||
|  |         labels: | ||||||
|  |           quantile: "99.9" | ||||||
|  |   - name: Aggregate model operations | ||||||
|  |     rules: | ||||||
|  |       - record: job:django_model_inserts_total:sum_rate1m | ||||||
|  |         expr: sum(rate(django_model_inserts_total[1m])) by (job, model) | ||||||
|  |       - record: job:django_model_updates_total:sum_rate1m | ||||||
|  |         expr: sum(rate(django_model_updates_total[1m])) by (job, model) | ||||||
|  |       - record: job:django_model_deletes_total:sum_rate1m | ||||||
|  |         expr: sum(rate(django_model_deletes_total[1m])) by (job, model) | ||||||
|  |   - name: Aggregate database operations | ||||||
|  |     rules: | ||||||
|  |       - record: job:django_db_new_connections_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_db_new_connections_total[30s])) by (alias, vendor) | ||||||
|  |       - record: job:django_db_new_connection_errors_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_db_new_connection_errors_total[30s])) by (alias, vendor) | ||||||
|  |       - record: job:django_db_execute_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_db_execute_total[30s])) by (alias, vendor) | ||||||
|  |       - record: job:django_db_execute_many_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_db_execute_many_total[30s])) by (alias, vendor) | ||||||
|  |       - record: job:django_db_errors_total:sum_rate30s | ||||||
|  |         expr: sum(rate(django_db_errors_total[30s])) by (alias, vendor, type) | ||||||
|  |   - name: Aggregate migrations | ||||||
|  |     rules: | ||||||
|  |       - record: job:django_migrations_applied_total:max | ||||||
|  |         expr: max(django_migrations_applied_total) by (job, connection) | ||||||
|  |       - record: job:django_migrations_unapplied_total:max | ||||||
|  |         expr: max(django_migrations_unapplied_total) by (job, connection) | ||||||
|  |   - name: Alerts | ||||||
|  |     rules: | ||||||
|  |       - alert: UnappliedMigrations | ||||||
|  |         expr: job:django_migrations_unapplied_total:max > 0 | ||||||
|  |         for: 1m | ||||||
|  |         labels: | ||||||
|  |           severity: testing | ||||||
|  | {{- end }} | ||||||
							
								
								
									
										17
									
								
								helm/templates/static-sm.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								helm/templates/static-sm.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | {{- if .Values.monitoring.enabled -}} | ||||||
|  | apiVersion: monitoring.coreos.com/v1 | ||||||
|  | kind: ServiceMonitor | ||||||
|  | metadata: | ||||||
|  |   labels: | ||||||
|  |     app.kubernetes.io/name: {{ include "authentik.name" . }} | ||||||
|  |     helm.sh/chart: {{ include "authentik.chart" . }} | ||||||
|  |     app.kubernetes.io/instance: {{ .Release.Name }} | ||||||
|  |     app.kubernetes.io/managed-by: {{ .Release.Service }} | ||||||
|  |   name: {{ include "authentik.fullname" . }}-static-monitoring | ||||||
|  | spec: | ||||||
|  |   endpoints: | ||||||
|  |   - port: http | ||||||
|  |   selector: | ||||||
|  |     matchLabels: | ||||||
|  |       k8s.goauthentik.io/component: static | ||||||
|  | {{- end }} | ||||||
| @ -88,9 +88,17 @@ spec: | |||||||
|                 secretKeyRef: |                 secretKeyRef: | ||||||
|                   name: "{{ .Release.Name }}-postgresql" |                   name: "{{ .Release.Name }}-postgresql" | ||||||
|                   key: "postgresql-password" |                   key: "postgresql-password" | ||||||
|  |             {{ if .Values.geoip.enabled -}} | ||||||
|  |             - name: AUTHENTIK_AUTHENTIK__GEOIP | ||||||
|  |               value: /geoip/GeoLite2-City.mmdb | ||||||
|  |             {{- end }} | ||||||
|           volumeMounts: |           volumeMounts: | ||||||
|             - name: authentik-uploads |             - name: authentik-uploads | ||||||
|               mountPath: /media |               mountPath: /media | ||||||
|  |             {{ if .Values.geoip.enabled -}} | ||||||
|  |             - name: geoip | ||||||
|  |               mountPath: /geoip | ||||||
|  |             {{- end }} | ||||||
|           ports: |           ports: | ||||||
|             - name: http |             - name: http | ||||||
|               containerPort: 8000 |               containerPort: 8000 | ||||||
| @ -116,3 +124,8 @@ spec: | |||||||
|         - name: authentik-uploads |         - name: authentik-uploads | ||||||
|           persistentVolumeClaim: |           persistentVolumeClaim: | ||||||
|             claimName: {{ include "authentik.fullname" . }}-uploads |             claimName: {{ include "authentik.fullname" . }}-uploads | ||||||
|  |         {{ if .Values.geoip.enabled -}} | ||||||
|  |         - name: geoip | ||||||
|  |           persistentVolumeClaim: | ||||||
|  |             claimName: {{ include "authentik.fullname" . }}-geoip | ||||||
|  |         {{- end }} | ||||||
|  | |||||||
							
								
								
									
										26
									
								
								helm/templates/web-sm.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								helm/templates/web-sm.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | {{- if .Values.monitoring.enabled -}} | ||||||
|  | apiVersion: monitoring.coreos.com/v1 | ||||||
|  | kind: ServiceMonitor | ||||||
|  | metadata: | ||||||
|  |   labels: | ||||||
|  |     app.kubernetes.io/name: {{ include "authentik.name" . }} | ||||||
|  |     helm.sh/chart: {{ include "authentik.chart" . }} | ||||||
|  |     app.kubernetes.io/instance: {{ .Release.Name }} | ||||||
|  |     app.kubernetes.io/managed-by: {{ .Release.Service }} | ||||||
|  |   name: {{ include "authentik.fullname" . }}-web-monitoring | ||||||
|  | spec: | ||||||
|  |   endpoints: | ||||||
|  |   - basicAuth: | ||||||
|  |       password: | ||||||
|  |         name: {{ include "authentik.fullname" . }}-secret-key | ||||||
|  |         key: SECRET_KEY | ||||||
|  |       username: | ||||||
|  |         name: {{ include "authentik.fullname" . }}-secret-key | ||||||
|  |         key: monitoring_username | ||||||
|  |     port: http | ||||||
|  |     path: /metrics/ | ||||||
|  |     interval: 10s | ||||||
|  |   selector: | ||||||
|  |     matchLabels: | ||||||
|  |       k8s.goauthentik.io/component: web | ||||||
|  | {{- end }} | ||||||
| @ -68,6 +68,15 @@ spec: | |||||||
|                 secretKeyRef: |                 secretKeyRef: | ||||||
|                   name: "{{ .Release.Name }}-postgresql" |                   name: "{{ .Release.Name }}-postgresql" | ||||||
|                   key: "postgresql-password" |                   key: "postgresql-password" | ||||||
|  |             {{ if .Values.geoip.enabled -}} | ||||||
|  |             - name: AUTHENTIK_AUTHENTIK__GEOIP | ||||||
|  |               value: /geoip/GeoLite2-City.mmdb | ||||||
|  |             {{- end }} | ||||||
|  |           {{ if .Values.geoip.enabled -}} | ||||||
|  |           volumeMounts: | ||||||
|  |             - name: geoip | ||||||
|  |               mountPath: /geoip | ||||||
|  |           {{- end }} | ||||||
|           resources: |           resources: | ||||||
|             requests: |             requests: | ||||||
|               cpu: 150m |               cpu: 150m | ||||||
| @ -75,3 +84,9 @@ spec: | |||||||
|             limits: |             limits: | ||||||
|               cpu: 300m |               cpu: 300m | ||||||
|               memory: 600M |               memory: 600M | ||||||
|  |       {{ if .Values.geoip.enabled -}} | ||||||
|  |       volumes: | ||||||
|  |         - name: geoip | ||||||
|  |           persistentVolumeClaim: | ||||||
|  |             claimName: {{ include "authentik.fullname" . }}-geoip | ||||||
|  |       {{- end -}} | ||||||
|  | |||||||
| @ -1,22 +0,0 @@ | |||||||
| image: |  | ||||||
|   tag: gh-master |  | ||||||
|   pullPolicy: Always |  | ||||||
|  |  | ||||||
| serverReplicas: 1 |  | ||||||
| workerReplicas: 1 |  | ||||||
|  |  | ||||||
| config: |  | ||||||
|   # Log level used by web and worker |  | ||||||
|   # Can be either debug, info, warning, error |  | ||||||
|   logLevel: debug |  | ||||||
|  |  | ||||||
| ingress: |  | ||||||
|   hosts: |  | ||||||
|     - authentik.127.0.0.1.nip.io |  | ||||||
|  |  | ||||||
| # These values influence the bundled postgresql and redis charts, but are also used by authentik to connect |  | ||||||
| postgresql: |  | ||||||
|   postgresqlPassword: EK-5jnKfjrGRm<77 |  | ||||||
|  |  | ||||||
| redis: |  | ||||||
|   password: password |  | ||||||
| @ -5,7 +5,7 @@ image: | |||||||
|   name: beryju/authentik |   name: beryju/authentik | ||||||
|   name_static: beryju/authentik-static |   name_static: beryju/authentik-static | ||||||
|   name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended |   name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended | ||||||
|   tag: 2021.3.3 |   tag: 2021.3.4 | ||||||
|   pullPolicy: IfNotPresent |   pullPolicy: IfNotPresent | ||||||
|  |  | ||||||
| serverReplicas: 1 | serverReplicas: 1 | ||||||
| @ -14,6 +14,9 @@ workerReplicas: 1 | |||||||
| # Enable the Kubernetes integration which lets authentik deploy outposts into kubernetes | # Enable the Kubernetes integration which lets authentik deploy outposts into kubernetes | ||||||
| kubernetesIntegration: true | kubernetesIntegration: true | ||||||
|  |  | ||||||
|  | monitoring: | ||||||
|  |   enabled: true | ||||||
|  |  | ||||||
| config: | config: | ||||||
|   # Optionally specify fixed secret_key, otherwise generated automatically |   # Optionally specify fixed secret_key, otherwise generated automatically | ||||||
|   # secretKey: _k*@6h2u2@q-dku57hhgzb7tnx*ba9wodcb^s9g0j59@=y(@_o |   # secretKey: _k*@6h2u2@q-dku57hhgzb7tnx*ba9wodcb^s9g0j59@=y(@_o | ||||||
| @ -41,6 +44,13 @@ config: | |||||||
|     # Email address authentik will send from, should have a correct @domain |     # Email address authentik will send from, should have a correct @domain | ||||||
|     from: authentik@localhost |     from: authentik@localhost | ||||||
|  |  | ||||||
|  | # Enable MaxMind GeoIP | ||||||
|  | geoip: | ||||||
|  |   enabled: false | ||||||
|  |   accountId: "" | ||||||
|  |   licenseKey: "" | ||||||
|  |   image: maxmindinc/geoipupdate:latest | ||||||
|  |  | ||||||
| # Enable Database Backups to S3 | # Enable Database Backups to S3 | ||||||
| # backup: | # backup: | ||||||
| #   accessKey: access-key | #   accessKey: access-key | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ stages: | |||||||
|           - task: PublishPipelineArtifact@1 |           - task: PublishPipelineArtifact@1 | ||||||
|             inputs: |             inputs: | ||||||
|               targetPath: 'outpost/pkg/' |               targetPath: 'outpost/pkg/' | ||||||
|               artifact: 'swagger_client' |               artifact: 'go_swagger_client' | ||||||
|               publishLocation: 'pipeline' |               publishLocation: 'pipeline' | ||||||
|   - stage: lint |   - stage: lint | ||||||
|     jobs: |     jobs: | ||||||
| @ -51,7 +51,7 @@ stages: | |||||||
|           - task: DownloadPipelineArtifact@2 |           - task: DownloadPipelineArtifact@2 | ||||||
|             inputs: |             inputs: | ||||||
|               buildType: 'current' |               buildType: 'current' | ||||||
|               artifactName: 'swagger_client' |               artifactName: 'go_swagger_client' | ||||||
|               path: "outpost/pkg/" |               path: "outpost/pkg/" | ||||||
|           - task: CmdLine@2 |           - task: CmdLine@2 | ||||||
|             inputs: |             inputs: | ||||||
| @ -70,7 +70,7 @@ stages: | |||||||
|           - task: DownloadPipelineArtifact@2 |           - task: DownloadPipelineArtifact@2 | ||||||
|             inputs: |             inputs: | ||||||
|               buildType: 'current' |               buildType: 'current' | ||||||
|               artifactName: 'swagger_client' |               artifactName: 'go_swagger_client' | ||||||
|               path: "outpost/pkg/" |               path: "outpost/pkg/" | ||||||
|           - task: Go@0 |           - task: Go@0 | ||||||
|             inputs: |             inputs: | ||||||
| @ -89,7 +89,7 @@ stages: | |||||||
|           - task: DownloadPipelineArtifact@2 |           - task: DownloadPipelineArtifact@2 | ||||||
|             inputs: |             inputs: | ||||||
|               buildType: 'current' |               buildType: 'current' | ||||||
|               artifactName: 'swagger_client' |               artifactName: 'go_swagger_client' | ||||||
|               path: "outpost/pkg/" |               path: "outpost/pkg/" | ||||||
|           - task: Bash@3 |           - task: Bash@3 | ||||||
|             inputs: |             inputs: | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ require ( | |||||||
| 	github.com/pkg/errors v0.9.1 | 	github.com/pkg/errors v0.9.1 | ||||||
| 	github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect | 	github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect | ||||||
| 	github.com/recws-org/recws v1.2.1 | 	github.com/recws-org/recws v1.2.1 | ||||||
| 	github.com/sirupsen/logrus v1.8.0 | 	github.com/sirupsen/logrus v1.8.1 | ||||||
| 	github.com/spf13/afero v1.5.1 // indirect | 	github.com/spf13/afero v1.5.1 // indirect | ||||||
| 	github.com/spf13/cast v1.3.1 // indirect | 	github.com/spf13/cast v1.3.1 // indirect | ||||||
| 	github.com/spf13/jwalterweatherman v1.1.0 // indirect | 	github.com/spf13/jwalterweatherman v1.1.0 // indirect | ||||||
|  | |||||||
| @ -618,6 +618,8 @@ github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM | |||||||
| github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= | ||||||
| github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU= | github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU= | ||||||
| github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= | github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= | ||||||
|  | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= | ||||||
|  | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= | ||||||
| github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= | ||||||
| github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | ||||||
| github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| package pkg | package pkg | ||||||
|  |  | ||||||
| const VERSION = "2021.3.3" | const VERSION = "2021.3.4" | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| FROM golang:1.16.0 AS builder | FROM golang:1.16.2 AS builder | ||||||
|  |  | ||||||
| WORKDIR /work | WORKDIR /work | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										975
									
								
								swagger.yaml
									
									
									
									
									
								
							
							
						
						
									
										975
									
								
								swagger.yaml
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -93,7 +93,10 @@ class TestFlowsEnroll(SeleniumTestCase): | |||||||
|  |  | ||||||
|         self.initial_stages() |         self.initial_stages() | ||||||
|  |  | ||||||
|         self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-sidebar"))) |         interface_admin = self.get_shadow_root("ak-interface-admin") | ||||||
|  |         wait = WebDriverWait(interface_admin, self.wait_timeout) | ||||||
|  |  | ||||||
|  |         wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-sidebar"))) | ||||||
|         self.driver.get(self.shell_url("authentik_core:user-settings")) |         self.driver.get(self.shell_url("authentik_core:user-settings")) | ||||||
|  |  | ||||||
|         user = User.objects.get(username="foo") |         user = User.objects.get(username="foo") | ||||||
| @ -188,7 +191,11 @@ class TestFlowsEnroll(SeleniumTestCase): | |||||||
|         self.driver.switch_to.window(self.driver.window_handles[0]) |         self.driver.switch_to.window(self.driver.window_handles[0]) | ||||||
|  |  | ||||||
|         # We're now logged in |         # We're now logged in | ||||||
|         self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-sidebar"))) |         wait = WebDriverWait( | ||||||
|  |             self.get_shadow_root("ak-interface-admin"), self.wait_timeout | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-sidebar"))) | ||||||
|         self.driver.get(self.shell_url("authentik_core:user-settings")) |         self.driver.get(self.shell_url("authentik_core:user-settings")) | ||||||
|  |  | ||||||
|         self.assert_user(User.objects.get(username="foo")) |         self.assert_user(User.objects.get(username="foo")) | ||||||
|  | |||||||
							
								
								
									
										273
									
								
								tests/e2e/test_provider_oauth2_oidc_implicit.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								tests/e2e/test_provider_oauth2_oidc_implicit.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,273 @@ | |||||||
|  | """test OAuth2 OpenID Provider flow""" | ||||||
|  | from json import loads | ||||||
|  | from sys import platform | ||||||
|  | from time import sleep | ||||||
|  | from unittest.case import skipUnless | ||||||
|  |  | ||||||
|  | from docker import DockerClient, from_env | ||||||
|  | from docker.models.containers import Container | ||||||
|  | from docker.types import Healthcheck | ||||||
|  | from selenium.webdriver.common.by import By | ||||||
|  | from selenium.webdriver.support import expected_conditions as ec | ||||||
|  | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
|  | from authentik.core.models import Application | ||||||
|  | from authentik.crypto.models import CertificateKeyPair | ||||||
|  | from authentik.flows.models import Flow | ||||||
|  | from authentik.policies.expression.models import ExpressionPolicy | ||||||
|  | from authentik.policies.models import PolicyBinding | ||||||
|  | from authentik.providers.oauth2.constants import ( | ||||||
|  |     SCOPE_OPENID, | ||||||
|  |     SCOPE_OPENID_EMAIL, | ||||||
|  |     SCOPE_OPENID_PROFILE, | ||||||
|  | ) | ||||||
|  | from authentik.providers.oauth2.generators import ( | ||||||
|  |     generate_client_id, | ||||||
|  |     generate_client_secret, | ||||||
|  | ) | ||||||
|  | from authentik.providers.oauth2.models import ClientTypes, OAuth2Provider, ScopeMapping | ||||||
|  | from tests.e2e.utils import ( | ||||||
|  |     USER, | ||||||
|  |     SeleniumTestCase, | ||||||
|  |     apply_migration, | ||||||
|  |     object_manager, | ||||||
|  |     retry, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | LOGGER = get_logger() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @skipUnless(platform.startswith("linux"), "requires local docker") | ||||||
|  | class TestProviderOAuth2OIDCImplicit(SeleniumTestCase): | ||||||
|  |     """test OAuth with OpenID Provider flow""" | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         self.client_id = generate_client_id() | ||||||
|  |         self.client_secret = generate_client_secret() | ||||||
|  |         self.application_slug = "test" | ||||||
|  |         super().setUp() | ||||||
|  |  | ||||||
|  |     def setup_client(self) -> Container: | ||||||
|  |         """Setup client saml-sp container which we test SAML against""" | ||||||
|  |         sleep(1) | ||||||
|  |         client: DockerClient = from_env() | ||||||
|  |         container = client.containers.run( | ||||||
|  |             image="beryju/oidc-test-client", | ||||||
|  |             detach=True, | ||||||
|  |             network_mode="host", | ||||||
|  |             auto_remove=True, | ||||||
|  |             healthcheck=Healthcheck( | ||||||
|  |                 test=["CMD", "wget", "--spider", "http://localhost:9009/health"], | ||||||
|  |                 interval=5 * 100 * 1000000, | ||||||
|  |                 start_period=1 * 100 * 1000000, | ||||||
|  |             ), | ||||||
|  |             environment={ | ||||||
|  |                 "OIDC_CLIENT_ID": self.client_id, | ||||||
|  |                 "OIDC_CLIENT_SECRET": self.client_secret, | ||||||
|  |                 "OIDC_PROVIDER": f"{self.live_server_url}/application/o/{self.application_slug}/", | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |         while True: | ||||||
|  |             container.reload() | ||||||
|  |             status = container.attrs.get("State", {}).get("Health", {}).get("Status") | ||||||
|  |             if status == "healthy": | ||||||
|  |                 return container | ||||||
|  |             LOGGER.info("Container failed healthcheck") | ||||||
|  |             sleep(1) | ||||||
|  |  | ||||||
|  |     @retry() | ||||||
|  |     @apply_migration("authentik_core", "0003_default_user") | ||||||
|  |     @apply_migration("authentik_flows", "0008_default_flows") | ||||||
|  |     @apply_migration("authentik_flows", "0010_provider_flows") | ||||||
|  |     @apply_migration("authentik_crypto", "0002_create_self_signed_kp") | ||||||
|  |     def test_redirect_uri_error(self): | ||||||
|  |         """test OpenID Provider flow (invalid redirect URI, check error message)""" | ||||||
|  |         sleep(1) | ||||||
|  |         # Bootstrap all needed objects | ||||||
|  |         authorization_flow = Flow.objects.get( | ||||||
|  |             slug="default-provider-authorization-implicit-consent" | ||||||
|  |         ) | ||||||
|  |         provider = OAuth2Provider.objects.create( | ||||||
|  |             name=self.application_slug, | ||||||
|  |             client_type=ClientTypes.CONFIDENTIAL, | ||||||
|  |             client_id=self.client_id, | ||||||
|  |             client_secret=self.client_secret, | ||||||
|  |             rsa_key=CertificateKeyPair.objects.first(), | ||||||
|  |             redirect_uris="http://localhost:9009/", | ||||||
|  |             authorization_flow=authorization_flow, | ||||||
|  |         ) | ||||||
|  |         provider.property_mappings.set( | ||||||
|  |             ScopeMapping.objects.filter( | ||||||
|  |                 scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE] | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         provider.save() | ||||||
|  |         Application.objects.create( | ||||||
|  |             name=self.application_slug, | ||||||
|  |             slug=self.application_slug, | ||||||
|  |             provider=provider, | ||||||
|  |         ) | ||||||
|  |         self.container = self.setup_client() | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost:9009/implicit/") | ||||||
|  |         sleep(2) | ||||||
|  |         self.assertEqual( | ||||||
|  |             self.driver.find_element(By.CLASS_NAME, "pf-c-title").text, | ||||||
|  |             "Redirect URI Error", | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     @retry() | ||||||
|  |     @apply_migration("authentik_core", "0003_default_user") | ||||||
|  |     @apply_migration("authentik_flows", "0008_default_flows") | ||||||
|  |     @apply_migration("authentik_flows", "0010_provider_flows") | ||||||
|  |     @apply_migration("authentik_crypto", "0002_create_self_signed_kp") | ||||||
|  |     @object_manager | ||||||
|  |     def test_authorization_consent_implied(self): | ||||||
|  |         """test OpenID Provider flow (default authorization flow with implied consent)""" | ||||||
|  |         sleep(1) | ||||||
|  |         # Bootstrap all needed objects | ||||||
|  |         authorization_flow = Flow.objects.get( | ||||||
|  |             slug="default-provider-authorization-implicit-consent" | ||||||
|  |         ) | ||||||
|  |         provider = OAuth2Provider.objects.create( | ||||||
|  |             name=self.application_slug, | ||||||
|  |             client_type=ClientTypes.CONFIDENTIAL, | ||||||
|  |             client_id=self.client_id, | ||||||
|  |             client_secret=self.client_secret, | ||||||
|  |             rsa_key=CertificateKeyPair.objects.first(), | ||||||
|  |             redirect_uris="http://localhost:9009/implicit/", | ||||||
|  |             authorization_flow=authorization_flow, | ||||||
|  |         ) | ||||||
|  |         provider.property_mappings.set( | ||||||
|  |             ScopeMapping.objects.filter( | ||||||
|  |                 scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE] | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         provider.save() | ||||||
|  |         Application.objects.create( | ||||||
|  |             name=self.application_slug, | ||||||
|  |             slug=self.application_slug, | ||||||
|  |             provider=provider, | ||||||
|  |         ) | ||||||
|  |         self.container = self.setup_client() | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost:9009/implicit/") | ||||||
|  |         self.login() | ||||||
|  |         self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pre"))) | ||||||
|  |         sleep(1) | ||||||
|  |         body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text) | ||||||
|  |         print(body) | ||||||
|  |         self.assertEqual(body["profile"]["nickname"], USER().username) | ||||||
|  |         self.assertEqual(body["profile"]["name"], USER().name) | ||||||
|  |         self.assertEqual(body["profile"]["email"], USER().email) | ||||||
|  |  | ||||||
|  |     @retry() | ||||||
|  |     @apply_migration("authentik_core", "0003_default_user") | ||||||
|  |     @apply_migration("authentik_flows", "0008_default_flows") | ||||||
|  |     @apply_migration("authentik_flows", "0010_provider_flows") | ||||||
|  |     @apply_migration("authentik_crypto", "0002_create_self_signed_kp") | ||||||
|  |     @object_manager | ||||||
|  |     def test_authorization_consent_explicit(self): | ||||||
|  |         """test OpenID Provider flow (default authorization flow with explicit consent)""" | ||||||
|  |         sleep(1) | ||||||
|  |         # Bootstrap all needed objects | ||||||
|  |         authorization_flow = Flow.objects.get( | ||||||
|  |             slug="default-provider-authorization-explicit-consent" | ||||||
|  |         ) | ||||||
|  |         provider = OAuth2Provider.objects.create( | ||||||
|  |             name=self.application_slug, | ||||||
|  |             authorization_flow=authorization_flow, | ||||||
|  |             client_type=ClientTypes.CONFIDENTIAL, | ||||||
|  |             client_id=self.client_id, | ||||||
|  |             client_secret=self.client_secret, | ||||||
|  |             rsa_key=CertificateKeyPair.objects.first(), | ||||||
|  |             redirect_uris="http://localhost:9009/implicit/", | ||||||
|  |         ) | ||||||
|  |         provider.property_mappings.set( | ||||||
|  |             ScopeMapping.objects.filter( | ||||||
|  |                 scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE] | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         provider.save() | ||||||
|  |         app = Application.objects.create( | ||||||
|  |             name=self.application_slug, | ||||||
|  |             slug=self.application_slug, | ||||||
|  |             provider=provider, | ||||||
|  |         ) | ||||||
|  |         self.container = self.setup_client() | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost:9009/implicit/") | ||||||
|  |         self.login() | ||||||
|  |  | ||||||
|  |         self.wait.until( | ||||||
|  |             ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         flow_executor = self.get_shadow_root("ak-flow-executor") | ||||||
|  |         consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor) | ||||||
|  |  | ||||||
|  |         self.assertIn( | ||||||
|  |             app.name, | ||||||
|  |             consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text, | ||||||
|  |         ) | ||||||
|  |         consent_stage.find_element( | ||||||
|  |             By.CSS_SELECTOR, | ||||||
|  |             ("[type=submit]"), | ||||||
|  |         ).click() | ||||||
|  |  | ||||||
|  |         self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pre"))) | ||||||
|  |         sleep(1) | ||||||
|  |         body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text) | ||||||
|  |  | ||||||
|  |         self.assertEqual(body["profile"]["nickname"], USER().username) | ||||||
|  |         self.assertEqual(body["profile"]["name"], USER().name) | ||||||
|  |         self.assertEqual(body["profile"]["email"], USER().email) | ||||||
|  |  | ||||||
|  |     @retry() | ||||||
|  |     @apply_migration("authentik_core", "0003_default_user") | ||||||
|  |     @apply_migration("authentik_flows", "0008_default_flows") | ||||||
|  |     @apply_migration("authentik_flows", "0010_provider_flows") | ||||||
|  |     @apply_migration("authentik_crypto", "0002_create_self_signed_kp") | ||||||
|  |     def test_authorization_denied(self): | ||||||
|  |         """test OpenID Provider flow (default authorization with access deny)""" | ||||||
|  |         sleep(1) | ||||||
|  |         # Bootstrap all needed objects | ||||||
|  |         authorization_flow = Flow.objects.get( | ||||||
|  |             slug="default-provider-authorization-explicit-consent" | ||||||
|  |         ) | ||||||
|  |         provider = OAuth2Provider.objects.create( | ||||||
|  |             name=self.application_slug, | ||||||
|  |             authorization_flow=authorization_flow, | ||||||
|  |             client_type=ClientTypes.CONFIDENTIAL, | ||||||
|  |             client_id=self.client_id, | ||||||
|  |             client_secret=self.client_secret, | ||||||
|  |             rsa_key=CertificateKeyPair.objects.first(), | ||||||
|  |             redirect_uris="http://localhost:9009/implicit/", | ||||||
|  |         ) | ||||||
|  |         provider.property_mappings.set( | ||||||
|  |             ScopeMapping.objects.filter( | ||||||
|  |                 scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE] | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         provider.save() | ||||||
|  |         app = Application.objects.create( | ||||||
|  |             name=self.application_slug, | ||||||
|  |             slug=self.application_slug, | ||||||
|  |             provider=provider, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         negative_policy = ExpressionPolicy.objects.create( | ||||||
|  |             name="negative-static", expression="return False" | ||||||
|  |         ) | ||||||
|  |         PolicyBinding.objects.create(target=app, policy=negative_policy, order=0) | ||||||
|  |  | ||||||
|  |         self.container = self.setup_client() | ||||||
|  |         self.driver.get("http://localhost:9009/implicit/") | ||||||
|  |         self.login() | ||||||
|  |         self.wait.until( | ||||||
|  |             ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1")) | ||||||
|  |         ) | ||||||
|  |         self.assertEqual( | ||||||
|  |             self.driver.find_element(By.CSS_SELECTOR, "header > h1").text, | ||||||
|  |             "Permission denied", | ||||||
|  |         ) | ||||||
| @ -3,11 +3,13 @@ from shutil import rmtree | |||||||
| from tempfile import mkdtemp | from tempfile import mkdtemp | ||||||
| from time import sleep | from time import sleep | ||||||
|  |  | ||||||
|  | import yaml | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from docker import DockerClient, from_env | from docker import DockerClient, from_env | ||||||
| from docker.models.containers import Container | from docker.models.containers import Container | ||||||
| from docker.types.healthcheck import Healthcheck | from docker.types.healthcheck import Healthcheck | ||||||
|  |  | ||||||
|  | from authentik import __version__ | ||||||
| from authentik.crypto.models import CertificateKeyPair | from authentik.crypto.models import CertificateKeyPair | ||||||
| from authentik.flows.models import Flow | from authentik.flows.models import Flow | ||||||
| from authentik.outposts.apps import AuthentikOutpostConfig | from authentik.outposts.apps import AuthentikOutpostConfig | ||||||
| @ -93,3 +95,14 @@ class OutpostDockerTests(TestCase): | |||||||
|         controller = DockerController(self.outpost, self.service_connection) |         controller = DockerController(self.outpost, self.service_connection) | ||||||
|         controller.up() |         controller.up() | ||||||
|         controller.down() |         controller.down() | ||||||
|  |  | ||||||
|  |     def test_docker_static(self): | ||||||
|  |         """test that deployment requires update""" | ||||||
|  |         controller = DockerController(self.outpost, self.service_connection) | ||||||
|  |         manifest = controller.get_static_deployment() | ||||||
|  |         compose = yaml.load(manifest, Loader=yaml.SafeLoader) | ||||||
|  |         self.assertEqual(compose["version"], "3.5") | ||||||
|  |         self.assertEqual( | ||||||
|  |             compose["services"]["authentik_proxy"]["image"], | ||||||
|  |             f"beryju/authentik-proxy:{__version__}", | ||||||
|  |         ) | ||||||
|  | |||||||
							
								
								
									
										108
									
								
								tests/integration/test_proxy_docker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								tests/integration/test_proxy_docker.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | """outpost tests""" | ||||||
|  | from shutil import rmtree | ||||||
|  | from tempfile import mkdtemp | ||||||
|  | from time import sleep | ||||||
|  |  | ||||||
|  | import yaml | ||||||
|  | from django.test import TestCase | ||||||
|  | from docker import DockerClient, from_env | ||||||
|  | from docker.models.containers import Container | ||||||
|  | from docker.types.healthcheck import Healthcheck | ||||||
|  |  | ||||||
|  | from authentik import __version__ | ||||||
|  | from authentik.crypto.models import CertificateKeyPair | ||||||
|  | from authentik.flows.models import Flow | ||||||
|  | from authentik.outposts.apps import AuthentikOutpostConfig | ||||||
|  | from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType | ||||||
|  | from authentik.providers.proxy.controllers.docker import DockerController | ||||||
|  | from authentik.providers.proxy.models import ProxyProvider | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestProxyDocker(TestCase): | ||||||
|  |     """Test Docker Controllers""" | ||||||
|  |  | ||||||
|  |     def _start_container(self, ssl_folder: str) -> Container: | ||||||
|  |         client: DockerClient = from_env() | ||||||
|  |         container = client.containers.run( | ||||||
|  |             image="library/docker:dind", | ||||||
|  |             detach=True, | ||||||
|  |             network_mode="host", | ||||||
|  |             remove=True, | ||||||
|  |             privileged=True, | ||||||
|  |             healthcheck=Healthcheck( | ||||||
|  |                 test=["CMD", "docker", "info"], | ||||||
|  |                 interval=5 * 100 * 1000000, | ||||||
|  |                 start_period=5 * 100 * 1000000, | ||||||
|  |             ), | ||||||
|  |             environment={"DOCKER_TLS_CERTDIR": "/ssl"}, | ||||||
|  |             volumes={ | ||||||
|  |                 f"{ssl_folder}/": { | ||||||
|  |                     "bind": "/ssl", | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |         while True: | ||||||
|  |             container.reload() | ||||||
|  |             status = container.attrs.get("State", {}).get("Health", {}).get("Status") | ||||||
|  |             if status == "healthy": | ||||||
|  |                 return container | ||||||
|  |             sleep(1) | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         super().setUp() | ||||||
|  |         self.ssl_folder = mkdtemp() | ||||||
|  |         self.container = self._start_container(self.ssl_folder) | ||||||
|  |         # Ensure that local connection have been created | ||||||
|  |         AuthentikOutpostConfig.init_local_connection() | ||||||
|  |         self.provider: ProxyProvider = ProxyProvider.objects.create( | ||||||
|  |             name="test", | ||||||
|  |             internal_host="http://localhost", | ||||||
|  |             external_host="http://localhost", | ||||||
|  |             authorization_flow=Flow.objects.first(), | ||||||
|  |         ) | ||||||
|  |         authentication_kp = CertificateKeyPair.objects.create( | ||||||
|  |             name="docker-authentication", | ||||||
|  |             certificate_data=open(f"{self.ssl_folder}/client/cert.pem").read(), | ||||||
|  |             key_data=open(f"{self.ssl_folder}/client/key.pem").read(), | ||||||
|  |         ) | ||||||
|  |         verification_kp = CertificateKeyPair.objects.create( | ||||||
|  |             name="docker-verification", | ||||||
|  |             certificate_data=open(f"{self.ssl_folder}/client/ca.pem").read(), | ||||||
|  |         ) | ||||||
|  |         self.service_connection = DockerServiceConnection.objects.create( | ||||||
|  |             url="https://localhost:2376", | ||||||
|  |             tls_verification=verification_kp, | ||||||
|  |             tls_authentication=authentication_kp, | ||||||
|  |         ) | ||||||
|  |         self.outpost: Outpost = Outpost.objects.create( | ||||||
|  |             name="test", | ||||||
|  |             type=OutpostType.PROXY, | ||||||
|  |             service_connection=self.service_connection, | ||||||
|  |         ) | ||||||
|  |         self.outpost.providers.add(self.provider) | ||||||
|  |         self.outpost.save() | ||||||
|  |  | ||||||
|  |     def tearDown(self) -> None: | ||||||
|  |         super().tearDown() | ||||||
|  |         self.container.kill() | ||||||
|  |         try: | ||||||
|  |             rmtree(self.ssl_folder) | ||||||
|  |         except PermissionError: | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |     def test_docker_controller(self): | ||||||
|  |         """test that deployment requires update""" | ||||||
|  |         controller = DockerController(self.outpost, self.service_connection) | ||||||
|  |         controller.up() | ||||||
|  |         controller.down() | ||||||
|  |  | ||||||
|  |     def test_docker_static(self): | ||||||
|  |         """test that deployment requires update""" | ||||||
|  |         controller = DockerController(self.outpost, self.service_connection) | ||||||
|  |         manifest = controller.get_static_deployment() | ||||||
|  |         compose = yaml.load(manifest, Loader=yaml.SafeLoader) | ||||||
|  |         self.assertEqual(compose["version"], "3.5") | ||||||
|  |         self.assertEqual( | ||||||
|  |             compose["services"]["authentik_proxy"]["image"], | ||||||
|  |             f"beryju/authentik-proxy:{__version__}", | ||||||
|  |         ) | ||||||
| @ -9,7 +9,7 @@ from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesCont | |||||||
| from authentik.providers.proxy.models import ProxyProvider | from authentik.providers.proxy.models import ProxyProvider | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestControllers(TestCase): | class TestProxyKubernetes(TestCase): | ||||||
|     """Test Controllers""" |     """Test Controllers""" | ||||||
|  |  | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|  | |||||||
| @ -4,3 +4,8 @@ node_modules | |||||||
| dist | dist | ||||||
| # don't lint nyc coverage output | # don't lint nyc coverage output | ||||||
| coverage | coverage | ||||||
|  | # don't lint generated code | ||||||
|  | src/api/apis | ||||||
|  | src/api/models | ||||||
|  | src/api/index.ts | ||||||
|  | src/api/runtime.ts | ||||||
|  | |||||||
| @ -10,6 +10,25 @@ variables: | |||||||
|     branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }} |     branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }} | ||||||
|  |  | ||||||
| stages: | stages: | ||||||
|  |   - stage: generate | ||||||
|  |     jobs: | ||||||
|  |       - job: swagger_generate | ||||||
|  |         pool: | ||||||
|  |           vmImage: 'ubuntu-latest' | ||||||
|  |         steps: | ||||||
|  |           - task: NodeTool@0 | ||||||
|  |             inputs: | ||||||
|  |               versionSpec: '12.x' | ||||||
|  |             displayName: 'Install Node.js' | ||||||
|  |           - task: CmdLine@2 | ||||||
|  |             inputs: | ||||||
|  |               script: | | ||||||
|  |                 docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/src/api --additional-properties=typescriptThreePlus=true | ||||||
|  |           - task: PublishPipelineArtifact@1 | ||||||
|  |             inputs: | ||||||
|  |               targetPath: 'web/src/api/' | ||||||
|  |               artifact: 'ts_swagger_client' | ||||||
|  |               publishLocation: 'pipeline' | ||||||
|   - stage: lint |   - stage: lint | ||||||
|     jobs: |     jobs: | ||||||
|       - job: eslint |       - job: eslint | ||||||
| @ -20,6 +39,11 @@ stages: | |||||||
|             inputs: |             inputs: | ||||||
|               versionSpec: '12.x' |               versionSpec: '12.x' | ||||||
|             displayName: 'Install Node.js' |             displayName: 'Install Node.js' | ||||||
|  |           - task: DownloadPipelineArtifact@2 | ||||||
|  |             inputs: | ||||||
|  |               buildType: 'current' | ||||||
|  |               artifactName: 'ts_swagger_client' | ||||||
|  |               path: "web/src/api/" | ||||||
|           - task: Npm@1 |           - task: Npm@1 | ||||||
|             inputs: |             inputs: | ||||||
|               command: 'install' |               command: 'install' | ||||||
| @ -37,6 +61,11 @@ stages: | |||||||
|             inputs: |             inputs: | ||||||
|               versionSpec: '12.x' |               versionSpec: '12.x' | ||||||
|             displayName: 'Install Node.js' |             displayName: 'Install Node.js' | ||||||
|  |           - task: DownloadPipelineArtifact@2 | ||||||
|  |             inputs: | ||||||
|  |               buildType: 'current' | ||||||
|  |               artifactName: 'ts_swagger_client' | ||||||
|  |               path: "web/src/api/" | ||||||
|           - task: Npm@1 |           - task: Npm@1 | ||||||
|             inputs: |             inputs: | ||||||
|               command: 'install' |               command: 'install' | ||||||
| @ -56,6 +85,11 @@ stages: | |||||||
|             inputs: |             inputs: | ||||||
|               versionSpec: '12.x' |               versionSpec: '12.x' | ||||||
|             displayName: 'Install Node.js' |             displayName: 'Install Node.js' | ||||||
|  |           - task: DownloadPipelineArtifact@2 | ||||||
|  |             inputs: | ||||||
|  |               buildType: 'current' | ||||||
|  |               artifactName: 'ts_swagger_client' | ||||||
|  |               path: "web/src/api/" | ||||||
|           - task: Npm@1 |           - task: Npm@1 | ||||||
|             inputs: |             inputs: | ||||||
|               command: 'install' |               command: 'install' | ||||||
| @ -71,16 +105,21 @@ stages: | |||||||
|         pool: |         pool: | ||||||
|           vmImage: 'ubuntu-latest' |           vmImage: 'ubuntu-latest' | ||||||
|         steps: |         steps: | ||||||
|         - task: Bash@3 |           - task: DownloadPipelineArtifact@2 | ||||||
|           inputs: |             inputs: | ||||||
|             targetType: 'inline' |               buildType: 'current' | ||||||
|             script: | |               artifactName: 'ts_swagger_client' | ||||||
|               python ./scripts/az_do_set_branch.py |               path: "web/src/api/" | ||||||
|         - task: Docker@2 |           - task: Bash@3 | ||||||
|           inputs: |             inputs: | ||||||
|             containerRegistry: 'beryjuorg-harbor' |               targetType: 'inline' | ||||||
|             repository: 'authentik/static' |               script: | | ||||||
|             command: 'buildAndPush' |                 python ./scripts/az_do_set_branch.py | ||||||
|             Dockerfile: 'web/Dockerfile' |           - task: Docker@2 | ||||||
|             tags: "gh-$(branchName)" |             inputs: | ||||||
|             buildContext: 'web/' |               containerRegistry: 'beryjuorg-harbor' | ||||||
|  |               repository: 'authentik/static' | ||||||
|  |               command: 'buildAndPush' | ||||||
|  |               Dockerfile: 'web/Dockerfile' | ||||||
|  |               tags: "gh-$(branchName)" | ||||||
|  |               buildContext: 'web/' | ||||||
|  | |||||||
							
								
								
									
										406
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										406
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -56,6 +56,15 @@ | |||||||
|                 "strip-json-comments": "^3.1.1" |                 "strip-json-comments": "^3.1.1" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|  |                 "globals": { | ||||||
|  |                     "version": "12.4.0", | ||||||
|  |                     "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", | ||||||
|  |                     "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", | ||||||
|  |                     "dev": true, | ||||||
|  |                     "requires": { | ||||||
|  |                         "type-fest": "^0.8.1" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|                 "ignore": { |                 "ignore": { | ||||||
|                     "version": "4.0.6", |                     "version": "4.0.6", | ||||||
|                     "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", |                     "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", | ||||||
| @ -103,9 +112,9 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@patternfly/patternfly": { |         "@patternfly/patternfly": { | ||||||
|             "version": "4.87.3", |             "version": "4.90.5", | ||||||
|             "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.87.3.tgz", |             "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.90.5.tgz", | ||||||
|             "integrity": "sha512-hDNMPa7B1zKD8LWFZO4SS5hC/N+yvuci2sAn8HJd+EIbAvbMAUkRsyZ0/XO3BG3RVtpSlgq7q8x1pAHC/FTFuA==" |             "integrity": "sha512-Fe0C8UkzSjtacQ+fHXlFB/LHzrv/c2K4z479C6dboOgkGQE1FyB0wt1NBfxij0D++rhOy04OOYdE+Tr0JSlZKw==" | ||||||
|         }, |         }, | ||||||
|         "@rollup/plugin-typescript": { |         "@rollup/plugin-typescript": { | ||||||
|             "version": "8.2.0", |             "version": "8.2.0", | ||||||
| @ -143,27 +152,27 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/browser": { |         "@sentry/browser": { | ||||||
|             "version": "6.2.1", |             "version": "6.2.2", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.2.1.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.2.2.tgz", | ||||||
|             "integrity": "sha512-OAikFZ9EimD3noxMp8tA6Cf6qJcQ2U8k5QSgTPwdx+09nZOGJzbRFteK7WWmrS93ZJdzN61lpSQbg5v+bmmfbQ==", |             "integrity": "sha512-K5UGyEePtVPZIFMoiRafhd4Ov0M1kdozVsVKIPZrOpJyjQdPNX+fYDNL/h0nVmgOlE2S/uu4fl4mEfe/6aLShw==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/core": "6.2.1", |                 "@sentry/core": "6.2.2", | ||||||
|                 "@sentry/types": "6.2.1", |                 "@sentry/types": "6.2.2", | ||||||
|                 "@sentry/utils": "6.2.1", |                 "@sentry/utils": "6.2.2", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@sentry/types": { |                 "@sentry/types": { | ||||||
|                     "version": "6.2.1", |                     "version": "6.2.2", | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.1.tgz", |                     "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.2.tgz", | ||||||
|                     "integrity": "sha512-h0OV1QT+fv5ojfK5/+iEXClu33HirmvbjcQC2jf05IHj9yXIOWy6EB10S8nBjuLiiFqQiAQYj3FN9Ip4eN8NJA==" |                     "integrity": "sha512-Y/1sRtw3a5JU4YdNBig8lLSVJ1UdYtuge+QP1CVLcLSAbq07Ok1bvF+Z+BlNcnHqle2Fl8aKuryG5Yu86enOyQ==" | ||||||
|                 }, |                 }, | ||||||
|                 "@sentry/utils": { |                 "@sentry/utils": { | ||||||
|                     "version": "6.2.1", |                     "version": "6.2.2", | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.1.tgz", |                     "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.2.tgz", | ||||||
|                     "integrity": "sha512-6kQgM/yBPdXu+3qbJnI6HBcWztN9QfiMkH++ZiKk4ERhg9d2LYWlze478uTU5Fyo/JQYcp+McpjtjpR9QIrr0g==", |                     "integrity": "sha512-qaee6X6VDNZ8HeO83/veaKw0KuhDE7j1R+Yryme3PywFzsoTzutDrEQjb7gvcHAhBaAYX8IHUBHgxcFI9BxI+w==", | ||||||
|                     "requires": { |                     "requires": { | ||||||
|                         "@sentry/types": "6.2.1", |                         "@sentry/types": "6.2.2", | ||||||
|                         "tslib": "^1.9.3" |                         "tslib": "^1.9.3" | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
| @ -175,48 +184,48 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/core": { |         "@sentry/core": { | ||||||
|             "version": "6.2.1", |             "version": "6.2.2", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.2.1.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.2.2.tgz", | ||||||
|             "integrity": "sha512-jPqQEtafxxDtLONhCbTHh/Uq8mZRhsfbwJTSVYfPVEe/ELfFZLQK7tP6rOh7zEWKbTkE0mE6XcaoH3ZRAhgrqg==", |             "integrity": "sha512-qqWbvvXtymfXh7N5eEvk97MCnMURuyFIgqWdVD4MQM6yIfDCy36CyGfuQ3ViHTLZGdIfEOhLL9/f4kzf1RzqBA==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/hub": "6.2.1", |                 "@sentry/hub": "6.2.2", | ||||||
|                 "@sentry/minimal": "6.2.1", |                 "@sentry/minimal": "6.2.2", | ||||||
|                 "@sentry/types": "6.2.1", |                 "@sentry/types": "6.2.2", | ||||||
|                 "@sentry/utils": "6.2.1", |                 "@sentry/utils": "6.2.2", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@sentry/hub": { |                 "@sentry/hub": { | ||||||
|                     "version": "6.2.1", |                     "version": "6.2.2", | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.1.tgz", |                     "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.2.tgz", | ||||||
|                     "integrity": "sha512-pG7wCQeRpzeP6t0bT4T0X029R19dbDS3/qswF8BL6bg0AI3afjfjBAZm/fqn1Uwe/uBoMHVVdbxgJDZeQ5d4rQ==", |                     "integrity": "sha512-VR6uQGRYt6RP633FHShlSLj0LUKGVrlTeSlwCoooWM5FR9lmi6akAaweuxpG78/kZvXrAWpjX6/nuYwHKGwzGA==", | ||||||
|                     "requires": { |                     "requires": { | ||||||
|                         "@sentry/types": "6.2.1", |                         "@sentry/types": "6.2.2", | ||||||
|                         "@sentry/utils": "6.2.1", |                         "@sentry/utils": "6.2.2", | ||||||
|                         "tslib": "^1.9.3" |                         "tslib": "^1.9.3" | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
|                 "@sentry/minimal": { |                 "@sentry/minimal": { | ||||||
|                     "version": "6.2.1", |                     "version": "6.2.2", | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.1.tgz", |                     "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.2.tgz", | ||||||
|                     "integrity": "sha512-wuSXB4Ayxv9rBEQ4pm7fnG4UU2ZPtPnnChoEfd4/mw1UthXSvmPFEn6O4pdo2G8fTkl8eqm6wT/Q7uIXMEmw+A==", |                     "integrity": "sha512-l0IgoGQgg1lTd4qDU8bQn25sbZBg8PwIHfuTLbGMlRr1flDXHOM1UXajWK/UKbAPelnU7M2JBSVzgl7PwjprzA==", | ||||||
|                     "requires": { |                     "requires": { | ||||||
|                         "@sentry/hub": "6.2.1", |                         "@sentry/hub": "6.2.2", | ||||||
|                         "@sentry/types": "6.2.1", |                         "@sentry/types": "6.2.2", | ||||||
|                         "tslib": "^1.9.3" |                         "tslib": "^1.9.3" | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
|                 "@sentry/types": { |                 "@sentry/types": { | ||||||
|                     "version": "6.2.1", |                     "version": "6.2.2", | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.1.tgz", |                     "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.2.tgz", | ||||||
|                     "integrity": "sha512-h0OV1QT+fv5ojfK5/+iEXClu33HirmvbjcQC2jf05IHj9yXIOWy6EB10S8nBjuLiiFqQiAQYj3FN9Ip4eN8NJA==" |                     "integrity": "sha512-Y/1sRtw3a5JU4YdNBig8lLSVJ1UdYtuge+QP1CVLcLSAbq07Ok1bvF+Z+BlNcnHqle2Fl8aKuryG5Yu86enOyQ==" | ||||||
|                 }, |                 }, | ||||||
|                 "@sentry/utils": { |                 "@sentry/utils": { | ||||||
|                     "version": "6.2.1", |                     "version": "6.2.2", | ||||||
|                     "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.1.tgz", |                     "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.2.tgz", | ||||||
|                     "integrity": "sha512-6kQgM/yBPdXu+3qbJnI6HBcWztN9QfiMkH++ZiKk4ERhg9d2LYWlze478uTU5Fyo/JQYcp+McpjtjpR9QIrr0g==", |                     "integrity": "sha512-qaee6X6VDNZ8HeO83/veaKw0KuhDE7j1R+Yryme3PywFzsoTzutDrEQjb7gvcHAhBaAYX8IHUBHgxcFI9BxI+w==", | ||||||
|                     "requires": { |                     "requires": { | ||||||
|                         "@sentry/types": "6.2.1", |                         "@sentry/types": "6.2.2", | ||||||
|                         "tslib": "^1.9.3" |                         "tslib": "^1.9.3" | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
| @ -228,12 +237,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/hub": { |         "@sentry/hub": { | ||||||
|             "version": "6.2.1", |             "version": "6.2.2", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.1.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.2.tgz", | ||||||
|             "integrity": "sha512-pG7wCQeRpzeP6t0bT4T0X029R19dbDS3/qswF8BL6bg0AI3afjfjBAZm/fqn1Uwe/uBoMHVVdbxgJDZeQ5d4rQ==", |             "integrity": "sha512-VR6uQGRYt6RP633FHShlSLj0LUKGVrlTeSlwCoooWM5FR9lmi6akAaweuxpG78/kZvXrAWpjX6/nuYwHKGwzGA==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/types": "6.2.1", |                 "@sentry/types": "6.2.2", | ||||||
|                 "@sentry/utils": "6.2.1", |                 "@sentry/utils": "6.2.2", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -245,12 +254,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/minimal": { |         "@sentry/minimal": { | ||||||
|             "version": "6.2.1", |             "version": "6.2.2", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.1.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.2.tgz", | ||||||
|             "integrity": "sha512-wuSXB4Ayxv9rBEQ4pm7fnG4UU2ZPtPnnChoEfd4/mw1UthXSvmPFEn6O4pdo2G8fTkl8eqm6wT/Q7uIXMEmw+A==", |             "integrity": "sha512-l0IgoGQgg1lTd4qDU8bQn25sbZBg8PwIHfuTLbGMlRr1flDXHOM1UXajWK/UKbAPelnU7M2JBSVzgl7PwjprzA==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/hub": "6.2.1", |                 "@sentry/hub": "6.2.2", | ||||||
|                 "@sentry/types": "6.2.1", |                 "@sentry/types": "6.2.2", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -262,14 +271,14 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/tracing": { |         "@sentry/tracing": { | ||||||
|             "version": "6.2.1", |             "version": "6.2.2", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.2.1.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.2.2.tgz", | ||||||
|             "integrity": "sha512-bvStY1SnL08wkSeVK3j9K5rivQQJdKFCPR2VYRFOCaUoleZ6ChPUnBvxQ/E2LXc0hk/y/wo1q4r5B0dfCCY+bQ==", |             "integrity": "sha512-mAkPoqtofNfka/u9rOVVDQPaEoTmr0AQh654g9ZqsaqsOJLKjB4FDLVNubWs90fjeKqHiYkI3ZHPak2TzHBPkw==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/hub": "6.2.1", |                 "@sentry/hub": "6.2.2", | ||||||
|                 "@sentry/minimal": "6.2.1", |                 "@sentry/minimal": "6.2.2", | ||||||
|                 "@sentry/types": "6.2.1", |                 "@sentry/types": "6.2.2", | ||||||
|                 "@sentry/utils": "6.2.1", |                 "@sentry/utils": "6.2.2", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -281,16 +290,16 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/types": { |         "@sentry/types": { | ||||||
|             "version": "6.2.1", |             "version": "6.2.2", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.1.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.2.tgz", | ||||||
|             "integrity": "sha512-h0OV1QT+fv5ojfK5/+iEXClu33HirmvbjcQC2jf05IHj9yXIOWy6EB10S8nBjuLiiFqQiAQYj3FN9Ip4eN8NJA==" |             "integrity": "sha512-Y/1sRtw3a5JU4YdNBig8lLSVJ1UdYtuge+QP1CVLcLSAbq07Ok1bvF+Z+BlNcnHqle2Fl8aKuryG5Yu86enOyQ==" | ||||||
|         }, |         }, | ||||||
|         "@sentry/utils": { |         "@sentry/utils": { | ||||||
|             "version": "6.2.1", |             "version": "6.2.2", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.1.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.2.tgz", | ||||||
|             "integrity": "sha512-6kQgM/yBPdXu+3qbJnI6HBcWztN9QfiMkH++ZiKk4ERhg9d2LYWlze478uTU5Fyo/JQYcp+McpjtjpR9QIrr0g==", |             "integrity": "sha512-qaee6X6VDNZ8HeO83/veaKw0KuhDE7j1R+Yryme3PywFzsoTzutDrEQjb7gvcHAhBaAYX8IHUBHgxcFI9BxI+w==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/types": "6.2.1", |                 "@sentry/types": "6.2.2", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -310,12 +319,13 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@types/clean-css": { |         "@types/clean-css": { | ||||||
|             "version": "4.2.2", |             "version": "4.2.3", | ||||||
|             "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.2.tgz", |             "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.3.tgz", | ||||||
|             "integrity": "sha512-xiTJn3bmDh1lA8c6iVJs4ZhHw+pcmxXlJQXOB6G1oULaak8rmarIeFKI4aTJ7849dEhaO612wgIualZfbxTJwA==", |             "integrity": "sha512-ET0ldU/vpXecy5vO8JRIhtJWSrk1vzXdJcp3Bjf8bARZynl6vfkhEKY/A7njfNIRlmyTGuVFuqnD6I3tOGdXpQ==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@types/node": "*" |                 "@types/node": "*", | ||||||
|  |                 "source-map": "^0.6.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@types/codemirror": { |         "@types/codemirror": { | ||||||
| @ -404,80 +414,96 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@types/uglify-js": { |         "@types/uglify-js": { | ||||||
|             "version": "3.11.0", |             "version": "3.13.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.11.0.tgz", |             "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.0.tgz", | ||||||
|             "integrity": "sha512-I0Yd8TUELTbgRHq2K65j8rnDPAzAP+DiaF/syLem7yXwYLsHZhPd+AM2iXsWmf9P2F2NlFCgl5erZPQx9IbM9Q==", |             "integrity": "sha512-EGkrJD5Uy+Pg0NUR8uA4bJ5WMfljyad0G+784vLCNUkD+QwOJXUbBYExXfVGf7YtyzdQp3L/XMYcliB987kL5Q==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "source-map": "^0.6.1" |                 "source-map": "^0.6.1" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/eslint-plugin": { |         "@typescript-eslint/eslint-plugin": { | ||||||
|             "version": "4.16.1", |             "version": "4.18.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.16.1.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.18.0.tgz", | ||||||
|             "integrity": "sha512-SK777klBdlkUZpZLC1mPvyOWk9yAFCWmug13eAjVQ4/Q1LATE/NbcQL1xDHkptQkZOLnPmLUA1Y54m8dqYwnoQ==", |             "integrity": "sha512-Lzkc/2+7EoH7+NjIWLS2lVuKKqbEmJhtXe3rmfA8cyiKnZm3IfLf51irnBcmow8Q/AptVV0XBZmBJKuUJTe6cQ==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/experimental-utils": "4.16.1", |                 "@typescript-eslint/experimental-utils": "4.18.0", | ||||||
|                 "@typescript-eslint/scope-manager": "4.16.1", |                 "@typescript-eslint/scope-manager": "4.18.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", | ||||||
|                 "regexpp": "^3.0.0", |                 "regexpp": "^3.0.0", | ||||||
|                 "semver": "^7.3.2", |                 "semver": "^7.3.2", | ||||||
|                 "tsutils": "^3.17.1" |                 "tsutils": "^3.17.1" | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         "@typescript-eslint/experimental-utils": { |  | ||||||
|             "version": "4.16.1", |  | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.16.1.tgz", |  | ||||||
|             "integrity": "sha512-0Hm3LSlMYFK17jO4iY3un1Ve9x1zLNn4EM50Lia+0EV99NdbK+cn0er7HC7IvBA23mBg3P+8dUkMXy4leL33UQ==", |  | ||||||
|             "dev": true, |  | ||||||
|             "requires": { |  | ||||||
|                 "@types/json-schema": "^7.0.3", |  | ||||||
|                 "@typescript-eslint/scope-manager": "4.16.1", |  | ||||||
|                 "@typescript-eslint/types": "4.16.1", |  | ||||||
|                 "@typescript-eslint/typescript-estree": "4.16.1", |  | ||||||
|                 "eslint-scope": "^5.0.0", |  | ||||||
|                 "eslint-utils": "^2.0.0" |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         "@typescript-eslint/parser": { |  | ||||||
|             "version": "4.16.1", |  | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.16.1.tgz", |  | ||||||
|             "integrity": "sha512-/c0LEZcDL5y8RyI1zLcmZMvJrsR6SM1uetskFkoh3dvqDKVXPsXI+wFB/CbVw7WkEyyTKobC1mUNp/5y6gRvXg==", |  | ||||||
|             "dev": true, |  | ||||||
|             "requires": { |  | ||||||
|                 "@typescript-eslint/scope-manager": "4.16.1", |  | ||||||
|                 "@typescript-eslint/types": "4.16.1", |  | ||||||
|                 "@typescript-eslint/typescript-estree": "4.16.1", |  | ||||||
|                 "debug": "^4.1.1" |  | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@typescript-eslint/scope-manager": { |                 "@typescript-eslint/scope-manager": { | ||||||
|                     "version": "4.16.1", |                     "version": "4.18.0", | ||||||
|                     "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz", |                     "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz", | ||||||
|                     "integrity": "sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw==", |                     "integrity": "sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ==", | ||||||
|                     "dev": true, |                     "dev": true, | ||||||
|                     "requires": { |                     "requires": { | ||||||
|                         "@typescript-eslint/types": "4.16.1", |                         "@typescript-eslint/types": "4.18.0", | ||||||
|                         "@typescript-eslint/visitor-keys": "4.16.1" |                         "@typescript-eslint/visitor-keys": "4.18.0" | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
|                 "@typescript-eslint/types": { |                 "@typescript-eslint/types": { | ||||||
|                     "version": "4.16.1", |                     "version": "4.18.0", | ||||||
|                     "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.16.1.tgz", |                     "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.18.0.tgz", | ||||||
|                     "integrity": "sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA==", |                     "integrity": "sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A==", | ||||||
|  |                     "dev": true | ||||||
|  |                 }, | ||||||
|  |                 "@typescript-eslint/visitor-keys": { | ||||||
|  |                     "version": "4.18.0", | ||||||
|  |                     "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz", | ||||||
|  |                     "integrity": "sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw==", | ||||||
|  |                     "dev": true, | ||||||
|  |                     "requires": { | ||||||
|  |                         "@typescript-eslint/types": "4.18.0", | ||||||
|  |                         "eslint-visitor-keys": "^2.0.0" | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "@typescript-eslint/experimental-utils": { | ||||||
|  |             "version": "4.18.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.18.0.tgz", | ||||||
|  |             "integrity": "sha512-92h723Kblt9JcT2RRY3QS2xefFKar4ZQFVs3GityOKWQYgtajxt/tuXIzL7sVCUlM1hgreiV5gkGYyBpdOwO6A==", | ||||||
|  |             "dev": true, | ||||||
|  |             "requires": { | ||||||
|  |                 "@types/json-schema": "^7.0.3", | ||||||
|  |                 "@typescript-eslint/scope-manager": "4.18.0", | ||||||
|  |                 "@typescript-eslint/types": "4.18.0", | ||||||
|  |                 "@typescript-eslint/typescript-estree": "4.18.0", | ||||||
|  |                 "eslint-scope": "^5.0.0", | ||||||
|  |                 "eslint-utils": "^2.0.0" | ||||||
|  |             }, | ||||||
|  |             "dependencies": { | ||||||
|  |                 "@typescript-eslint/scope-manager": { | ||||||
|  |                     "version": "4.18.0", | ||||||
|  |                     "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz", | ||||||
|  |                     "integrity": "sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ==", | ||||||
|  |                     "dev": true, | ||||||
|  |                     "requires": { | ||||||
|  |                         "@typescript-eslint/types": "4.18.0", | ||||||
|  |                         "@typescript-eslint/visitor-keys": "4.18.0" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 "@typescript-eslint/types": { | ||||||
|  |                     "version": "4.18.0", | ||||||
|  |                     "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.18.0.tgz", | ||||||
|  |                     "integrity": "sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A==", | ||||||
|                     "dev": true |                     "dev": true | ||||||
|                 }, |                 }, | ||||||
|                 "@typescript-eslint/typescript-estree": { |                 "@typescript-eslint/typescript-estree": { | ||||||
|                     "version": "4.16.1", |                     "version": "4.18.0", | ||||||
|                     "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz", |                     "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz", | ||||||
|                     "integrity": "sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg==", |                     "integrity": "sha512-wt4xvF6vvJI7epz+rEqxmoNQ4ZADArGQO9gDU+cM0U5fdVv7N+IAuVoVAoZSOZxzGHBfvE3XQMLdy+scsqFfeg==", | ||||||
|                     "dev": true, |                     "dev": true, | ||||||
|                     "requires": { |                     "requires": { | ||||||
|                         "@typescript-eslint/types": "4.16.1", |                         "@typescript-eslint/types": "4.18.0", | ||||||
|                         "@typescript-eslint/visitor-keys": "4.16.1", |                         "@typescript-eslint/visitor-keys": "4.18.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", | ||||||
| @ -486,12 +512,12 @@ | |||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
|                 "@typescript-eslint/visitor-keys": { |                 "@typescript-eslint/visitor-keys": { | ||||||
|                     "version": "4.16.1", |                     "version": "4.18.0", | ||||||
|                     "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz", |                     "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz", | ||||||
|                     "integrity": "sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w==", |                     "integrity": "sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw==", | ||||||
|                     "dev": true, |                     "dev": true, | ||||||
|                     "requires": { |                     "requires": { | ||||||
|                         "@typescript-eslint/types": "4.16.1", |                         "@typescript-eslint/types": "4.18.0", | ||||||
|                         "eslint-visitor-keys": "^2.0.0" |                         "eslint-visitor-keys": "^2.0.0" | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
| @ -511,30 +537,42 @@ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/scope-manager": { |         "@typescript-eslint/parser": { | ||||||
|             "version": "4.16.1", |             "version": "4.18.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.18.0.tgz", | ||||||
|             "integrity": "sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw==", |             "integrity": "sha512-W3z5S0ZbecwX3PhJEAnq4mnjK5JJXvXUDBYIYGoweCyWyuvAKfGHvzmpUzgB5L4cRBb+cTu9U/ro66dx7dIimA==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/types": "4.16.1", |                 "@typescript-eslint/scope-manager": "4.18.0", | ||||||
|                 "@typescript-eslint/visitor-keys": "4.16.1" |                 "@typescript-eslint/types": "4.18.0", | ||||||
|  |                 "@typescript-eslint/typescript-estree": "4.18.0", | ||||||
|  |                 "debug": "^4.1.1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         "@typescript-eslint/scope-manager": { | ||||||
|  |             "version": "4.18.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz", | ||||||
|  |             "integrity": "sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ==", | ||||||
|  |             "dev": true, | ||||||
|  |             "requires": { | ||||||
|  |                 "@typescript-eslint/types": "4.18.0", | ||||||
|  |                 "@typescript-eslint/visitor-keys": "4.18.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/types": { |         "@typescript-eslint/types": { | ||||||
|             "version": "4.16.1", |             "version": "4.18.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.16.1.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.18.0.tgz", | ||||||
|             "integrity": "sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA==", |             "integrity": "sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A==", | ||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/typescript-estree": { |         "@typescript-eslint/typescript-estree": { | ||||||
|             "version": "4.16.1", |             "version": "4.18.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz", | ||||||
|             "integrity": "sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg==", |             "integrity": "sha512-wt4xvF6vvJI7epz+rEqxmoNQ4ZADArGQO9gDU+cM0U5fdVv7N+IAuVoVAoZSOZxzGHBfvE3XQMLdy+scsqFfeg==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/types": "4.16.1", |                 "@typescript-eslint/types": "4.18.0", | ||||||
|                 "@typescript-eslint/visitor-keys": "4.16.1", |                 "@typescript-eslint/visitor-keys": "4.18.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", | ||||||
| @ -559,12 +597,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/visitor-keys": { |         "@typescript-eslint/visitor-keys": { | ||||||
|             "version": "4.16.1", |             "version": "4.18.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz", | ||||||
|             "integrity": "sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w==", |             "integrity": "sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/types": "4.16.1", |                 "@typescript-eslint/types": "4.18.0", | ||||||
|                 "eslint-visitor-keys": "^2.0.0" |                 "eslint-visitor-keys": "^2.0.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
| @ -1103,9 +1141,9 @@ | |||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|         "eslint": { |         "eslint": { | ||||||
|             "version": "7.21.0", |             "version": "7.22.0", | ||||||
|             "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz", |             "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.22.0.tgz", | ||||||
|             "integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==", |             "integrity": "sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@babel/code-frame": "7.12.11", |                 "@babel/code-frame": "7.12.11", | ||||||
| @ -1125,7 +1163,7 @@ | |||||||
|                 "file-entry-cache": "^6.0.1", |                 "file-entry-cache": "^6.0.1", | ||||||
|                 "functional-red-black-tree": "^1.0.1", |                 "functional-red-black-tree": "^1.0.1", | ||||||
|                 "glob-parent": "^5.0.0", |                 "glob-parent": "^5.0.0", | ||||||
|                 "globals": "^12.1.0", |                 "globals": "^13.6.0", | ||||||
|                 "ignore": "^4.0.6", |                 "ignore": "^4.0.6", | ||||||
|                 "import-fresh": "^3.0.0", |                 "import-fresh": "^3.0.0", | ||||||
|                 "imurmurhash": "^0.1.4", |                 "imurmurhash": "^0.1.4", | ||||||
| @ -1133,7 +1171,7 @@ | |||||||
|                 "js-yaml": "^3.13.1", |                 "js-yaml": "^3.13.1", | ||||||
|                 "json-stable-stringify-without-jsonify": "^1.0.1", |                 "json-stable-stringify-without-jsonify": "^1.0.1", | ||||||
|                 "levn": "^0.4.1", |                 "levn": "^0.4.1", | ||||||
|                 "lodash": "^4.17.20", |                 "lodash": "^4.17.21", | ||||||
|                 "minimatch": "^3.0.4", |                 "minimatch": "^3.0.4", | ||||||
|                 "natural-compare": "^1.4.0", |                 "natural-compare": "^1.4.0", | ||||||
|                 "optionator": "^0.9.1", |                 "optionator": "^0.9.1", | ||||||
| @ -1202,6 +1240,12 @@ | |||||||
|                     "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", |                     "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", | ||||||
|                     "dev": true |                     "dev": true | ||||||
|                 }, |                 }, | ||||||
|  |                 "lodash": { | ||||||
|  |                     "version": "4.17.21", | ||||||
|  |                     "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", | ||||||
|  |                     "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", | ||||||
|  |                     "dev": true | ||||||
|  |                 }, | ||||||
|                 "supports-color": { |                 "supports-color": { | ||||||
|                     "version": "7.2.0", |                     "version": "7.2.0", | ||||||
|                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", |                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", | ||||||
| @ -1662,12 +1706,20 @@ | |||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|         "globals": { |         "globals": { | ||||||
|             "version": "12.4.0", |             "version": "13.6.0", | ||||||
|             "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", |             "resolved": "https://registry.npmjs.org/globals/-/globals-13.6.0.tgz", | ||||||
|             "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", |             "integrity": "sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "type-fest": "^0.8.1" |                 "type-fest": "^0.20.2" | ||||||
|  |             }, | ||||||
|  |             "dependencies": { | ||||||
|  |                 "type-fest": { | ||||||
|  |                     "version": "0.20.2", | ||||||
|  |                     "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", | ||||||
|  |                     "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", | ||||||
|  |                     "dev": true | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "globby": { |         "globby": { | ||||||
| @ -2280,16 +2332,16 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "minify-html-literals": { |         "minify-html-literals": { | ||||||
|             "version": "1.3.2", |             "version": "1.3.5", | ||||||
|             "resolved": "https://registry.npmjs.org/minify-html-literals/-/minify-html-literals-1.3.2.tgz", |             "resolved": "https://registry.npmjs.org/minify-html-literals/-/minify-html-literals-1.3.5.tgz", | ||||||
|             "integrity": "sha512-DBdi0md84vjvwmLoo9xleFV5FkhzOwfKBqcmoVFL54c9CFlSBtG9KTKEQqiwscB+acewculqys1cDnwyrYlNtg==", |             "integrity": "sha512-p8T8ryePRR8FVfJZLVFmM53WY25FL0moCCTycUDuAu6rf9GMLwy0gNjXBGNin3Yun7Y+tIWd28axOf0t2EpAlQ==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@types/html-minifier": "^3.5.3", |                 "@types/html-minifier": "^3.5.3", | ||||||
|                 "clean-css": "^4.2.1", |                 "clean-css": "^4.2.1", | ||||||
|                 "html-minifier": "^4.0.0", |                 "html-minifier": "^4.0.0", | ||||||
|                 "magic-string": "^0.25.0", |                 "magic-string": "^0.25.0", | ||||||
|                 "parse-literals": "^1.2.0" |                 "parse-literals": "^1.2.1" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "minimatch": { |         "minimatch": { | ||||||
| @ -2489,20 +2541,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "parse-literals": { |         "parse-literals": { | ||||||
|             "version": "1.2.0", |             "version": "1.2.1", | ||||||
|             "resolved": "https://registry.npmjs.org/parse-literals/-/parse-literals-1.2.0.tgz", |             "resolved": "https://registry.npmjs.org/parse-literals/-/parse-literals-1.2.1.tgz", | ||||||
|             "integrity": "sha512-gh4zPwvFSXx9ginX8lu9MP3OPHN3VV12PXI8IXD6oMCklFqM82pfbU9e/PKf9r7oLpbqlDSDyHYSVlxxuq3Iew==", |             "integrity": "sha512-Ml0w104Ph2wwzuRdxrg9booVWsngXbB4bZ5T2z6WyF8b5oaNkUmBiDtahi34yUIpXD8Y13JjAK6UyIyApJ73RQ==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "typescript": "^2.9.2 || ^3.0.0" |                 "typescript": "^2.9.2 || ^3.0.0 || ^4.0.0" | ||||||
|             }, |  | ||||||
|             "dependencies": { |  | ||||||
|                 "typescript": { |  | ||||||
|                     "version": "3.9.7", |  | ||||||
|                     "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", |  | ||||||
|                     "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", |  | ||||||
|                     "dev": true |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "parse5": { |         "parse5": { | ||||||
| @ -2717,9 +2761,9 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "rollup": { |         "rollup": { | ||||||
|             "version": "2.40.0", |             "version": "2.41.2", | ||||||
|             "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.40.0.tgz", |             "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.41.2.tgz", | ||||||
|             "integrity": "sha512-WiOGAPbXoHu+TOz6hyYUxIksOwsY/21TRWoO593jgYt8mvYafYqQl+axaA8y1z2HFazNUUrsMSjahV2A6/2R9A==", |             "integrity": "sha512-6u8fJJXJx6fmvKrAC9DHYZgONvSkz8S9b/VFBjoQ6dkKdHyPpPbpqiNl2Bao9XBzDHpq672X6sGZ9G1ZBqAHMg==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "fsevents": "~2.3.1" |                 "fsevents": "~2.3.1" | ||||||
|             } |             } | ||||||
| @ -2801,12 +2845,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "rollup-plugin-minify-html-literals": { |         "rollup-plugin-minify-html-literals": { | ||||||
|             "version": "1.2.5", |             "version": "1.2.6", | ||||||
|             "resolved": "https://registry.npmjs.org/rollup-plugin-minify-html-literals/-/rollup-plugin-minify-html-literals-1.2.5.tgz", |             "resolved": "https://registry.npmjs.org/rollup-plugin-minify-html-literals/-/rollup-plugin-minify-html-literals-1.2.6.tgz", | ||||||
|             "integrity": "sha512-x4FzCnbBpYdme7MQDS3+18CvYLqakAtM/JmA3hqXplwzMeZWW3l14KU7H33RhJlHH8Klgv49hGtBRLWLfjCudw==", |             "integrity": "sha512-JRq2fjlCTiw0zu+1Sy3ClHGCxA79dWGr4HLHWSQgd060StVW9fBVksuj8Xw/suPkNSGClJf/4xNQ1MF6JeXPaw==", | ||||||
|             "dev": true, |             "dev": true, | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "minify-html-literals": "^1.3.2", |                 "minify-html-literals": "^1.3.5", | ||||||
|                 "rollup-pluginutils": "^2.8.2" |                 "rollup-pluginutils": "^2.8.2" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
| @ -3246,9 +3290,9 @@ | |||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "ajv": { |                 "ajv": { | ||||||
|                     "version": "7.1.1", |                     "version": "7.2.1", | ||||||
|                     "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", |                     "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.1.tgz", | ||||||
|                     "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", |                     "integrity": "sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ==", | ||||||
|                     "dev": true, |                     "dev": true, | ||||||
|                     "requires": { |                     "requires": { | ||||||
|                         "fast-deep-equal": "^3.1.1", |                         "fast-deep-equal": "^3.1.1", | ||||||
| @ -3383,15 +3427,15 @@ | |||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|         "typescript": { |         "typescript": { | ||||||
|             "version": "4.2.2", |             "version": "4.2.3", | ||||||
|             "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.2.tgz", |             "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", | ||||||
|             "integrity": "sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ==", |             "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", | ||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|         "uglify-js": { |         "uglify-js": { | ||||||
|             "version": "3.11.0", |             "version": "3.13.0", | ||||||
|             "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.0.tgz", |             "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.0.tgz", | ||||||
|             "integrity": "sha512-e1KQFRCpOxnrJsJVqDUCjURq+wXvIn7cK2sRAx9XL3HYLL9aezOP4Pb1+Y3/o693EPk111Yj2Q+IUXxcpHlygQ==", |             "integrity": "sha512-TWYSWa9T2pPN4DIJYbU9oAjQx+5qdV5RUDxwARg8fmJZrD/V27Zj0JngW5xg1DFz42G0uDYl2XhzF6alSzD62w==", | ||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|         "union-value": { |         "union-value": { | ||||||
| @ -3479,9 +3523,9 @@ | |||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|         "v8-compile-cache": { |         "v8-compile-cache": { | ||||||
|             "version": "2.2.0", |             "version": "2.3.0", | ||||||
|             "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", |             "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", | ||||||
|             "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", |             "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", | ||||||
|             "dev": true |             "dev": true | ||||||
|         }, |         }, | ||||||
|         "vscode-css-languageservice": { |         "vscode-css-languageservice": { | ||||||
|  | |||||||
| @ -11,9 +11,9 @@ | |||||||
|     }, |     }, | ||||||
|     "dependencies": { |     "dependencies": { | ||||||
|         "@fortawesome/fontawesome-free": "^5.15.2", |         "@fortawesome/fontawesome-free": "^5.15.2", | ||||||
|         "@patternfly/patternfly": "^4.87.3", |         "@patternfly/patternfly": "^4.90.5", | ||||||
|         "@sentry/browser": "^6.2.1", |         "@sentry/browser": "^6.2.2", | ||||||
|         "@sentry/tracing": "^6.2.1", |         "@sentry/tracing": "^6.2.2", | ||||||
|         "@types/chart.js": "^2.9.31", |         "@types/chart.js": "^2.9.31", | ||||||
|         "@types/codemirror": "0.0.108", |         "@types/codemirror": "0.0.108", | ||||||
|         "@types/grecaptcha": "^3.0.1", |         "@types/grecaptcha": "^3.0.1", | ||||||
| @ -24,7 +24,7 @@ | |||||||
|         "flowchart.js": "^1.15.0", |         "flowchart.js": "^1.15.0", | ||||||
|         "lit-element": "^2.4.0", |         "lit-element": "^2.4.0", | ||||||
|         "lit-html": "^1.3.0", |         "lit-html": "^1.3.0", | ||||||
|         "rollup": "^2.40.0", |         "rollup": "^2.41.2", | ||||||
|         "rollup-plugin-copy": "^3.4.0", |         "rollup-plugin-copy": "^3.4.0", | ||||||
|         "rollup-plugin-cssimport": "^1.0.2", |         "rollup-plugin-cssimport": "^1.0.2", | ||||||
|         "rollup-plugin-external-globals": "^0.6.1", |         "rollup-plugin-external-globals": "^0.6.1", | ||||||
| @ -33,17 +33,17 @@ | |||||||
|     }, |     }, | ||||||
|     "devDependencies": { |     "devDependencies": { | ||||||
|         "@rollup/plugin-typescript": "^8.2.0", |         "@rollup/plugin-typescript": "^8.2.0", | ||||||
|         "@typescript-eslint/eslint-plugin": "^4.16.1", |         "@typescript-eslint/eslint-plugin": "^4.18.0", | ||||||
|         "@typescript-eslint/parser": "^4.16.1", |         "@typescript-eslint/parser": "^4.18.0", | ||||||
|         "eslint": "^7.21.0", |         "eslint": "^7.22.0", | ||||||
|         "eslint-config-google": "^0.14.0", |         "eslint-config-google": "^0.14.0", | ||||||
|         "eslint-plugin-lit": "^1.3.0", |         "eslint-plugin-lit": "^1.3.0", | ||||||
|         "rollup-plugin-commonjs": "^10.1.0", |         "rollup-plugin-commonjs": "^10.1.0", | ||||||
|         "rollup-plugin-minify-html-literals": "^1.2.5", |         "rollup-plugin-minify-html-literals": "^1.2.6", | ||||||
|         "rollup-plugin-node-resolve": "^5.2.0", |         "rollup-plugin-node-resolve": "^5.2.0", | ||||||
|         "rollup-plugin-sourcemaps": "^0.6.3", |         "rollup-plugin-sourcemaps": "^0.6.3", | ||||||
|         "rollup-plugin-terser": "^7.0.2", |         "rollup-plugin-terser": "^7.0.2", | ||||||
|         "ts-lit-plugin": "^1.2.1", |         "ts-lit-plugin": "^1.2.1", | ||||||
|         "typescript": "^4.2.2" |         "typescript": "^4.2.3" | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -8,16 +8,23 @@ import copy from "rollup-plugin-copy"; | |||||||
| import externalGlobals from "rollup-plugin-external-globals"; | import externalGlobals from "rollup-plugin-external-globals"; | ||||||
|  |  | ||||||
| const resources = [ | const resources = [ | ||||||
|     { src: "node_modules/@patternfly/patternfly/patternfly.css", dest: "dist/" }, |     { src: "node_modules/@patternfly/patternfly/patternfly-base.css", dest: "dist/" }, | ||||||
|     { src: "node_modules/@patternfly/patternfly/patternfly-addons.css", dest: "dist/" }, |  | ||||||
|     { src: "node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css", dest: "dist/" }, |  | ||||||
|     { src: "node_modules/@patternfly/patternfly/assets/*", dest: "dist/assets/" }, |     { src: "node_modules/@patternfly/patternfly/assets/*", dest: "dist/assets/" }, | ||||||
|     { src: "src/index.html", dest: "dist" }, |     { src: "src/index.html", dest: "dist" }, | ||||||
|     { src: "src/authentik.css", dest: "dist" }, |  | ||||||
|     { src: "src/assets/*", dest: "dist/assets" }, |     { src: "src/assets/*", dest: "dist/assets" }, | ||||||
|     { src: "./icons/*", dest: "dist/assets/icons" }, |     { src: "./icons/*", dest: "dist/assets/icons" }, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|  | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | ||||||
|  | function manualChunks(id) { | ||||||
|  |     if (id.includes("node_modules")) { | ||||||
|  |         return "vendor"; | ||||||
|  |     } | ||||||
|  |     if (id.includes("src/api/")) { | ||||||
|  |         return "api"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| export default [ | export default [ | ||||||
|     { |     { | ||||||
|         input: "./src/main.ts", |         input: "./src/main.ts", | ||||||
| @ -26,6 +33,7 @@ export default [ | |||||||
|                 format: "es", |                 format: "es", | ||||||
|                 dir: "dist", |                 dir: "dist", | ||||||
|                 sourcemap: true, |                 sourcemap: true, | ||||||
|  |                 manualChunks: manualChunks, | ||||||
|             }, |             }, | ||||||
|         ], |         ], | ||||||
|         plugins: [ |         plugins: [ | ||||||
| @ -55,6 +63,7 @@ export default [ | |||||||
|                 format: "es", |                 format: "es", | ||||||
|                 dir: "dist", |                 dir: "dist", | ||||||
|                 sourcemap: true, |                 sourcemap: true, | ||||||
|  |                 manualChunks: manualChunks, | ||||||
|             }, |             }, | ||||||
|         ], |         ], | ||||||
|         plugins: [ |         plugins: [ | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								web/src/api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								web/src/api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | apis/** | ||||||
|  | models/** | ||||||
|  | index.ts | ||||||
|  | runtime.ts | ||||||
|  | .openapi-generator/** | ||||||
							
								
								
									
										23
									
								
								web/src/api/.openapi-generator-ignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web/src/api/.openapi-generator-ignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | # OpenAPI Generator Ignore | ||||||
|  | # Generated by openapi-generator https://github.com/openapitools/openapi-generator | ||||||
|  |  | ||||||
|  | # Use this file to prevent files from being overwritten by the generator. | ||||||
|  | # The patterns follow closely to .gitignore or .dockerignore. | ||||||
|  |  | ||||||
|  | # As an example, the C# client generator defines ApiClient.cs. | ||||||
|  | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: | ||||||
|  | #ApiClient.cs | ||||||
|  |  | ||||||
|  | # You can match any string of characters against a directory, file or extension with a single asterisk (*): | ||||||
|  | #foo/*/qux | ||||||
|  | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux | ||||||
|  |  | ||||||
|  | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): | ||||||
|  | #foo/**/qux | ||||||
|  | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux | ||||||
|  |  | ||||||
|  | # You can also negate patterns with an exclamation (!). | ||||||
|  | # For example, you can ignore all files in a docs folder with the file extension .md: | ||||||
|  | #docs/*.md | ||||||
|  | # Then explicitly reverse the ignore rule for a single file: | ||||||
|  | #!docs/README.md | ||||||
| @ -1,32 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { Provider } from "./Providers"; |  | ||||||
|  |  | ||||||
| export class Application { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     slug: string; |  | ||||||
|     provider?: Provider; |  | ||||||
|  |  | ||||||
|     launch_url: string; |  | ||||||
|     meta_launch_url: string; |  | ||||||
|     meta_icon: string; |  | ||||||
|     meta_description: string; |  | ||||||
|     meta_publisher: string; |  | ||||||
|     policies: string[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<Application> { |  | ||||||
|         return DefaultClient.fetch<Application>(["core", "applications", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Application>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Application>>(["core", "applications"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/applications/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,26 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
|  |  | ||||||
| export class CertificateKeyPair { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     fingerprint: string; |  | ||||||
|     cert_expiry: number; |  | ||||||
|     cert_subject: string; |  | ||||||
|     private_key_available: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<CertificateKeyPair> { |  | ||||||
|         return DefaultClient.fetch<CertificateKeyPair>(["crypto", "certificatekeypairs", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<CertificateKeyPair>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<CertificateKeyPair>>(["crypto", "certificatekeypairs"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/crypto/certificates/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,10 +1,3 @@ | |||||||
| import { gettext } from "django"; |  | ||||||
| import { showMessage } from "../elements/messages/MessageContainer"; |  | ||||||
| import { getCookie } from "../utils"; |  | ||||||
| import { NotFoundError, RequestError } from "./Error"; |  | ||||||
|  |  | ||||||
| export const VERSION = "v2beta"; |  | ||||||
|  |  | ||||||
| export interface QueryArguments { | export interface QueryArguments { | ||||||
|     page?: number; |     page?: number; | ||||||
|     page_size?: number; |     page_size?: number; | ||||||
| @ -13,104 +6,27 @@ export interface QueryArguments { | |||||||
|  |  | ||||||
| export interface BaseInheritanceModel { | export interface BaseInheritanceModel { | ||||||
|  |  | ||||||
|     object_type: string; |     objectType: string; | ||||||
|  |  | ||||||
|     verbose_name: string; |     verboseName: string; | ||||||
|     verbose_name_plural: string; |     verboseNamePlural: string; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export class Client { | export interface AKPagination { | ||||||
|     makeUrl(url: string[], query?: QueryArguments): string { |  | ||||||
|         let builtUrl = `/api/${VERSION}/${url.join("/")}/`; |  | ||||||
|         if (query) { |  | ||||||
|             const queryString = Object.keys(query) |  | ||||||
|                 .filter((k) => query[k] !== null) |  | ||||||
|                 // we default to a string in query[k] as we've filtered out the null above |  | ||||||
|                 // this is just for type-hinting |  | ||||||
|                 .map((k) => encodeURIComponent(k) + "=" + encodeURIComponent(query[k] || "")) |  | ||||||
|                 .join("&"); |  | ||||||
|             builtUrl += `?${queryString}`; |  | ||||||
|         } |  | ||||||
|         return builtUrl; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fetch<T>(url: string[], query?: QueryArguments): Promise<T> { |  | ||||||
|         const finalUrl = this.makeUrl(url, query); |  | ||||||
|         return fetch(finalUrl) |  | ||||||
|             .then((r) => { |  | ||||||
|                 if (r.status > 300) { |  | ||||||
|                     switch (r.status) { |  | ||||||
|                     case 404: |  | ||||||
|                         throw new NotFoundError(`URL ${finalUrl} not found`); |  | ||||||
|                     default: |  | ||||||
|                         throw new RequestError(r.statusText); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 return r; |  | ||||||
|             }) |  | ||||||
|             .catch((e) => { |  | ||||||
|                 showMessage({ |  | ||||||
|                     level_tag: "error", |  | ||||||
|                     message: gettext(`Unexpected error while fetching: ${e.toString()}`), |  | ||||||
|                 }); |  | ||||||
|                 return e; |  | ||||||
|             }) |  | ||||||
|             .then((r) => r.json()) |  | ||||||
|             .then((r) => <T>r); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private writeRequest<T>(url: string[], body: T, method: string, query?: QueryArguments): Promise<T> { |  | ||||||
|         const finalUrl = this.makeUrl(url, query); |  | ||||||
|         const csrftoken = getCookie("authentik_csrf"); |  | ||||||
|         const request = new Request(finalUrl, { |  | ||||||
|             headers: { |  | ||||||
|                 "Accept": "application/json", |  | ||||||
|                 "Content-Type": "application/json", |  | ||||||
|                 "X-CSRFToken": csrftoken, |  | ||||||
|             }, |  | ||||||
|         }); |  | ||||||
|         return fetch(request, { |  | ||||||
|             method: method, |  | ||||||
|             mode: "same-origin", |  | ||||||
|             body: JSON.stringify(body), |  | ||||||
|         }) |  | ||||||
|             .then((r) => { |  | ||||||
|                 if (r.status > 300) { |  | ||||||
|                     switch (r.status) { |  | ||||||
|                     case 404: |  | ||||||
|                         throw new NotFoundError(`URL ${finalUrl} not found`); |  | ||||||
|                     default: |  | ||||||
|                         throw new RequestError(r.statusText); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 return r; |  | ||||||
|             }) |  | ||||||
|             .then((r) => r.json()) |  | ||||||
|             .then((r) => <T>r); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     update<T>(url: string[], body: T, query?: QueryArguments): Promise<T> { |  | ||||||
|         return this.writeRequest(url, body, "PATCH", query); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export const DefaultClient = new Client(); |  | ||||||
|  |  | ||||||
| export interface PBPagination { |  | ||||||
|     next?: number; |     next?: number; | ||||||
|     previous?: number; |     previous?: number; | ||||||
|  |  | ||||||
|     count: number; |     count: number; | ||||||
|     current: number; |     current: number; | ||||||
|     total_pages: number; |     totalPages: number; | ||||||
|  |  | ||||||
|     start_index: number; |     startIndex: number; | ||||||
|     end_index: number; |     endIndex: number; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface AKResponse<T> { | export interface AKResponse<T> { | ||||||
|     pagination: PBPagination; |     pagination: AKPagination; | ||||||
|  |  | ||||||
|     results: Array<T>; |     results: Array<T>; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,42 +1,39 @@ | |||||||
| import { DefaultClient } from "./Client"; |  | ||||||
| import * as Sentry from "@sentry/browser"; | import * as Sentry from "@sentry/browser"; | ||||||
| import { Integrations } from "@sentry/tracing"; | import { Integrations } from "@sentry/tracing"; | ||||||
| import { VERSION } from "../constants"; | import { VERSION } from "../constants"; | ||||||
| import { SentryIgnoredError } from "../common/errors"; | import { SentryIgnoredError } from "../common/errors"; | ||||||
|  | import { Configuration } from "./runtime"; | ||||||
|  | import { RootApi } from "./apis"; | ||||||
|  | import { Config } from "."; | ||||||
|  | import { getCookie } from "../utils"; | ||||||
|  |  | ||||||
| export class Config { | export const DEFAULT_CONFIG = new Configuration({ | ||||||
|     branding_logo: string; |     basePath: "/api/v2beta", | ||||||
|     branding_title: string; |     headers: { | ||||||
|  |         "X-CSRFToken": getCookie("authentik_csrf"), | ||||||
|     error_reporting_enabled: boolean; |  | ||||||
|     error_reporting_environment: string; |  | ||||||
|     error_reporting_send_pii: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |     } | ||||||
|  | }); | ||||||
|  |  | ||||||
|     static get(): Promise<Config> { | export function configureSentry(): Promise<Config> { | ||||||
|         return DefaultClient.fetch<Config>(["root", "config"]).then((config) => { |     return new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => { | ||||||
|             if (config.error_reporting_enabled) { |         if (config.errorReportingEnabled) { | ||||||
|                 Sentry.init({ |             Sentry.init({ | ||||||
|                     dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", |                 dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", | ||||||
|                     release: `authentik@${VERSION}`, |                 release: `authentik@${VERSION}`, | ||||||
|                     integrations: [ |                 integrations: [ | ||||||
|                         new Integrations.BrowserTracing(), |                     new Integrations.BrowserTracing(), | ||||||
|                     ], |                 ], | ||||||
|                     tracesSampleRate: 0.6, |                 tracesSampleRate: 0.6, | ||||||
|                     environment: config.error_reporting_environment, |                 environment: config.errorReportingEnvironment, | ||||||
|                     beforeSend(event: Sentry.Event, hint: Sentry.EventHint) { |                 beforeSend(event: Sentry.Event, hint: Sentry.EventHint) { | ||||||
|                         if (hint.originalException instanceof SentryIgnoredError) { |                     if (hint.originalException instanceof SentryIgnoredError) { | ||||||
|                             return null; |                         return null; | ||||||
|                         } |                     } | ||||||
|                         return event; |                     return event; | ||||||
|                     }, |                 }, | ||||||
|                 }); |             }); | ||||||
|                 console.debug("authentik/config: Sentry enabled."); |             console.debug("authentik/config: Sentry enabled."); | ||||||
|             } |         } | ||||||
|             return config; |         return config; | ||||||
|         }); |     }); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,30 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { Event } from "./Events"; |  | ||||||
|  |  | ||||||
| export class Notification { |  | ||||||
|     pk: string; |  | ||||||
|     severity: string; |  | ||||||
|     body: string; |  | ||||||
|     created: string; |  | ||||||
|     event?: Event; |  | ||||||
|     seen: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Notification> { |  | ||||||
|         return DefaultClient.fetch<Notification>(["events", "notifications", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Notification>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Notification>>(["events", "notifications"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static markSeen(pk: string): Promise<{seen: boolean}> { |  | ||||||
|         return DefaultClient.update(["events", "notifications", pk], { |  | ||||||
|             "seen": true |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,26 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { Group } from "./Groups"; |  | ||||||
|  |  | ||||||
| export class Rule { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     transports: string[]; |  | ||||||
|     severity: string; |  | ||||||
|     group?: Group; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Rule> { |  | ||||||
|         return DefaultClient.fetch<Rule>(["events", "rules", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Rule>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Rule>>(["events", "rules"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/events/rules/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,25 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
|  |  | ||||||
| export class Transport { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     mode: string; |  | ||||||
|     mode_verbose: string; |  | ||||||
|     webhook_url: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Transport> { |  | ||||||
|         return DefaultClient.fetch<Transport>(["events", "transports", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Transport>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Transport>>(["events", "transports"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/events/transports/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,4 +1,4 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | import { Event } from "./models"; | ||||||
|  |  | ||||||
| export interface EventUser { | export interface EventUser { | ||||||
|     pk: number; |     pk: number; | ||||||
| @ -11,37 +11,7 @@ export interface EventContext { | |||||||
|     [key: string]: EventContext | string | number | string[]; |     [key: string]: EventContext | string | number | string[]; | ||||||
| } | } | ||||||
|  |  | ||||||
| export class Event { | export interface EventWithContext extends Event { | ||||||
|     pk: string; |  | ||||||
|     user: EventUser; |     user: EventUser; | ||||||
|     action: string; |  | ||||||
|     app: string; |  | ||||||
|     context: EventContext; |     context: EventContext; | ||||||
|     client_ip: string; |  | ||||||
|     created: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Event> { |  | ||||||
|         return DefaultClient.fetch<Event>(["events", "events", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Event>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Event>>(["events", "events"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // events/events/top_per_user/?filter_action=authorize_application |  | ||||||
|     static topForUser(action: string): Promise<TopNEvent[]> { |  | ||||||
|         return DefaultClient.fetch<TopNEvent[]>(["events", "events", "top_per_user"], { |  | ||||||
|             "filter_action": action, |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface TopNEvent { |  | ||||||
|     application: { [key: string]: string}; |  | ||||||
|     counted_events: number; |  | ||||||
|     unique_users: number; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,12 +1,4 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments, BaseInheritanceModel } from "./Client"; | import { ChallengeTypeEnum } from "./models"; | ||||||
| import { TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export enum ChallengeTypes { |  | ||||||
|     native = "native", |  | ||||||
|     response = "response", |  | ||||||
|     shell = "shell", |  | ||||||
|     redirect = "redirect", |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface Error { | export interface Error { | ||||||
|     code: string; |     code: string; | ||||||
| @ -18,11 +10,12 @@ export interface ErrorDict { | |||||||
| } | } | ||||||
|  |  | ||||||
| export interface Challenge { | export interface Challenge { | ||||||
|     type: ChallengeTypes; |     type: ChallengeTypeEnum; | ||||||
|     component?: string; |     component?: string; | ||||||
|     title?: string; |     title?: string; | ||||||
|     response_errors?: ErrorDict; |     response_errors?: ErrorDict; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface WithUserInfoChallenge extends Challenge { | export interface WithUserInfoChallenge extends Challenge { | ||||||
|     pending_user: string; |     pending_user: string; | ||||||
|     pending_user_avatar: string; |     pending_user_avatar: string; | ||||||
| @ -31,6 +24,7 @@ export interface WithUserInfoChallenge extends Challenge { | |||||||
| export interface ShellChallenge extends Challenge { | export interface ShellChallenge extends Challenge { | ||||||
|     body: string; |     body: string; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface RedirectChallenge extends Challenge { | export interface RedirectChallenge extends Challenge { | ||||||
|     to: string; |     to: string; | ||||||
| } | } | ||||||
| @ -44,104 +38,3 @@ export enum FlowDesignation { | |||||||
|     Recovery = "recovery", |     Recovery = "recovery", | ||||||
|     StageConfiguration = "stage_configuration", |     StageConfiguration = "stage_configuration", | ||||||
| } | } | ||||||
|  |  | ||||||
| export class Flow { |  | ||||||
|     pk: string; |  | ||||||
|     policybindingmodel_ptr_id: string; |  | ||||||
|     name: string; |  | ||||||
|     slug: string; |  | ||||||
|     title: string; |  | ||||||
|     designation: FlowDesignation; |  | ||||||
|     background: string; |  | ||||||
|     stages: string[]; |  | ||||||
|     policies: string[]; |  | ||||||
|     cache_count: number; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<Flow> { |  | ||||||
|         return DefaultClient.fetch<Flow>(["flows", "instances", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static diagram(slug: string): Promise<{ diagram: string }> { |  | ||||||
|         return DefaultClient.fetch<{ diagram: string }>(["flows", "instances", slug, "diagram"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Flow>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Flow>>(["flows", "instances"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static cached(): Promise<number> { |  | ||||||
|         return DefaultClient.fetch<{ count: number }>(["flows", "instances", "cached"]).then(r => { |  | ||||||
|             return r.count; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static executor(slug: string): Promise<Challenge> { |  | ||||||
|         return DefaultClient.fetch(["flows", "executor", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/flows/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class Stage implements BaseInheritanceModel { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     object_type: string; |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|     flow_set: Flow[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<Stage> { |  | ||||||
|         return DefaultClient.fetch<Stage>(["stages", "all", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Stage>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Stage>>(["stages", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["stages", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/stages/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class FlowStageBinding { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     policybindingmodel_ptr_id: string; |  | ||||||
|     target: string; |  | ||||||
|     stage: string; |  | ||||||
|     stage_obj: Stage; |  | ||||||
|     evaluate_on_plan: boolean; |  | ||||||
|     re_evaluate_policies: boolean; |  | ||||||
|     order: number; |  | ||||||
|     policies: string[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<FlowStageBinding> { |  | ||||||
|         return DefaultClient.fetch<FlowStageBinding>(["flows", "bindings", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<FlowStageBinding>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<FlowStageBinding>>(["flows", "bindings"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/stages/bindings/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,28 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { EventContext } from "./Events"; |  | ||||||
|  |  | ||||||
| export class Group { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     is_superuser: boolean; |  | ||||||
|     attributes: EventContext; |  | ||||||
|     parent?: Group; |  | ||||||
|     users: number[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Group> { |  | ||||||
|         return DefaultClient.fetch<Group>(["core", "groups", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Group>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Group>>(["core", "groups"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/groups/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,27 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { EventContext } from "./Events"; |  | ||||||
| import { User } from "./Users"; |  | ||||||
|  |  | ||||||
| export class Invitation { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     expires: number; |  | ||||||
|     fixed_date: EventContext; |  | ||||||
|     created_by: User; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Invitation> { |  | ||||||
|         return DefaultClient.fetch<Invitation>(["stages", "invitation", "invitations", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Invitation>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Invitation>>(["stages", "invitation", "invitations"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/stages/invitations/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,79 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { Provider, TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export interface OutpostHealth { |  | ||||||
|     last_seen: number; |  | ||||||
|     version: string; |  | ||||||
|     version_should: string; |  | ||||||
|     version_outdated: boolean; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class Outpost { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     providers: number[]; |  | ||||||
|     providers_obj: Provider[]; |  | ||||||
|     service_connection?: string; |  | ||||||
|     _config: QueryArguments; |  | ||||||
|     token_identifier: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Outpost> { |  | ||||||
|         return DefaultClient.fetch<Outpost>(["outposts", "outposts", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Outpost>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Outpost>>(["outposts", "outposts"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static health(pk: string): Promise<OutpostHealth[]> { |  | ||||||
|         return DefaultClient.fetch<OutpostHealth[]>(["outposts", "outposts", pk, "health"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/outposts/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface OutpostServiceConnectionState { |  | ||||||
|     version: string; |  | ||||||
|     healthy: boolean; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class OutpostServiceConnection { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     local: boolean; |  | ||||||
|     object_type: string; |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<OutpostServiceConnection> { |  | ||||||
|         return DefaultClient.fetch<OutpostServiceConnection>(["outposts", "service_connections", "all", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<OutpostServiceConnection>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<OutpostServiceConnection>>(["outposts", "service_connections", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static state(pk: string): Promise<OutpostServiceConnectionState> { |  | ||||||
|         return DefaultClient.fetch<OutpostServiceConnectionState>(["outposts", "service_connections", "all", pk, "state"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["outposts", "service_connections", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/outpost_service_connections/${rest}`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| import { DefaultClient, BaseInheritanceModel, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export class Policy implements BaseInheritanceModel { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     execution_logging: boolean; |  | ||||||
|     object_type: string; |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|     bound_to: number; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Policy> { |  | ||||||
|         return DefaultClient.fetch<Policy>(["policies", "all", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Policy>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Policy>>(["policies", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static cached(): Promise<number> { |  | ||||||
|         return DefaultClient.fetch<{ count: number }>(["policies", "all", "cached"]).then(r => { |  | ||||||
|             return r.count; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["policies", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/policies/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,31 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { Group } from "./Groups"; |  | ||||||
| import { Policy } from "./Policies"; |  | ||||||
| import { User } from "./Users"; |  | ||||||
|  |  | ||||||
| export class PolicyBinding { |  | ||||||
|     pk: string; |  | ||||||
|     policy?: Policy; |  | ||||||
|     group?: Group; |  | ||||||
|     user?: User; |  | ||||||
|     target: string; |  | ||||||
|     enabled: boolean; |  | ||||||
|     order: number; |  | ||||||
|     timeout: number; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<PolicyBinding> { |  | ||||||
|         return DefaultClient.fetch<PolicyBinding>(["policies", "bindings", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<PolicyBinding>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<PolicyBinding>>(["policies", "bindings"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/policies/bindings/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,30 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { Stage } from "./Flows"; |  | ||||||
|  |  | ||||||
| export class Prompt { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     field_key: string; |  | ||||||
|     label: string; |  | ||||||
|     type: string; |  | ||||||
|     required: boolean; |  | ||||||
|     placeholder: string; |  | ||||||
|     order: number; |  | ||||||
|     promptstage_set: Stage[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Prompt> { |  | ||||||
|         return DefaultClient.fetch<Prompt>(["stages", "prompt", "prompts", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Prompt>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Prompt>>(["stages", "prompt", "prompts"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/stages_prompts/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,31 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export class PropertyMapping { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     expression: string; |  | ||||||
|  |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<PropertyMapping> { |  | ||||||
|         return DefaultClient.fetch<PropertyMapping>(["propertymappings", "all", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<PropertyMapping>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<PropertyMapping>>(["propertymappings", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["propertymappings", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/property-mappings/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,40 +0,0 @@ | |||||||
| import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
|  |  | ||||||
| export interface TypeCreate { |  | ||||||
|     name: string; |  | ||||||
|     description: string; |  | ||||||
|     link: string; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class Provider implements BaseInheritanceModel { |  | ||||||
|     pk: number; |  | ||||||
|     name: string; |  | ||||||
|     authorization_flow: string; |  | ||||||
|     object_type: string; |  | ||||||
|  |  | ||||||
|     assigned_application_slug?: string; |  | ||||||
|     assigned_application_name?: string; |  | ||||||
|  |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(id: number): Promise<Provider> { |  | ||||||
|         return DefaultClient.fetch<Provider>(["providers", "all", id.toString()]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Provider>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Provider>>(["providers", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["providers", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/providers/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,34 +0,0 @@ | |||||||
| import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export class Source implements BaseInheritanceModel { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     slug: string; |  | ||||||
|     enabled: boolean; |  | ||||||
|     authentication_flow: string; |  | ||||||
|     enrollment_flow: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|     object_type: string; |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<Source> { |  | ||||||
|         return DefaultClient.fetch<Source>(["sources", "all", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Source>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Source>>(["sources", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["sources", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/sources/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,33 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments } from "./Client"; |  | ||||||
|  |  | ||||||
| export enum TaskStatus { |  | ||||||
|     SUCCESSFUL = 1, |  | ||||||
|     WARNING = 2, |  | ||||||
|     ERROR = 4, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class SystemTask { |  | ||||||
|  |  | ||||||
|     task_name: string; |  | ||||||
|     task_description: string; |  | ||||||
|     task_finish_timestamp: number; |  | ||||||
|     status: TaskStatus; |  | ||||||
|     messages: string[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(task_name: string): Promise<SystemTask> { |  | ||||||
|         return DefaultClient.fetch<SystemTask>(["admin", "system_tasks", task_name]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<SystemTask[]> { |  | ||||||
|         return DefaultClient.fetch<SystemTask[]>(["admin", "system_tasks"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static retry(task_name: string): string { |  | ||||||
|         return DefaultClient.makeUrl(["admin", "system_tasks", task_name, "retry"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,47 +0,0 @@ | |||||||
| import { AKResponse, DefaultClient, QueryArguments } from "./Client"; |  | ||||||
| import { User } from "./Users"; |  | ||||||
|  |  | ||||||
| export enum TokenIntent { |  | ||||||
|     INTENT_VERIFICATION = "verification", |  | ||||||
|     INTENT_API = "api", |  | ||||||
|     INTENT_RECOVERY = "recovery", |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class Token { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     identifier: string; |  | ||||||
|     intent: TokenIntent; |  | ||||||
|     user: User; |  | ||||||
|     description: string; |  | ||||||
|  |  | ||||||
|     expires: number; |  | ||||||
|     expiring: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<User> { |  | ||||||
|         return DefaultClient.fetch<User>(["core", "tokens", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Token>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Token>>(["core", "tokens"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/tokens/${rest}`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static userUrl(rest: string): string { |  | ||||||
|         return `/-/user/tokens/${rest}`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getKey(identifier: string): Promise<string> { |  | ||||||
|         return DefaultClient.fetch<{ key: string }>(["core", "tokens", identifier, "view_key"]).then( |  | ||||||
|             (r) => r.key |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,45 +1,11 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | import { CoreApi } from "./apis"; | ||||||
|  | import { DEFAULT_CONFIG } from "./Config"; | ||||||
|  | import { User } from "./models"; | ||||||
|  |  | ||||||
| let _globalMePromise: Promise<User>; | let _globalMePromise: Promise<User>; | ||||||
|  | export function me(): Promise<User> { | ||||||
| export class User { |     if (!_globalMePromise) { | ||||||
|     pk: number; |         _globalMePromise = new CoreApi(DEFAULT_CONFIG).coreUsersMe({}); | ||||||
|     username: string; |  | ||||||
|     name: string; |  | ||||||
|     is_superuser: boolean; |  | ||||||
|     email: boolean; |  | ||||||
|     avatar: string; |  | ||||||
|     is_active: boolean; |  | ||||||
|     last_login: number; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<User> { |  | ||||||
|         return DefaultClient.fetch<User>(["core", "users", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<User>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<User>>(["core", "users"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/users/${rest}`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static me(): Promise<User> { |  | ||||||
|         if (!_globalMePromise) { |  | ||||||
|             _globalMePromise = DefaultClient.fetch<User>(["core", "users", "me"]); |  | ||||||
|         } |  | ||||||
|         return _globalMePromise; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static count(): Promise<number> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<User>>(["core", "users"], { |  | ||||||
|             "page_size": 1 |  | ||||||
|         }).then(r => { |  | ||||||
|             return r.pagination.count; |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
|  |     return _globalMePromise; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,17 +0,0 @@ | |||||||
| import { DefaultClient } from "./Client"; |  | ||||||
|  |  | ||||||
| export class Version { |  | ||||||
|  |  | ||||||
|     version_current: string; |  | ||||||
|     version_latest: string; |  | ||||||
|     outdated: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(): Promise<Version> { |  | ||||||
|         return DefaultClient.fetch<Version>(["admin", "version"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
							
								
								
									
										97
									
								
								web/src/api/legacy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								web/src/api/legacy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | |||||||
|  | export class AdminURLManager { | ||||||
|  |  | ||||||
|  |     static applications(rest: string): string { | ||||||
|  |         return `/administration/applications/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static cryptoCertificates(rest: string): string { | ||||||
|  |         return `/administration/crypto/certificates/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static policies(rest: string): string { | ||||||
|  |         return `/administration/policies/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static policyBindings(rest: string): string { | ||||||
|  |         return `/administration/policies/bindings/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static providers(rest: string): string { | ||||||
|  |         return `/administration/providers/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static propertyMappings(rest: string): string { | ||||||
|  |         return `/administration/property-mappings/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static outposts(rest: string): string { | ||||||
|  |         return `/administration/outposts/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static outpostServiceConnections(rest: string): string { | ||||||
|  |         return `/administration/outpost_service_connections/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static flows(rest: string): string { | ||||||
|  |         return `/administration/flows/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static stages(rest: string): string { | ||||||
|  |         return `/administration/stages/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static stagePrompts(rest: string): string { | ||||||
|  |         return `/administration/stages_prompts/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static stageInvitations(rest: string): string { | ||||||
|  |         return `/administration/stages/invitations/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static stageBindings(rest: string): string { | ||||||
|  |         return `/administration/stages/bindings/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static sources(rest: string): string { | ||||||
|  |         return `/administration/sources/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static tokens(rest: string): string { | ||||||
|  |         return `/administration/tokens/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static eventRules(rest: string): string { | ||||||
|  |         return `/administration/events/rules/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static eventTransports(rest: string): string { | ||||||
|  |         return `/administration/events/transports/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static users(rest: string): string { | ||||||
|  |         return `/administration/users/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static groups(rest: string): string { | ||||||
|  |         return `/administration/groups/${rest}`; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class UserURLManager { | ||||||
|  |  | ||||||
|  |     static tokens(rest: string): string { | ||||||
|  |         return `/-/user/tokens/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class AppURLManager { | ||||||
|  |  | ||||||
|  |     static sourceSAML(slug: string, rest: string): string { | ||||||
|  |         return `/source/saml/${slug}/${rest}`; | ||||||
|  |     } | ||||||
|  |     static providerSAML(rest: string): string { | ||||||
|  |         return `/application/saml/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @ -1,43 +0,0 @@ | |||||||
| import { DefaultClient } from "../Client"; |  | ||||||
| import { Provider } from "../Providers"; |  | ||||||
|  |  | ||||||
| export interface OAuth2SetupURLs { |  | ||||||
|  |  | ||||||
|     issuer?: string; |  | ||||||
|     authorize: string; |  | ||||||
|     token: string; |  | ||||||
|     user_info: string; |  | ||||||
|     provider_info?: string; |  | ||||||
|     logout?: string; |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class OAuth2Provider extends Provider { |  | ||||||
|     client_type: string |  | ||||||
|     client_id: string; |  | ||||||
|     client_secret: string; |  | ||||||
|     token_validity: string; |  | ||||||
|     include_claims_in_id_token: boolean; |  | ||||||
|     jwt_alg: string; |  | ||||||
|     rsa_key: string; |  | ||||||
|     redirect_uris: string; |  | ||||||
|     sub_mode: string; |  | ||||||
|     issuer_mode: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(id: number): Promise<OAuth2Provider> { |  | ||||||
|         return DefaultClient.fetch<OAuth2Provider>(["providers", "oauth2", id.toString()]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getLaunchURls(id: number): Promise<OAuth2SetupURLs> { |  | ||||||
|         return DefaultClient.fetch(["providers", "oauth2", id.toString(), "setup_urls"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static appUrl(rest: string): string { |  | ||||||
|         return `/application/oauth2/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	