Compare commits
	
		
			76 Commits
		
	
	
		
			version/20
			...
			version/20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7da90ff7e4 | |||
| 61b5714652 | |||
| d2df426489 | |||
| e6c75ed173 | |||
| a353c6956e | |||
| a367d8515f | |||
| 2b7a22a29a | |||
| e6712a50d2 | |||
| c621f62d92 | |||
| a0648cd925 | |||
| 2650e672bb | |||
| 53b9376789 | |||
| d15e50025c | |||
| 0af66a26ab | |||
| bf754369d9 | |||
| 02dc112f8f | |||
| 2d4e7ebab5 | |||
| a7d0a50859 | |||
| 71c9108f89 | |||
| f8bcdb26b3 | |||
| 45f1d95bf9 | |||
| 5dab198c47 | |||
| ad91abe9de | |||
| fa30755241 | |||
| 552f8c6a9a | |||
| 101f916247 | |||
| 2acdcf74e1 | |||
| ddb8610032 | |||
| 22ad850e6c | |||
| 57925ed60a | |||
| 48cc2f17c1 | |||
| 448108fca0 | |||
| c1254f6212 | |||
| c8120c0d3e | |||
| 52016e0806 | |||
| e555bdd42b | |||
| 1a619c90de | |||
| 18faf30b0c | |||
| b3bd979ecd | |||
| db113c5e8f | |||
| 78bcb90a1e | |||
| b64ecbde22 | |||
| 43bab840ec | |||
| f020b79384 | |||
| 820f658b49 | |||
| 5d460a2537 | |||
| efc46f52e6 | |||
| 9fac51f8c7 | |||
| fe4b2d1a34 | |||
| f8abe3e210 | |||
| 3ced67b151 | |||
| cd5631ec76 | |||
| 95df7c7f30 | |||
| 1e934aa5d5 | |||
| d93927755a | |||
| ddb3b71dce | |||
| bf9826873e | |||
| 6869b3c16a | |||
| 9b71b8da5f | |||
| bfc8e9200f | |||
| c4311abc9f | |||
| ec42869e00 | |||
| 45963c2ffc | |||
| 1aa27b5e80 | |||
| 1737feec91 | |||
| a0e0fb930a | |||
| 4a32c3ca11 | |||
| d307539fd0 | |||
| c060a3eec2 | |||
| 4612ae1ff4 | |||
| 7af883d80c | |||
| 4a5374d03f | |||
| 3b536f6e55 | |||
| 6aa13a8666 | |||
| 24e4924dec | |||
| a252f303c0 | 
@ -1,5 +1,5 @@
 | 
				
			|||||||
[bumpversion]
 | 
					[bumpversion]
 | 
				
			||||||
current_version = 2021.2.1-rc1
 | 
					current_version = 2021.2.3-stable
 | 
				
			||||||
tag = True
 | 
					tag = True
 | 
				
			||||||
commit = True
 | 
					commit = True
 | 
				
			||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
 | 
					parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@ -16,6 +16,14 @@ updates:
 | 
				
			|||||||
  open-pull-requests-limit: 10
 | 
					  open-pull-requests-limit: 10
 | 
				
			||||||
  assignees:
 | 
					  assignees:
 | 
				
			||||||
  - BeryJu
 | 
					  - BeryJu
 | 
				
			||||||
 | 
					- package-ecosystem: npm
 | 
				
			||||||
 | 
					  directory: "/website"
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					    interval: daily
 | 
				
			||||||
 | 
					    time: "04:00"
 | 
				
			||||||
 | 
					  open-pull-requests-limit: 10
 | 
				
			||||||
 | 
					  assignees:
 | 
				
			||||||
 | 
					  - BeryJu
 | 
				
			||||||
- package-ecosystem: pip
 | 
					- package-ecosystem: pip
 | 
				
			||||||
  directory: "/"
 | 
					  directory: "/"
 | 
				
			||||||
  schedule:
 | 
					  schedule:
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										14
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.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.2.1-rc1
 | 
					          -t beryju/authentik:2021.2.3-stable
 | 
				
			||||||
          -t beryju/authentik:latest
 | 
					          -t beryju/authentik:latest
 | 
				
			||||||
          -f Dockerfile .
 | 
					          -f Dockerfile .
 | 
				
			||||||
      - name: Push Docker Container to Registry (versioned)
 | 
					      - name: Push Docker Container to Registry (versioned)
 | 
				
			||||||
        run: docker push beryju/authentik:2021.2.1-rc1
 | 
					        run: docker push beryju/authentik:2021.2.3-stable
 | 
				
			||||||
      - name: Push Docker Container to Registry (latest)
 | 
					      - name: Push Docker Container to Registry (latest)
 | 
				
			||||||
        run: docker push beryju/authentik:latest
 | 
					        run: docker push beryju/authentik:latest
 | 
				
			||||||
  build-proxy:
 | 
					  build-proxy:
 | 
				
			||||||
@ -48,11 +48,11 @@ jobs:
 | 
				
			|||||||
          cd outpost/
 | 
					          cd outpost/
 | 
				
			||||||
          docker build \
 | 
					          docker build \
 | 
				
			||||||
          --no-cache \
 | 
					          --no-cache \
 | 
				
			||||||
          -t beryju/authentik-proxy:2021.2.1-rc1 \
 | 
					          -t beryju/authentik-proxy:2021.2.3-stable \
 | 
				
			||||||
          -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.2.1-rc1
 | 
					        run: docker push beryju/authentik-proxy:2021.2.3-stable
 | 
				
			||||||
      - name: Push Docker Container to Registry (latest)
 | 
					      - name: Push Docker Container to Registry (latest)
 | 
				
			||||||
        run: docker push beryju/authentik-proxy:latest
 | 
					        run: docker push beryju/authentik-proxy:latest
 | 
				
			||||||
  build-static:
 | 
					  build-static:
 | 
				
			||||||
@ -69,11 +69,11 @@ jobs:
 | 
				
			|||||||
          cd web/
 | 
					          cd web/
 | 
				
			||||||
          docker build \
 | 
					          docker build \
 | 
				
			||||||
          --no-cache \
 | 
					          --no-cache \
 | 
				
			||||||
          -t beryju/authentik-static:2021.2.1-rc1 \
 | 
					          -t beryju/authentik-static:2021.2.3-stable \
 | 
				
			||||||
          -t beryju/authentik-static:latest \
 | 
					          -t beryju/authentik-static:latest \
 | 
				
			||||||
          -f Dockerfile .
 | 
					          -f Dockerfile .
 | 
				
			||||||
      - name: Push Docker Container to Registry (versioned)
 | 
					      - name: Push Docker Container to Registry (versioned)
 | 
				
			||||||
        run: docker push beryju/authentik-static:2021.2.1-rc1
 | 
					        run: docker push beryju/authentik-static:2021.2.3-stable
 | 
				
			||||||
      - name: Push Docker Container to Registry (latest)
 | 
					      - name: Push Docker Container to Registry (latest)
 | 
				
			||||||
        run: docker push beryju/authentik-static:latest
 | 
					        run: docker push beryju/authentik-static:latest
 | 
				
			||||||
  test-release:
 | 
					  test-release:
 | 
				
			||||||
@ -107,5 +107,5 @@ jobs:
 | 
				
			|||||||
          SENTRY_PROJECT: authentik
 | 
					          SENTRY_PROJECT: authentik
 | 
				
			||||||
          SENTRY_URL: https://sentry.beryju.org
 | 
					          SENTRY_URL: https://sentry.beryju.org
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          tagName: 2021.2.1-rc1
 | 
					          tagName: 2021.2.3-stable
 | 
				
			||||||
          environment: beryjuorg-prod
 | 
					          environment: beryjuorg-prod
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										179
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										179
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							@ -53,10 +53,10 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "autobahn": {
 | 
					        "autobahn": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:410a93e0e29882c8b5d5ab05d220b07609b886ef5f23c0b8d39153254ffd6895",
 | 
					                "sha256:93df8fc9d1821c9dabff9fed52181a9ad6eea5e9989d53102c391607d7c1666e",
 | 
				
			||||||
                "sha256:52ee4236ff9a1fcbbd9500439dcf3284284b37f8a6b31ecc8a36e00cf9f95049"
 | 
					                "sha256:cceed2121b7a93024daa93c91fae33007f8346f0e522796421f36a6183abea99"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "version": "==20.12.3"
 | 
					            "version": "==21.1.1"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "automat": {
 | 
					        "automat": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
@ -74,17 +74,17 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "boto3": {
 | 
					        "boto3": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:1a282c1cd7d5028cbb3a75d747df32162295253f55d263ac85840e264830963b"
 | 
					                "sha256:d6aafb804fca2b67c65dda78ad8b4afed901e004071208b84c804d345ad9ebba"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "index": "pypi",
 | 
					            "index": "pypi",
 | 
				
			||||||
            "version": "==1.17.2"
 | 
					            "version": "==1.17.5"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "botocore": {
 | 
					        "botocore": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:7442fdbbdc841bfac7f94f92ecb807de070e32ed205743eb72d4ea27c5e8e778",
 | 
					                "sha256:04a1df759681f5f171accb354d863bfed0774d64a4e8ee35ff49835755660a4e",
 | 
				
			||||||
                "sha256:bf587b044983a91a0124cc133ff167b8528c19fbbc8f0b956d9a1ac256cad7d7"
 | 
					                "sha256:3c55f0db5e08920727f4fa24a87aed60060643f4b0b5665c62ec762f79e82d6b"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "version": "==1.20.2"
 | 
					            "version": "==1.20.5"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "cachetools": {
 | 
					        "cachetools": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
@ -223,22 +223,15 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "cryptography": {
 | 
					        "cryptography": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:0003a52a123602e1acee177dc90dd201f9bb1e73f24a070db7d36c588e8f5c7d",
 | 
					                "sha256:287032b6a7d86abc98e8e977b20138c53fea40e5b24e29090d5a675a973dcd10",
 | 
				
			||||||
                "sha256:0e85aaae861d0485eb5a79d33226dd6248d2a9f133b81532c8f5aae37de10ff7",
 | 
					                "sha256:288c65eea20bd89b11102c47b118bc1e0749386b0a0dfebba414076c5d4c8188",
 | 
				
			||||||
                "sha256:594a1db4511bc4d960571536abe21b4e5c3003e8750ab8365fafce71c5d86901",
 | 
					                "sha256:7eed937ad9b53280a5f53570d3a7dc93cb4412b6a3d58d4c6bb78cc26319c729",
 | 
				
			||||||
                "sha256:69e836c9e5ff4373ce6d3ab311c1a2eed274793083858d3cd4c7d12ce20d5f9c",
 | 
					                "sha256:dab437c2e84628703e3358f0f06555a6259bc5039209d51aa3b05af667ff4fd0",
 | 
				
			||||||
                "sha256:788a3c9942df5e4371c199d10383f44a105d67d401fb4304178020142f020244",
 | 
					                "sha256:ee5e19f0856b6fbbdbab15c2787ca65d203801d2d65d0b8de6218f424206c848",
 | 
				
			||||||
                "sha256:7e177e4bea2de937a584b13645cab32f25e3d96fc0bc4a4cf99c27dc77682be6",
 | 
					                "sha256:f21be9ec6b44c223b2024bbe59d394fadc7be320d18a8d595419afadb6cd5620",
 | 
				
			||||||
                "sha256:83d9d2dfec70364a74f4e7c70ad04d3ca2e6a08b703606993407bf46b97868c5",
 | 
					                "sha256:f6ea140d2736b7e1f0de4f988c43f76b0b3f3d365080e091715429ba218dce28"
 | 
				
			||||||
                "sha256:84ef7a0c10c24a7773163f917f1cb6b4444597efd505a8aed0a22e8c4780f27e",
 | 
					 | 
				
			||||||
                "sha256:9e21301f7a1e7c03dbea73e8602905a4ebba641547a462b26dd03451e5769e7c",
 | 
					 | 
				
			||||||
                "sha256:9f6b0492d111b43de5f70052e24c1f0951cb9e6022188ebcb1cc3a3d301469b0",
 | 
					 | 
				
			||||||
                "sha256:a69bd3c68b98298f490e84519b954335154917eaab52cf582fa2c5c7efc6e812",
 | 
					 | 
				
			||||||
                "sha256:b4890d5fb9b7a23e3bf8abf5a8a7da8e228f1e97dc96b30b95685df840b6914a",
 | 
					 | 
				
			||||||
                "sha256:c366df0401d1ec4e548bebe8f91d55ebcc0ec3137900d214dd7aac8427ef3030",
 | 
					 | 
				
			||||||
                "sha256:dc42f645f8f3a489c3dd416730a514e7a91a59510ddaadc09d04224c098d3302"
 | 
					 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "version": "==3.3.1"
 | 
					            "version": "==3.4.4"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "dacite": {
 | 
					        "dacite": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
@ -790,84 +783,74 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "pycryptodome": {
 | 
					        "pycryptodome": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:19cb674df6c74a14b8b408aa30ba8a89bd1c01e23505100fb45f930fbf0ed0d9",
 | 
					                "sha256:09c1555a3fa450e7eaca41ea11cd00afe7c91fef52353488e65663777d8524e0",
 | 
				
			||||||
                "sha256:1cfdb92dca388e27e732caa72a1cc624520fe93752a665c3b6cd8f1a91b34916",
 | 
					                "sha256:12222a5edc9ca4a29de15fbd5339099c4c26c56e13c2ceddf0b920794f26165d",
 | 
				
			||||||
                "sha256:27397aee992af69d07502126561d851ba3845aa808f0e55c71ad0efa264dd7d4",
 | 
					                "sha256:1723ebee5561628ce96748501cdaa7afaa67329d753933296321f0be55358dce",
 | 
				
			||||||
                "sha256:28f75e58d02019a7edc7d4135203d2501dfc47256d175c72c9798f9a129a49a7",
 | 
					                "sha256:1c5e1ca507de2ad93474be5cfe2bfa76b7cf039a1a32fc196f40935944871a06",
 | 
				
			||||||
                "sha256:2a68df525b387201a43b27b879ce8c08948a430e883a756d6c9e3acdaa7d7bd8",
 | 
					                "sha256:2603c98ae04aac675fefcf71a6c87dc4bb74a75e9071ae3923bbc91a59f08d35",
 | 
				
			||||||
                "sha256:411745c6dce4eff918906eebcde78771d44795d747e194462abb120d2e537cd9",
 | 
					                "sha256:2dea65df54349cdfa43d6b2e8edb83f5f8d6861e5cf7b1fbc3e34c5694c85e27",
 | 
				
			||||||
                "sha256:46e96aeb8a9ca8b1edf9b1fd0af4bf6afcf3f1ca7fa35529f5d60b98f3e4e959",
 | 
					                "sha256:31c1df17b3dc5f39600a4057d7db53ac372f492c955b9b75dd439f5d8b460129",
 | 
				
			||||||
                "sha256:4ed27951b0a17afd287299e2206a339b5b6d12de9321e1a1575261ef9c4a851b",
 | 
					                "sha256:38661348ecb71476037f1e1f553159b80d256c00f6c0b00502acac891f7116d9",
 | 
				
			||||||
                "sha256:50826b49fbca348a61529693b0031cdb782c39060fb9dca5ac5dff858159dc5a",
 | 
					                "sha256:3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673",
 | 
				
			||||||
                "sha256:5598dc6c9dbfe882904e54584322893eff185b98960bbe2cdaaa20e8a437b6e5",
 | 
					                "sha256:3f840c49d38986f6e17dbc0673d37947c88bc9d2d9dba1c01b979b36f8447db1",
 | 
				
			||||||
                "sha256:5c3c4865730dfb0263f822b966d6d58429d8b1e560d1ddae37685fd9e7c63161",
 | 
					                "sha256:501ab36aae360e31d0ec370cf5ce8ace6cb4112060d099b993bc02b36ac83fb6",
 | 
				
			||||||
                "sha256:5f19e6ef750f677d924d9c7141f54bade3cd56695bbfd8a9ef15d0378557dfe4",
 | 
					                "sha256:60386d1d4cfaad299803b45a5bc2089696eaf6cdd56f9fc17479a6f89595cfc8",
 | 
				
			||||||
                "sha256:60febcf5baf70c566d9d9351c47fbd8321da9a4edf2eff45c4c31c86164ca794",
 | 
					                "sha256:6260e24d41149268122dd39d4ebd5941e9d107f49463f7e071fd397e29923b0c",
 | 
				
			||||||
                "sha256:62c488a21c253dadc9f731a32f0ac61e4e436d81a1ea6f7d1d9146ed4d20d6bd",
 | 
					                "sha256:6bbf7fee7b7948b29d7e71fcacf48bac0c57fb41332007061a933f2d996f9713",
 | 
				
			||||||
                "sha256:6d3baaf82681cfb1a842f1c8f77beac791ceedd99af911e4f5fabec32bae2259",
 | 
					                "sha256:6d2df5223b12437e644ce0a3be7809471ffa71de44ccd28b02180401982594a6",
 | 
				
			||||||
                "sha256:6e4227849e4231a3f5b35ea5bdedf9a82b3883500e5624f00a19156e9a9ef861",
 | 
					                "sha256:758949ca62690b1540dfb24ad773c6da9cd0e425189e83e39c038bbd52b8e438",
 | 
				
			||||||
                "sha256:6e89bb3826e6f84501e8e3b205c22595d0c5492c2f271cbb9ee1c48eb1866645",
 | 
					                "sha256:77997519d8eb8a4adcd9a47b9cec18f9b323e296986528186c0e9a7a15d6a07e",
 | 
				
			||||||
                "sha256:70d807d11d508433daf96244ec1c64e55039e8a35931fc5ea9eee94dbe3cb6b5",
 | 
					                "sha256:7fd519b89585abf57bf47d90166903ec7b43af4fe23c92273ea09e6336af5c07",
 | 
				
			||||||
                "sha256:76b1a34d74bb2c91bce460cdc74d1347592045627a955e9a252554481c17c52f",
 | 
					                "sha256:98213ac2b18dc1969a47bc65a79a8fca02a414249d0c8635abb081c7f38c91b6",
 | 
				
			||||||
                "sha256:7798e73225a699651888489fbb1dbc565e03a509942a8ce6194bbe6fb582a41f",
 | 
					                "sha256:99b2f3fc51d308286071d0953f92055504a6ffe829a832a9fc7a04318a7683dd",
 | 
				
			||||||
                "sha256:834b790bbb6bd18956f625af4004d9c15eed12d5186d8e57851454ae76d52215",
 | 
					                "sha256:9b6f711b25e01931f1c61ce0115245a23cdc8b80bf8539ac0363bdcf27d649b6",
 | 
				
			||||||
                "sha256:843e5f10ecdf9d307032b8b91afe9da1d6ed5bb89d0bbec5c8dcb4ba44008e11",
 | 
					                "sha256:a3105a0eb63eacf98c2ecb0eb4aa03f77f40fbac2bdde22020bb8a536b226bb8",
 | 
				
			||||||
                "sha256:8f9f84059039b672a5a705b3c5aa21747867bacc30a72e28bf0d147cc8ef85ed",
 | 
					                "sha256:a8eb8b6ea09ec1c2535bf39914377bc8abcab2c7d30fa9225eb4fe412024e427",
 | 
				
			||||||
                "sha256:9000877383e2189dafd1b2fc68c6c726eca9a3cfb6d68148fbb72ccf651959b6",
 | 
					                "sha256:a92d5c414e8ee1249e850789052608f582416e82422502dc0ac8c577808a9067",
 | 
				
			||||||
                "sha256:910e202a557e1131b1c1b3f17a63914d57aac55cf9fb9b51644962841c3995c4",
 | 
					                "sha256:d3d6958d53ad307df5e8469cc44474a75393a434addf20ecd451f38a72fe29b8",
 | 
				
			||||||
                "sha256:946399d15eccebafc8ce0257fc4caffe383c75e6b0633509bd011e357368306c",
 | 
					                "sha256:e0a4d5933a88a2c98bbe19c0c722f5483dc628d7a38338ac2cb64a7dbd34064b",
 | 
				
			||||||
                "sha256:a199e9ca46fc6e999e5f47fce342af4b56c7de85fae893c69ab6aa17531fb1e1",
 | 
					                "sha256:e3bf558c6aeb49afa9f0c06cee7fb5947ee5a1ff3bd794b653d39926b49077fa",
 | 
				
			||||||
                "sha256:a3d8a9efa213be8232c59cdc6b65600276508e375e0a119d710826248fd18d37",
 | 
					                "sha256:e61e363d9a5d7916f3a4ce984a929514c0df3daf3b1b2eb5e6edbb131ee771cf",
 | 
				
			||||||
                "sha256:a4599c0ca0fc027c780c1c45ed996d5bef03e571470b7b1c7171ec1e1a90914c",
 | 
					                "sha256:f977cdf725b20f6b8229b0c87acb98c7717e742ef9f46b113985303ae12a99da",
 | 
				
			||||||
                "sha256:b4e6b269a8ddaede774e5c3adbef6bf452ee144e6db8a716d23694953348cd86",
 | 
					                "sha256:fc7489a50323a0df02378bc2fff86eb69d94cc5639914346c736be981c6a02e7"
 | 
				
			||||||
                "sha256:b68794fba45bdb367eeb71249c26d23e61167510a1d0c3d6cf0f2f14636e62ee",
 | 
					 | 
				
			||||||
                "sha256:d7ec2bd8f57c559dd24e71891c51c25266a8deb66fc5f02cc97c7fb593d1780a",
 | 
					 | 
				
			||||||
                "sha256:e15bde67ccb7d4417f627dd16ffe2f5a4c2941ce5278444e884cb26d73ecbc61",
 | 
					 | 
				
			||||||
                "sha256:eb01f9997e4d6a8ec8a1ad1f676ba5a362781ff64e8189fe2985258ba9cb9706",
 | 
					 | 
				
			||||||
                "sha256:faa682c404c218e8788c3126c9a4b8fbcc54dc245b5b6e8ea5b46f3b63bd0c84"
 | 
					 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "index": "pypi",
 | 
					            "index": "pypi",
 | 
				
			||||||
            "version": "==3.9.9"
 | 
					            "version": "==3.10.1"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "pycryptodomex": {
 | 
					        "pycryptodomex": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:15c03ffdac17731b126880622823d30d0a3cc7203cd219e6b9814140a44e7fab",
 | 
					                "sha256:00a584ee52bf5e27d540129ca9bf7c4a7e7447f24ff4a220faa1304ad0c09bcd",
 | 
				
			||||||
                "sha256:20fb7f4efc494016eab1bc2f555bc0a12dd5ca61f35c95df8061818ffb2c20a3",
 | 
					                "sha256:04265a7a84ae002001249bd1de2823bcf46832bd4b58f6965567cb8a07cf4f00",
 | 
				
			||||||
                "sha256:28ee3bcb4d609aea3040cad995a8e2c9c6dc57c12183dadd69e53880c35333b9",
 | 
					                "sha256:0bd35af6a18b724c689e56f2dbbdd8e409288be71952d271ba3d9614b31d188c",
 | 
				
			||||||
                "sha256:305e3c46f20d019cd57543c255e7ba49e432e275d7c0de8913b6dbe57a851bc8",
 | 
					                "sha256:20c45a30f3389148f94edb77f3b216c677a277942f62a2b81a1cc0b6b2dde7fc",
 | 
				
			||||||
                "sha256:3547b87b16aad6afb28c9b3a9cd870e11b5e7b5ac649b74265258d96d8de1130",
 | 
					                "sha256:2959304d1ce31ab303d9fb5db2b294814278b35154d9b30bf7facc52d6088d0a",
 | 
				
			||||||
                "sha256:3642252d7bfc4403a42050e18ba748bedebd5a998a8cba89665a4f42aea4c380",
 | 
					                "sha256:36dab7f506948056ceba2d57c1ade74e898401960de697cefc02f3519bd26c1b",
 | 
				
			||||||
                "sha256:404faa3e518f8bea516aae2aac47d4d960397199a15b4bd6f66cad97825469a0",
 | 
					                "sha256:37ec1b407ec032c7a0c1fdd2da12813f560bad38ae61ad9c7ce3c0573b3e5e30",
 | 
				
			||||||
                "sha256:42669638e4f7937b7141044a2fbd1019caca62bd2cdd8b535f731426ab07bde1",
 | 
					                "sha256:3b8eb85b3cc7f083d87978c264d10ff9de3b4bfc46f1c6fdc2792e7d7ebc87bb",
 | 
				
			||||||
                "sha256:4632d55a140b28e20be3cd7a3057af52fb747298ff0fd3290d4e9f245b5004ba",
 | 
					                "sha256:3dfce70c4e425607ae87b8eae67c9c7dbba59a33b62d70f79417aef0bc5c735b",
 | 
				
			||||||
                "sha256:4a88c9383d273bdce3afc216020282c9c5c39ec0bd9462b1a206af6afa377cf0",
 | 
					                "sha256:418f51c61eab52d9920f4ef468d22c89dab1be5ac796f71cf3802f6a6e667df0",
 | 
				
			||||||
                "sha256:4ce1fc1e6d2fd2d6dc197607153327989a128c093e0e94dca63408f506622c3e",
 | 
					                "sha256:4195604f75cdc1db9bccdb9e44d783add3c817319c30aaff011670c9ed167690",
 | 
				
			||||||
                "sha256:55cf4e99b3ba0122dee570dc7661b97bf35c16aab3e2ccb5070709d282a1c7ab",
 | 
					                "sha256:4344ab16faf6c2d9df2b6772995623698fb2d5f114dace4ab2ff335550cf71d5",
 | 
				
			||||||
                "sha256:5e486cab2dfcfaec934dd4f5d5837f4a9428b690f4d92a3b020fd31d1497ca64",
 | 
					                "sha256:541cd3e3e252fb19a7b48f420b798b53483302b7fe4d9954c947605d0a263d62",
 | 
				
			||||||
                "sha256:65ec88c8271448d2ea109d35c1f297b09b872c57214ab7e832e413090d3469a9",
 | 
					                "sha256:564063e3782474c92cbb333effd06e6eb718471783c6e67f28c63f0fc3ac7b23",
 | 
				
			||||||
                "sha256:6c95a3361ce70068cf69526a58751f73ddac5ba27a3c2379b057efa2f5338c8c",
 | 
					                "sha256:72f44b5be46faef2a1bf2a85902511b31f4dd7b01ce0c3978e92edb2cc812a82",
 | 
				
			||||||
                "sha256:73240335f4a1baf12880ebac6df66ab4d3a9212db9f3efe809c36a27280d16f8",
 | 
					                "sha256:8a98e02cbf8f624add45deff444539bf26345b479fc04fa0937b23cd84078d91",
 | 
				
			||||||
                "sha256:7651211e15109ac0058a49159265d9f6e6423c8a81c65434d3c56d708417a05b",
 | 
					                "sha256:940db96449d7b2ebb2c7bf190be1514f3d67914bd37e54e8d30a182bd375a1a9",
 | 
				
			||||||
                "sha256:7b5b7c5896f8172ea0beb283f7f9428e0ab88ec248ce0a5b8c98d73e26267d51",
 | 
					                "sha256:961333e7ee896651f02d4692242aa36b787b8e8e0baa2256717b2b9d55ae0a3c",
 | 
				
			||||||
                "sha256:836fe39282e75311ce4c38468be148f7fac0df3d461c5de58c5ff1ddb8966bac",
 | 
					                "sha256:9f713ffb4e27b5575bd917c70bbc3f7b348241a351015dbbc514c01b7061ff7e",
 | 
				
			||||||
                "sha256:871852044f55295449fbf225538c2c4118525093c32f0a6c43c91bed0452d7e3",
 | 
					                "sha256:a6584ae58001d17bb4dc0faa8a426919c2c028ef4d90ceb4191802ca6edb8204",
 | 
				
			||||||
                "sha256:892e93f3e7e10c751d6c17fa0dc422f7984cfd5eb6690011f9264dc73e2775fc",
 | 
					                "sha256:c2b680987f418858e89dbb4f09c8c919ece62811780a27051ace72b2f69fb1be",
 | 
				
			||||||
                "sha256:934e460c5058346c6f1d62fdf3db5680fbdfbfd212722d24d8277bf47cd9ebdc",
 | 
					                "sha256:d8fae5ba3d34c868ae43614e0bd6fb61114b2687ac3255798791ce075d95aece",
 | 
				
			||||||
                "sha256:9736f3f3e1761024200637a080a4f922f5298ad5d780e10dbb5634fe8c65b34c",
 | 
					                "sha256:dbd2c361db939a4252589baa94da4404d45e3fc70da1a31e541644cdf354336e",
 | 
				
			||||||
                "sha256:a1d38a96da57e6103423a446079ead600b450cf0f8ebf56a231895abf77e7ffc",
 | 
					                "sha256:e090a8609e2095aa86978559b140cf8968af99ee54b8791b29ff804838f29f10",
 | 
				
			||||||
                "sha256:a385fceaa0cdb97f0098f1c1e9ec0b46cc09186ddf60ec23538e871b1dddb6dc",
 | 
					                "sha256:e4a1245e7b846e88ba63e7543483bda61b9acbaee61eadbead5a1ce479d94740",
 | 
				
			||||||
                "sha256:a7cf1c14e47027d9fb9d26aa62e5d603994227bd635e58a8df4b1d2d1b6a8ed7",
 | 
					                "sha256:ec9901d19cadb80d9235ee41cc58983f18660314a0eb3fc7b11b0522ac3b6c4a",
 | 
				
			||||||
                "sha256:a9aac1a30b00b5038d3d8e48248f3b58ea15c827b67325c0d18a447552e30fc8",
 | 
					                "sha256:f2abeb4c4ce7584912f4d637b2c57f23720d35dd2892bfeb1b2c84b6fb7a8c88",
 | 
				
			||||||
                "sha256:b696876ee583d15310be57311e90e153a84b7913ac93e6b99675c0c9867926d0",
 | 
					                "sha256:f3bb267df679f70a9f40f17d62d22fe12e8b75e490f41807e7560de4d3e6bf9f",
 | 
				
			||||||
                "sha256:bef9e9d39393dc7baec39ba4bac6c73826a4db02114cdeade2552a9d6afa16e2",
 | 
					                "sha256:f933ecf4cb736c7af60a6a533db2bf569717f2318b265f92907acff1db43bc34",
 | 
				
			||||||
                "sha256:c885fe4d5f26ce8ca20c97d02e88f5fdd92c01e1cc771ad0951b21e1641faf6d",
 | 
					                "sha256:fc9c55dc1ed57db76595f2d19a479fc1c3a1be2c9da8de798a93d286c5f65f38"
 | 
				
			||||||
                "sha256:d2d1388595cb5d27d9220d5cbaff4f37c6ec696a25882eb06d224d241e6e93fb",
 | 
					 | 
				
			||||||
                "sha256:d2e853e0f9535e693fade97768cf7293f3febabecc5feb1e9b2ffdfe1044ab96",
 | 
					 | 
				
			||||||
                "sha256:d62fbab185a6b01c5469eda9f0795f3d1a5bba24f5a5813f362e4b73a3c4dc70",
 | 
					 | 
				
			||||||
                "sha256:f20a62397e09704049ce9007bea4f6bad965ba9336a760c6f4ef1b4192e12d6d",
 | 
					 | 
				
			||||||
                "sha256:f81f7311250d9480e36dec819127897ae772e7e8de07abfabe931b8566770b8e"
 | 
					 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "version": "==3.9.9"
 | 
					            "version": "==3.10.1"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "pyhamcrest": {
 | 
					        "pyhamcrest": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
@ -1445,10 +1428,10 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "gitpython": {
 | 
					        "gitpython": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:42dbefd8d9e2576c496ed0059f3103dcef7125b9ce16f9d5f9c834aed44a1dac",
 | 
					                "sha256:8621a7e777e276a5ec838b59280ba5272dd144a18169c36c903d8b38b99f750a",
 | 
				
			||||||
                "sha256:867ec3dfb126aac0f8296b19fb63b8c4a399f32b4b6fafe84c4b10af5fa9f7b5"
 | 
					                "sha256:c5347c81d232d9b8e7f47b68a83e5dc92e7952127133c5f2df9133f2c75a1b29"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "version": "==3.1.12"
 | 
					            "version": "==3.1.13"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "iniconfig": {
 | 
					        "iniconfig": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
 | 
				
			|||||||
@ -1,2 +1,2 @@
 | 
				
			|||||||
"""authentik"""
 | 
					"""authentik"""
 | 
				
			||||||
__version__ = "2021.2.1-rc1"
 | 
					__version__ = "2021.2.3-stable"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +0,0 @@
 | 
				
			|||||||
"""authentik core source form fields"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SOURCE_FORM_FIELDS = [
 | 
					 | 
				
			||||||
    "name",
 | 
					 | 
				
			||||||
    "slug",
 | 
					 | 
				
			||||||
    "enabled",
 | 
					 | 
				
			||||||
    "authentication_flow",
 | 
					 | 
				
			||||||
    "enrollment_flow",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
SOURCE_SERIALIZER_FIELDS = [
 | 
					 | 
				
			||||||
    "pk",
 | 
					 | 
				
			||||||
    "name",
 | 
					 | 
				
			||||||
    "slug",
 | 
					 | 
				
			||||||
    "enabled",
 | 
					 | 
				
			||||||
    "authentication_flow",
 | 
					 | 
				
			||||||
    "enrollment_flow",
 | 
					 | 
				
			||||||
    "verbose_name",
 | 
					 | 
				
			||||||
    "verbose_name_plural",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					{% extends base_template|default:"generic/form.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load authentik_utils %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block above_form %}
 | 
				
			||||||
 | 
					<h1>
 | 
				
			||||||
 | 
					    {% trans 'Generate Certificate-Key Pair' %}
 | 
				
			||||||
 | 
					</h1>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block action %}
 | 
				
			||||||
 | 
					{% trans 'Generate Certificate-Key Pair' %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
@ -26,6 +26,12 @@
 | 
				
			|||||||
                        </ak-spinner-button>
 | 
					                        </ak-spinner-button>
 | 
				
			||||||
                        <div slot="modal"></div>
 | 
					                        <div slot="modal"></div>
 | 
				
			||||||
                    </ak-modal-button>
 | 
					                    </ak-modal-button>
 | 
				
			||||||
 | 
					                    <ak-modal-button href="{% url 'authentik_admin:certificatekeypair-generate' %}">
 | 
				
			||||||
 | 
					                        <ak-spinner-button slot="trigger" class="pf-m-primary">
 | 
				
			||||||
 | 
					                            {% trans 'Generate' %}
 | 
				
			||||||
 | 
					                        </ak-spinner-button>
 | 
				
			||||||
 | 
					                        <div slot="modal"></div>
 | 
				
			||||||
 | 
					                    </ak-modal-button>
 | 
				
			||||||
                    <button role="ak-refresh" class="pf-c-button pf-m-primary">
 | 
					                    <button role="ak-refresh" class="pf-c-button pf-m-primary">
 | 
				
			||||||
                        {% trans 'Refresh' %}
 | 
					                        {% trans 'Refresh' %}
 | 
				
			||||||
                    </button>
 | 
					                    </button>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,149 +0,0 @@
 | 
				
			|||||||
{% extends "administration/base.html" %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% load i18n %}
 | 
					 | 
				
			||||||
{% load humanize %}
 | 
					 | 
				
			||||||
{% load authentik_utils %}
 | 
					 | 
				
			||||||
{% load admin_reflection %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% block content %}
 | 
					 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-light">
 | 
					 | 
				
			||||||
    <div class="pf-c-content">
 | 
					 | 
				
			||||||
        <h1>
 | 
					 | 
				
			||||||
            <i class="pf-icon pf-icon-zone"></i>
 | 
					 | 
				
			||||||
            {% trans 'Outposts' %}
 | 
					 | 
				
			||||||
        </h1>
 | 
					 | 
				
			||||||
        <p>{% trans "Outposts are deployments of authentik components to support different environments and protocols, like reverse proxies." %}</p>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
 | 
					 | 
				
			||||||
    <div class="pf-c-card">
 | 
					 | 
				
			||||||
        {% if object_list %}
 | 
					 | 
				
			||||||
        <div class="pf-c-toolbar">
 | 
					 | 
				
			||||||
            <div class="pf-c-toolbar__content">
 | 
					 | 
				
			||||||
                {% include 'partials/toolbar_search.html' %}
 | 
					 | 
				
			||||||
                <div class="pf-c-toolbar__bulk-select">
 | 
					 | 
				
			||||||
                    <ak-modal-button href="{% url 'authentik_admin:outpost-create' %}">
 | 
					 | 
				
			||||||
                        <ak-spinner-button slot="trigger" class="pf-m-primary">
 | 
					 | 
				
			||||||
                            {% trans 'Create' %}
 | 
					 | 
				
			||||||
                        </ak-spinner-button>
 | 
					 | 
				
			||||||
                        <div slot="modal"></div>
 | 
					 | 
				
			||||||
                    </ak-modal-button>
 | 
					 | 
				
			||||||
                    <button role="ak-refresh" class="pf-c-button pf-m-primary">
 | 
					 | 
				
			||||||
                        {% trans 'Refresh' %}
 | 
					 | 
				
			||||||
                    </button>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                {% include 'partials/pagination.html' %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
 | 
					 | 
				
			||||||
            <thead>
 | 
					 | 
				
			||||||
                <tr role="row">
 | 
					 | 
				
			||||||
                    <th role="columnheader" scope="col">{% trans 'Name' %}</th>
 | 
					 | 
				
			||||||
                    <th role="columnheader" scope="col">{% trans 'Providers' %}</th>
 | 
					 | 
				
			||||||
                    <th role="columnheader" scope="col">{% trans 'Health' %}</th>
 | 
					 | 
				
			||||||
                    <th role="columnheader" scope="col">{% trans 'Version' %}</th>
 | 
					 | 
				
			||||||
                    <th role="cell"></th>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
            </thead>
 | 
					 | 
				
			||||||
            <tbody role="rowgroup">
 | 
					 | 
				
			||||||
                {% for outpost in object_list %}
 | 
					 | 
				
			||||||
                <tr role="row">
 | 
					 | 
				
			||||||
                    <th role="columnheader">
 | 
					 | 
				
			||||||
                        <span>{{ outpost.name }}</span>
 | 
					 | 
				
			||||||
                    </th>
 | 
					 | 
				
			||||||
                    <td role="cell">
 | 
					 | 
				
			||||||
                        <span>
 | 
					 | 
				
			||||||
                            {{ outpost.providers.all.select_subclasses|join:", " }}
 | 
					 | 
				
			||||||
                        </span>
 | 
					 | 
				
			||||||
                    </td>
 | 
					 | 
				
			||||||
                    {% with states=outpost.state %}
 | 
					 | 
				
			||||||
                    {% if states|length > 0 %}
 | 
					 | 
				
			||||||
                        <td role="cell">
 | 
					 | 
				
			||||||
                            {% for state in states %}
 | 
					 | 
				
			||||||
                            <div>
 | 
					 | 
				
			||||||
                                {% if state.last_seen %}
 | 
					 | 
				
			||||||
                                <i class="fas fa-check pf-m-success"></i> {{ state.last_seen|naturaltime }}
 | 
					 | 
				
			||||||
                                {% else %}
 | 
					 | 
				
			||||||
                                <i class="fas fa-times pf-m-danger"></i> {% trans 'Unhealthy' %}
 | 
					 | 
				
			||||||
                                {% endif %}
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                            {% endfor %}
 | 
					 | 
				
			||||||
                        </td>
 | 
					 | 
				
			||||||
                        <td role="cell">
 | 
					 | 
				
			||||||
                            {% for state in states %}
 | 
					 | 
				
			||||||
                                <div>
 | 
					 | 
				
			||||||
                                    {% if not state.version %}
 | 
					 | 
				
			||||||
                                    <i class="fas fa-question-circle"></i>
 | 
					 | 
				
			||||||
                                    {% elif state.version_outdated %}
 | 
					 | 
				
			||||||
                                    <i class="fas fa-times pf-m-danger"></i> {% blocktrans with is=state.version should=state.version_should %}{{ is }}, should be {{ should }}{% endblocktrans %}
 | 
					 | 
				
			||||||
                                    {% else %}
 | 
					 | 
				
			||||||
                                    <i class="fas fa-check pf-m-success"></i> {{ state.version }}
 | 
					 | 
				
			||||||
                                    {% endif %}
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                            {% endfor %}
 | 
					 | 
				
			||||||
                        </td>
 | 
					 | 
				
			||||||
                    {% else %}
 | 
					 | 
				
			||||||
                        <td role="cell">
 | 
					 | 
				
			||||||
                            <i class="fas fa-question-circle"></i>
 | 
					 | 
				
			||||||
                        </td>
 | 
					 | 
				
			||||||
                        <td role="cell">
 | 
					 | 
				
			||||||
                            <i class="fas fa-question-circle"></i>
 | 
					 | 
				
			||||||
                        </td>
 | 
					 | 
				
			||||||
                    {% endif %}
 | 
					 | 
				
			||||||
                    {% endwith %}
 | 
					 | 
				
			||||||
                    <td>
 | 
					 | 
				
			||||||
                        <ak-modal-button href="{% url 'authentik_admin:outpost-update' pk=outpost.pk %}">
 | 
					 | 
				
			||||||
                            <ak-spinner-button slot="trigger" class="pf-m-secondary">
 | 
					 | 
				
			||||||
                                {% trans 'Edit' %}
 | 
					 | 
				
			||||||
                            </ak-spinner-button>
 | 
					 | 
				
			||||||
                            <div slot="modal"></div>
 | 
					 | 
				
			||||||
                        </ak-modal-button>
 | 
					 | 
				
			||||||
                        <ak-modal-button href="{% url 'authentik_admin:outpost-delete' pk=outpost.pk %}">
 | 
					 | 
				
			||||||
                            <ak-spinner-button slot="trigger" class="pf-m-danger">
 | 
					 | 
				
			||||||
                                {% trans 'Delete' %}
 | 
					 | 
				
			||||||
                            </ak-spinner-button>
 | 
					 | 
				
			||||||
                            <div slot="modal"></div>
 | 
					 | 
				
			||||||
                        </ak-modal-button>
 | 
					 | 
				
			||||||
                        {% get_htmls outpost as htmls %}
 | 
					 | 
				
			||||||
                        {% for html in htmls %}
 | 
					 | 
				
			||||||
                        {{ html|safe }}
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                    </td>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
                {% endfor %}
 | 
					 | 
				
			||||||
            </tbody>
 | 
					 | 
				
			||||||
        </table>
 | 
					 | 
				
			||||||
        <div class="pf-c-pagination pf-m-bottom">
 | 
					 | 
				
			||||||
            {% include 'partials/pagination.html' %}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        {% else %}
 | 
					 | 
				
			||||||
        <div class="pf-c-toolbar">
 | 
					 | 
				
			||||||
            <div class="pf-c-toolbar__content">
 | 
					 | 
				
			||||||
                {% include 'partials/toolbar_search.html' %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="pf-c-empty-state">
 | 
					 | 
				
			||||||
            <div class="pf-c-empty-state__content">
 | 
					 | 
				
			||||||
                <i class="fas fa-map-marker pf-c-empty-state__icon" aria-hidden="true"></i>
 | 
					 | 
				
			||||||
                <h1 class="pf-c-title pf-m-lg">
 | 
					 | 
				
			||||||
                    {% trans 'No Outposts.' %}
 | 
					 | 
				
			||||||
                </h1>
 | 
					 | 
				
			||||||
                <div class="pf-c-empty-state__body">
 | 
					 | 
				
			||||||
                {% if request.GET.search != "" %}
 | 
					 | 
				
			||||||
                    {% trans "Your search query doesn't match any outposts." %}
 | 
					 | 
				
			||||||
                {% else %}
 | 
					 | 
				
			||||||
                    {% trans 'Currently no outposts exist. Click the button below to create one.' %}
 | 
					 | 
				
			||||||
                {% endif %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <ak-modal-button href="{% url 'authentik_admin:outpost-create' %}">
 | 
					 | 
				
			||||||
                    <ak-spinner-button slot="trigger" class="pf-m-primary">
 | 
					 | 
				
			||||||
                        {% trans 'Create' %}
 | 
					 | 
				
			||||||
                    </ak-spinner-button>
 | 
					 | 
				
			||||||
                    <div slot="modal"></div>
 | 
					 | 
				
			||||||
                </ak-modal-button>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        {% endif %}
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					 | 
				
			||||||
@ -3,7 +3,6 @@
 | 
				
			|||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
{% load humanize %}
 | 
					{% load humanize %}
 | 
				
			||||||
{% load authentik_utils %}
 | 
					{% load authentik_utils %}
 | 
				
			||||||
{% load admin_reflection %}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-light">
 | 
					<section class="pf-c-page__main-section pf-m-light">
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@
 | 
				
			|||||||
            <span class="pf-c-form__label-text">{% trans 'Passing' %}</span>
 | 
					            <span class="pf-c-form__label-text">{% trans 'Passing' %}</span>
 | 
				
			||||||
        </label>
 | 
					        </label>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="pf-c-form__group-control">
 | 
					    <div class="pf-c-form__group-label">
 | 
				
			||||||
        <div class="c-form__horizontal-group">
 | 
					        <div class="c-form__horizontal-group">
 | 
				
			||||||
            <span class="pf-c-form__label-text">{{ result.passing|yesno:"Yes,No" }}</span>
 | 
					            <span class="pf-c-form__label-text">{{ result.passing|yesno:"Yes,No" }}</span>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
@ -26,7 +26,7 @@
 | 
				
			|||||||
            <span class="pf-c-form__label-text">{% trans 'Messages' %}</span>
 | 
					            <span class="pf-c-form__label-text">{% trans 'Messages' %}</span>
 | 
				
			||||||
        </label>
 | 
					        </label>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="pf-c-form__group-control">
 | 
					    <div class="pf-c-form__group-label">
 | 
				
			||||||
        <div class="c-form__horizontal-group">
 | 
					        <div class="c-form__horizontal-group">
 | 
				
			||||||
            <ul>
 | 
					            <ul>
 | 
				
			||||||
                {% for m in result.messages %}
 | 
					                {% for m in result.messages %}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,139 +0,0 @@
 | 
				
			|||||||
{% extends "administration/base.html" %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% load i18n %}
 | 
					 | 
				
			||||||
{% load authentik_utils %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% block content %}
 | 
					 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-light">
 | 
					 | 
				
			||||||
    <div class="pf-c-content">
 | 
					 | 
				
			||||||
        <h1>
 | 
					 | 
				
			||||||
            <i class="pf-icon pf-icon-blueprint"></i>
 | 
					 | 
				
			||||||
            {% trans 'Property Mappings' %}
 | 
					 | 
				
			||||||
        </h1>
 | 
					 | 
				
			||||||
        <p>{% trans "Control how authentik exposes and interprets information." %}
 | 
					 | 
				
			||||||
        </p>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
 | 
					 | 
				
			||||||
    <div class="pf-c-card">
 | 
					 | 
				
			||||||
        {% if object_list %}
 | 
					 | 
				
			||||||
        <div class="pf-c-toolbar">
 | 
					 | 
				
			||||||
            <div class="pf-c-toolbar__content">
 | 
					 | 
				
			||||||
                {% include 'partials/toolbar_search.html' %}
 | 
					 | 
				
			||||||
                <div class="pf-c-toolbar__bulk-select">
 | 
					 | 
				
			||||||
                    <ak-dropdown class="pf-c-dropdown">
 | 
					 | 
				
			||||||
                        <button class="pf-m-primary pf-c-dropdown__toggle" type="button">
 | 
					 | 
				
			||||||
                            <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
 | 
					 | 
				
			||||||
                            <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
 | 
					 | 
				
			||||||
                        </button>
 | 
					 | 
				
			||||||
                        <ul class="pf-c-dropdown__menu" hidden>
 | 
					 | 
				
			||||||
                            {% for type, name in types.items %}
 | 
					 | 
				
			||||||
                            <li>
 | 
					 | 
				
			||||||
                                <ak-modal-button href="{% url 'authentik_admin:property-mapping-create' %}?type={{ type }}">
 | 
					 | 
				
			||||||
                                    <button slot="trigger" class="pf-c-dropdown__menu-item">
 | 
					 | 
				
			||||||
                                        {{ name|verbose_name }}<br>
 | 
					 | 
				
			||||||
                                        <small>
 | 
					 | 
				
			||||||
                                            {{ name|doc }}
 | 
					 | 
				
			||||||
                                        </small>
 | 
					 | 
				
			||||||
                                    </button>
 | 
					 | 
				
			||||||
                                    <div slot="modal"></div>
 | 
					 | 
				
			||||||
                                </ak-modal-button>
 | 
					 | 
				
			||||||
                            </li>
 | 
					 | 
				
			||||||
                            {% endfor %}
 | 
					 | 
				
			||||||
                        </ul>
 | 
					 | 
				
			||||||
                    </ak-dropdown>
 | 
					 | 
				
			||||||
                    <button role="ak-refresh" class="pf-c-button pf-m-primary">
 | 
					 | 
				
			||||||
                        {% trans 'Refresh' %}
 | 
					 | 
				
			||||||
                    </button>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                {% include 'partials/pagination.html' %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
 | 
					 | 
				
			||||||
            <thead>
 | 
					 | 
				
			||||||
                <tr role="row">
 | 
					 | 
				
			||||||
                    <th role="columnheader" scope="col">{% trans 'Name' %}</th>
 | 
					 | 
				
			||||||
                    <th role="columnheader" scope="col">{% trans 'Type' %}</th>
 | 
					 | 
				
			||||||
                    <th role="cell"></th>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
            </thead>
 | 
					 | 
				
			||||||
            <tbody role="rowgroup">
 | 
					 | 
				
			||||||
                {% for property_mapping in object_list %}
 | 
					 | 
				
			||||||
                <tr role="row">
 | 
					 | 
				
			||||||
                    <td role="cell">
 | 
					 | 
				
			||||||
                        <span>
 | 
					 | 
				
			||||||
                            {{ property_mapping.name }}
 | 
					 | 
				
			||||||
                        </span>
 | 
					 | 
				
			||||||
                    </td>
 | 
					 | 
				
			||||||
                    <td role="cell">
 | 
					 | 
				
			||||||
                        <span>
 | 
					 | 
				
			||||||
                            {{ property_mapping|verbose_name }}
 | 
					 | 
				
			||||||
                        </span>
 | 
					 | 
				
			||||||
                    </td>
 | 
					 | 
				
			||||||
                    <td>
 | 
					 | 
				
			||||||
                        <ak-modal-button href="{% url 'authentik_admin:property-mapping-update' pk=property_mapping.pk %}">
 | 
					 | 
				
			||||||
                            <ak-spinner-button slot="trigger" class="pf-m-secondary">
 | 
					 | 
				
			||||||
                                {% trans 'Edit' %}
 | 
					 | 
				
			||||||
                            </ak-spinner-button>
 | 
					 | 
				
			||||||
                            <div slot="modal"></div>
 | 
					 | 
				
			||||||
                        </ak-modal-button>
 | 
					 | 
				
			||||||
                        <ak-modal-button href="{% url 'authentik_admin:property-mapping-delete' pk=property_mapping.pk %}">
 | 
					 | 
				
			||||||
                            <ak-spinner-button slot="trigger" class="pf-m-danger">
 | 
					 | 
				
			||||||
                                {% trans 'Delete' %}
 | 
					 | 
				
			||||||
                            </ak-spinner-button>
 | 
					 | 
				
			||||||
                            <div slot="modal"></div>
 | 
					 | 
				
			||||||
                        </ak-modal-button>
 | 
					 | 
				
			||||||
                    </td>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
                {% endfor %}
 | 
					 | 
				
			||||||
            </tbody>
 | 
					 | 
				
			||||||
        </table>
 | 
					 | 
				
			||||||
        <div class="pf-c-pagination pf-m-bottom">
 | 
					 | 
				
			||||||
            {% include 'partials/pagination.html' %}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        {% else %}
 | 
					 | 
				
			||||||
        <div class="pf-c-toolbar">
 | 
					 | 
				
			||||||
            <div class="pf-c-toolbar__content">
 | 
					 | 
				
			||||||
                {% include 'partials/toolbar_search.html' %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="pf-c-empty-state">
 | 
					 | 
				
			||||||
            <div class="pf-c-empty-state__content">
 | 
					 | 
				
			||||||
                <i class="pf-icon pf-icon-blueprint pf-c-empty-state__icon" aria-hidden="true"></i>
 | 
					 | 
				
			||||||
                <h1 class="pf-c-title pf-m-lg">
 | 
					 | 
				
			||||||
                    {% trans 'No Property Mappings.' %}
 | 
					 | 
				
			||||||
                </h1>
 | 
					 | 
				
			||||||
                <div class="pf-c-empty-state__body">
 | 
					 | 
				
			||||||
                {% if request.GET.search != "" %}
 | 
					 | 
				
			||||||
                    {% trans "Your search query doesn't match any property mappings." %}
 | 
					 | 
				
			||||||
                {% else %}
 | 
					 | 
				
			||||||
                    {% trans 'Currently no property mappings exist. Click the button below to create one.' %}
 | 
					 | 
				
			||||||
                {% endif %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <ak-dropdown class="pf-c-dropdown">
 | 
					 | 
				
			||||||
                    <button class="pf-m-primary pf-c-dropdown__toggle" type="button">
 | 
					 | 
				
			||||||
                        <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
 | 
					 | 
				
			||||||
                        <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
 | 
					 | 
				
			||||||
                    </button>
 | 
					 | 
				
			||||||
                    <ul class="pf-c-dropdown__menu" hidden>
 | 
					 | 
				
			||||||
                        {% for type, name in types.items %}
 | 
					 | 
				
			||||||
                        <li>
 | 
					 | 
				
			||||||
                            <ak-modal-button href="{% url 'authentik_admin:property-mapping-create' %}?type={{ type }}">
 | 
					 | 
				
			||||||
                                <button slot="trigger" class="pf-c-dropdown__menu-item">
 | 
					 | 
				
			||||||
                                    {{ name|verbose_name }}<br>
 | 
					 | 
				
			||||||
                                    <small>
 | 
					 | 
				
			||||||
                                        {{ name|doc }}
 | 
					 | 
				
			||||||
                                    </small>
 | 
					 | 
				
			||||||
                                </button>
 | 
					 | 
				
			||||||
                                <div slot="modal"></div>
 | 
					 | 
				
			||||||
                            </ak-modal-button>
 | 
					 | 
				
			||||||
                        </li>
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                    </ul>
 | 
					 | 
				
			||||||
                </ak-dropdown>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        {% endif %}
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					 | 
				
			||||||
@ -1,181 +0,0 @@
 | 
				
			|||||||
{% extends "administration/base.html" %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% load i18n %}
 | 
					 | 
				
			||||||
{% load authentik_utils %}
 | 
					 | 
				
			||||||
{% load admin_reflection %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% block content %}
 | 
					 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-light">
 | 
					 | 
				
			||||||
    <div class="pf-c-content">
 | 
					 | 
				
			||||||
        <h1>
 | 
					 | 
				
			||||||
            <i class="pf-icon pf-icon-integration"></i>
 | 
					 | 
				
			||||||
            {% trans 'Providers' %}
 | 
					 | 
				
			||||||
        </h1>
 | 
					 | 
				
			||||||
        <p>{% trans "Provide support for protocols like SAML and OAuth to assigned applications." %}
 | 
					 | 
				
			||||||
    </p>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
 | 
					 | 
				
			||||||
    <div class="pf-c-card">
 | 
					 | 
				
			||||||
        {% if object_list %}
 | 
					 | 
				
			||||||
        <div class="pf-c-toolbar">
 | 
					 | 
				
			||||||
            <div class="pf-c-toolbar__content">
 | 
					 | 
				
			||||||
                {% include 'partials/toolbar_search.html' %}
 | 
					 | 
				
			||||||
                <div class="pf-c-toolbar__bulk-select">
 | 
					 | 
				
			||||||
                    <ak-dropdown class="pf-c-dropdown">
 | 
					 | 
				
			||||||
                        <button class="pf-m-primary pf-c-dropdown__toggle" type="button">
 | 
					 | 
				
			||||||
                            <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
 | 
					 | 
				
			||||||
                            <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
 | 
					 | 
				
			||||||
                        </button>
 | 
					 | 
				
			||||||
                        <ul class="pf-c-dropdown__menu" hidden>
 | 
					 | 
				
			||||||
                            {% for type, name in types.items %}
 | 
					 | 
				
			||||||
                            <li>
 | 
					 | 
				
			||||||
                                <ak-modal-button href="{% url 'authentik_admin:provider-create' %}?type={{ type }}">
 | 
					 | 
				
			||||||
                                    <button slot="trigger" class="pf-c-dropdown__menu-item">
 | 
					 | 
				
			||||||
                                        {{ name|verbose_name }}<br>
 | 
					 | 
				
			||||||
                                        <small>
 | 
					 | 
				
			||||||
                                            {{ name|doc }}
 | 
					 | 
				
			||||||
                                        </small>
 | 
					 | 
				
			||||||
                                    </button>
 | 
					 | 
				
			||||||
                                    <div slot="modal"></div>
 | 
					 | 
				
			||||||
                                </ak-modal-button>
 | 
					 | 
				
			||||||
                            </li>
 | 
					 | 
				
			||||||
                            {% endfor %}
 | 
					 | 
				
			||||||
                            <li>
 | 
					 | 
				
			||||||
                                <ak-modal-button href="{% url 'authentik_admin:provider-saml-from-metadata' %}">
 | 
					 | 
				
			||||||
                                    <button slot="trigger" class="pf-c-dropdown__menu-item">
 | 
					 | 
				
			||||||
                                        {% trans 'SAML Provider from Metadata' %}<br>
 | 
					 | 
				
			||||||
                                        <small>
 | 
					 | 
				
			||||||
                                            {% trans "Create a SAML Provider by importing its Metadata." %}
 | 
					 | 
				
			||||||
                                        </small>
 | 
					 | 
				
			||||||
                                    </button>
 | 
					 | 
				
			||||||
                                    <div slot="modal"></div>
 | 
					 | 
				
			||||||
                                </ak-modal-button>
 | 
					 | 
				
			||||||
                            </li>
 | 
					 | 
				
			||||||
                        </ul>
 | 
					 | 
				
			||||||
                    </ak-dropdown>
 | 
					 | 
				
			||||||
                    <button role="ak-refresh" class="pf-c-button pf-m-primary">
 | 
					 | 
				
			||||||
                        {% trans 'Refresh' %}
 | 
					 | 
				
			||||||
                    </button>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                {% include 'partials/pagination.html' %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
 | 
					 | 
				
			||||||
            <thead>
 | 
					 | 
				
			||||||
                <tr role="row">
 | 
					 | 
				
			||||||
                    <th role="columnheader" scope="col">{% trans 'Name' %}</th>
 | 
					 | 
				
			||||||
                    <th role="columnheader" scope="col">{% trans 'Type' %}</th>
 | 
					 | 
				
			||||||
                    <th role="cell"></th>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
            </thead>
 | 
					 | 
				
			||||||
            <tbody role="rowgroup">
 | 
					 | 
				
			||||||
                {% for provider in object_list %}
 | 
					 | 
				
			||||||
                <tr role="row">
 | 
					 | 
				
			||||||
                    <th role="columnheader">
 | 
					 | 
				
			||||||
                        <div>
 | 
					 | 
				
			||||||
                            <div>{{ provider.name }}</div>
 | 
					 | 
				
			||||||
                            {% if not provider.application %}
 | 
					 | 
				
			||||||
                            <i class="pf-icon pf-icon-warning-triangle"></i>
 | 
					 | 
				
			||||||
                            <small>{% trans 'Warning: Provider not assigned to any application.' %}</small>
 | 
					 | 
				
			||||||
                            {% else %}
 | 
					 | 
				
			||||||
                            <i class="pf-icon pf-icon-ok"></i>
 | 
					 | 
				
			||||||
                            <small>
 | 
					 | 
				
			||||||
                                {% blocktrans with app=provider.application %}
 | 
					 | 
				
			||||||
                                    Assigned to application {{ app }}.
 | 
					 | 
				
			||||||
                                {% endblocktrans %}
 | 
					 | 
				
			||||||
                            </small>
 | 
					 | 
				
			||||||
                            {% endif %}
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    </th>
 | 
					 | 
				
			||||||
                    <td role="cell">
 | 
					 | 
				
			||||||
                        <span>
 | 
					 | 
				
			||||||
                            {{ provider|verbose_name }}
 | 
					 | 
				
			||||||
                        </span>
 | 
					 | 
				
			||||||
                    </td>
 | 
					 | 
				
			||||||
                    <td>
 | 
					 | 
				
			||||||
                        <ak-modal-button href="{% url 'authentik_admin:provider-update' pk=provider.pk %}">
 | 
					 | 
				
			||||||
                            <ak-spinner-button slot="trigger" class="pf-m-secondary">
 | 
					 | 
				
			||||||
                                {% trans 'Edit' %}
 | 
					 | 
				
			||||||
                            </ak-spinner-button>
 | 
					 | 
				
			||||||
                            <div slot="modal"></div>
 | 
					 | 
				
			||||||
                        </ak-modal-button>
 | 
					 | 
				
			||||||
                        <ak-modal-button href="{% url 'authentik_admin:provider-delete' pk=provider.pk %}">
 | 
					 | 
				
			||||||
                            <ak-spinner-button slot="trigger" class="pf-m-danger">
 | 
					 | 
				
			||||||
                                {% trans 'Delete' %}
 | 
					 | 
				
			||||||
                            </ak-spinner-button>
 | 
					 | 
				
			||||||
                            <div slot="modal"></div>
 | 
					 | 
				
			||||||
                        </ak-modal-button>
 | 
					 | 
				
			||||||
                        {% get_links provider as links %}
 | 
					 | 
				
			||||||
                        {% for name, href in links.items %}
 | 
					 | 
				
			||||||
                            <a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                        {% get_htmls provider as htmls %}
 | 
					 | 
				
			||||||
                        {% for html in htmls %}
 | 
					 | 
				
			||||||
                            {{ html|safe }}
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                    </td>
 | 
					 | 
				
			||||||
                </tr>
 | 
					 | 
				
			||||||
                {% endfor %}
 | 
					 | 
				
			||||||
            </tbody>
 | 
					 | 
				
			||||||
        </table>
 | 
					 | 
				
			||||||
        <div class="pf-c-pagination pf-m-bottom">
 | 
					 | 
				
			||||||
            {% include 'partials/pagination.html' %}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        {% else %}
 | 
					 | 
				
			||||||
        <div class="pf-c-toolbar">
 | 
					 | 
				
			||||||
            <div class="pf-c-toolbar__content">
 | 
					 | 
				
			||||||
                {% include 'partials/toolbar_search.html' %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="pf-c-empty-state">
 | 
					 | 
				
			||||||
            <div class="pf-c-empty-state__content">
 | 
					 | 
				
			||||||
                <i class="pf-icon-integration pf-c-empty-state__icon" aria-hidden="true"></i>
 | 
					 | 
				
			||||||
                <h1 class="pf-c-title pf-m-lg">
 | 
					 | 
				
			||||||
                    {% trans 'No Providers.' %}
 | 
					 | 
				
			||||||
                </h1>
 | 
					 | 
				
			||||||
                <div class="pf-c-empty-state__body">
 | 
					 | 
				
			||||||
                {% if request.GET.search != "" %}
 | 
					 | 
				
			||||||
                    {% trans "Your search query doesn't match any providers." %}
 | 
					 | 
				
			||||||
                {% else %}
 | 
					 | 
				
			||||||
                    {% trans 'Currently no providers exist. Click the button below to create one.' %}
 | 
					 | 
				
			||||||
                {% endif %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <ak-dropdown class="pf-c-dropdown">
 | 
					 | 
				
			||||||
                    <button class="pf-m-primary pf-c-dropdown__toggle" type="button">
 | 
					 | 
				
			||||||
                        <span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
 | 
					 | 
				
			||||||
                        <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
 | 
					 | 
				
			||||||
                    </button>
 | 
					 | 
				
			||||||
                    <ul class="pf-c-dropdown__menu" hidden>
 | 
					 | 
				
			||||||
                        {% for type, name in types.items %}
 | 
					 | 
				
			||||||
                        <li>
 | 
					 | 
				
			||||||
                            <ak-modal-button href="{% url 'authentik_admin:provider-create' %}?type={{ type }}">
 | 
					 | 
				
			||||||
                                <button slot="trigger" class="pf-c-dropdown__menu-item">
 | 
					 | 
				
			||||||
                                    {{ name|verbose_name }}<br>
 | 
					 | 
				
			||||||
                                    <small>
 | 
					 | 
				
			||||||
                                        {{ name|doc }}
 | 
					 | 
				
			||||||
                                    </small>
 | 
					 | 
				
			||||||
                                </button>
 | 
					 | 
				
			||||||
                                <div slot="modal"></div>
 | 
					 | 
				
			||||||
                            </ak-modal-button>
 | 
					 | 
				
			||||||
                        </li>
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                        <li>
 | 
					 | 
				
			||||||
                            <ak-modal-button href="{% url 'authentik_admin:provider-saml-from-metadata' %}">
 | 
					 | 
				
			||||||
                                <button slot="trigger" class="pf-c-dropdown__menu-item">
 | 
					 | 
				
			||||||
                                    {% trans 'SAML Provider from Metadata' %}<br>
 | 
					 | 
				
			||||||
                                    <small>
 | 
					 | 
				
			||||||
                                        {% trans "Create a SAML Provider by importing its Metadata." %}
 | 
					 | 
				
			||||||
                                    </small>
 | 
					 | 
				
			||||||
                                </button>
 | 
					 | 
				
			||||||
                                <div slot="modal"></div>
 | 
					 | 
				
			||||||
                            </ak-modal-button>
 | 
					 | 
				
			||||||
                        </li>
 | 
					 | 
				
			||||||
                    </ul>
 | 
					 | 
				
			||||||
                </ak-dropdown>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        {% endif %}
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					 | 
				
			||||||
@ -2,7 +2,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
{% load authentik_utils %}
 | 
					{% load authentik_utils %}
 | 
				
			||||||
{% load admin_reflection %}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-light">
 | 
					<section class="pf-c-page__main-section pf-m-light">
 | 
				
			||||||
@ -63,7 +62,7 @@
 | 
				
			|||||||
                {% for source in object_list %}
 | 
					                {% for source in object_list %}
 | 
				
			||||||
                <tr role="row">
 | 
					                <tr role="row">
 | 
				
			||||||
                    <th role="columnheader">
 | 
					                    <th role="columnheader">
 | 
				
			||||||
                        <a href="/sources/{{ source.slug }}/">
 | 
					                        <a href="/sources/{{ source.slug }}">
 | 
				
			||||||
                            <div>{{ source.name }}</div>
 | 
					                            <div>{{ source.name }}</div>
 | 
				
			||||||
                            {% if not source.enabled %}
 | 
					                            {% if not source.enabled %}
 | 
				
			||||||
                            <small>{% trans 'Disabled' %}</small>
 | 
					                            <small>{% trans 'Disabled' %}</small>
 | 
				
			||||||
@ -93,10 +92,6 @@
 | 
				
			|||||||
                            </ak-spinner-button>
 | 
					                            </ak-spinner-button>
 | 
				
			||||||
                            <div slot="modal"></div>
 | 
					                            <div slot="modal"></div>
 | 
				
			||||||
                        </ak-modal-button>
 | 
					                        </ak-modal-button>
 | 
				
			||||||
                        {% get_links source as links %}
 | 
					 | 
				
			||||||
                        {% for name, href in links %}
 | 
					 | 
				
			||||||
                            <a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                    </td>
 | 
					                    </td>
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
                {% endfor %}
 | 
					                {% endfor %}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
{% load authentik_utils %}
 | 
					{% load authentik_utils %}
 | 
				
			||||||
{% load admin_reflection %}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-light">
 | 
					<section class="pf-c-page__main-section pf-m-light">
 | 
				
			||||||
@ -88,10 +87,6 @@
 | 
				
			|||||||
                            </ak-spinner-button>
 | 
					                            </ak-spinner-button>
 | 
				
			||||||
                            <div slot="modal"></div>
 | 
					                            <div slot="modal"></div>
 | 
				
			||||||
                        </ak-modal-button>
 | 
					                        </ak-modal-button>
 | 
				
			||||||
                        {% get_links stage as links %}
 | 
					 | 
				
			||||||
                        {% for name, href in links.items %}
 | 
					 | 
				
			||||||
                        <a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                    </td>
 | 
					                    </td>
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
                {% endfor %}
 | 
					                {% endfor %}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
{% load authentik_utils %}
 | 
					{% load authentik_utils %}
 | 
				
			||||||
{% load admin_reflection %}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-light">
 | 
					<section class="pf-c-page__main-section pf-m-light">
 | 
				
			||||||
@ -90,10 +89,6 @@
 | 
				
			|||||||
                            </ak-spinner-button>
 | 
					                            </ak-spinner-button>
 | 
				
			||||||
                            <div slot="modal"></div>
 | 
					                            <div slot="modal"></div>
 | 
				
			||||||
                        </ak-modal-button>
 | 
					                        </ak-modal-button>
 | 
				
			||||||
                        {% get_links prompt as links %}
 | 
					 | 
				
			||||||
                        {% for name, href in links.items %}
 | 
					 | 
				
			||||||
                        <a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                    </td>
 | 
					                    </td>
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
                {% endfor %}
 | 
					                {% endfor %}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,62 +0,0 @@
 | 
				
			|||||||
"""authentik admin templatetags"""
 | 
					 | 
				
			||||||
from django import template
 | 
					 | 
				
			||||||
from django.db.models import Model
 | 
					 | 
				
			||||||
from django.utils.html import mark_safe
 | 
					 | 
				
			||||||
from structlog.stdlib import get_logger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
register = template.Library()
 | 
					 | 
				
			||||||
LOGGER = get_logger()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@register.simple_tag()
 | 
					 | 
				
			||||||
def get_links(model_instance):
 | 
					 | 
				
			||||||
    """Find all link_ methods on an object instance, run them and return as dict"""
 | 
					 | 
				
			||||||
    prefix = "link_"
 | 
					 | 
				
			||||||
    links = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not isinstance(model_instance, Model):
 | 
					 | 
				
			||||||
        LOGGER.warning("Model is not instance of Model", model_instance=model_instance)
 | 
					 | 
				
			||||||
        return links
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        for name in dir(model_instance):
 | 
					 | 
				
			||||||
            if not name.startswith(prefix):
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            value = getattr(model_instance, name)
 | 
					 | 
				
			||||||
            if not callable(value):
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            human_name = name.replace(prefix, "").replace("_", " ").capitalize()
 | 
					 | 
				
			||||||
            link = value()
 | 
					 | 
				
			||||||
            if link:
 | 
					 | 
				
			||||||
                links[human_name] = link
 | 
					 | 
				
			||||||
    except NotImplementedError:
 | 
					 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return links
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@register.simple_tag(takes_context=True)
 | 
					 | 
				
			||||||
def get_htmls(context, model_instance):
 | 
					 | 
				
			||||||
    """Find all html_ methods on an object instance, run them and return as dict"""
 | 
					 | 
				
			||||||
    prefix = "html_"
 | 
					 | 
				
			||||||
    htmls = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not isinstance(model_instance, Model):
 | 
					 | 
				
			||||||
        LOGGER.warning("Model is not instance of Model", model_instance=model_instance)
 | 
					 | 
				
			||||||
        return htmls
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        for name in dir(model_instance):
 | 
					 | 
				
			||||||
            if not name.startswith(prefix):
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            value = getattr(model_instance, name)
 | 
					 | 
				
			||||||
            if not callable(value):
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            if name.startswith(prefix):
 | 
					 | 
				
			||||||
                html = value(context.get("request"))
 | 
					 | 
				
			||||||
                if html:
 | 
					 | 
				
			||||||
                    htmls.append(mark_safe(html))
 | 
					 | 
				
			||||||
    except NotImplementedError:
 | 
					 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return htmls
 | 
					 | 
				
			||||||
@ -24,7 +24,7 @@ from authentik.admin.views import (
 | 
				
			|||||||
    tokens,
 | 
					    tokens,
 | 
				
			||||||
    users,
 | 
					    users,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.providers.saml.views import MetadataImportView
 | 
					from authentik.providers.saml.views.metadata import MetadataImportView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
@ -61,7 +61,6 @@ urlpatterns = [
 | 
				
			|||||||
        name="token-delete",
 | 
					        name="token-delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Sources
 | 
					    # Sources
 | 
				
			||||||
    path("sources/", sources.SourceListView.as_view(), name="sources"),
 | 
					 | 
				
			||||||
    path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
 | 
					    path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "sources/<uuid:pk>/update/",
 | 
					        "sources/<uuid:pk>/update/",
 | 
				
			||||||
@ -113,7 +112,6 @@ urlpatterns = [
 | 
				
			|||||||
        name="policy-binding-delete",
 | 
					        name="policy-binding-delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Providers
 | 
					    # Providers
 | 
				
			||||||
    path("providers/", providers.ProviderListView.as_view(), name="providers"),
 | 
					 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "providers/create/",
 | 
					        "providers/create/",
 | 
				
			||||||
        providers.ProviderCreateView.as_view(),
 | 
					        providers.ProviderCreateView.as_view(),
 | 
				
			||||||
@ -170,22 +168,22 @@ urlpatterns = [
 | 
				
			|||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Stage Prompts
 | 
					    # Stage Prompts
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "stages/prompts/",
 | 
					        "stages_prompts/",
 | 
				
			||||||
        stages_prompts.PromptListView.as_view(),
 | 
					        stages_prompts.PromptListView.as_view(),
 | 
				
			||||||
        name="stage-prompts",
 | 
					        name="stage-prompts",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "stages/prompts/create/",
 | 
					        "stages_prompts/create/",
 | 
				
			||||||
        stages_prompts.PromptCreateView.as_view(),
 | 
					        stages_prompts.PromptCreateView.as_view(),
 | 
				
			||||||
        name="stage-prompt-create",
 | 
					        name="stage-prompt-create",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "stages/prompts/<uuid:pk>/update/",
 | 
					        "stages_prompts/<uuid:pk>/update/",
 | 
				
			||||||
        stages_prompts.PromptUpdateView.as_view(),
 | 
					        stages_prompts.PromptUpdateView.as_view(),
 | 
				
			||||||
        name="stage-prompt-update",
 | 
					        name="stage-prompt-update",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "stages/prompts/<uuid:pk>/delete/",
 | 
					        "stages_prompts/<uuid:pk>/delete/",
 | 
				
			||||||
        stages_prompts.PromptDeleteView.as_view(),
 | 
					        stages_prompts.PromptDeleteView.as_view(),
 | 
				
			||||||
        name="stage-prompt-delete",
 | 
					        name="stage-prompt-delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
@ -296,6 +294,11 @@ urlpatterns = [
 | 
				
			|||||||
        certificate_key_pair.CertificateKeyPairCreateView.as_view(),
 | 
					        certificate_key_pair.CertificateKeyPairCreateView.as_view(),
 | 
				
			||||||
        name="certificatekeypair-create",
 | 
					        name="certificatekeypair-create",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 | 
					    path(
 | 
				
			||||||
 | 
					        "crypto/certificates/generate/",
 | 
				
			||||||
 | 
					        certificate_key_pair.CertificateKeyPairGenerateView.as_view(),
 | 
				
			||||||
 | 
					        name="certificatekeypair-generate",
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "crypto/certificates/<uuid:pk>/update/",
 | 
					        "crypto/certificates/<uuid:pk>/update/",
 | 
				
			||||||
        certificate_key_pair.CertificateKeyPairUpdateView.as_view(),
 | 
					        certificate_key_pair.CertificateKeyPairUpdateView.as_view(),
 | 
				
			||||||
@ -307,11 +310,6 @@ urlpatterns = [
 | 
				
			|||||||
        name="certificatekeypair-delete",
 | 
					        name="certificatekeypair-delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Outposts
 | 
					    # Outposts
 | 
				
			||||||
    path(
 | 
					 | 
				
			||||||
        "outposts/",
 | 
					 | 
				
			||||||
        outposts.OutpostListView.as_view(),
 | 
					 | 
				
			||||||
        name="outposts",
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "outposts/create/",
 | 
					        "outposts/create/",
 | 
				
			||||||
        outposts.OutpostCreateView.as_view(),
 | 
					        outposts.OutpostCreateView.as_view(),
 | 
				
			||||||
@ -329,22 +327,22 @@ urlpatterns = [
 | 
				
			|||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Outpost Service Connections
 | 
					    # Outpost Service Connections
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "outposts/service_connections/",
 | 
					        "outpost_service_connections/",
 | 
				
			||||||
        outposts_service_connections.OutpostServiceConnectionListView.as_view(),
 | 
					        outposts_service_connections.OutpostServiceConnectionListView.as_view(),
 | 
				
			||||||
        name="outpost-service-connections",
 | 
					        name="outpost-service-connections",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "outposts/service_connections/create/",
 | 
					        "outpost_service_connections/create/",
 | 
				
			||||||
        outposts_service_connections.OutpostServiceConnectionCreateView.as_view(),
 | 
					        outposts_service_connections.OutpostServiceConnectionCreateView.as_view(),
 | 
				
			||||||
        name="outpost-service-connection-create",
 | 
					        name="outpost-service-connection-create",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "outposts/service_connections/<uuid:pk>/update/",
 | 
					        "outpost_service_connections/<uuid:pk>/update/",
 | 
				
			||||||
        outposts_service_connections.OutpostServiceConnectionUpdateView.as_view(),
 | 
					        outposts_service_connections.OutpostServiceConnectionUpdateView.as_view(),
 | 
				
			||||||
        name="outpost-service-connection-update",
 | 
					        name="outpost-service-connection-update",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "outposts/service_connections/<uuid:pk>/delete/",
 | 
					        "outpost_service_connections/<uuid:pk>/delete/",
 | 
				
			||||||
        outposts_service_connections.OutpostServiceConnectionDeleteView.as_view(),
 | 
					        outposts_service_connections.OutpostServiceConnectionDeleteView.as_view(),
 | 
				
			||||||
        name="outpost-service-connection-delete",
 | 
					        name="outpost-service-connection-delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@ from django.contrib.auth.mixins import (
 | 
				
			|||||||
    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
					    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
					from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			||||||
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 guardian.mixins import PermissionRequiredMixin
 | 
					from guardian.mixins import PermissionRequiredMixin
 | 
				
			||||||
@ -29,7 +28,6 @@ class ApplicationCreateView(
 | 
				
			|||||||
    permission_required = "authentik_core.add_application"
 | 
					    permission_required = "authentik_core.add_application"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/create.html"
 | 
					    template_name = "generic/create.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_core:shell")
 | 
					 | 
				
			||||||
    success_message = _("Successfully created Application")
 | 
					    success_message = _("Successfully created Application")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,7 +45,6 @@ class ApplicationUpdateView(
 | 
				
			|||||||
    permission_required = "authentik_core.change_application"
 | 
					    permission_required = "authentik_core.change_application"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/update.html"
 | 
					    template_name = "generic/update.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_core:shell")
 | 
					 | 
				
			||||||
    success_message = _("Successfully updated Application")
 | 
					    success_message = _("Successfully updated Application")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -60,5 +57,4 @@ class ApplicationDeleteView(
 | 
				
			|||||||
    permission_required = "authentik_core.delete_application"
 | 
					    permission_required = "authentik_core.delete_application"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/delete.html"
 | 
					    template_name = "generic/delete.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_core:shell")
 | 
					 | 
				
			||||||
    success_message = _("Successfully deleted Application")
 | 
					    success_message = _("Successfully deleted Application")
 | 
				
			||||||
 | 
				
			|||||||
@ -4,9 +4,11 @@ from django.contrib.auth.mixins import (
 | 
				
			|||||||
    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
					    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
					from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			||||||
 | 
					from django.http.response import HttpResponse
 | 
				
			||||||
from django.urls import reverse_lazy
 | 
					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 ListView, UpdateView
 | 
					from django.views.generic import ListView, UpdateView
 | 
				
			||||||
 | 
					from django.views.generic.edit import FormView
 | 
				
			||||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
 | 
					from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.views.utils import (
 | 
					from authentik.admin.views.utils import (
 | 
				
			||||||
@ -15,7 +17,11 @@ from authentik.admin.views.utils import (
 | 
				
			|||||||
    SearchListMixin,
 | 
					    SearchListMixin,
 | 
				
			||||||
    UserPaginateListMixin,
 | 
					    UserPaginateListMixin,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.crypto.forms import CertificateKeyPairForm
 | 
					from authentik.crypto.builder import CertificateBuilder
 | 
				
			||||||
 | 
					from authentik.crypto.forms import (
 | 
				
			||||||
 | 
					    CertificateKeyPairForm,
 | 
				
			||||||
 | 
					    CertificateKeyPairGenerateForm,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from authentik.crypto.models import CertificateKeyPair
 | 
					from authentik.crypto.models import CertificateKeyPair
 | 
				
			||||||
from authentik.lib.views import CreateAssignPermView
 | 
					from authentik.lib.views import CreateAssignPermView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -52,7 +58,35 @@ class CertificateKeyPairCreateView(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/create.html"
 | 
					    template_name = "generic/create.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:certificate_key_pair")
 | 
					    success_url = reverse_lazy("authentik_admin:certificate_key_pair")
 | 
				
			||||||
    success_message = _("Successfully created CertificateKeyPair")
 | 
					    success_message = _("Successfully created Certificate-Key Pair")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CertificateKeyPairGenerateView(
 | 
				
			||||||
 | 
					    SuccessMessageMixin,
 | 
				
			||||||
 | 
					    BackSuccessUrlMixin,
 | 
				
			||||||
 | 
					    LoginRequiredMixin,
 | 
				
			||||||
 | 
					    DjangoPermissionRequiredMixin,
 | 
				
			||||||
 | 
					    FormView,
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    """Generate new CertificateKeyPair"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    model = CertificateKeyPair
 | 
				
			||||||
 | 
					    form_class = CertificateKeyPairGenerateForm
 | 
				
			||||||
 | 
					    permission_required = "authentik_crypto.add_certificatekeypair"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    template_name = "administration/certificatekeypair/generate.html"
 | 
				
			||||||
 | 
					    success_url = reverse_lazy("authentik_admin:certificate_key_pair")
 | 
				
			||||||
 | 
					    success_message = _("Successfully generated Certificate-Key Pair")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def form_valid(self, form: CertificateKeyPairGenerateForm) -> HttpResponse:
 | 
				
			||||||
 | 
					        builder = CertificateBuilder()
 | 
				
			||||||
 | 
					        builder.common_name = form.data["common_name"]
 | 
				
			||||||
 | 
					        builder.build(
 | 
				
			||||||
 | 
					            subject_alt_names=form.data.get("subject_alt_name", "").split(","),
 | 
				
			||||||
 | 
					            validity_days=int(form.data["validity_days"]),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        builder.save()
 | 
				
			||||||
 | 
					        return super().form_valid(form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CertificateKeyPairUpdateView(
 | 
					class CertificateKeyPairUpdateView(
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@ from django.contrib.auth.mixins import (
 | 
				
			|||||||
    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
					    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
					from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			||||||
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 guardian.mixins import PermissionRequiredMixin
 | 
					from guardian.mixins import PermissionRequiredMixin
 | 
				
			||||||
@ -29,7 +28,6 @@ class NotificationRuleCreateView(
 | 
				
			|||||||
    permission_required = "authentik_events.add_NotificationRule"
 | 
					    permission_required = "authentik_events.add_NotificationRule"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/create.html"
 | 
					    template_name = "generic/create.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_core:shell")
 | 
					 | 
				
			||||||
    success_message = _("Successfully created Notification Rule")
 | 
					    success_message = _("Successfully created Notification Rule")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,7 +45,6 @@ class NotificationRuleUpdateView(
 | 
				
			|||||||
    permission_required = "authentik_events.change_NotificationRule"
 | 
					    permission_required = "authentik_events.change_NotificationRule"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/update.html"
 | 
					    template_name = "generic/update.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_core:shell")
 | 
					 | 
				
			||||||
    success_message = _("Successfully updated Notification Rule")
 | 
					    success_message = _("Successfully updated Notification Rule")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -60,5 +57,4 @@ class NotificationRuleDeleteView(
 | 
				
			|||||||
    permission_required = "authentik_events.delete_NotificationRule"
 | 
					    permission_required = "authentik_events.delete_NotificationRule"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/delete.html"
 | 
					    template_name = "generic/delete.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_core:shell")
 | 
					 | 
				
			||||||
    success_message = _("Successfully deleted Notification Rule")
 | 
					    success_message = _("Successfully deleted Notification Rule")
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@ from django.contrib.auth.mixins import (
 | 
				
			|||||||
    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
					    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
					from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			||||||
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 guardian.mixins import PermissionRequiredMixin
 | 
					from guardian.mixins import PermissionRequiredMixin
 | 
				
			||||||
@ -29,7 +28,6 @@ class NotificationTransportCreateView(
 | 
				
			|||||||
    permission_required = "authentik_events.add_notificationtransport"
 | 
					    permission_required = "authentik_events.add_notificationtransport"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/create.html"
 | 
					    template_name = "generic/create.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_core:shell")
 | 
					 | 
				
			||||||
    success_message = _("Successfully created Notification Transport")
 | 
					    success_message = _("Successfully created Notification Transport")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,7 +45,6 @@ class NotificationTransportUpdateView(
 | 
				
			|||||||
    permission_required = "authentik_events.change_notificationtransport"
 | 
					    permission_required = "authentik_events.change_notificationtransport"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/update.html"
 | 
					    template_name = "generic/update.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_core:shell")
 | 
					 | 
				
			||||||
    success_message = _("Successfully updated Notification Transport")
 | 
					    success_message = _("Successfully updated Notification Transport")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -60,5 +57,4 @@ class NotificationTransportDeleteView(
 | 
				
			|||||||
    permission_required = "authentik_events.delete_notificationtransport"
 | 
					    permission_required = "authentik_events.delete_notificationtransport"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/delete.html"
 | 
					    template_name = "generic/delete.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_core:shell")
 | 
					 | 
				
			||||||
    success_message = _("Successfully deleted Notification Transport")
 | 
					    success_message = _("Successfully deleted Notification Transport")
 | 
				
			||||||
 | 
				
			|||||||
@ -7,38 +7,16 @@ from django.contrib.auth.mixins import (
 | 
				
			|||||||
    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
					    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
					from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			||||||
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 ListView, UpdateView
 | 
					from django.views.generic import UpdateView
 | 
				
			||||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
 | 
					from guardian.mixins import PermissionRequiredMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.views.utils import (
 | 
					from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView
 | 
				
			||||||
    BackSuccessUrlMixin,
 | 
					 | 
				
			||||||
    DeleteMessageView,
 | 
					 | 
				
			||||||
    SearchListMixin,
 | 
					 | 
				
			||||||
    UserPaginateListMixin,
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
from authentik.lib.views import CreateAssignPermView
 | 
					from authentik.lib.views import CreateAssignPermView
 | 
				
			||||||
from authentik.outposts.forms import OutpostForm
 | 
					from authentik.outposts.forms import OutpostForm
 | 
				
			||||||
from authentik.outposts.models import Outpost, OutpostConfig
 | 
					from authentik.outposts.models import Outpost, OutpostConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OutpostListView(
 | 
					 | 
				
			||||||
    LoginRequiredMixin,
 | 
					 | 
				
			||||||
    PermissionListMixin,
 | 
					 | 
				
			||||||
    UserPaginateListMixin,
 | 
					 | 
				
			||||||
    SearchListMixin,
 | 
					 | 
				
			||||||
    ListView,
 | 
					 | 
				
			||||||
):
 | 
					 | 
				
			||||||
    """Show list of all outposts"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    model = Outpost
 | 
					 | 
				
			||||||
    permission_required = "authentik_outposts.view_outpost"
 | 
					 | 
				
			||||||
    ordering = "name"
 | 
					 | 
				
			||||||
    template_name = "administration/outpost/list.html"
 | 
					 | 
				
			||||||
    search_fields = ["name", "_config"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class OutpostCreateView(
 | 
					class OutpostCreateView(
 | 
				
			||||||
    SuccessMessageMixin,
 | 
					    SuccessMessageMixin,
 | 
				
			||||||
    BackSuccessUrlMixin,
 | 
					    BackSuccessUrlMixin,
 | 
				
			||||||
@ -53,7 +31,6 @@ class OutpostCreateView(
 | 
				
			|||||||
    permission_required = "authentik_outposts.add_outpost"
 | 
					    permission_required = "authentik_outposts.add_outpost"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/create.html"
 | 
					    template_name = "generic/create.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:outposts")
 | 
					 | 
				
			||||||
    success_message = _("Successfully created Outpost")
 | 
					    success_message = _("Successfully created Outpost")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_initial(self) -> Dict[str, Any]:
 | 
					    def get_initial(self) -> Dict[str, Any]:
 | 
				
			||||||
@ -78,7 +55,6 @@ class OutpostUpdateView(
 | 
				
			|||||||
    permission_required = "authentik_outposts.change_outpost"
 | 
					    permission_required = "authentik_outposts.change_outpost"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/update.html"
 | 
					    template_name = "generic/update.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:outposts")
 | 
					 | 
				
			||||||
    success_message = _("Successfully updated Outpost")
 | 
					    success_message = _("Successfully updated Outpost")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -89,5 +65,4 @@ class OutpostDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessa
 | 
				
			|||||||
    permission_required = "authentik_outposts.delete_outpost"
 | 
					    permission_required = "authentik_outposts.delete_outpost"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/delete.html"
 | 
					    template_name = "generic/delete.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:outposts")
 | 
					 | 
				
			||||||
    success_message = _("Successfully deleted Outpost")
 | 
					    success_message = _("Successfully deleted Outpost")
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,6 @@ class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
 | 
				
			|||||||
    form_class = PolicyCacheClearForm
 | 
					    form_class = PolicyCacheClearForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/form_non_model.html"
 | 
					    template_name = "generic/form_non_model.html"
 | 
				
			||||||
    success_url = "/"
 | 
					 | 
				
			||||||
    success_message = _("Successfully cleared Policy cache")
 | 
					    success_message = _("Successfully cleared Policy cache")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
 | 
					    def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
 | 
				
			||||||
@ -39,7 +38,6 @@ class FlowCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
 | 
				
			|||||||
    form_class = FlowCacheClearForm
 | 
					    form_class = FlowCacheClearForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/form_non_model.html"
 | 
					    template_name = "generic/form_non_model.html"
 | 
				
			||||||
    success_url = "/"
 | 
					 | 
				
			||||||
    success_message = _("Successfully cleared Flow cache")
 | 
					    success_message = _("Successfully cleared Flow cache")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
 | 
					    def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
 | 
				
			||||||
 | 
				
			|||||||
@ -115,6 +115,7 @@ class PolicyTestView(LoginRequiredMixin, DetailView, PermissionRequiredMixin, Fo
 | 
				
			|||||||
        user = form.cleaned_data.get("user")
 | 
					        user = form.cleaned_data.get("user")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        p_request = PolicyRequest(user)
 | 
					        p_request = PolicyRequest(user)
 | 
				
			||||||
 | 
					        p_request.debug = True
 | 
				
			||||||
        p_request.http_request = self.request
 | 
					        p_request.http_request = self.request
 | 
				
			||||||
        p_request.context = form.cleaned_data.get("context", {})
 | 
					        p_request.context = form.cleaned_data.get("context", {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,6 @@ from django.contrib.auth.mixins import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
					from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			||||||
from django.http import HttpResponse
 | 
					from django.http 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 FormView
 | 
					from django.views.generic import FormView
 | 
				
			||||||
from django.views.generic.detail import DetailView
 | 
					from django.views.generic.detail import DetailView
 | 
				
			||||||
@ -37,7 +36,6 @@ class PropertyMappingCreateView(
 | 
				
			|||||||
    permission_required = "authentik_core.add_propertymapping"
 | 
					    permission_required = "authentik_core.add_propertymapping"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/create.html"
 | 
					    template_name = "generic/create.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:property-mappings")
 | 
					 | 
				
			||||||
    success_message = _("Successfully created Property Mapping")
 | 
					    success_message = _("Successfully created Property Mapping")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,7 +52,6 @@ class PropertyMappingUpdateView(
 | 
				
			|||||||
    permission_required = "authentik_core.change_propertymapping"
 | 
					    permission_required = "authentik_core.change_propertymapping"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/update.html"
 | 
					    template_name = "generic/update.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:property-mappings")
 | 
					 | 
				
			||||||
    success_message = _("Successfully updated Property Mapping")
 | 
					    success_message = _("Successfully updated Property Mapping")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -67,7 +64,6 @@ class PropertyMappingDeleteView(
 | 
				
			|||||||
    permission_required = "authentik_core.delete_propertymapping"
 | 
					    permission_required = "authentik_core.delete_propertymapping"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/delete.html"
 | 
					    template_name = "generic/delete.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:property-mappings")
 | 
					 | 
				
			||||||
    success_message = _("Successfully deleted Property Mapping")
 | 
					    success_message = _("Successfully deleted Property Mapping")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,38 +4,18 @@ from django.contrib.auth.mixins import (
 | 
				
			|||||||
    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
					    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
					from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			||||||
from django.urls import reverse_lazy
 | 
					 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
 | 
					from guardian.mixins import PermissionRequiredMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.views.utils import (
 | 
					from authentik.admin.views.utils import (
 | 
				
			||||||
    BackSuccessUrlMixin,
 | 
					    BackSuccessUrlMixin,
 | 
				
			||||||
    DeleteMessageView,
 | 
					    DeleteMessageView,
 | 
				
			||||||
    InheritanceCreateView,
 | 
					    InheritanceCreateView,
 | 
				
			||||||
    InheritanceListView,
 | 
					 | 
				
			||||||
    InheritanceUpdateView,
 | 
					    InheritanceUpdateView,
 | 
				
			||||||
    SearchListMixin,
 | 
					 | 
				
			||||||
    UserPaginateListMixin,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.core.models import Provider
 | 
					from authentik.core.models import Provider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProviderListView(
 | 
					 | 
				
			||||||
    LoginRequiredMixin,
 | 
					 | 
				
			||||||
    PermissionListMixin,
 | 
					 | 
				
			||||||
    UserPaginateListMixin,
 | 
					 | 
				
			||||||
    SearchListMixin,
 | 
					 | 
				
			||||||
    InheritanceListView,
 | 
					 | 
				
			||||||
):
 | 
					 | 
				
			||||||
    """Show list of all providers"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    model = Provider
 | 
					 | 
				
			||||||
    permission_required = "authentik_core.add_provider"
 | 
					 | 
				
			||||||
    template_name = "administration/provider/list.html"
 | 
					 | 
				
			||||||
    ordering = "pk"
 | 
					 | 
				
			||||||
    search_fields = ["pk", "name"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ProviderCreateView(
 | 
					class ProviderCreateView(
 | 
				
			||||||
    SuccessMessageMixin,
 | 
					    SuccessMessageMixin,
 | 
				
			||||||
    BackSuccessUrlMixin,
 | 
					    BackSuccessUrlMixin,
 | 
				
			||||||
@ -49,7 +29,6 @@ class ProviderCreateView(
 | 
				
			|||||||
    permission_required = "authentik_core.add_provider"
 | 
					    permission_required = "authentik_core.add_provider"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/create.html"
 | 
					    template_name = "generic/create.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:providers")
 | 
					 | 
				
			||||||
    success_message = _("Successfully created Provider")
 | 
					    success_message = _("Successfully created Provider")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,7 +45,6 @@ class ProviderUpdateView(
 | 
				
			|||||||
    permission_required = "authentik_core.change_provider"
 | 
					    permission_required = "authentik_core.change_provider"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/update.html"
 | 
					    template_name = "generic/update.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:providers")
 | 
					 | 
				
			||||||
    success_message = _("Successfully updated Provider")
 | 
					    success_message = _("Successfully updated Provider")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -79,5 +57,4 @@ class ProviderDeleteView(
 | 
				
			|||||||
    permission_required = "authentik_core.delete_provider"
 | 
					    permission_required = "authentik_core.delete_provider"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/delete.html"
 | 
					    template_name = "generic/delete.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:providers")
 | 
					 | 
				
			||||||
    success_message = _("Successfully deleted Provider")
 | 
					    success_message = _("Successfully deleted Provider")
 | 
				
			||||||
 | 
				
			|||||||
@ -4,38 +4,18 @@ from django.contrib.auth.mixins import (
 | 
				
			|||||||
    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
					    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
					from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			||||||
from django.urls import reverse_lazy
 | 
					 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
 | 
					from guardian.mixins import PermissionRequiredMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.views.utils import (
 | 
					from authentik.admin.views.utils import (
 | 
				
			||||||
    BackSuccessUrlMixin,
 | 
					    BackSuccessUrlMixin,
 | 
				
			||||||
    DeleteMessageView,
 | 
					    DeleteMessageView,
 | 
				
			||||||
    InheritanceCreateView,
 | 
					    InheritanceCreateView,
 | 
				
			||||||
    InheritanceListView,
 | 
					 | 
				
			||||||
    InheritanceUpdateView,
 | 
					    InheritanceUpdateView,
 | 
				
			||||||
    SearchListMixin,
 | 
					 | 
				
			||||||
    UserPaginateListMixin,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.core.models import Source
 | 
					from authentik.core.models import Source
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SourceListView(
 | 
					 | 
				
			||||||
    LoginRequiredMixin,
 | 
					 | 
				
			||||||
    PermissionListMixin,
 | 
					 | 
				
			||||||
    UserPaginateListMixin,
 | 
					 | 
				
			||||||
    SearchListMixin,
 | 
					 | 
				
			||||||
    InheritanceListView,
 | 
					 | 
				
			||||||
):
 | 
					 | 
				
			||||||
    """Show list of all sources"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    model = Source
 | 
					 | 
				
			||||||
    permission_required = "authentik_core.view_source"
 | 
					 | 
				
			||||||
    ordering = "name"
 | 
					 | 
				
			||||||
    template_name = "administration/source/list.html"
 | 
					 | 
				
			||||||
    search_fields = ["name", "slug"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SourceCreateView(
 | 
					class SourceCreateView(
 | 
				
			||||||
    SuccessMessageMixin,
 | 
					    SuccessMessageMixin,
 | 
				
			||||||
    BackSuccessUrlMixin,
 | 
					    BackSuccessUrlMixin,
 | 
				
			||||||
@ -49,7 +29,6 @@ class SourceCreateView(
 | 
				
			|||||||
    permission_required = "authentik_core.add_source"
 | 
					    permission_required = "authentik_core.add_source"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/create.html"
 | 
					    template_name = "generic/create.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:sources")
 | 
					 | 
				
			||||||
    success_message = _("Successfully created Source")
 | 
					    success_message = _("Successfully created Source")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,7 +45,6 @@ class SourceUpdateView(
 | 
				
			|||||||
    permission_required = "authentik_core.change_source"
 | 
					    permission_required = "authentik_core.change_source"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/update.html"
 | 
					    template_name = "generic/update.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:sources")
 | 
					 | 
				
			||||||
    success_message = _("Successfully updated Source")
 | 
					    success_message = _("Successfully updated Source")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -77,5 +55,4 @@ class SourceDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessag
 | 
				
			|||||||
    permission_required = "authentik_core.delete_source"
 | 
					    permission_required = "authentik_core.delete_source"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/delete.html"
 | 
					    template_name = "generic/delete.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:sources")
 | 
					 | 
				
			||||||
    success_message = _("Successfully deleted Source")
 | 
					    success_message = _("Successfully deleted Source")
 | 
				
			||||||
 | 
				
			|||||||
@ -29,11 +29,12 @@ from authentik.flows.api import (
 | 
				
			|||||||
    FlowViewSet,
 | 
					    FlowViewSet,
 | 
				
			||||||
    StageViewSet,
 | 
					    StageViewSet,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.outposts.api import (
 | 
					from authentik.outposts.api.outpost_service_connections import (
 | 
				
			||||||
    DockerServiceConnectionViewSet,
 | 
					    DockerServiceConnectionViewSet,
 | 
				
			||||||
    KubernetesServiceConnectionViewSet,
 | 
					    KubernetesServiceConnectionViewSet,
 | 
				
			||||||
    OutpostViewSet,
 | 
					    ServiceConnectionViewSet,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					from authentik.outposts.api.outposts import OutpostViewSet
 | 
				
			||||||
from authentik.policies.api import (
 | 
					from authentik.policies.api import (
 | 
				
			||||||
    PolicyBindingViewSet,
 | 
					    PolicyBindingViewSet,
 | 
				
			||||||
    PolicyCacheViewSet,
 | 
					    PolicyCacheViewSet,
 | 
				
			||||||
@ -88,6 +89,7 @@ router.register("core/users", UserViewSet)
 | 
				
			|||||||
router.register("core/tokens", TokenViewSet)
 | 
					router.register("core/tokens", TokenViewSet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.register("outposts/outposts", OutpostViewSet)
 | 
					router.register("outposts/outposts", OutpostViewSet)
 | 
				
			||||||
 | 
					router.register("outposts/service_connections/all", ServiceConnectionViewSet)
 | 
				
			||||||
router.register("outposts/service_connections/docker", DockerServiceConnectionViewSet)
 | 
					router.register("outposts/service_connections/docker", DockerServiceConnectionViewSet)
 | 
				
			||||||
router.register(
 | 
					router.register(
 | 
				
			||||||
    "outposts/service_connections/kubernetes", KubernetesServiceConnectionViewSet
 | 
					    "outposts/service_connections/kubernetes", KubernetesServiceConnectionViewSet
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,18 @@
 | 
				
			|||||||
"""Provider API Views"""
 | 
					"""Provider API Views"""
 | 
				
			||||||
 | 
					from django.shortcuts import reverse
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from drf_yasg2.utils import swagger_auto_schema
 | 
				
			||||||
 | 
					from rest_framework.decorators import action
 | 
				
			||||||
from rest_framework.fields import ReadOnlyField
 | 
					from rest_framework.fields import ReadOnlyField
 | 
				
			||||||
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.response import Response
 | 
				
			||||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
 | 
					from rest_framework.serializers import ModelSerializer, SerializerMethodField
 | 
				
			||||||
from rest_framework.viewsets import ModelViewSet
 | 
					from rest_framework.viewsets import ModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.api.utils import MetaNameSerializer
 | 
					from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
 | 
				
			||||||
from authentik.core.models import Provider
 | 
					from authentik.core.models import Provider
 | 
				
			||||||
 | 
					from authentik.lib.templatetags.authentik_utils import verbose_name
 | 
				
			||||||
 | 
					from authentik.lib.utils.reflection import all_subclasses
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProviderSerializer(ModelSerializer, MetaNameSerializer):
 | 
					class ProviderSerializer(ModelSerializer, MetaNameSerializer):
 | 
				
			||||||
@ -51,3 +59,26 @@ class ProviderViewSet(ModelViewSet):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def get_queryset(self):
 | 
					    def get_queryset(self):
 | 
				
			||||||
        return Provider.objects.select_subclasses()
 | 
					        return Provider.objects.select_subclasses()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})
 | 
				
			||||||
 | 
					    @action(detail=False)
 | 
				
			||||||
 | 
					    def types(self, request: Request) -> Response:
 | 
				
			||||||
 | 
					        """Get all creatable provider types"""
 | 
				
			||||||
 | 
					        data = []
 | 
				
			||||||
 | 
					        for subclass in all_subclasses(self.queryset.model):
 | 
				
			||||||
 | 
					            data.append(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": verbose_name(subclass),
 | 
				
			||||||
 | 
					                    "description": subclass.__doc__,
 | 
				
			||||||
 | 
					                    "link": reverse("authentik_admin:provider-create")
 | 
				
			||||||
 | 
					                    + f"?type={subclass.__name__}",
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        data.append(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "name": _("SAML Provider from Metadata"),
 | 
				
			||||||
 | 
					                "description": _("Create a SAML Provider by importing its Metadata."),
 | 
				
			||||||
 | 
					                "link": reverse("authentik_admin:provider-saml-from-metadata"),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return Response(TypeCreateSerializer(data, many=True).data)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,31 +1,41 @@
 | 
				
			|||||||
"""Source API Views"""
 | 
					"""Source API Views"""
 | 
				
			||||||
 | 
					from django.shortcuts import reverse
 | 
				
			||||||
 | 
					from drf_yasg2.utils import swagger_auto_schema
 | 
				
			||||||
 | 
					from rest_framework.decorators import action
 | 
				
			||||||
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.response import Response
 | 
				
			||||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
 | 
					from rest_framework.serializers import ModelSerializer, SerializerMethodField
 | 
				
			||||||
from rest_framework.viewsets import ReadOnlyModelViewSet
 | 
					from rest_framework.viewsets import ReadOnlyModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.forms.source import SOURCE_SERIALIZER_FIELDS
 | 
					from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
 | 
				
			||||||
from authentik.core.api.utils import MetaNameSerializer
 | 
					 | 
				
			||||||
from authentik.core.models import Source
 | 
					from authentik.core.models import Source
 | 
				
			||||||
 | 
					from authentik.lib.templatetags.authentik_utils import verbose_name
 | 
				
			||||||
 | 
					from authentik.lib.utils.reflection import all_subclasses
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SourceSerializer(ModelSerializer, MetaNameSerializer):
 | 
					class SourceSerializer(ModelSerializer, MetaNameSerializer):
 | 
				
			||||||
    """Source Serializer"""
 | 
					    """Source Serializer"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    __type__ = SerializerMethodField(method_name="get_type")
 | 
					    object_type = SerializerMethodField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_type(self, obj):
 | 
					    def get_object_type(self, obj):
 | 
				
			||||||
        """Get object type so that we know which API Endpoint to use to get the full object"""
 | 
					        """Get object type so that we know which API Endpoint to use to get the full object"""
 | 
				
			||||||
        return obj._meta.object_name.lower().replace("source", "")
 | 
					        return obj._meta.object_name.lower().replace("source", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def to_representation(self, instance: Source):
 | 
					 | 
				
			||||||
        # pyright: reportGeneralTypeIssues=false
 | 
					 | 
				
			||||||
        if instance.__class__ == Source:
 | 
					 | 
				
			||||||
            return super().to_representation(instance)
 | 
					 | 
				
			||||||
        return instance.serializer(instance=instance).data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model = Source
 | 
					        model = Source
 | 
				
			||||||
        fields = SOURCE_SERIALIZER_FIELDS + ["__type__"]
 | 
					        fields = SOURCE_SERIALIZER_FIELDS = [
 | 
				
			||||||
 | 
					            "pk",
 | 
				
			||||||
 | 
					            "name",
 | 
				
			||||||
 | 
					            "slug",
 | 
				
			||||||
 | 
					            "enabled",
 | 
				
			||||||
 | 
					            "authentication_flow",
 | 
				
			||||||
 | 
					            "enrollment_flow",
 | 
				
			||||||
 | 
					            "object_type",
 | 
				
			||||||
 | 
					            "verbose_name",
 | 
				
			||||||
 | 
					            "verbose_name_plural",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SourceViewSet(ReadOnlyModelViewSet):
 | 
					class SourceViewSet(ReadOnlyModelViewSet):
 | 
				
			||||||
@ -37,3 +47,19 @@ class SourceViewSet(ReadOnlyModelViewSet):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def get_queryset(self):
 | 
					    def get_queryset(self):
 | 
				
			||||||
        return Source.objects.select_subclasses()
 | 
					        return Source.objects.select_subclasses()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})
 | 
				
			||||||
 | 
					    @action(detail=False)
 | 
				
			||||||
 | 
					    def types(self, request: Request) -> Response:
 | 
				
			||||||
 | 
					        """Get all creatable source types"""
 | 
				
			||||||
 | 
					        data = []
 | 
				
			||||||
 | 
					        for subclass in all_subclasses(self.queryset.model):
 | 
				
			||||||
 | 
					            data.append(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": verbose_name(subclass),
 | 
				
			||||||
 | 
					                    "description": subclass.__doc__,
 | 
				
			||||||
 | 
					                    "link": reverse("authentik_admin:source-create")
 | 
				
			||||||
 | 
					                    + f"?type={subclass.__name__}",
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        return Response(TypeCreateSerializer(data, many=True).data)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,12 @@
 | 
				
			|||||||
"""Tokens API Viewset"""
 | 
					"""Tokens API Viewset"""
 | 
				
			||||||
 | 
					from django.db.models.base import Model
 | 
				
			||||||
from django.http.response import Http404
 | 
					from django.http.response import Http404
 | 
				
			||||||
 | 
					from drf_yasg2.utils import swagger_auto_schema
 | 
				
			||||||
from rest_framework.decorators import action
 | 
					from rest_framework.decorators import action
 | 
				
			||||||
 | 
					from rest_framework.fields import CharField
 | 
				
			||||||
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.core.models import Token
 | 
					from authentik.core.models import Token
 | 
				
			||||||
@ -19,6 +22,18 @@ class TokenSerializer(ModelSerializer):
 | 
				
			|||||||
        fields = ["pk", "identifier", "intent", "user", "description"]
 | 
					        fields = ["pk", "identifier", "intent", "user", "description"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TokenViewSerializer(Serializer):
 | 
				
			||||||
 | 
					    """Show token's current key"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    key = CharField(read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create(self, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, instance: Model, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TokenViewSet(ModelViewSet):
 | 
					class TokenViewSet(ModelViewSet):
 | 
				
			||||||
    """Token Viewset"""
 | 
					    """Token Viewset"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,12 +41,15 @@ class TokenViewSet(ModelViewSet):
 | 
				
			|||||||
    queryset = Token.filter_not_expired()
 | 
					    queryset = Token.filter_not_expired()
 | 
				
			||||||
    serializer_class = TokenSerializer
 | 
					    serializer_class = TokenSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @swagger_auto_schema(responses={200: TokenViewSerializer(many=False)})
 | 
				
			||||||
    @action(detail=True)
 | 
					    @action(detail=True)
 | 
				
			||||||
 | 
					    # pylint: disable=unused-argument
 | 
				
			||||||
    def view_key(self, request: Request, identifier: str) -> Response:
 | 
					    def view_key(self, request: Request, identifier: str) -> Response:
 | 
				
			||||||
        """Return token key and log access"""
 | 
					        """Return token key and log access"""
 | 
				
			||||||
        tokens = Token.filter_not_expired(identifier=identifier)
 | 
					        token: Token = self.get_object()
 | 
				
			||||||
        if not tokens.exists():
 | 
					        if token.is_expired:
 | 
				
			||||||
            raise Http404
 | 
					            raise Http404
 | 
				
			||||||
        token = tokens.first()
 | 
					        Event.new(EventAction.SECRET_VIEW, secret=token).from_http(  # noqa # nosec
 | 
				
			||||||
        Event.new(EventAction.TOKEN_VIEW, token=token).from_http(request)
 | 
					            request
 | 
				
			||||||
        return Response({"key": token.key})
 | 
					        )
 | 
				
			||||||
 | 
					        return Response(TokenViewSerializer({"key": token.key}).data)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
"""API Utilities"""
 | 
					"""API Utilities"""
 | 
				
			||||||
from django.db.models import Model
 | 
					from django.db.models import Model
 | 
				
			||||||
 | 
					from rest_framework.fields import CharField
 | 
				
			||||||
from rest_framework.serializers import Serializer, SerializerMethodField
 | 
					from rest_framework.serializers import Serializer, SerializerMethodField
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,3 +23,17 @@ class MetaNameSerializer(Serializer):
 | 
				
			|||||||
    def get_verbose_name_plural(self, obj: Model) -> str:
 | 
					    def get_verbose_name_plural(self, obj: Model) -> str:
 | 
				
			||||||
        """Return object's plural verbose_name"""
 | 
					        """Return object's plural verbose_name"""
 | 
				
			||||||
        return obj._meta.verbose_name_plural
 | 
					        return obj._meta.verbose_name_plural
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TypeCreateSerializer(Serializer):
 | 
				
			||||||
 | 
					    """Types of an object that can be created"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = CharField(read_only=True)
 | 
				
			||||||
 | 
					    description = CharField(read_only=True)
 | 
				
			||||||
 | 
					    link = CharField(read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create(self, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, instance: Model, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
				
			|||||||
@ -2,15 +2,29 @@
 | 
				
			|||||||
from cryptography.hazmat.backends import default_backend
 | 
					from cryptography.hazmat.backends import default_backend
 | 
				
			||||||
from cryptography.hazmat.primitives.serialization import load_pem_private_key
 | 
					from cryptography.hazmat.primitives.serialization import load_pem_private_key
 | 
				
			||||||
from cryptography.x509 import load_pem_x509_certificate
 | 
					from cryptography.x509 import load_pem_x509_certificate
 | 
				
			||||||
from rest_framework.serializers import ModelSerializer, ValidationError
 | 
					from django.db.models import Model
 | 
				
			||||||
 | 
					from drf_yasg2.utils import swagger_auto_schema
 | 
				
			||||||
 | 
					from rest_framework.decorators import action
 | 
				
			||||||
 | 
					from rest_framework.fields import CharField, DateTimeField, SerializerMethodField
 | 
				
			||||||
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.response import Response
 | 
				
			||||||
 | 
					from rest_framework.serializers import ModelSerializer, Serializer, ValidationError
 | 
				
			||||||
from rest_framework.viewsets import ModelViewSet
 | 
					from rest_framework.viewsets import ModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.crypto.models import CertificateKeyPair
 | 
					from authentik.crypto.models import CertificateKeyPair
 | 
				
			||||||
 | 
					from authentik.events.models import Event, EventAction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CertificateKeyPairSerializer(ModelSerializer):
 | 
					class CertificateKeyPairSerializer(ModelSerializer):
 | 
				
			||||||
    """CertificateKeyPair Serializer"""
 | 
					    """CertificateKeyPair Serializer"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cert_expiry = DateTimeField(source="certificate.not_valid_after", read_only=True)
 | 
				
			||||||
 | 
					    cert_subject = SerializerMethodField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_cert_subject(self, instance: CertificateKeyPair) -> str:
 | 
				
			||||||
 | 
					        """Get certificate subject as full rfc4514"""
 | 
				
			||||||
 | 
					        return instance.certificate.subject.rfc4514_string()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def validate_certificate_data(self, value):
 | 
					    def validate_certificate_data(self, value):
 | 
				
			||||||
        """Verify that input is a valid PEM x509 Certificate"""
 | 
					        """Verify that input is a valid PEM x509 Certificate"""
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@ -36,7 +50,31 @@ class CertificateKeyPairSerializer(ModelSerializer):
 | 
				
			|||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model = CertificateKeyPair
 | 
					        model = CertificateKeyPair
 | 
				
			||||||
        fields = ["pk", "name", "certificate_data", "key_data"]
 | 
					        fields = [
 | 
				
			||||||
 | 
					            "pk",
 | 
				
			||||||
 | 
					            "name",
 | 
				
			||||||
 | 
					            "fingerprint",
 | 
				
			||||||
 | 
					            "certificate_data",
 | 
				
			||||||
 | 
					            "key_data",
 | 
				
			||||||
 | 
					            "cert_expiry",
 | 
				
			||||||
 | 
					            "cert_subject",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        extra_kwargs = {
 | 
				
			||||||
 | 
					            "key_data": {"write_only": True},
 | 
				
			||||||
 | 
					            "certificate_data": {"write_only": True},
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CertificateDataSerializer(Serializer):
 | 
				
			||||||
 | 
					    """Get CertificateKeyPair's data"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data = CharField(read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create(self, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, instance: Model, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CertificateKeyPairViewSet(ModelViewSet):
 | 
					class CertificateKeyPairViewSet(ModelViewSet):
 | 
				
			||||||
@ -44,3 +82,31 @@ class CertificateKeyPairViewSet(ModelViewSet):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    queryset = CertificateKeyPair.objects.all()
 | 
					    queryset = CertificateKeyPair.objects.all()
 | 
				
			||||||
    serializer_class = CertificateKeyPairSerializer
 | 
					    serializer_class = CertificateKeyPairSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)})
 | 
				
			||||||
 | 
					    @action(detail=True)
 | 
				
			||||||
 | 
					    # pylint: disable=invalid-name, unused-argument
 | 
				
			||||||
 | 
					    def view_certificate(self, request: Request, pk: str) -> Response:
 | 
				
			||||||
 | 
					        """Return certificate-key pairs certificate and log access"""
 | 
				
			||||||
 | 
					        certificate: CertificateKeyPair = self.get_object()
 | 
				
			||||||
 | 
					        Event.new(  # noqa # nosec
 | 
				
			||||||
 | 
					            EventAction.SECRET_VIEW,
 | 
				
			||||||
 | 
					            secret=certificate,
 | 
				
			||||||
 | 
					            type="certificate",
 | 
				
			||||||
 | 
					        ).from_http(request)
 | 
				
			||||||
 | 
					        return Response(
 | 
				
			||||||
 | 
					            CertificateDataSerializer({"data": certificate.certificate_data}).data
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @swagger_auto_schema(responses={200: CertificateDataSerializer(many=False)})
 | 
				
			||||||
 | 
					    @action(detail=True)
 | 
				
			||||||
 | 
					    # pylint: disable=invalid-name, unused-argument
 | 
				
			||||||
 | 
					    def view_private_key(self, request: Request, pk: str) -> Response:
 | 
				
			||||||
 | 
					        """Return certificate-key pairs private key and log access"""
 | 
				
			||||||
 | 
					        certificate: CertificateKeyPair = self.get_object()
 | 
				
			||||||
 | 
					        Event.new(  # noqa # nosec
 | 
				
			||||||
 | 
					            EventAction.SECRET_VIEW,
 | 
				
			||||||
 | 
					            secret=certificate,
 | 
				
			||||||
 | 
					            type="private_key",
 | 
				
			||||||
 | 
					        ).from_http(request)
 | 
				
			||||||
 | 
					        return Response(CertificateDataSerializer({"data": certificate.key_data}).data)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
"""Create self-signed certificates"""
 | 
					"""Create self-signed certificates"""
 | 
				
			||||||
import datetime
 | 
					import datetime
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from cryptography import x509
 | 
					from cryptography import x509
 | 
				
			||||||
from cryptography.hazmat.backends import default_backend
 | 
					from cryptography.hazmat.backends import default_backend
 | 
				
			||||||
@ -8,6 +9,9 @@ from cryptography.hazmat.primitives import hashes, serialization
 | 
				
			|||||||
from cryptography.hazmat.primitives.asymmetric import rsa
 | 
					from cryptography.hazmat.primitives.asymmetric import rsa
 | 
				
			||||||
from cryptography.x509.oid import NameOID
 | 
					from cryptography.x509.oid import NameOID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik import __version__
 | 
				
			||||||
 | 
					from authentik.crypto.models import CertificateKeyPair
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CertificateBuilder:
 | 
					class CertificateBuilder:
 | 
				
			||||||
    """Build self-signed certificates"""
 | 
					    """Build self-signed certificates"""
 | 
				
			||||||
@ -17,19 +21,39 @@ class CertificateBuilder:
 | 
				
			|||||||
    __builder = None
 | 
					    __builder = None
 | 
				
			||||||
    __certificate = None
 | 
					    __certificate = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    common_name: str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.__public_key = None
 | 
					        self.__public_key = None
 | 
				
			||||||
        self.__private_key = None
 | 
					        self.__private_key = None
 | 
				
			||||||
        self.__builder = None
 | 
					        self.__builder = None
 | 
				
			||||||
        self.__certificate = None
 | 
					        self.__certificate = None
 | 
				
			||||||
 | 
					        self.common_name = "authentik Self-signed Certificate"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def build(self):
 | 
					    def save(self) -> Optional[CertificateKeyPair]:
 | 
				
			||||||
 | 
					        """Save generated certificate as model"""
 | 
				
			||||||
 | 
					        if not self.__certificate:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        return CertificateKeyPair.objects.create(
 | 
				
			||||||
 | 
					            name=self.common_name,
 | 
				
			||||||
 | 
					            certificate_data=self.certificate,
 | 
				
			||||||
 | 
					            key_data=self.private_key,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def build(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        validity_days: int = 365,
 | 
				
			||||||
 | 
					        subject_alt_names: Optional[list[str]] = None,
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
        """Build self-signed certificate"""
 | 
					        """Build self-signed certificate"""
 | 
				
			||||||
        one_day = datetime.timedelta(1, 0, 0)
 | 
					        one_day = datetime.timedelta(1, 0, 0)
 | 
				
			||||||
        self.__private_key = rsa.generate_private_key(
 | 
					        self.__private_key = rsa.generate_private_key(
 | 
				
			||||||
            public_exponent=65537, key_size=2048, backend=default_backend()
 | 
					            public_exponent=65537, key_size=2048, backend=default_backend()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.__public_key = self.__private_key.public_key()
 | 
					        self.__public_key = self.__private_key.public_key()
 | 
				
			||||||
 | 
					        alt_names: list[x509.GeneralName] = [
 | 
				
			||||||
 | 
					            x509.DNSName(x) for x in subject_alt_names or []
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
        self.__builder = (
 | 
					        self.__builder = (
 | 
				
			||||||
            x509.CertificateBuilder()
 | 
					            x509.CertificateBuilder()
 | 
				
			||||||
            .subject_name(
 | 
					            .subject_name(
 | 
				
			||||||
@ -37,7 +61,7 @@ class CertificateBuilder:
 | 
				
			|||||||
                    [
 | 
					                    [
 | 
				
			||||||
                        x509.NameAttribute(
 | 
					                        x509.NameAttribute(
 | 
				
			||||||
                            NameOID.COMMON_NAME,
 | 
					                            NameOID.COMMON_NAME,
 | 
				
			||||||
                            "authentik Self-signed Certificate",
 | 
					                            self.common_name,
 | 
				
			||||||
                        ),
 | 
					                        ),
 | 
				
			||||||
                        x509.NameAttribute(NameOID.ORGANIZATION_NAME, "authentik"),
 | 
					                        x509.NameAttribute(NameOID.ORGANIZATION_NAME, "authentik"),
 | 
				
			||||||
                        x509.NameAttribute(
 | 
					                        x509.NameAttribute(
 | 
				
			||||||
@ -51,13 +75,16 @@ class CertificateBuilder:
 | 
				
			|||||||
                    [
 | 
					                    [
 | 
				
			||||||
                        x509.NameAttribute(
 | 
					                        x509.NameAttribute(
 | 
				
			||||||
                            NameOID.COMMON_NAME,
 | 
					                            NameOID.COMMON_NAME,
 | 
				
			||||||
                            "authentik Self-signed Certificate",
 | 
					                            f"authentik {__version__}",
 | 
				
			||||||
                        ),
 | 
					                        ),
 | 
				
			||||||
                    ]
 | 
					                    ]
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            .add_extension(x509.SubjectAlternativeName(alt_names), critical=True)
 | 
				
			||||||
            .not_valid_before(datetime.datetime.today() - one_day)
 | 
					            .not_valid_before(datetime.datetime.today() - one_day)
 | 
				
			||||||
            .not_valid_after(datetime.datetime.today() + datetime.timedelta(days=365))
 | 
					            .not_valid_after(
 | 
				
			||||||
 | 
					                datetime.datetime.today() + datetime.timedelta(days=validity_days)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .serial_number(int(uuid.uuid4()))
 | 
					            .serial_number(int(uuid.uuid4()))
 | 
				
			||||||
            .public_key(self.__public_key)
 | 
					            .public_key(self.__public_key)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,14 @@ from django.utils.translation import gettext_lazy as _
 | 
				
			|||||||
from authentik.crypto.models import CertificateKeyPair
 | 
					from authentik.crypto.models import CertificateKeyPair
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CertificateKeyPairGenerateForm(forms.Form):
 | 
				
			||||||
 | 
					    """CertificateKeyPair generation form"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    common_name = forms.CharField()
 | 
				
			||||||
 | 
					    subject_alt_name = forms.CharField(required=False, label=_("Subject-alt name"))
 | 
				
			||||||
 | 
					    validity_days = forms.IntegerField(initial=365)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CertificateKeyPairForm(forms.ModelForm):
 | 
					class CertificateKeyPairForm(forms.ModelForm):
 | 
				
			||||||
    """CertificateKeyPair Form"""
 | 
					    """CertificateKeyPair Form"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										61
									
								
								authentik/events/migrations/0013_auto_20210209_1657.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								authentik/events/migrations/0013_auto_20210209_1657.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					# Generated by Django 3.1.6 on 2021-02-09 16:57
 | 
				
			||||||
 | 
					from django.apps.registry import Apps
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					from django.db.backends.base.schema import BaseDatabaseSchemaEditor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def token_view_to_secret_view(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
 | 
				
			||||||
 | 
					    from authentik.events.models import EventAction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    db_alias = schema_editor.connection.alias
 | 
				
			||||||
 | 
					    Event = apps.get_model("authentik_events", "Event")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    events = Event.objects.using(db_alias).filter(action="token_view")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for event in events:
 | 
				
			||||||
 | 
					        event.context["secret"] = event.context.pop("token")
 | 
				
			||||||
 | 
					        event.action = EventAction.SECRET_VIEW
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Event.objects.using(db_alias).bulk_update(events, ["context", "action"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ("authentik_events", "0012_auto_20210202_1821"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.AlterField(
 | 
				
			||||||
 | 
					            model_name="event",
 | 
				
			||||||
 | 
					            name="action",
 | 
				
			||||||
 | 
					            field=models.TextField(
 | 
				
			||||||
 | 
					                choices=[
 | 
				
			||||||
 | 
					                    ("login", "Login"),
 | 
				
			||||||
 | 
					                    ("login_failed", "Login Failed"),
 | 
				
			||||||
 | 
					                    ("logout", "Logout"),
 | 
				
			||||||
 | 
					                    ("user_write", "User Write"),
 | 
				
			||||||
 | 
					                    ("suspicious_request", "Suspicious Request"),
 | 
				
			||||||
 | 
					                    ("password_set", "Password Set"),
 | 
				
			||||||
 | 
					                    ("secret_view", "Secret View"),
 | 
				
			||||||
 | 
					                    ("invitation_used", "Invite Used"),
 | 
				
			||||||
 | 
					                    ("authorize_application", "Authorize Application"),
 | 
				
			||||||
 | 
					                    ("source_linked", "Source Linked"),
 | 
				
			||||||
 | 
					                    ("impersonation_started", "Impersonation Started"),
 | 
				
			||||||
 | 
					                    ("impersonation_ended", "Impersonation Ended"),
 | 
				
			||||||
 | 
					                    ("policy_execution", "Policy Execution"),
 | 
				
			||||||
 | 
					                    ("policy_exception", "Policy Exception"),
 | 
				
			||||||
 | 
					                    ("property_mapping_exception", "Property Mapping Exception"),
 | 
				
			||||||
 | 
					                    ("system_task_execution", "System Task Execution"),
 | 
				
			||||||
 | 
					                    ("system_task_exception", "System Task Exception"),
 | 
				
			||||||
 | 
					                    ("configuration_error", "Configuration Error"),
 | 
				
			||||||
 | 
					                    ("model_created", "Model Created"),
 | 
				
			||||||
 | 
					                    ("model_updated", "Model Updated"),
 | 
				
			||||||
 | 
					                    ("model_deleted", "Model Deleted"),
 | 
				
			||||||
 | 
					                    ("update_available", "Update Available"),
 | 
				
			||||||
 | 
					                    ("custom_", "Custom Prefix"),
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.RunPython(token_view_to_secret_view),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@ -42,7 +42,7 @@ class EventAction(models.TextChoices):
 | 
				
			|||||||
    SUSPICIOUS_REQUEST = "suspicious_request"
 | 
					    SUSPICIOUS_REQUEST = "suspicious_request"
 | 
				
			||||||
    PASSWORD_SET = "password_set"  # noqa # nosec
 | 
					    PASSWORD_SET = "password_set"  # noqa # nosec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    TOKEN_VIEW = "token_view"  # nosec
 | 
					    SECRET_VIEW = "secret_view"  # noqa # nosec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    INVITE_USED = "invitation_used"
 | 
					    INVITE_USED = "invitation_used"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -67,7 +67,7 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
 | 
				
			|||||||
    # Create the notification objects
 | 
					    # Create the notification objects
 | 
				
			||||||
    for transport in trigger.transports.all():
 | 
					    for transport in trigger.transports.all():
 | 
				
			||||||
        for user in trigger.group.users.all():
 | 
					        for user in trigger.group.users.all():
 | 
				
			||||||
            LOGGER.debug("created notif")
 | 
					            LOGGER.debug("created notification")
 | 
				
			||||||
            notification = Notification.objects.create(
 | 
					            notification = Notification.objects.create(
 | 
				
			||||||
                severity=trigger.severity, body=event.summary, event=event, user=user
 | 
					                severity=trigger.severity, body=event.summary, event=event, user=user
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
				
			|||||||
@ -98,6 +98,10 @@ class BaseEvaluator:
 | 
				
			|||||||
                exec(ast_obj, self._globals, _locals)  # nosec # noqa
 | 
					                exec(ast_obj, self._globals, _locals)  # nosec # noqa
 | 
				
			||||||
                result = _locals["result"]
 | 
					                result = _locals["result"]
 | 
				
			||||||
            except Exception as exc:
 | 
					            except Exception as exc:
 | 
				
			||||||
 | 
					                # So, this is a bit questionable. Essentially, we are edit the stacktrace
 | 
				
			||||||
 | 
					                # so the user only sees information relevant to them
 | 
				
			||||||
 | 
					                # and none of our surrounding error handling
 | 
				
			||||||
 | 
					                exc.__traceback__ = exc.__traceback__.tb_next
 | 
				
			||||||
                self.handle_error(exc, expression_source)
 | 
					                self.handle_error(exc, expression_source)
 | 
				
			||||||
                raise exc
 | 
					                raise exc
 | 
				
			||||||
            return result
 | 
					            return result
 | 
				
			||||||
 | 
				
			|||||||
@ -1,30 +1,28 @@
 | 
				
			|||||||
"""Outpost API Views"""
 | 
					"""Outpost API Views"""
 | 
				
			||||||
from rest_framework.serializers import JSONField, ModelSerializer
 | 
					from rest_framework.serializers import ModelSerializer
 | 
				
			||||||
from rest_framework.viewsets import ModelViewSet
 | 
					from rest_framework.viewsets import ModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.outposts.models import (
 | 
					from authentik.outposts.models import (
 | 
				
			||||||
    DockerServiceConnection,
 | 
					    DockerServiceConnection,
 | 
				
			||||||
    KubernetesServiceConnection,
 | 
					    KubernetesServiceConnection,
 | 
				
			||||||
    Outpost,
 | 
					    OutpostServiceConnection,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OutpostSerializer(ModelSerializer):
 | 
					class ServiceConnectionSerializer(ModelSerializer):
 | 
				
			||||||
    """Outpost Serializer"""
 | 
					    """ServiceConnection Serializer"""
 | 
				
			||||||
 | 
					 | 
				
			||||||
    _config = JSONField()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model = Outpost
 | 
					        model = OutpostServiceConnection
 | 
				
			||||||
        fields = ["pk", "name", "providers", "service_connection", "_config"]
 | 
					        fields = ["pk", "name"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OutpostViewSet(ModelViewSet):
 | 
					class ServiceConnectionViewSet(ModelViewSet):
 | 
				
			||||||
    """Outpost Viewset"""
 | 
					    """ServiceConnection Viewset"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    queryset = Outpost.objects.all()
 | 
					    queryset = OutpostServiceConnection.objects.all()
 | 
				
			||||||
    serializer_class = OutpostSerializer
 | 
					    serializer_class = ServiceConnectionSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DockerServiceConnectionSerializer(ModelSerializer):
 | 
					class DockerServiceConnectionSerializer(ModelSerializer):
 | 
				
			||||||
							
								
								
									
										79
									
								
								authentik/outposts/api/outposts.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								authentik/outposts/api/outposts.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					"""Outpost API Views"""
 | 
				
			||||||
 | 
					from django.db.models import Model
 | 
				
			||||||
 | 
					from drf_yasg2.utils import swagger_auto_schema
 | 
				
			||||||
 | 
					from rest_framework.decorators import action
 | 
				
			||||||
 | 
					from rest_framework.fields import BooleanField, CharField, DateTimeField
 | 
				
			||||||
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.response import Response
 | 
				
			||||||
 | 
					from rest_framework.serializers import JSONField, ModelSerializer, Serializer
 | 
				
			||||||
 | 
					from rest_framework.viewsets import ModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.core.api.providers import ProviderSerializer
 | 
				
			||||||
 | 
					from authentik.outposts.models import Outpost
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OutpostSerializer(ModelSerializer):
 | 
				
			||||||
 | 
					    """Outpost Serializer"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _config = JSONField()
 | 
				
			||||||
 | 
					    providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        model = Outpost
 | 
				
			||||||
 | 
					        fields = [
 | 
				
			||||||
 | 
					            "pk",
 | 
				
			||||||
 | 
					            "name",
 | 
				
			||||||
 | 
					            "providers",
 | 
				
			||||||
 | 
					            "providers_obj",
 | 
				
			||||||
 | 
					            "service_connection",
 | 
				
			||||||
 | 
					            "token_identifier",
 | 
				
			||||||
 | 
					            "_config",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OutpostHealthSerializer(Serializer):
 | 
				
			||||||
 | 
					    """Outpost health status"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    last_seen = DateTimeField(read_only=True)
 | 
				
			||||||
 | 
					    version = CharField(read_only=True)
 | 
				
			||||||
 | 
					    version_should = CharField(read_only=True)
 | 
				
			||||||
 | 
					    version_outdated = BooleanField(read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create(self, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, instance: Model, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OutpostViewSet(ModelViewSet):
 | 
				
			||||||
 | 
					    """Outpost Viewset"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    queryset = Outpost.objects.all()
 | 
				
			||||||
 | 
					    serializer_class = OutpostSerializer
 | 
				
			||||||
 | 
					    filterset_fields = {
 | 
				
			||||||
 | 
					        "providers": ["isnull"],
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    search_fields = [
 | 
				
			||||||
 | 
					        "name",
 | 
				
			||||||
 | 
					        "providers__name",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @swagger_auto_schema(responses={200: OutpostHealthSerializer(many=True)})
 | 
				
			||||||
 | 
					    @action(methods=["GET"], detail=True)
 | 
				
			||||||
 | 
					    # pylint: disable=invalid-name, unused-argument
 | 
				
			||||||
 | 
					    def health(self, request: Request, pk: int) -> Response:
 | 
				
			||||||
 | 
					        """Get outposts current health"""
 | 
				
			||||||
 | 
					        outpost: Outpost = self.get_object()
 | 
				
			||||||
 | 
					        states = []
 | 
				
			||||||
 | 
					        for state in outpost.state:
 | 
				
			||||||
 | 
					            states.append(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "last_seen": state.last_seen,
 | 
				
			||||||
 | 
					                    "version": state.version,
 | 
				
			||||||
 | 
					                    "version_should": state.version_should,
 | 
				
			||||||
 | 
					                    "version_outdated": state.version_outdated,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        return Response(OutpostHealthSerializer(states, many=True).data)
 | 
				
			||||||
@ -9,7 +9,6 @@ from django.core.cache import cache
 | 
				
			|||||||
from django.db import models, transaction
 | 
					from django.db import models, transaction
 | 
				
			||||||
from django.db.models.base import Model
 | 
					from django.db.models.base import Model
 | 
				
			||||||
from django.forms.models import ModelForm
 | 
					from django.forms.models import ModelForm
 | 
				
			||||||
from django.http import HttpRequest
 | 
					 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from docker.client import DockerClient
 | 
					from docker.client import DockerClient
 | 
				
			||||||
from docker.errors import DockerException
 | 
					from docker.errors import DockerException
 | 
				
			||||||
@ -33,7 +32,6 @@ from authentik.crypto.models import CertificateKeyPair
 | 
				
			|||||||
from authentik.lib.config import CONFIG
 | 
					from authentik.lib.config import CONFIG
 | 
				
			||||||
from authentik.lib.models import InheritanceForeignKey
 | 
					from authentik.lib.models import InheritanceForeignKey
 | 
				
			||||||
from authentik.lib.sentry import SentryIgnoredException
 | 
					from authentik.lib.sentry import SentryIgnoredException
 | 
				
			||||||
from authentik.lib.utils.template import render_to_string
 | 
					 | 
				
			||||||
from authentik.outposts.docker_tls import DockerInlineTLS
 | 
					from authentik.outposts.docker_tls import DockerInlineTLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OUR_VERSION = parse(__version__)
 | 
					OUR_VERSION = parse(__version__)
 | 
				
			||||||
@ -378,13 +376,6 @@ class Outpost(models.Model):
 | 
				
			|||||||
                objects.append(provider)
 | 
					                objects.append(provider)
 | 
				
			||||||
        return objects
 | 
					        return objects
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def html_deployment_view(self, request: HttpRequest) -> Optional[str]:
 | 
					 | 
				
			||||||
        """return template and context modal to view token and other config info"""
 | 
					 | 
				
			||||||
        return render_to_string(
 | 
					 | 
				
			||||||
            "outposts/deployment_modal.html",
 | 
					 | 
				
			||||||
            {"outpost": self, "full_url": request.build_absolute_uri("/")},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __str__(self) -> str:
 | 
					    def __str__(self) -> str:
 | 
				
			||||||
        return f"Outpost {self.name}"
 | 
					        return f"Outpost {self.name}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@ UPDATE_TRIGGERING_MODELS = (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@receiver(post_save)
 | 
					@receiver(post_save)
 | 
				
			||||||
 | 
					# pylint: disable=unused-argument
 | 
				
			||||||
def post_save_update(sender, instance: Model, **_):
 | 
					def post_save_update(sender, instance: Model, **_):
 | 
				
			||||||
    """If an Outpost is saved, Ensure that token is created/updated
 | 
					    """If an Outpost is saved, Ensure that token is created/updated
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -29,7 +30,7 @@ def post_save_update(sender, instance: Model, **_):
 | 
				
			|||||||
        return
 | 
					        return
 | 
				
			||||||
    if instance.__module__ == "__fake__":
 | 
					    if instance.__module__ == "__fake__":
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    if sender not in UPDATE_TRIGGERING_MODELS:
 | 
					    if not isinstance(instance, UPDATE_TRIGGERING_MODELS):
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    outpost_post_save.delay(class_to_path(instance.__class__), instance.pk)
 | 
					    outpost_post_save.delay(class_to_path(instance.__class__), instance.pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,43 +0,0 @@
 | 
				
			|||||||
{% load i18n %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<ak-modal-button>
 | 
					 | 
				
			||||||
    <button slot="trigger" class="pf-c-button pf-m-tertiary">
 | 
					 | 
				
			||||||
        {% trans 'View Deployment Info' %}
 | 
					 | 
				
			||||||
    </button>
 | 
					 | 
				
			||||||
    <div slot="modal">
 | 
					 | 
				
			||||||
        <div class="pf-c-modal-box__header">
 | 
					 | 
				
			||||||
            <h1 class="pf-c-title pf-m-2xl" id="modal-title">{% trans 'Outpost Deployment Info' %}</h1>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="pf-c-modal-box__body" id="modal-description">
 | 
					 | 
				
			||||||
            <p><a href="https://goauthentik.io/docs/outposts/outposts/#deploy">{% trans 'View deployment documentation' %}</a></p>
 | 
					 | 
				
			||||||
            <form class="pf-c-form">
 | 
					 | 
				
			||||||
                <div class="pf-c-form__group">
 | 
					 | 
				
			||||||
                    <label class="pf-c-form__label" for="help-text-simple-form-name">
 | 
					 | 
				
			||||||
                        <span class="pf-c-form__label-text">AUTHENTIK_HOST</span>
 | 
					 | 
				
			||||||
                    </label>
 | 
					 | 
				
			||||||
                    <input class="pf-c-form-control" readonly type="text" value="{{ full_url }}" />
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <div class="pf-c-form__group">
 | 
					 | 
				
			||||||
                    <label class="pf-c-form__label" for="help-text-simple-form-name">
 | 
					 | 
				
			||||||
                        <span class="pf-c-form__label-text">AUTHENTIK_TOKEN</span>
 | 
					 | 
				
			||||||
                    </label>
 | 
					 | 
				
			||||||
                    <div>
 | 
					 | 
				
			||||||
                        <ak-token-copy-button identifier="{{ outpost.token_identifier }}">
 | 
					 | 
				
			||||||
                            {% trans 'Click to copy token' %}
 | 
					 | 
				
			||||||
                        </ak-token-copy-button>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <h3>{% trans 'If your authentik Instance is using a self-signed certificate, set this value.' %}</h3>
 | 
					 | 
				
			||||||
                <div class="pf-c-form__group">
 | 
					 | 
				
			||||||
                    <label class="pf-c-form__label" for="help-text-simple-form-name">
 | 
					 | 
				
			||||||
                        <span class="pf-c-form__label-text">AUTHENTIK_INSECURE</span>
 | 
					 | 
				
			||||||
                    </label>
 | 
					 | 
				
			||||||
                    <input class="pf-c-form-control" readonly type="text" value="true" />
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </form>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <footer class="pf-c-modal-box__footer pf-m-align-left">
 | 
					 | 
				
			||||||
            <a class="pf-c-button pf-m-primary">{% trans 'Close' %}</a>
 | 
					 | 
				
			||||||
        </footer>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</ak-modal-button>
 | 
					 | 
				
			||||||
@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					# Generated by Django 3.1.6 on 2021-02-09 16:57
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ("authentik_policies_event_matcher", "0006_auto_20210203_1134"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.AlterField(
 | 
				
			||||||
 | 
					            model_name="eventmatcherpolicy",
 | 
				
			||||||
 | 
					            name="action",
 | 
				
			||||||
 | 
					            field=models.TextField(
 | 
				
			||||||
 | 
					                blank=True,
 | 
				
			||||||
 | 
					                choices=[
 | 
				
			||||||
 | 
					                    ("login", "Login"),
 | 
				
			||||||
 | 
					                    ("login_failed", "Login Failed"),
 | 
				
			||||||
 | 
					                    ("logout", "Logout"),
 | 
				
			||||||
 | 
					                    ("user_write", "User Write"),
 | 
				
			||||||
 | 
					                    ("suspicious_request", "Suspicious Request"),
 | 
				
			||||||
 | 
					                    ("password_set", "Password Set"),
 | 
				
			||||||
 | 
					                    ("secret_view", "Secret View"),
 | 
				
			||||||
 | 
					                    ("invitation_used", "Invite Used"),
 | 
				
			||||||
 | 
					                    ("authorize_application", "Authorize Application"),
 | 
				
			||||||
 | 
					                    ("source_linked", "Source Linked"),
 | 
				
			||||||
 | 
					                    ("impersonation_started", "Impersonation Started"),
 | 
				
			||||||
 | 
					                    ("impersonation_ended", "Impersonation Ended"),
 | 
				
			||||||
 | 
					                    ("policy_execution", "Policy Execution"),
 | 
				
			||||||
 | 
					                    ("policy_exception", "Policy Exception"),
 | 
				
			||||||
 | 
					                    ("property_mapping_exception", "Property Mapping Exception"),
 | 
				
			||||||
 | 
					                    ("system_task_execution", "System Task Execution"),
 | 
				
			||||||
 | 
					                    ("system_task_exception", "System Task Exception"),
 | 
				
			||||||
 | 
					                    ("configuration_error", "Configuration Error"),
 | 
				
			||||||
 | 
					                    ("model_created", "Model Created"),
 | 
				
			||||||
 | 
					                    ("model_updated", "Model Updated"),
 | 
				
			||||||
 | 
					                    ("model_deleted", "Model Deleted"),
 | 
				
			||||||
 | 
					                    ("update_available", "Update Available"),
 | 
				
			||||||
 | 
					                    ("custom_", "Custom Prefix"),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                help_text="Match created events with this action type. When left empty, all action types will be matched.",
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@ -55,10 +55,6 @@ class PolicyEvaluator(BaseEvaluator):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def handle_error(self, exc: Exception, expression_source: str):
 | 
					    def handle_error(self, exc: Exception, expression_source: str):
 | 
				
			||||||
        """Exception Handler"""
 | 
					        """Exception Handler"""
 | 
				
			||||||
        # So, this is a bit questionable. Essentially, we are edit the stacktrace
 | 
					 | 
				
			||||||
        # so the user only sees information relevant to them
 | 
					 | 
				
			||||||
        # and none of our surrounding error handling
 | 
					 | 
				
			||||||
        exc.__traceback__ = exc.__traceback__.tb_next
 | 
					 | 
				
			||||||
        raise PolicyException(exc)
 | 
					        raise PolicyException(exc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def evaluate(self, expression_source: str) -> PolicyResult:
 | 
					    def evaluate(self, expression_source: str) -> PolicyResult:
 | 
				
			||||||
 | 
				
			|||||||
@ -80,7 +80,7 @@ class PolicyProcess(PROCESS_CLASS):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            policy_result = self.binding.policy.passes(self.request)
 | 
					            policy_result = self.binding.policy.passes(self.request)
 | 
				
			||||||
            if self.binding.policy.execution_logging:
 | 
					            if self.binding.policy.execution_logging and not self.request.debug:
 | 
				
			||||||
                self.create_event(
 | 
					                self.create_event(
 | 
				
			||||||
                    EventAction.POLICY_EXECUTION,
 | 
					                    EventAction.POLICY_EXECUTION,
 | 
				
			||||||
                    message="Policy Execution",
 | 
					                    message="Policy Execution",
 | 
				
			||||||
@ -94,16 +94,18 @@ class PolicyProcess(PROCESS_CLASS):
 | 
				
			|||||||
                + "".join(format_tb(src_exc.__traceback__))
 | 
					                + "".join(format_tb(src_exc.__traceback__))
 | 
				
			||||||
                + str(src_exc)
 | 
					                + str(src_exc)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            # Create policy exception event
 | 
					            # Create policy exception event, only when we're not debugging
 | 
				
			||||||
            self.create_event(EventAction.POLICY_EXCEPTION, message=error_string)
 | 
					            if not self.request.debug:
 | 
				
			||||||
 | 
					                self.create_event(EventAction.POLICY_EXCEPTION, message=error_string)
 | 
				
			||||||
            LOGGER.debug("P_ENG(proc): error", exc=src_exc)
 | 
					            LOGGER.debug("P_ENG(proc): error", exc=src_exc)
 | 
				
			||||||
            policy_result = PolicyResult(False, str(src_exc))
 | 
					            policy_result = PolicyResult(False, str(src_exc))
 | 
				
			||||||
        policy_result.source_policy = self.binding.policy
 | 
					        policy_result.source_policy = self.binding.policy
 | 
				
			||||||
        # Invert result if policy.negate is set
 | 
					        # Invert result if policy.negate is set
 | 
				
			||||||
        if self.binding.negate:
 | 
					        if self.binding.negate:
 | 
				
			||||||
            policy_result.passing = not policy_result.passing
 | 
					            policy_result.passing = not policy_result.passing
 | 
				
			||||||
        key = cache_key(self.binding, self.request)
 | 
					        if not self.request.debug:
 | 
				
			||||||
        cache.set(key, policy_result)
 | 
					            key = cache_key(self.binding, self.request)
 | 
				
			||||||
 | 
					            cache.set(key, policy_result)
 | 
				
			||||||
        LOGGER.debug(
 | 
					        LOGGER.debug(
 | 
				
			||||||
            "P_ENG(proc): finished and cached ",
 | 
					            "P_ENG(proc): finished and cached ",
 | 
				
			||||||
            policy=self.binding.policy,
 | 
					            policy=self.binding.policy,
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@ class PolicyRequest:
 | 
				
			|||||||
    http_request: Optional[HttpRequest]
 | 
					    http_request: Optional[HttpRequest]
 | 
				
			||||||
    obj: Optional[Model]
 | 
					    obj: Optional[Model]
 | 
				
			||||||
    context: dict[str, Any]
 | 
					    context: dict[str, Any]
 | 
				
			||||||
 | 
					    debug: bool = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, user: User):
 | 
					    def __init__(self, user: User):
 | 
				
			||||||
        super().__init__()
 | 
					        super().__init__()
 | 
				
			||||||
 | 
				
			|||||||
@ -59,11 +59,11 @@ class OAuth2ProviderViewSet(ModelViewSet):
 | 
				
			|||||||
    queryset = OAuth2Provider.objects.all()
 | 
					    queryset = OAuth2Provider.objects.all()
 | 
				
			||||||
    serializer_class = OAuth2ProviderSerializer
 | 
					    serializer_class = OAuth2ProviderSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @action(methods=["GET"], detail=True)
 | 
					 | 
				
			||||||
    @swagger_auto_schema(responses={200: OAuth2ProviderSetupURLs(many=False)})
 | 
					    @swagger_auto_schema(responses={200: OAuth2ProviderSetupURLs(many=False)})
 | 
				
			||||||
 | 
					    @action(methods=["GET"], detail=True)
 | 
				
			||||||
    # pylint: disable=invalid-name
 | 
					    # pylint: disable=invalid-name
 | 
				
			||||||
    def setup_urls(self, request: Request, pk: int) -> str:
 | 
					    def setup_urls(self, request: Request, pk: int) -> str:
 | 
				
			||||||
        """Return metadata as XML string"""
 | 
					        """Get Providers setup URLs"""
 | 
				
			||||||
        provider = get_object_or_404(OAuth2Provider, pk=pk)
 | 
					        provider = get_object_or_404(OAuth2Provider, pk=pk)
 | 
				
			||||||
        data = {
 | 
					        data = {
 | 
				
			||||||
            "issuer": provider.get_issuer(request),
 | 
					            "issuer": provider.get_issuer(request),
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,8 @@ return {
 | 
				
			|||||||
    "family_name": "",
 | 
					    "family_name": "",
 | 
				
			||||||
    "preferred_username": user.username,
 | 
					    "preferred_username": user.username,
 | 
				
			||||||
    "nickname": user.username,
 | 
					    "nickname": user.username,
 | 
				
			||||||
 | 
					    # groups is not part of the official userinfo schema, but is a quasi-standard
 | 
				
			||||||
 | 
					    "groups": [group.name for group in user.ak_groups.all()],
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -253,6 +253,7 @@ class OAuthFulfillmentStage(StageView):
 | 
				
			|||||||
                EventAction.AUTHORIZE_APPLICATION,
 | 
					                EventAction.AUTHORIZE_APPLICATION,
 | 
				
			||||||
                authorized_application=application,
 | 
					                authorized_application=application,
 | 
				
			||||||
                flow=self.executor.plan.flow_pk,
 | 
					                flow=self.executor.plan.flow_pk,
 | 
				
			||||||
 | 
					                scopes=", ".join(self.params.scope),
 | 
				
			||||||
            ).from_http(self.request)
 | 
					            ).from_http(self.request)
 | 
				
			||||||
            return redirect(self.create_response_uri())
 | 
					            return redirect(self.create_response_uri())
 | 
				
			||||||
        except (ClientIdError, RedirectUriError) as error:
 | 
					        except (ClientIdError, RedirectUriError) as error:
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,7 @@ class ProxyProviderForm(forms.ModelForm):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        self.fields["certificate"].queryset = CertificateKeyPair.objects.filter(
 | 
					        self.fields["certificate"].queryset = CertificateKeyPair.objects.filter(
 | 
				
			||||||
            key_data__isnull=False
 | 
					            key_data__isnull=False
 | 
				
			||||||
        )
 | 
					        ).exclude(key_data="")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save(self, *args, **kwargs):
 | 
					    def save(self, *args, **kwargs):
 | 
				
			||||||
        actual_save = super().save(*args, **kwargs)
 | 
					        actual_save = super().save(*args, **kwargs)
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ from rest_framework.viewsets import ModelViewSet
 | 
				
			|||||||
from authentik.core.api.providers import ProviderSerializer
 | 
					from authentik.core.api.providers import ProviderSerializer
 | 
				
			||||||
from authentik.core.api.utils import MetaNameSerializer
 | 
					from authentik.core.api.utils import MetaNameSerializer
 | 
				
			||||||
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
 | 
					from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
 | 
				
			||||||
from authentik.providers.saml.views import DescriptorDownloadView
 | 
					from authentik.providers.saml.views.metadata import DescriptorDownloadView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SAMLProviderSerializer(ProviderSerializer):
 | 
					class SAMLProviderSerializer(ProviderSerializer):
 | 
				
			||||||
@ -54,10 +54,10 @@ class SAMLProviderViewSet(ModelViewSet):
 | 
				
			|||||||
    queryset = SAMLProvider.objects.all()
 | 
					    queryset = SAMLProvider.objects.all()
 | 
				
			||||||
    serializer_class = SAMLProviderSerializer
 | 
					    serializer_class = SAMLProviderSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @action(methods=["GET"], detail=True)
 | 
					 | 
				
			||||||
    @swagger_auto_schema(responses={200: SAMLMetadataSerializer(many=False)})
 | 
					    @swagger_auto_schema(responses={200: SAMLMetadataSerializer(many=False)})
 | 
				
			||||||
 | 
					    @action(methods=["GET"], detail=True)
 | 
				
			||||||
    # pylint: disable=invalid-name
 | 
					    # pylint: disable=invalid-name
 | 
				
			||||||
    def metadata(self, request: Request, pk: int) -> str:
 | 
					    def metadata(self, request: Request, pk: int) -> Response:
 | 
				
			||||||
        """Return metadata as XML string"""
 | 
					        """Return metadata as XML string"""
 | 
				
			||||||
        provider = get_object_or_404(SAMLProvider, pk=pk)
 | 
					        provider = get_object_or_404(SAMLProvider, pk=pk)
 | 
				
			||||||
        metadata = DescriptorDownloadView.get_metadata(request, provider)
 | 
					        metadata = DescriptorDownloadView.get_metadata(request, provider)
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,7 @@ class SAMLProviderManager(ObjectManager):
 | 
				
			|||||||
                name="authentik default SAML Mapping: UPN",
 | 
					                name="authentik default SAML Mapping: UPN",
 | 
				
			||||||
                saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn",
 | 
					                saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn",
 | 
				
			||||||
                expression="return user.attributes.get('upn', user.email)",
 | 
					                expression="return user.attributes.get('upn', user.email)",
 | 
				
			||||||
 | 
					                friendly_name="",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            EnsureExists(
 | 
					            EnsureExists(
 | 
				
			||||||
                SAMLPropertyMapping,
 | 
					                SAMLPropertyMapping,
 | 
				
			||||||
@ -26,6 +27,7 @@ class SAMLProviderManager(ObjectManager):
 | 
				
			|||||||
                name="authentik default SAML Mapping: Name",
 | 
					                name="authentik default SAML Mapping: Name",
 | 
				
			||||||
                saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
 | 
					                saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
 | 
				
			||||||
                expression="return user.name",
 | 
					                expression="return user.name",
 | 
				
			||||||
 | 
					                friendly_name="",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            EnsureExists(
 | 
					            EnsureExists(
 | 
				
			||||||
                SAMLPropertyMapping,
 | 
					                SAMLPropertyMapping,
 | 
				
			||||||
@ -33,6 +35,7 @@ class SAMLProviderManager(ObjectManager):
 | 
				
			|||||||
                name="authentik default SAML Mapping: Email",
 | 
					                name="authentik default SAML Mapping: Email",
 | 
				
			||||||
                saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
 | 
					                saml_name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
 | 
				
			||||||
                expression="return user.email",
 | 
					                expression="return user.email",
 | 
				
			||||||
 | 
					                friendly_name="",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            EnsureExists(
 | 
					            EnsureExists(
 | 
				
			||||||
                SAMLPropertyMapping,
 | 
					                SAMLPropertyMapping,
 | 
				
			||||||
@ -40,6 +43,7 @@ class SAMLProviderManager(ObjectManager):
 | 
				
			|||||||
                name="authentik default SAML Mapping: Username",
 | 
					                name="authentik default SAML Mapping: Username",
 | 
				
			||||||
                saml_name="http://schemas.goauthentik.io/2021/02/saml/username",
 | 
					                saml_name="http://schemas.goauthentik.io/2021/02/saml/username",
 | 
				
			||||||
                expression="return user.username",
 | 
					                expression="return user.username",
 | 
				
			||||||
 | 
					                friendly_name="",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            EnsureExists(
 | 
					            EnsureExists(
 | 
				
			||||||
                SAMLPropertyMapping,
 | 
					                SAMLPropertyMapping,
 | 
				
			||||||
@ -47,6 +51,7 @@ class SAMLProviderManager(ObjectManager):
 | 
				
			|||||||
                name="authentik default SAML Mapping: User ID",
 | 
					                name="authentik default SAML Mapping: User ID",
 | 
				
			||||||
                saml_name="http://schemas.goauthentik.io/2021/02/saml/uid",
 | 
					                saml_name="http://schemas.goauthentik.io/2021/02/saml/uid",
 | 
				
			||||||
                expression="return user.pk",
 | 
					                expression="return user.pk",
 | 
				
			||||||
 | 
					                friendly_name="",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            EnsureExists(
 | 
					            EnsureExists(
 | 
				
			||||||
                SAMLPropertyMapping,
 | 
					                SAMLPropertyMapping,
 | 
				
			||||||
@ -54,6 +59,7 @@ class SAMLProviderManager(ObjectManager):
 | 
				
			|||||||
                name="authentik default SAML Mapping: Groups",
 | 
					                name="authentik default SAML Mapping: Groups",
 | 
				
			||||||
                saml_name="http://schemas.xmlsoap.org/claims/Group",
 | 
					                saml_name="http://schemas.xmlsoap.org/claims/Group",
 | 
				
			||||||
                expression=GROUP_EXPRESSION,
 | 
					                expression=GROUP_EXPRESSION,
 | 
				
			||||||
 | 
					                friendly_name="",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            EnsureExists(
 | 
					            EnsureExists(
 | 
				
			||||||
                SAMLPropertyMapping,
 | 
					                SAMLPropertyMapping,
 | 
				
			||||||
@ -63,5 +69,6 @@ class SAMLProviderManager(ObjectManager):
 | 
				
			|||||||
                    "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
 | 
					                    "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
                expression="return user.username",
 | 
					                expression="return user.username",
 | 
				
			||||||
 | 
					                friendly_name="",
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,29 +1,29 @@
 | 
				
			|||||||
"""authentik SAML IDP URLs"""
 | 
					"""authentik SAML IDP URLs"""
 | 
				
			||||||
from django.urls import path
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.providers.saml import views
 | 
					from authentik.providers.saml.views import metadata, sso
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    # SSO Bindings
 | 
					    # SSO Bindings
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "<slug:application_slug>/sso/binding/redirect/",
 | 
					        "<slug:application_slug>/sso/binding/redirect/",
 | 
				
			||||||
        views.SAMLSSOBindingRedirectView.as_view(),
 | 
					        sso.SAMLSSOBindingRedirectView.as_view(),
 | 
				
			||||||
        name="sso-redirect",
 | 
					        name="sso-redirect",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "<slug:application_slug>/sso/binding/post/",
 | 
					        "<slug:application_slug>/sso/binding/post/",
 | 
				
			||||||
        views.SAMLSSOBindingPOSTView.as_view(),
 | 
					        sso.SAMLSSOBindingPOSTView.as_view(),
 | 
				
			||||||
        name="sso-post",
 | 
					        name="sso-post",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # SSO IdP Initiated
 | 
					    # SSO IdP Initiated
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "<slug:application_slug>/sso/binding/init/",
 | 
					        "<slug:application_slug>/sso/binding/init/",
 | 
				
			||||||
        views.SAMLSSOBindingInitView.as_view(),
 | 
					        sso.SAMLSSOBindingInitView.as_view(),
 | 
				
			||||||
        name="sso-init",
 | 
					        name="sso-init",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "<slug:application_slug>/metadata/",
 | 
					        "<slug:application_slug>/metadata/",
 | 
				
			||||||
        views.DescriptorDownloadView.as_view(),
 | 
					        metadata.DescriptorDownloadView.as_view(),
 | 
				
			||||||
        name="metadata",
 | 
					        name="metadata",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,284 +0,0 @@
 | 
				
			|||||||
"""authentik SAML IDP Views"""
 | 
					 | 
				
			||||||
from typing import Optional
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib import messages
 | 
					 | 
				
			||||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
					 | 
				
			||||||
from django.core.validators import URLValidator
 | 
					 | 
				
			||||||
from django.http import HttpRequest, HttpResponse
 | 
					 | 
				
			||||||
from django.shortcuts import get_object_or_404, redirect, render
 | 
					 | 
				
			||||||
from django.urls.base import reverse_lazy
 | 
					 | 
				
			||||||
from django.utils.decorators import method_decorator
 | 
					 | 
				
			||||||
from django.utils.http import urlencode
 | 
					 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					 | 
				
			||||||
from django.views import View
 | 
					 | 
				
			||||||
from django.views.decorators.csrf import csrf_exempt
 | 
					 | 
				
			||||||
from django.views.generic.edit import FormView
 | 
					 | 
				
			||||||
from structlog.stdlib import get_logger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from authentik.core.models import Application, Provider
 | 
					 | 
				
			||||||
from authentik.events.models import Event, EventAction
 | 
					 | 
				
			||||||
from authentik.flows.models import in_memory_stage
 | 
					 | 
				
			||||||
from authentik.flows.planner import (
 | 
					 | 
				
			||||||
    PLAN_CONTEXT_APPLICATION,
 | 
					 | 
				
			||||||
    PLAN_CONTEXT_SSO,
 | 
					 | 
				
			||||||
    FlowPlanner,
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
from authentik.flows.stage import StageView
 | 
					 | 
				
			||||||
from authentik.flows.views import SESSION_KEY_PLAN
 | 
					 | 
				
			||||||
from authentik.lib.utils.urls import redirect_with_qs
 | 
					 | 
				
			||||||
from authentik.lib.views import bad_request_message
 | 
					 | 
				
			||||||
from authentik.policies.views import PolicyAccessView
 | 
					 | 
				
			||||||
from authentik.providers.saml.exceptions import CannotHandleAssertion
 | 
					 | 
				
			||||||
from authentik.providers.saml.forms import SAMLProviderImportForm
 | 
					 | 
				
			||||||
from authentik.providers.saml.models import SAMLBindings, SAMLProvider
 | 
					 | 
				
			||||||
from authentik.providers.saml.processors.assertion import AssertionProcessor
 | 
					 | 
				
			||||||
from authentik.providers.saml.processors.metadata import MetadataProcessor
 | 
					 | 
				
			||||||
from authentik.providers.saml.processors.metadata_parser import (
 | 
					 | 
				
			||||||
    ServiceProviderMetadataParser,
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
from authentik.providers.saml.processors.request_parser import (
 | 
					 | 
				
			||||||
    AuthNRequest,
 | 
					 | 
				
			||||||
    AuthNRequestParser,
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
from authentik.providers.saml.utils.encoding import deflate_and_base64_encode, nice64
 | 
					 | 
				
			||||||
from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_TEMPLATE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
LOGGER = get_logger()
 | 
					 | 
				
			||||||
URL_VALIDATOR = URLValidator(schemes=("http", "https"))
 | 
					 | 
				
			||||||
REQUEST_KEY_SAML_REQUEST = "SAMLRequest"
 | 
					 | 
				
			||||||
REQUEST_KEY_SAML_SIGNATURE = "Signature"
 | 
					 | 
				
			||||||
REQUEST_KEY_SAML_SIG_ALG = "SigAlg"
 | 
					 | 
				
			||||||
REQUEST_KEY_SAML_RESPONSE = "SAMLResponse"
 | 
					 | 
				
			||||||
REQUEST_KEY_RELAY_STATE = "RelayState"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SESSION_KEY_AUTH_N_REQUEST = "authn_request"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SAMLSSOView(PolicyAccessView):
 | 
					 | 
				
			||||||
    """ "SAML SSO Base View, which plans a flow and injects our final stage.
 | 
					 | 
				
			||||||
    Calls get/post handler."""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def resolve_provider_application(self):
 | 
					 | 
				
			||||||
        self.application = get_object_or_404(
 | 
					 | 
				
			||||||
            Application, slug=self.kwargs["application_slug"]
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.provider: SAMLProvider = get_object_or_404(
 | 
					 | 
				
			||||||
            SAMLProvider, pk=self.application.provider_id
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_saml_request(self) -> Optional[HttpRequest]:
 | 
					 | 
				
			||||||
        """Handler to verify the SAML Request. Must be implemented by a subclass"""
 | 
					 | 
				
			||||||
        raise NotImplementedError
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # pylint: disable=unused-argument
 | 
					 | 
				
			||||||
    def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
 | 
					 | 
				
			||||||
        """Verify the SAML Request, and if valid initiate the FlowPlanner for the application"""
 | 
					 | 
				
			||||||
        # Call the method handler, which checks the SAML
 | 
					 | 
				
			||||||
        # Request and returns a HTTP Response on error
 | 
					 | 
				
			||||||
        method_response = self.check_saml_request()
 | 
					 | 
				
			||||||
        if method_response:
 | 
					 | 
				
			||||||
            return method_response
 | 
					 | 
				
			||||||
        # Regardless, we start the planner and return to it
 | 
					 | 
				
			||||||
        planner = FlowPlanner(self.provider.authorization_flow)
 | 
					 | 
				
			||||||
        planner.allow_empty_flows = True
 | 
					 | 
				
			||||||
        plan = planner.plan(
 | 
					 | 
				
			||||||
            request,
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                PLAN_CONTEXT_SSO: True,
 | 
					 | 
				
			||||||
                PLAN_CONTEXT_APPLICATION: self.application,
 | 
					 | 
				
			||||||
                PLAN_CONTEXT_CONSENT_TEMPLATE: "providers/saml/consent.html",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        plan.append(in_memory_stage(SAMLFlowFinalView))
 | 
					 | 
				
			||||||
        request.session[SESSION_KEY_PLAN] = plan
 | 
					 | 
				
			||||||
        return redirect_with_qs(
 | 
					 | 
				
			||||||
            "authentik_flows:flow-executor-shell",
 | 
					 | 
				
			||||||
            request.GET,
 | 
					 | 
				
			||||||
            flow_slug=self.provider.authorization_flow.slug,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def post(self, request: HttpRequest, application_slug: str) -> HttpResponse:
 | 
					 | 
				
			||||||
        """GET and POST use the same handler, but we can't
 | 
					 | 
				
			||||||
        override .dispatch easily because PolicyAccessView's dispatch"""
 | 
					 | 
				
			||||||
        return self.get(request, application_slug)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SAMLSSOBindingRedirectView(SAMLSSOView):
 | 
					 | 
				
			||||||
    """SAML Handler for SSO/Redirect bindings, which are sent via GET"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_saml_request(self) -> Optional[HttpRequest]:
 | 
					 | 
				
			||||||
        """Handle REDIRECT bindings"""
 | 
					 | 
				
			||||||
        if REQUEST_KEY_SAML_REQUEST not in self.request.GET:
 | 
					 | 
				
			||||||
            LOGGER.info("handle_saml_request: SAML payload missing")
 | 
					 | 
				
			||||||
            return bad_request_message(
 | 
					 | 
				
			||||||
                self.request, "The SAML request payload is missing."
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            auth_n_request = AuthNRequestParser(self.provider).parse_detached(
 | 
					 | 
				
			||||||
                self.request.GET[REQUEST_KEY_SAML_REQUEST],
 | 
					 | 
				
			||||||
                self.request.GET.get(REQUEST_KEY_RELAY_STATE),
 | 
					 | 
				
			||||||
                self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE),
 | 
					 | 
				
			||||||
                self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
 | 
					 | 
				
			||||||
        except CannotHandleAssertion as exc:
 | 
					 | 
				
			||||||
            Event.new(
 | 
					 | 
				
			||||||
                EventAction.CONFIGURATION_ERROR,
 | 
					 | 
				
			||||||
                provider=self.provider,
 | 
					 | 
				
			||||||
                message=str(exc),
 | 
					 | 
				
			||||||
            ).save()
 | 
					 | 
				
			||||||
            LOGGER.info(str(exc))
 | 
					 | 
				
			||||||
            return bad_request_message(self.request, str(exc))
 | 
					 | 
				
			||||||
        return None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@method_decorator(csrf_exempt, name="dispatch")
 | 
					 | 
				
			||||||
class SAMLSSOBindingPOSTView(SAMLSSOView):
 | 
					 | 
				
			||||||
    """SAML Handler for SSO/POST bindings"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_saml_request(self) -> Optional[HttpRequest]:
 | 
					 | 
				
			||||||
        """Handle POST bindings"""
 | 
					 | 
				
			||||||
        if REQUEST_KEY_SAML_REQUEST not in self.request.POST:
 | 
					 | 
				
			||||||
            LOGGER.info("check_saml_request: SAML payload missing")
 | 
					 | 
				
			||||||
            return bad_request_message(
 | 
					 | 
				
			||||||
                self.request, "The SAML request payload is missing."
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            auth_n_request = AuthNRequestParser(self.provider).parse(
 | 
					 | 
				
			||||||
                self.request.POST[REQUEST_KEY_SAML_REQUEST],
 | 
					 | 
				
			||||||
                self.request.POST.get(REQUEST_KEY_RELAY_STATE),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
 | 
					 | 
				
			||||||
        except CannotHandleAssertion as exc:
 | 
					 | 
				
			||||||
            LOGGER.info(str(exc))
 | 
					 | 
				
			||||||
            return bad_request_message(self.request, str(exc))
 | 
					 | 
				
			||||||
        return None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SAMLSSOBindingInitView(SAMLSSOView):
 | 
					 | 
				
			||||||
    """SAML Handler for for IdP Initiated login flows"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_saml_request(self) -> Optional[HttpRequest]:
 | 
					 | 
				
			||||||
        """Create SAML Response from scratch"""
 | 
					 | 
				
			||||||
        LOGGER.debug(
 | 
					 | 
				
			||||||
            "handle_saml_no_request: No SAML Request, using IdP-initiated flow."
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        auth_n_request = AuthNRequestParser(self.provider).idp_initiated()
 | 
					 | 
				
			||||||
        self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This View doesn't have a URL on purpose, as its called by the FlowExecutor
 | 
					 | 
				
			||||||
class SAMLFlowFinalView(StageView):
 | 
					 | 
				
			||||||
    """View used by FlowExecutor after all stages have passed. Logs the authorization,
 | 
					 | 
				
			||||||
    and redirects to the SP (if REDIRECT is configured) or shows and auto-submit for
 | 
					 | 
				
			||||||
    (if POST is configured)."""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
 | 
					 | 
				
			||||||
        application: Application = self.executor.plan.context[PLAN_CONTEXT_APPLICATION]
 | 
					 | 
				
			||||||
        provider: SAMLProvider = get_object_or_404(
 | 
					 | 
				
			||||||
            SAMLProvider, pk=application.provider_id
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        # Log Application Authorization
 | 
					 | 
				
			||||||
        Event.new(
 | 
					 | 
				
			||||||
            EventAction.AUTHORIZE_APPLICATION,
 | 
					 | 
				
			||||||
            authorized_application=application,
 | 
					 | 
				
			||||||
            flow=self.executor.plan.flow_pk,
 | 
					 | 
				
			||||||
        ).from_http(self.request)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if SESSION_KEY_AUTH_N_REQUEST not in self.request.session:
 | 
					 | 
				
			||||||
            return self.executor.stage_invalid()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auth_n_request: AuthNRequest = self.request.session.pop(
 | 
					 | 
				
			||||||
            SESSION_KEY_AUTH_N_REQUEST
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        response = AssertionProcessor(
 | 
					 | 
				
			||||||
            provider, request, auth_n_request
 | 
					 | 
				
			||||||
        ).build_response()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if provider.sp_binding == SAMLBindings.POST:
 | 
					 | 
				
			||||||
            form_attrs = {
 | 
					 | 
				
			||||||
                "ACSUrl": provider.acs_url,
 | 
					 | 
				
			||||||
                REQUEST_KEY_SAML_RESPONSE: nice64(response),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if auth_n_request.relay_state:
 | 
					 | 
				
			||||||
                form_attrs[REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
 | 
					 | 
				
			||||||
            return render(
 | 
					 | 
				
			||||||
                self.request,
 | 
					 | 
				
			||||||
                "generic/autosubmit_form.html",
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    "url": provider.acs_url,
 | 
					 | 
				
			||||||
                    "title": _("Redirecting to %(app)s..." % {"app": application.name}),
 | 
					 | 
				
			||||||
                    "attrs": form_attrs,
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        if provider.sp_binding == SAMLBindings.REDIRECT:
 | 
					 | 
				
			||||||
            url_args = {
 | 
					 | 
				
			||||||
                REQUEST_KEY_SAML_RESPONSE: deflate_and_base64_encode(response),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if auth_n_request.relay_state:
 | 
					 | 
				
			||||||
                url_args[REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
 | 
					 | 
				
			||||||
            querystring = urlencode(url_args)
 | 
					 | 
				
			||||||
            return redirect(f"{provider.acs_url}?{querystring}")
 | 
					 | 
				
			||||||
        return bad_request_message(request, "Invalid sp_binding specified")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class DescriptorDownloadView(View):
 | 
					 | 
				
			||||||
    """Replies with the XML Metadata IDSSODescriptor."""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def get_metadata(request: HttpRequest, provider: SAMLProvider) -> str:
 | 
					 | 
				
			||||||
        """Return rendered XML Metadata"""
 | 
					 | 
				
			||||||
        return MetadataProcessor(provider, request).build_entity_descriptor()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
 | 
					 | 
				
			||||||
        """Replies with the XML Metadata IDSSODescriptor."""
 | 
					 | 
				
			||||||
        application = get_object_or_404(Application, slug=application_slug)
 | 
					 | 
				
			||||||
        provider: SAMLProvider = get_object_or_404(
 | 
					 | 
				
			||||||
            SAMLProvider, pk=application.provider_id
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            metadata = DescriptorDownloadView.get_metadata(request, provider)
 | 
					 | 
				
			||||||
        except Provider.application.RelatedObjectDoesNotExist:  # pylint: disable=no-member
 | 
					 | 
				
			||||||
            return bad_request_message(
 | 
					 | 
				
			||||||
                request, "Provider is not assigned to an application."
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            response = HttpResponse(metadata, content_type="application/xml")
 | 
					 | 
				
			||||||
            response[
 | 
					 | 
				
			||||||
                "Content-Disposition"
 | 
					 | 
				
			||||||
            ] = f'attachment; filename="{provider.name}_authentik_meta.xml"'
 | 
					 | 
				
			||||||
            return response
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class MetadataImportView(LoginRequiredMixin, FormView):
 | 
					 | 
				
			||||||
    """Import Metadata from XML, and create provider"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    form_class = SAMLProviderImportForm
 | 
					 | 
				
			||||||
    template_name = "providers/saml/import.html"
 | 
					 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:providers")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def dispatch(self, request, *args, **kwargs):
 | 
					 | 
				
			||||||
        if not request.user.is_superuser:
 | 
					 | 
				
			||||||
            return self.handle_no_permission()
 | 
					 | 
				
			||||||
        return super().dispatch(request, *args, **kwargs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def form_valid(self, form: SAMLProviderImportForm) -> HttpResponse:
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            metadata = ServiceProviderMetadataParser().parse(
 | 
					 | 
				
			||||||
                form.cleaned_data["metadata"].read().decode()
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            metadata.to_provider(
 | 
					 | 
				
			||||||
                form.cleaned_data["provider_name"],
 | 
					 | 
				
			||||||
                form.cleaned_data["authorization_flow"],
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            messages.success(self.request, _("Successfully created Provider"))
 | 
					 | 
				
			||||||
        except ValueError as exc:
 | 
					 | 
				
			||||||
            LOGGER.warning(str(exc))
 | 
					 | 
				
			||||||
            messages.error(
 | 
					 | 
				
			||||||
                self.request,
 | 
					 | 
				
			||||||
                _("Failed to import Metadata: %(message)s" % {"message": str(exc)}),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            return super().form_invalid(form)
 | 
					 | 
				
			||||||
        return super().form_valid(form)
 | 
					 | 
				
			||||||
							
								
								
									
										0
									
								
								authentik/providers/saml/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								authentik/providers/saml/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										82
									
								
								authentik/providers/saml/views/flows.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								authentik/providers/saml/views/flows.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					"""authentik SAML IDP Views"""
 | 
				
			||||||
 | 
					from django.core.validators import URLValidator
 | 
				
			||||||
 | 
					from django.http import HttpRequest, HttpResponse
 | 
				
			||||||
 | 
					from django.shortcuts import get_object_or_404, redirect, render
 | 
				
			||||||
 | 
					from django.utils.http import urlencode
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.core.models import Application
 | 
				
			||||||
 | 
					from authentik.events.models import Event, EventAction
 | 
				
			||||||
 | 
					from authentik.flows.planner import PLAN_CONTEXT_APPLICATION
 | 
				
			||||||
 | 
					from authentik.flows.stage import StageView
 | 
				
			||||||
 | 
					from authentik.lib.views import bad_request_message
 | 
				
			||||||
 | 
					from authentik.providers.saml.models import SAMLBindings, SAMLProvider
 | 
				
			||||||
 | 
					from authentik.providers.saml.processors.assertion import AssertionProcessor
 | 
				
			||||||
 | 
					from authentik.providers.saml.processors.request_parser import AuthNRequest
 | 
				
			||||||
 | 
					from authentik.providers.saml.utils.encoding import deflate_and_base64_encode, nice64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGGER = get_logger()
 | 
				
			||||||
 | 
					URL_VALIDATOR = URLValidator(schemes=("http", "https"))
 | 
				
			||||||
 | 
					REQUEST_KEY_SAML_REQUEST = "SAMLRequest"
 | 
				
			||||||
 | 
					REQUEST_KEY_SAML_SIGNATURE = "Signature"
 | 
				
			||||||
 | 
					REQUEST_KEY_SAML_SIG_ALG = "SigAlg"
 | 
				
			||||||
 | 
					REQUEST_KEY_SAML_RESPONSE = "SAMLResponse"
 | 
				
			||||||
 | 
					REQUEST_KEY_RELAY_STATE = "RelayState"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SESSION_KEY_AUTH_N_REQUEST = "authn_request"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# This View doesn't have a URL on purpose, as its called by the FlowExecutor
 | 
				
			||||||
 | 
					class SAMLFlowFinalView(StageView):
 | 
				
			||||||
 | 
					    """View used by FlowExecutor after all stages have passed. Logs the authorization,
 | 
				
			||||||
 | 
					    and redirects to the SP (if REDIRECT is configured) or shows and auto-submit for
 | 
				
			||||||
 | 
					    (if POST is configured)."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
 | 
				
			||||||
 | 
					        application: Application = self.executor.plan.context[PLAN_CONTEXT_APPLICATION]
 | 
				
			||||||
 | 
					        provider: SAMLProvider = get_object_or_404(
 | 
				
			||||||
 | 
					            SAMLProvider, pk=application.provider_id
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        # Log Application Authorization
 | 
				
			||||||
 | 
					        Event.new(
 | 
				
			||||||
 | 
					            EventAction.AUTHORIZE_APPLICATION,
 | 
				
			||||||
 | 
					            authorized_application=application,
 | 
				
			||||||
 | 
					            flow=self.executor.plan.flow_pk,
 | 
				
			||||||
 | 
					        ).from_http(self.request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if SESSION_KEY_AUTH_N_REQUEST not in self.request.session:
 | 
				
			||||||
 | 
					            return self.executor.stage_invalid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auth_n_request: AuthNRequest = self.request.session.pop(
 | 
				
			||||||
 | 
					            SESSION_KEY_AUTH_N_REQUEST
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        response = AssertionProcessor(
 | 
				
			||||||
 | 
					            provider, request, auth_n_request
 | 
				
			||||||
 | 
					        ).build_response()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if provider.sp_binding == SAMLBindings.POST:
 | 
				
			||||||
 | 
					            form_attrs = {
 | 
				
			||||||
 | 
					                "ACSUrl": provider.acs_url,
 | 
				
			||||||
 | 
					                REQUEST_KEY_SAML_RESPONSE: nice64(response),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if auth_n_request.relay_state:
 | 
				
			||||||
 | 
					                form_attrs[REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
 | 
				
			||||||
 | 
					            return render(
 | 
				
			||||||
 | 
					                self.request,
 | 
				
			||||||
 | 
					                "generic/autosubmit_form.html",
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": provider.acs_url,
 | 
				
			||||||
 | 
					                    "title": _("Redirecting to %(app)s..." % {"app": application.name}),
 | 
				
			||||||
 | 
					                    "attrs": form_attrs,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        if provider.sp_binding == SAMLBindings.REDIRECT:
 | 
				
			||||||
 | 
					            url_args = {
 | 
				
			||||||
 | 
					                REQUEST_KEY_SAML_RESPONSE: deflate_and_base64_encode(response),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if auth_n_request.relay_state:
 | 
				
			||||||
 | 
					                url_args[REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
 | 
				
			||||||
 | 
					            querystring = urlencode(url_args)
 | 
				
			||||||
 | 
					            return redirect(f"{provider.acs_url}?{querystring}")
 | 
				
			||||||
 | 
					        return bad_request_message(request, "Invalid sp_binding specified")
 | 
				
			||||||
							
								
								
									
										82
									
								
								authentik/providers/saml/views/metadata.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								authentik/providers/saml/views/metadata.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					"""authentik SAML IDP Views"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib import messages
 | 
				
			||||||
 | 
					from django.contrib.auth.mixins import LoginRequiredMixin
 | 
				
			||||||
 | 
					from django.http import HttpRequest, HttpResponse
 | 
				
			||||||
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
 | 
					from django.urls.base import reverse_lazy
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from django.views import View
 | 
				
			||||||
 | 
					from django.views.generic.edit import FormView
 | 
				
			||||||
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.core.models import Application, Provider
 | 
				
			||||||
 | 
					from authentik.lib.views import bad_request_message
 | 
				
			||||||
 | 
					from authentik.providers.saml.forms import SAMLProviderImportForm
 | 
				
			||||||
 | 
					from authentik.providers.saml.models import SAMLProvider
 | 
				
			||||||
 | 
					from authentik.providers.saml.processors.metadata import MetadataProcessor
 | 
				
			||||||
 | 
					from authentik.providers.saml.processors.metadata_parser import (
 | 
				
			||||||
 | 
					    ServiceProviderMetadataParser,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGGER = get_logger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DescriptorDownloadView(View):
 | 
				
			||||||
 | 
					    """Replies with the XML Metadata IDSSODescriptor."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def get_metadata(request: HttpRequest, provider: SAMLProvider) -> str:
 | 
				
			||||||
 | 
					        """Return rendered XML Metadata"""
 | 
				
			||||||
 | 
					        return MetadataProcessor(provider, request).build_entity_descriptor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
 | 
				
			||||||
 | 
					        """Replies with the XML Metadata IDSSODescriptor."""
 | 
				
			||||||
 | 
					        application = get_object_or_404(Application, slug=application_slug)
 | 
				
			||||||
 | 
					        provider: SAMLProvider = get_object_or_404(
 | 
				
			||||||
 | 
					            SAMLProvider, pk=application.provider_id
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            metadata = DescriptorDownloadView.get_metadata(request, provider)
 | 
				
			||||||
 | 
					        except Provider.application.RelatedObjectDoesNotExist:  # pylint: disable=no-member
 | 
				
			||||||
 | 
					            return bad_request_message(
 | 
				
			||||||
 | 
					                request, "Provider is not assigned to an application."
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            response = HttpResponse(metadata, content_type="application/xml")
 | 
				
			||||||
 | 
					            response[
 | 
				
			||||||
 | 
					                "Content-Disposition"
 | 
				
			||||||
 | 
					            ] = f'attachment; filename="{provider.name}_authentik_meta.xml"'
 | 
				
			||||||
 | 
					            return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MetadataImportView(LoginRequiredMixin, FormView):
 | 
				
			||||||
 | 
					    """Import Metadata from XML, and create provider"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    form_class = SAMLProviderImportForm
 | 
				
			||||||
 | 
					    template_name = "providers/saml/import.html"
 | 
				
			||||||
 | 
					    success_url = reverse_lazy("authentik_admin:providers")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dispatch(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        if not request.user.is_superuser:
 | 
				
			||||||
 | 
					            return self.handle_no_permission()
 | 
				
			||||||
 | 
					        return super().dispatch(request, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def form_valid(self, form: SAMLProviderImportForm) -> HttpResponse:
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            metadata = ServiceProviderMetadataParser().parse(
 | 
				
			||||||
 | 
					                form.cleaned_data["metadata"].read().decode()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            metadata.to_provider(
 | 
				
			||||||
 | 
					                form.cleaned_data["provider_name"],
 | 
				
			||||||
 | 
					                form.cleaned_data["authorization_flow"],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            messages.success(self.request, _("Successfully created Provider"))
 | 
				
			||||||
 | 
					        except ValueError as exc:
 | 
				
			||||||
 | 
					            LOGGER.warning(str(exc))
 | 
				
			||||||
 | 
					            messages.error(
 | 
				
			||||||
 | 
					                self.request,
 | 
				
			||||||
 | 
					                _("Failed to import Metadata: %(message)s" % {"message": str(exc)}),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            return super().form_invalid(form)
 | 
				
			||||||
 | 
					        return super().form_valid(form)
 | 
				
			||||||
							
								
								
									
										150
									
								
								authentik/providers/saml/views/sso.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								authentik/providers/saml/views/sso.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,150 @@
 | 
				
			|||||||
 | 
					"""authentik SAML IDP Views"""
 | 
				
			||||||
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.http import HttpRequest, HttpResponse
 | 
				
			||||||
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
 | 
					from django.utils.decorators import method_decorator
 | 
				
			||||||
 | 
					from django.views.decorators.csrf import csrf_exempt
 | 
				
			||||||
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.core.models import Application
 | 
				
			||||||
 | 
					from authentik.events.models import Event, EventAction
 | 
				
			||||||
 | 
					from authentik.flows.models import in_memory_stage
 | 
				
			||||||
 | 
					from authentik.flows.planner import (
 | 
				
			||||||
 | 
					    PLAN_CONTEXT_APPLICATION,
 | 
				
			||||||
 | 
					    PLAN_CONTEXT_SSO,
 | 
				
			||||||
 | 
					    FlowPlanner,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from authentik.flows.views import SESSION_KEY_PLAN
 | 
				
			||||||
 | 
					from authentik.lib.utils.urls import redirect_with_qs
 | 
				
			||||||
 | 
					from authentik.lib.views import bad_request_message
 | 
				
			||||||
 | 
					from authentik.policies.views import PolicyAccessView
 | 
				
			||||||
 | 
					from authentik.providers.saml.exceptions import CannotHandleAssertion
 | 
				
			||||||
 | 
					from authentik.providers.saml.models import SAMLProvider
 | 
				
			||||||
 | 
					from authentik.providers.saml.processors.request_parser import AuthNRequestParser
 | 
				
			||||||
 | 
					from authentik.providers.saml.views.flows import (
 | 
				
			||||||
 | 
					    REQUEST_KEY_RELAY_STATE,
 | 
				
			||||||
 | 
					    REQUEST_KEY_SAML_REQUEST,
 | 
				
			||||||
 | 
					    REQUEST_KEY_SAML_SIG_ALG,
 | 
				
			||||||
 | 
					    REQUEST_KEY_SAML_SIGNATURE,
 | 
				
			||||||
 | 
					    SESSION_KEY_AUTH_N_REQUEST,
 | 
				
			||||||
 | 
					    SAMLFlowFinalView,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_TEMPLATE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGGER = get_logger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SAMLSSOView(PolicyAccessView):
 | 
				
			||||||
 | 
					    """ "SAML SSO Base View, which plans a flow and injects our final stage.
 | 
				
			||||||
 | 
					    Calls get/post handler."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def resolve_provider_application(self):
 | 
				
			||||||
 | 
					        self.application = get_object_or_404(
 | 
				
			||||||
 | 
					            Application, slug=self.kwargs["application_slug"]
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.provider: SAMLProvider = get_object_or_404(
 | 
				
			||||||
 | 
					            SAMLProvider, pk=self.application.provider_id
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_saml_request(self) -> Optional[HttpRequest]:
 | 
				
			||||||
 | 
					        """Handler to verify the SAML Request. Must be implemented by a subclass"""
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # pylint: disable=unused-argument
 | 
				
			||||||
 | 
					    def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
 | 
				
			||||||
 | 
					        """Verify the SAML Request, and if valid initiate the FlowPlanner for the application"""
 | 
				
			||||||
 | 
					        # Call the method handler, which checks the SAML
 | 
				
			||||||
 | 
					        # Request and returns a HTTP Response on error
 | 
				
			||||||
 | 
					        method_response = self.check_saml_request()
 | 
				
			||||||
 | 
					        if method_response:
 | 
				
			||||||
 | 
					            return method_response
 | 
				
			||||||
 | 
					        # Regardless, we start the planner and return to it
 | 
				
			||||||
 | 
					        planner = FlowPlanner(self.provider.authorization_flow)
 | 
				
			||||||
 | 
					        planner.allow_empty_flows = True
 | 
				
			||||||
 | 
					        plan = planner.plan(
 | 
				
			||||||
 | 
					            request,
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                PLAN_CONTEXT_SSO: True,
 | 
				
			||||||
 | 
					                PLAN_CONTEXT_APPLICATION: self.application,
 | 
				
			||||||
 | 
					                PLAN_CONTEXT_CONSENT_TEMPLATE: "providers/saml/consent.html",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        plan.append(in_memory_stage(SAMLFlowFinalView))
 | 
				
			||||||
 | 
					        request.session[SESSION_KEY_PLAN] = plan
 | 
				
			||||||
 | 
					        return redirect_with_qs(
 | 
				
			||||||
 | 
					            "authentik_flows:flow-executor-shell",
 | 
				
			||||||
 | 
					            request.GET,
 | 
				
			||||||
 | 
					            flow_slug=self.provider.authorization_flow.slug,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def post(self, request: HttpRequest, application_slug: str) -> HttpResponse:
 | 
				
			||||||
 | 
					        """GET and POST use the same handler, but we can't
 | 
				
			||||||
 | 
					        override .dispatch easily because PolicyAccessView's dispatch"""
 | 
				
			||||||
 | 
					        return self.get(request, application_slug)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SAMLSSOBindingRedirectView(SAMLSSOView):
 | 
				
			||||||
 | 
					    """SAML Handler for SSO/Redirect bindings, which are sent via GET"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_saml_request(self) -> Optional[HttpRequest]:
 | 
				
			||||||
 | 
					        """Handle REDIRECT bindings"""
 | 
				
			||||||
 | 
					        if REQUEST_KEY_SAML_REQUEST not in self.request.GET:
 | 
				
			||||||
 | 
					            LOGGER.info("handle_saml_request: SAML payload missing")
 | 
				
			||||||
 | 
					            return bad_request_message(
 | 
				
			||||||
 | 
					                self.request, "The SAML request payload is missing."
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            auth_n_request = AuthNRequestParser(self.provider).parse_detached(
 | 
				
			||||||
 | 
					                self.request.GET[REQUEST_KEY_SAML_REQUEST],
 | 
				
			||||||
 | 
					                self.request.GET.get(REQUEST_KEY_RELAY_STATE),
 | 
				
			||||||
 | 
					                self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE),
 | 
				
			||||||
 | 
					                self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
 | 
				
			||||||
 | 
					        except CannotHandleAssertion as exc:
 | 
				
			||||||
 | 
					            Event.new(
 | 
				
			||||||
 | 
					                EventAction.CONFIGURATION_ERROR,
 | 
				
			||||||
 | 
					                provider=self.provider,
 | 
				
			||||||
 | 
					                message=str(exc),
 | 
				
			||||||
 | 
					            ).save()
 | 
				
			||||||
 | 
					            LOGGER.info(str(exc))
 | 
				
			||||||
 | 
					            return bad_request_message(self.request, str(exc))
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@method_decorator(csrf_exempt, name="dispatch")
 | 
				
			||||||
 | 
					class SAMLSSOBindingPOSTView(SAMLSSOView):
 | 
				
			||||||
 | 
					    """SAML Handler for SSO/POST bindings"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_saml_request(self) -> Optional[HttpRequest]:
 | 
				
			||||||
 | 
					        """Handle POST bindings"""
 | 
				
			||||||
 | 
					        if REQUEST_KEY_SAML_REQUEST not in self.request.POST:
 | 
				
			||||||
 | 
					            LOGGER.info("check_saml_request: SAML payload missing")
 | 
				
			||||||
 | 
					            return bad_request_message(
 | 
				
			||||||
 | 
					                self.request, "The SAML request payload is missing."
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            auth_n_request = AuthNRequestParser(self.provider).parse(
 | 
				
			||||||
 | 
					                self.request.POST[REQUEST_KEY_SAML_REQUEST],
 | 
				
			||||||
 | 
					                self.request.POST.get(REQUEST_KEY_RELAY_STATE),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
 | 
				
			||||||
 | 
					        except CannotHandleAssertion as exc:
 | 
				
			||||||
 | 
					            LOGGER.info(str(exc))
 | 
				
			||||||
 | 
					            return bad_request_message(self.request, str(exc))
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SAMLSSOBindingInitView(SAMLSSOView):
 | 
				
			||||||
 | 
					    """SAML Handler for for IdP Initiated login flows"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_saml_request(self) -> Optional[HttpRequest]:
 | 
				
			||||||
 | 
					        """Create SAML Response from scratch"""
 | 
				
			||||||
 | 
					        LOGGER.debug(
 | 
				
			||||||
 | 
					            "handle_saml_no_request: No SAML Request, using IdP-initiated flow."
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        auth_n_request = AuthNRequestParser(self.provider).idp_initiated()
 | 
				
			||||||
 | 
					        self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
 | 
				
			||||||
@ -90,16 +90,10 @@ class ASGILogger:
 | 
				
			|||||||
            if message["type"] == "http.response.body" and not message.get(
 | 
					            if message["type"] == "http.response.body" and not message.get(
 | 
				
			||||||
                "more_body", None
 | 
					                "more_body", None
 | 
				
			||||||
            ):
 | 
					            ):
 | 
				
			||||||
                runtime = int((time() - self.start) * 10 ** 6)
 | 
					                runtime = int((time() - self.start) * 1000)
 | 
				
			||||||
                self.log(runtime)
 | 
					                self.log(runtime)
 | 
				
			||||||
            await send(message)
 | 
					            await send(message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.headers.get(b"host", b"") == b"authentik-healthcheck-host":
 | 
					 | 
				
			||||||
            # Don't log healthcheck/readiness requests
 | 
					 | 
				
			||||||
            await send({"type": "http.response.start", "status": 204, "headers": []})
 | 
					 | 
				
			||||||
            await send({"type": "http.response.body", "body": ""})
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.start = time()
 | 
					        self.start = time()
 | 
				
			||||||
        if scope["type"] == "lifespan":
 | 
					        if scope["type"] == "lifespan":
 | 
				
			||||||
            # https://code.djangoproject.com/ticket/31508
 | 
					            # https://code.djangoproject.com/ticket/31508
 | 
				
			||||||
@ -129,7 +123,7 @@ class ASGILogger:
 | 
				
			|||||||
            method=self.scope.get("method", ""),
 | 
					            method=self.scope.get("method", ""),
 | 
				
			||||||
            scheme=self.scope.get("scheme", ""),
 | 
					            scheme=self.scope.get("scheme", ""),
 | 
				
			||||||
            status=self.status_code,
 | 
					            status=self.status_code,
 | 
				
			||||||
            size=self.content_length / 1000 if self.content_length > 0 else "-",
 | 
					            size=self.content_length / 1000 if self.content_length > 0 else 0,
 | 
				
			||||||
            runtime=runtime,
 | 
					            runtime=runtime,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,8 @@
 | 
				
			|||||||
from base64 import b64encode
 | 
					from base64 import b64encode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.db import connections
 | 
				
			||||||
 | 
					from django.db.utils import OperationalError
 | 
				
			||||||
from django.http import HttpRequest, HttpResponse
 | 
					from django.http import HttpRequest, HttpResponse
 | 
				
			||||||
from django.views import View
 | 
					from django.views import View
 | 
				
			||||||
from django_prometheus.exports import ExportToDjangoView
 | 
					from django_prometheus.exports import ExportToDjangoView
 | 
				
			||||||
@ -23,3 +25,22 @@ class MetricsView(View):
 | 
				
			|||||||
            return response
 | 
					            return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return ExportToDjangoView(request)
 | 
					        return ExportToDjangoView(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LiveView(View):
 | 
				
			||||||
 | 
					    """View for liveness probe, always returns Http 201"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dispatch(self, request: HttpRequest) -> HttpResponse:
 | 
				
			||||||
 | 
					        return HttpResponse(status=201)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReadyView(View):
 | 
				
			||||||
 | 
					    """View for liveness probe, always returns Http 201"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dispatch(self, request: HttpRequest) -> HttpResponse:
 | 
				
			||||||
 | 
					        db_conn = connections["default"]
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            _ = db_conn.cursor()
 | 
				
			||||||
 | 
					        except OperationalError:
 | 
				
			||||||
 | 
					            return HttpResponse(status=503)
 | 
				
			||||||
 | 
					        return HttpResponse(status=201)
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ from structlog.stdlib import get_logger
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from authentik.core.views import error
 | 
					from authentik.core.views import error
 | 
				
			||||||
from authentik.lib.utils.reflection import get_apps
 | 
					from authentik.lib.utils.reflection import get_apps
 | 
				
			||||||
from authentik.root.monitoring import MetricsView
 | 
					from authentik.root.monitoring import LiveView, MetricsView, ReadyView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = get_logger()
 | 
					LOGGER = get_logger()
 | 
				
			||||||
admin.autodiscover()
 | 
					admin.autodiscover()
 | 
				
			||||||
@ -57,6 +57,8 @@ for _authentik_app in get_apps():
 | 
				
			|||||||
urlpatterns += [
 | 
					urlpatterns += [
 | 
				
			||||||
    path("administration/django/", admin.site.urls),
 | 
					    path("administration/django/", admin.site.urls),
 | 
				
			||||||
    path("metrics/", MetricsView.as_view(), name="metrics"),
 | 
					    path("metrics/", MetricsView.as_view(), name="metrics"),
 | 
				
			||||||
 | 
					    path("-/health/live/", LiveView.as_view(), name="health-live"),
 | 
				
			||||||
 | 
					    path("-/health/ready/", ReadyView.as_view(), name="health-ready"),
 | 
				
			||||||
    path("-/jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
 | 
					    path("-/jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +1,27 @@
 | 
				
			|||||||
"""Source API Views"""
 | 
					"""Source API Views"""
 | 
				
			||||||
from rest_framework.serializers import ModelSerializer
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.core.cache import cache
 | 
				
			||||||
 | 
					from django.db.models.base import Model
 | 
				
			||||||
 | 
					from drf_yasg2.utils import swagger_auto_schema
 | 
				
			||||||
 | 
					from rest_framework.decorators import action
 | 
				
			||||||
 | 
					from rest_framework.fields import DateTimeField
 | 
				
			||||||
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.response import Response
 | 
				
			||||||
 | 
					from rest_framework.serializers import ModelSerializer, Serializer
 | 
				
			||||||
from rest_framework.viewsets import ModelViewSet
 | 
					from rest_framework.viewsets import ModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.forms.source import SOURCE_SERIALIZER_FIELDS
 | 
					from authentik.core.api.sources import SourceSerializer
 | 
				
			||||||
from authentik.core.api.utils import MetaNameSerializer
 | 
					from authentik.core.api.utils import MetaNameSerializer
 | 
				
			||||||
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
 | 
					from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LDAPSourceSerializer(ModelSerializer, MetaNameSerializer):
 | 
					class LDAPSourceSerializer(SourceSerializer):
 | 
				
			||||||
    """LDAP Source Serializer"""
 | 
					    """LDAP Source Serializer"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = LDAPSource
 | 
					        model = LDAPSource
 | 
				
			||||||
        fields = SOURCE_SERIALIZER_FIELDS + [
 | 
					        fields = SourceSerializer.Meta.fields + [
 | 
				
			||||||
            "server_uri",
 | 
					            "server_uri",
 | 
				
			||||||
            "bind_cn",
 | 
					            "bind_cn",
 | 
				
			||||||
            "bind_password",
 | 
					            "bind_password",
 | 
				
			||||||
@ -34,6 +43,39 @@ class LDAPSourceSerializer(ModelSerializer, MetaNameSerializer):
 | 
				
			|||||||
        extra_kwargs = {"bind_password": {"write_only": True}}
 | 
					        extra_kwargs = {"bind_password": {"write_only": True}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LDAPSourceSyncStatusSerializer(Serializer):
 | 
				
			||||||
 | 
					    """LDAP Sync status"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    last_sync = DateTimeField(read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create(self, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, instance: Model, validated_data: dict) -> Model:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LDAPSourceViewSet(ModelViewSet):
 | 
				
			||||||
 | 
					    """LDAP Source Viewset"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    queryset = LDAPSource.objects.all()
 | 
				
			||||||
 | 
					    serializer_class = LDAPSourceSerializer
 | 
				
			||||||
 | 
					    lookup_field = "slug"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @swagger_auto_schema(responses={200: LDAPSourceSyncStatusSerializer(many=False)})
 | 
				
			||||||
 | 
					    @action(methods=["GET"], detail=True)
 | 
				
			||||||
 | 
					    # pylint: disable=unused-argument
 | 
				
			||||||
 | 
					    def sync_status(self, request: Request, slug: str) -> Response:
 | 
				
			||||||
 | 
					        """Get source's sync status"""
 | 
				
			||||||
 | 
					        source = self.get_object()
 | 
				
			||||||
 | 
					        last_sync = cache.get(source.state_cache_prefix("last_sync"), None)
 | 
				
			||||||
 | 
					        return Response(
 | 
				
			||||||
 | 
					            LDAPSourceSyncStatusSerializer(
 | 
				
			||||||
 | 
					                {"last_sync": datetime.fromtimestamp(last_sync)}
 | 
				
			||||||
 | 
					            ).data
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LDAPPropertyMappingSerializer(ModelSerializer, MetaNameSerializer):
 | 
					class LDAPPropertyMappingSerializer(ModelSerializer, MetaNameSerializer):
 | 
				
			||||||
    """LDAP PropertyMapping Serializer"""
 | 
					    """LDAP PropertyMapping Serializer"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,13 +91,6 @@ class LDAPPropertyMappingSerializer(ModelSerializer, MetaNameSerializer):
 | 
				
			|||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LDAPSourceViewSet(ModelViewSet):
 | 
					 | 
				
			||||||
    """LDAP Source Viewset"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    queryset = LDAPSource.objects.all()
 | 
					 | 
				
			||||||
    serializer_class = LDAPSourceSerializer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class LDAPPropertyMappingViewSet(ModelViewSet):
 | 
					class LDAPPropertyMappingViewSet(ModelViewSet):
 | 
				
			||||||
    """LDAP PropertyMapping Viewset"""
 | 
					    """LDAP PropertyMapping Viewset"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,6 @@
 | 
				
			|||||||
"""authentik LDAP Models"""
 | 
					"""authentik LDAP Models"""
 | 
				
			||||||
from datetime import datetime
 | 
					 | 
				
			||||||
from typing import Optional, Type
 | 
					from typing import Optional, Type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.core.cache import cache
 | 
					 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.forms import ModelForm
 | 
					from django.forms import ModelForm
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
@ -11,7 +9,6 @@ from rest_framework.serializers import Serializer
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import Group, PropertyMapping, Source
 | 
					from authentik.core.models import Group, PropertyMapping, Source
 | 
				
			||||||
from authentik.lib.models import DomainlessURLValidator
 | 
					from authentik.lib.models import DomainlessURLValidator
 | 
				
			||||||
from authentik.lib.utils.template import render_to_string
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LDAPSource(Source):
 | 
					class LDAPSource(Source):
 | 
				
			||||||
@ -91,16 +88,6 @@ class LDAPSource(Source):
 | 
				
			|||||||
        """Key by which the ldap source status is saved"""
 | 
					        """Key by which the ldap source status is saved"""
 | 
				
			||||||
        return f"source_ldap_{self.pk}_state_{suffix}"
 | 
					        return f"source_ldap_{self.pk}_state_{suffix}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					 | 
				
			||||||
    def ui_additional_info(self) -> str:
 | 
					 | 
				
			||||||
        last_sync = cache.get(self.state_cache_prefix("last_sync"), None)
 | 
					 | 
				
			||||||
        if last_sync:
 | 
					 | 
				
			||||||
            last_sync = datetime.fromtimestamp(last_sync)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return render_to_string(
 | 
					 | 
				
			||||||
            "ldap/source_list_status.html", {"source": self, "last_sync": last_sync}
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _connection: Optional[Connection] = None
 | 
					    _connection: Optional[Connection] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ from time import time
 | 
				
			|||||||
from django.core.cache import cache
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.utils.text import slugify
 | 
					from django.utils.text import slugify
 | 
				
			||||||
from ldap3.core.exceptions import LDAPException
 | 
					from ldap3.core.exceptions import LDAPException
 | 
				
			||||||
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
 | 
					from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
 | 
				
			||||||
from authentik.root.celery import CELERY_APP
 | 
					from authentik.root.celery import CELERY_APP
 | 
				
			||||||
@ -12,6 +13,8 @@ from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
 | 
				
			|||||||
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
 | 
					from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
 | 
				
			||||||
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
 | 
					from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGGER = get_logger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CELERY_APP.task()
 | 
					@CELERY_APP.task()
 | 
				
			||||||
def ldap_sync_all():
 | 
					def ldap_sync_all():
 | 
				
			||||||
@ -21,7 +24,7 @@ def ldap_sync_all():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CELERY_APP.task(bind=True, base=MonitoredTask)
 | 
					@CELERY_APP.task(bind=True, base=MonitoredTask)
 | 
				
			||||||
def ldap_sync(self: MonitoredTask, source_pk: int):
 | 
					def ldap_sync(self: MonitoredTask, source_pk: str):
 | 
				
			||||||
    """Synchronization of an LDAP Source"""
 | 
					    """Synchronization of an LDAP Source"""
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        source: LDAPSource = LDAPSource.objects.get(pk=source_pk)
 | 
					        source: LDAPSource = LDAPSource.objects.get(pk=source_pk)
 | 
				
			||||||
@ -49,4 +52,5 @@ def ldap_sync(self: MonitoredTask, source_pk: int):
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    except LDAPException as exc:
 | 
					    except LDAPException as exc:
 | 
				
			||||||
 | 
					        LOGGER.debug(exc)
 | 
				
			||||||
        self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
 | 
					        self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +0,0 @@
 | 
				
			|||||||
{% load humanize %}
 | 
					 | 
				
			||||||
{% load i18n %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% if last_sync %}
 | 
					 | 
				
			||||||
<i class="fas fa-check pf-m-success"></i> {% blocktrans with last_sync=last_sync|naturaltime %}Synced {{ last_sync }}.{% endblocktrans %}
 | 
					 | 
				
			||||||
{% else %}
 | 
					 | 
				
			||||||
<i class="fas fa-times pf-m-danger"></i> Not synced yet/Sync in Progress
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
@ -1,18 +1,30 @@
 | 
				
			|||||||
"""OAuth Source Serializer"""
 | 
					"""OAuth Source Serializer"""
 | 
				
			||||||
from rest_framework.serializers import ModelSerializer
 | 
					from django.urls.base import reverse_lazy
 | 
				
			||||||
 | 
					from rest_framework.fields import SerializerMethodField
 | 
				
			||||||
from rest_framework.viewsets import ModelViewSet
 | 
					from rest_framework.viewsets import ModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.forms.source import SOURCE_SERIALIZER_FIELDS
 | 
					from authentik.core.api.sources import SourceSerializer
 | 
				
			||||||
from authentik.core.api.utils import MetaNameSerializer
 | 
					 | 
				
			||||||
from authentik.sources.oauth.models import OAuthSource
 | 
					from authentik.sources.oauth.models import OAuthSource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OAuthSourceSerializer(ModelSerializer, MetaNameSerializer):
 | 
					class OAuthSourceSerializer(SourceSerializer):
 | 
				
			||||||
    """OAuth Source Serializer"""
 | 
					    """OAuth Source Serializer"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    callback_url = SerializerMethodField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_callback_url(self, instance: OAuthSource) -> str:
 | 
				
			||||||
 | 
					        """Get OAuth Callback URL"""
 | 
				
			||||||
 | 
					        relative_url = reverse_lazy(
 | 
				
			||||||
 | 
					            "authentik_sources_oauth:oauth-client-callback",
 | 
				
			||||||
 | 
					            kwargs={"source_slug": instance.slug},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        if "request" not in self.context:
 | 
				
			||||||
 | 
					            return relative_url
 | 
				
			||||||
 | 
					        return self.context["request"].build_absolute_uri(relative_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = OAuthSource
 | 
					        model = OAuthSource
 | 
				
			||||||
        fields = SOURCE_SERIALIZER_FIELDS + [
 | 
					        fields = SourceSerializer.Meta.fields + [
 | 
				
			||||||
            "provider_type",
 | 
					            "provider_type",
 | 
				
			||||||
            "request_token_url",
 | 
					            "request_token_url",
 | 
				
			||||||
            "authorization_url",
 | 
					            "authorization_url",
 | 
				
			||||||
@ -20,7 +32,9 @@ class OAuthSourceSerializer(ModelSerializer, MetaNameSerializer):
 | 
				
			|||||||
            "profile_url",
 | 
					            "profile_url",
 | 
				
			||||||
            "consumer_key",
 | 
					            "consumer_key",
 | 
				
			||||||
            "consumer_secret",
 | 
					            "consumer_secret",
 | 
				
			||||||
 | 
					            "callback_url",
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					        extra_kwargs = {"consumer_secret": {"write_only": True}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OAuthSourceViewSet(ModelViewSet):
 | 
					class OAuthSourceViewSet(ModelViewSet):
 | 
				
			||||||
@ -28,3 +42,4 @@ class OAuthSourceViewSet(ModelViewSet):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    queryset = OAuthSource.objects.all()
 | 
					    queryset = OAuthSource.objects.all()
 | 
				
			||||||
    serializer_class = OAuthSourceSerializer
 | 
					    serializer_class = OAuthSourceSerializer
 | 
				
			||||||
 | 
					    lookup_field = "slug"
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django import forms
 | 
					from django import forms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.forms.source import SOURCE_FORM_FIELDS
 | 
					 | 
				
			||||||
from authentik.flows.models import Flow, FlowDesignation
 | 
					from authentik.flows.models import Flow, FlowDesignation
 | 
				
			||||||
from authentik.sources.oauth.models import OAuthSource
 | 
					from authentik.sources.oauth.models import OAuthSource
 | 
				
			||||||
from authentik.sources.oauth.types.manager import MANAGER
 | 
					from authentik.sources.oauth.types.manager import MANAGER
 | 
				
			||||||
@ -27,7 +26,12 @@ class OAuthSourceForm(forms.ModelForm):
 | 
				
			|||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model = OAuthSource
 | 
					        model = OAuthSource
 | 
				
			||||||
        fields = SOURCE_FORM_FIELDS + [
 | 
					        fields = [
 | 
				
			||||||
 | 
					            "name",
 | 
				
			||||||
 | 
					            "slug",
 | 
				
			||||||
 | 
					            "enabled",
 | 
				
			||||||
 | 
					            "authentication_flow",
 | 
				
			||||||
 | 
					            "enrollment_flow",
 | 
				
			||||||
            "provider_type",
 | 
					            "provider_type",
 | 
				
			||||||
            "request_token_url",
 | 
					            "request_token_url",
 | 
				
			||||||
            "authorization_url",
 | 
					            "authorization_url",
 | 
				
			||||||
 | 
				
			|||||||
@ -64,14 +64,6 @@ class OAuthSource(Source):
 | 
				
			|||||||
            name=self.name,
 | 
					            name=self.name,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					 | 
				
			||||||
    def ui_additional_info(self) -> str:
 | 
					 | 
				
			||||||
        url = reverse_lazy(
 | 
					 | 
				
			||||||
            "authentik_sources_oauth:oauth-client-callback",
 | 
					 | 
				
			||||||
            kwargs={"source_slug": self.slug},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return f"Callback URL: <pre>{url}</pre>"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def ui_user_settings(self) -> Optional[str]:
 | 
					    def ui_user_settings(self) -> Optional[str]:
 | 
				
			||||||
        view_name = "authentik_sources_oauth:oauth-client-user"
 | 
					        view_name = "authentik_sources_oauth:oauth-client-user"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +1,17 @@
 | 
				
			|||||||
"""SAMLSource API Views"""
 | 
					"""SAMLSource API Views"""
 | 
				
			||||||
from rest_framework.serializers import ModelSerializer
 | 
					 | 
				
			||||||
from rest_framework.viewsets import ModelViewSet
 | 
					from rest_framework.viewsets import ModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.forms.source import SOURCE_FORM_FIELDS
 | 
					from authentik.core.api.sources import SourceSerializer
 | 
				
			||||||
from authentik.core.api.utils import MetaNameSerializer
 | 
					 | 
				
			||||||
from authentik.sources.saml.models import SAMLSource
 | 
					from authentik.sources.saml.models import SAMLSource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SAMLSourceSerializer(ModelSerializer, MetaNameSerializer):
 | 
					class SAMLSourceSerializer(SourceSerializer):
 | 
				
			||||||
    """SAMLSource Serializer"""
 | 
					    """SAMLSource Serializer"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model = SAMLSource
 | 
					        model = SAMLSource
 | 
				
			||||||
        fields = SOURCE_FORM_FIELDS + [
 | 
					        fields = SourceSerializer.Meta.fields + [
 | 
				
			||||||
            "issuer",
 | 
					            "issuer",
 | 
				
			||||||
            "sso_url",
 | 
					            "sso_url",
 | 
				
			||||||
            "slo_url",
 | 
					            "slo_url",
 | 
				
			||||||
@ -32,3 +30,4 @@ class SAMLSourceViewSet(ModelViewSet):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    queryset = SAMLSource.objects.all()
 | 
					    queryset = SAMLSource.objects.all()
 | 
				
			||||||
    serializer_class = SAMLSourceSerializer
 | 
					    serializer_class = SAMLSourceSerializer
 | 
				
			||||||
 | 
					    lookup_field = "slug"
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django import forms
 | 
					from django import forms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.forms.source import SOURCE_FORM_FIELDS
 | 
					 | 
				
			||||||
from authentik.crypto.models import CertificateKeyPair
 | 
					from authentik.crypto.models import CertificateKeyPair
 | 
				
			||||||
from authentik.flows.models import Flow, FlowDesignation
 | 
					from authentik.flows.models import Flow, FlowDesignation
 | 
				
			||||||
from authentik.sources.saml.models import SAMLSource
 | 
					from authentik.sources.saml.models import SAMLSource
 | 
				
			||||||
@ -28,7 +27,12 @@ class SAMLSourceForm(forms.ModelForm):
 | 
				
			|||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model = SAMLSource
 | 
					        model = SAMLSource
 | 
				
			||||||
        fields = SOURCE_FORM_FIELDS + [
 | 
					        fields = [
 | 
				
			||||||
 | 
					            "name",
 | 
				
			||||||
 | 
					            "slug",
 | 
				
			||||||
 | 
					            "enabled",
 | 
				
			||||||
 | 
					            "authentication_flow",
 | 
				
			||||||
 | 
					            "enrollment_flow",
 | 
				
			||||||
            "issuer",
 | 
					            "issuer",
 | 
				
			||||||
            "sso_url",
 | 
					            "sso_url",
 | 
				
			||||||
            "slo_url",
 | 
					            "slo_url",
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ services:
 | 
				
			|||||||
    networks:
 | 
					    networks:
 | 
				
			||||||
      - internal
 | 
					      - internal
 | 
				
			||||||
  server:
 | 
					  server:
 | 
				
			||||||
    image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.1-rc1}
 | 
					    image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.3-stable}
 | 
				
			||||||
    command: server
 | 
					    command: server
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      AUTHENTIK_REDIS__HOST: redis
 | 
					      AUTHENTIK_REDIS__HOST: redis
 | 
				
			||||||
@ -40,12 +40,12 @@ services:
 | 
				
			|||||||
      traefik.http.routers.app-router.rule: PathPrefix(`/`)
 | 
					      traefik.http.routers.app-router.rule: PathPrefix(`/`)
 | 
				
			||||||
      traefik.http.routers.app-router.service: app-service
 | 
					      traefik.http.routers.app-router.service: app-service
 | 
				
			||||||
      traefik.http.routers.app-router.tls: 'true'
 | 
					      traefik.http.routers.app-router.tls: 'true'
 | 
				
			||||||
      traefik.http.services.app-service.loadbalancer.healthcheck.hostname: authentik-healthcheck-host
 | 
					      traefik.http.services.app-service.loadbalancer.healthcheck.path: /-/health/live/
 | 
				
			||||||
      traefik.http.services.app-service.loadbalancer.server.port: '8000'
 | 
					      traefik.http.services.app-service.loadbalancer.server.port: '8000'
 | 
				
			||||||
    env_file:
 | 
					    env_file:
 | 
				
			||||||
      - .env
 | 
					      - .env
 | 
				
			||||||
  worker:
 | 
					  worker:
 | 
				
			||||||
    image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.1-rc1}
 | 
					    image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.3-stable}
 | 
				
			||||||
    command: worker
 | 
					    command: worker
 | 
				
			||||||
    networks:
 | 
					    networks:
 | 
				
			||||||
      - internal
 | 
					      - internal
 | 
				
			||||||
@ -62,7 +62,7 @@ services:
 | 
				
			|||||||
    env_file:
 | 
					    env_file:
 | 
				
			||||||
      - .env
 | 
					      - .env
 | 
				
			||||||
  static:
 | 
					  static:
 | 
				
			||||||
    image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.1-rc1}
 | 
					    image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.3-stable}
 | 
				
			||||||
    networks:
 | 
					    networks:
 | 
				
			||||||
      - internal
 | 
					      - internal
 | 
				
			||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
 | 
				
			|||||||
@ -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.2.1-rc1"
 | 
					version: "2021.2.3-stable"
 | 
				
			||||||
icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg
 | 
					icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg
 | 
				
			||||||
dependencies:
 | 
					dependencies:
 | 
				
			||||||
  - name: postgresql
 | 
					  - name: postgresql
 | 
				
			||||||
 | 
				
			|||||||
@ -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.2.1-rc1           | Image tag |
 | 
					| image.tag                         | 2021.2.3-stable           | Image tag |
 | 
				
			||||||
| image.pullPolicy                  | IfNotPresent            | Image Pull Policy used for all deployments |
 | 
					| image.pullPolicy                  | IfNotPresent            | Image Pull Policy used for all deployments |
 | 
				
			||||||
| serverReplicas                    | 1                       | Replicas for the Server deployment |
 | 
					| serverReplicas                    | 1                       | Replicas for the Server deployment |
 | 
				
			||||||
| workerReplicas                    | 1                       | Replicas for the Worker deployment |
 | 
					| workerReplicas                    | 1                       | Replicas for the Worker deployment |
 | 
				
			||||||
 | 
				
			|||||||
@ -97,18 +97,12 @@ spec:
 | 
				
			|||||||
              protocol: TCP
 | 
					              protocol: TCP
 | 
				
			||||||
          livenessProbe:
 | 
					          livenessProbe:
 | 
				
			||||||
            httpGet:
 | 
					            httpGet:
 | 
				
			||||||
              path: /
 | 
					              path: /-/health/live/
 | 
				
			||||||
              port: http
 | 
					              port: http
 | 
				
			||||||
              httpHeaders:
 | 
					 | 
				
			||||||
                - name: Host
 | 
					 | 
				
			||||||
                  value: authentik-healthcheck-host
 | 
					 | 
				
			||||||
          readinessProbe:
 | 
					          readinessProbe:
 | 
				
			||||||
            httpGet:
 | 
					            httpGet:
 | 
				
			||||||
              path: /
 | 
					              path: /-/health/ready/
 | 
				
			||||||
              port: http
 | 
					              port: http
 | 
				
			||||||
              httpHeaders:
 | 
					 | 
				
			||||||
                - name: Host
 | 
					 | 
				
			||||||
                  value: authentik-healthcheck-host
 | 
					 | 
				
			||||||
          resources:
 | 
					          resources:
 | 
				
			||||||
            requests:
 | 
					            requests:
 | 
				
			||||||
              cpu: 100m
 | 
					              cpu: 100m
 | 
				
			||||||
 | 
				
			|||||||
@ -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.2.1-rc1
 | 
					  tag: 2021.2.3-stable
 | 
				
			||||||
  pullPolicy: IfNotPresent
 | 
					  pullPolicy: IfNotPresent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
serverReplicas: 1
 | 
					serverReplicas: 1
 | 
				
			||||||
 | 
				
			|||||||
@ -3,40 +3,39 @@ module goauthentik.io/outpost
 | 
				
			|||||||
go 1.14
 | 
					go 1.14
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	cloud.google.com/go v0.64.0 // indirect
 | 
					 | 
				
			||||||
	github.com/coreos/go-oidc v2.2.1+incompatible
 | 
						github.com/coreos/go-oidc v2.2.1+incompatible
 | 
				
			||||||
	github.com/getsentry/sentry-go v0.9.0
 | 
						github.com/getsentry/sentry-go v0.9.0
 | 
				
			||||||
	github.com/go-openapi/errors v0.19.9
 | 
						github.com/go-openapi/errors v0.20.0
 | 
				
			||||||
	github.com/go-openapi/runtime v0.19.24
 | 
						github.com/go-openapi/runtime v0.19.26
 | 
				
			||||||
	github.com/go-openapi/strfmt v0.19.12
 | 
						github.com/go-openapi/strfmt v0.20.0
 | 
				
			||||||
	github.com/go-openapi/swag v0.19.12
 | 
						github.com/go-openapi/swag v0.19.14
 | 
				
			||||||
	github.com/go-openapi/validate v0.20.1
 | 
						github.com/go-openapi/validate v0.20.2
 | 
				
			||||||
	github.com/go-redis/redis/v7 v7.4.0 // indirect
 | 
						github.com/go-redis/redis/v7 v7.4.0 // indirect
 | 
				
			||||||
	github.com/go-swagger/go-swagger v0.25.0 // indirect
 | 
						github.com/go-swagger/go-swagger v0.26.1 // indirect
 | 
				
			||||||
	github.com/gorilla/handlers v1.5.1 // indirect
 | 
						github.com/golang/protobuf v1.4.3 // indirect
 | 
				
			||||||
	github.com/gorilla/websocket v1.4.2
 | 
						github.com/gorilla/websocket v1.4.2
 | 
				
			||||||
	github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
 | 
						github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
 | 
				
			||||||
	github.com/justinas/alice v1.2.0
 | 
						github.com/justinas/alice v1.2.0
 | 
				
			||||||
	github.com/kr/pretty v0.2.1 // indirect
 | 
						github.com/kr/pretty v0.2.1 // indirect
 | 
				
			||||||
	github.com/magiconair/properties v1.8.4 // indirect
 | 
						github.com/magiconair/properties v1.8.4 // indirect
 | 
				
			||||||
	github.com/mitchellh/mapstructure v1.4.1 // indirect
 | 
						github.com/mailru/easyjson v0.7.7 // indirect
 | 
				
			||||||
	github.com/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc
 | 
						github.com/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc
 | 
				
			||||||
	github.com/pelletier/go-toml v1.8.1 // indirect
 | 
						github.com/pelletier/go-toml v1.8.1 // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.9.1
 | 
						github.com/pkg/errors v0.9.1
 | 
				
			||||||
	github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f // indirect
 | 
						github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect
 | 
				
			||||||
	github.com/recws-org/recws v1.2.2
 | 
						github.com/recws-org/recws v1.2.1
 | 
				
			||||||
	github.com/sirupsen/logrus v1.7.0
 | 
						github.com/sirupsen/logrus v1.7.0
 | 
				
			||||||
	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
 | 
				
			||||||
	github.com/spf13/pflag v1.0.5 // indirect
 | 
						github.com/spf13/pflag v1.0.5 // indirect
 | 
				
			||||||
	github.com/spf13/viper v1.7.1 // indirect
 | 
						github.com/spf13/viper v1.7.1 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect
 | 
						golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 // indirect
 | 
				
			||||||
	golang.org/x/mod v0.4.1 // indirect
 | 
						golang.org/x/mod v0.4.1 // indirect
 | 
				
			||||||
	golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
 | 
						golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect
 | 
						golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
 | 
				
			||||||
	golang.org/x/text v0.3.5 // indirect
 | 
						golang.org/x/tools v0.1.0 // indirect
 | 
				
			||||||
	golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 // indirect
 | 
						google.golang.org/appengine v1.6.7 // indirect
 | 
				
			||||||
	gopkg.in/ini.v1 v1.62.0 // indirect
 | 
						gopkg.in/ini.v1 v1.62.0 // indirect
 | 
				
			||||||
	gopkg.in/square/go-jose.v2 v2.5.1 // indirect
 | 
						gopkg.in/square/go-jose.v2 v2.5.1 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										117
									
								
								outpost/go.sum
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								outpost/go.sum
									
									
									
									
									
								
							@ -13,8 +13,8 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
 | 
				
			|||||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
 | 
					cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
 | 
				
			||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
 | 
					cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
 | 
				
			||||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
 | 
					cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
 | 
				
			||||||
cloud.google.com/go v0.64.0 h1:xVP3LPvMjGT4J0a55y02Gw5y/dkY/rxGz58sfK1jqIo=
 | 
					cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
 | 
				
			||||||
cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4=
 | 
					cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
 | 
				
			||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 | 
					cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 | 
				
			||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 | 
					cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 | 
				
			||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
 | 
					cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
 | 
				
			||||||
@ -35,11 +35,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
 | 
				
			|||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 | 
					cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 | 
				
			||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
					dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
				
			||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
 | 
					github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
 | 
				
			||||||
github.com/BeryJu/authentik v0.0.0-20210108085217-fd6d99f4f999 h1:ymxzvnxKNUomJIRG1VP3I6ls5mWn8r1xWD82bHASEk0=
 | 
					 | 
				
			||||||
github.com/BeryJu/authentik v0.0.0-20210116180903-8acb9dde5f2f h1:pLOJgn8bIzavtn0h874lys3gs7uk1RnMqMWIttOWY8Y=
 | 
					 | 
				
			||||||
github.com/BeryJu/authentik/proxy v0.0.0-20210108085217-fd6d99f4f999 h1:XYHeaZx7fm4JNx77MHMO6ek/Gdp+sZa2jIJyjC294Gw=
 | 
					 | 
				
			||||||
github.com/BeryJu/authentik/proxy v0.0.0-20210116180903-8acb9dde5f2f h1:bp617AbteaVcZBXMtr4/A+FSSVGKqRWlTo5chcirq8k=
 | 
					 | 
				
			||||||
github.com/BeryJu/authentik/proxy v0.0.0-20210116180903-8acb9dde5f2f/go.mod h1:6/VeRMuLHUE3Ywr1uIpjxnmOJJsAfld7OOOi+uocxQw=
 | 
					 | 
				
			||||||
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw=
 | 
					github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw=
 | 
				
			||||||
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
 | 
					github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
 | 
				
			||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 | 
					github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 | 
				
			||||||
@ -167,6 +162,8 @@ github.com/go-openapi/analysis v0.19.10 h1:5BHISBAXOc/aJK25irLZnx2D3s6WyYaY9D4gm
 | 
				
			|||||||
github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=
 | 
					github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=
 | 
				
			||||||
github.com/go-openapi/analysis v0.19.16 h1:Ub9e++M8sDwtHD+S587TYi+6ANBG1NRYGZDihqk0SaY=
 | 
					github.com/go-openapi/analysis v0.19.16 h1:Ub9e++M8sDwtHD+S587TYi+6ANBG1NRYGZDihqk0SaY=
 | 
				
			||||||
github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk=
 | 
					github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk=
 | 
				
			||||||
 | 
					github.com/go-openapi/analysis v0.20.0 h1:UN09o0kNhleunxW7LR+KnltD0YrJ8FF03pSqvAN3Vro=
 | 
				
			||||||
 | 
					github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og=
 | 
				
			||||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
					github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
				
			||||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
					github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
				
			||||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
 | 
					github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
 | 
				
			||||||
@ -179,6 +176,8 @@ github.com/go-openapi/errors v0.19.8 h1:doM+tQdZbUm9gydV9yR+iQNmztbjj7I3sW4sIcAw
 | 
				
			|||||||
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 | 
					github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 | 
				
			||||||
github.com/go-openapi/errors v0.19.9 h1:9SnKdGhiPZHF3ttwFMiCBEb8jQ4IDdrK+5+a0oTygA4=
 | 
					github.com/go-openapi/errors v0.19.9 h1:9SnKdGhiPZHF3ttwFMiCBEb8jQ4IDdrK+5+a0oTygA4=
 | 
				
			||||||
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 | 
					github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 | 
				
			||||||
 | 
					github.com/go-openapi/errors v0.20.0 h1:Sxpo9PjEHDzhs3FbnGNonvDgWcMW2U7wGTcDDSFSceM=
 | 
				
			||||||
 | 
					github.com/go-openapi/errors v0.20.0/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 | 
				
			||||||
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
 | 
					github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
 | 
				
			||||||
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
 | 
					github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
 | 
				
			||||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 | 
					github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 | 
				
			||||||
@ -193,7 +192,6 @@ github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3Hfo
 | 
				
			|||||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
 | 
					github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
 | 
				
			||||||
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
 | 
					github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
 | 
				
			||||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
 | 
					github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
 | 
				
			||||||
github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
 | 
					 | 
				
			||||||
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
 | 
					github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
 | 
				
			||||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
 | 
					github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
 | 
				
			||||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
					github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
				
			||||||
@ -209,14 +207,18 @@ github.com/go-openapi/loads v0.19.7 h1:6cALLpCAq4tYhaic7TMbEzjv8vq/wg+0AFivNy/Bm
 | 
				
			|||||||
github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
 | 
					github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
 | 
				
			||||||
github.com/go-openapi/loads v0.20.0 h1:Pymw1O8zDmWeNv4kVsHd0W3cvgdp8juRa4U/U/8D/Pk=
 | 
					github.com/go-openapi/loads v0.20.0 h1:Pymw1O8zDmWeNv4kVsHd0W3cvgdp8juRa4U/U/8D/Pk=
 | 
				
			||||||
github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4=
 | 
					github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4=
 | 
				
			||||||
 | 
					github.com/go-openapi/loads v0.20.1/go.mod h1:/6LfFL8fDvTSX8ypmYXIq3U9Q7nfniSOStW22m864WM=
 | 
				
			||||||
 | 
					github.com/go-openapi/loads v0.20.2 h1:z5p5Xf5wujMxS1y8aP+vxwW5qYT2zdJBbXKmQUG3lcc=
 | 
				
			||||||
 | 
					github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o=
 | 
				
			||||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
 | 
					github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
 | 
				
			||||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
 | 
					github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
 | 
				
			||||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
 | 
					github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
 | 
				
			||||||
github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
 | 
					github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
 | 
				
			||||||
github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98=
 | 
					github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98=
 | 
				
			||||||
github.com/go-openapi/runtime v0.19.20/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
 | 
					 | 
				
			||||||
github.com/go-openapi/runtime v0.19.24 h1:TqagMVlRAOTwllE/7hNKx6rQ10O6T8ZzeJdMjSTKaD4=
 | 
					github.com/go-openapi/runtime v0.19.24 h1:TqagMVlRAOTwllE/7hNKx6rQ10O6T8ZzeJdMjSTKaD4=
 | 
				
			||||||
github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
 | 
					github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
 | 
				
			||||||
 | 
					github.com/go-openapi/runtime v0.19.26 h1:K/6PoVNj5WJXUnMk+VEbELeXjtBkCS1UxTDa04tdXE0=
 | 
				
			||||||
 | 
					github.com/go-openapi/runtime v0.19.26/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M=
 | 
				
			||||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
					github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
				
			||||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
					github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
				
			||||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
 | 
					github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
 | 
				
			||||||
@ -228,6 +230,10 @@ github.com/go-openapi/spec v0.19.15 h1:uxh8miNJEfMm8l8ekpY7i39LcORm1xSRtoipEGl1J
 | 
				
			|||||||
github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
 | 
					github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
 | 
				
			||||||
github.com/go-openapi/spec v0.20.0 h1:HGLc8AJ7ynOxwv0Lq4TsnwLsWMawHAYiJIFzbcML86I=
 | 
					github.com/go-openapi/spec v0.20.0 h1:HGLc8AJ7ynOxwv0Lq4TsnwLsWMawHAYiJIFzbcML86I=
 | 
				
			||||||
github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
 | 
					github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
 | 
				
			||||||
 | 
					github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ=
 | 
				
			||||||
 | 
					github.com/go-openapi/spec v0.20.2/go.mod h1:RW6Xcbs6LOyWLU/mXGdzn2Qc+3aj+ASfI7rvSZh1Vls=
 | 
				
			||||||
 | 
					github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
 | 
				
			||||||
 | 
					github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
 | 
				
			||||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
					github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
				
			||||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
					github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
				
			||||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
 | 
					github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
 | 
				
			||||||
@ -238,8 +244,8 @@ github.com/go-openapi/strfmt v0.19.5 h1:0utjKrw+BAh8s57XE9Xz8DUBsVvPmRUB6styvl9w
 | 
				
			|||||||
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
 | 
					github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
 | 
				
			||||||
github.com/go-openapi/strfmt v0.19.11 h1:0+YvbNh05rmBkgztd6zHp4OCFn7Mtu30bn46NQo2ZRw=
 | 
					github.com/go-openapi/strfmt v0.19.11 h1:0+YvbNh05rmBkgztd6zHp4OCFn7Mtu30bn46NQo2ZRw=
 | 
				
			||||||
github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
 | 
					github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
 | 
				
			||||||
github.com/go-openapi/strfmt v0.19.12 h1:GcJYVoo6b2fsAzIxHTL6bTIcGo7vCJTfty+mdyj5VXo=
 | 
					github.com/go-openapi/strfmt v0.20.0 h1:l2omNtmNbMc39IGptl9BuXBEKcZfS8zjrTsPKTiJiDM=
 | 
				
			||||||
github.com/go-openapi/strfmt v0.19.12/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
 | 
					github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
 | 
				
			||||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
					github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
				
			||||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
					github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
				
			||||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 | 
					github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 | 
				
			||||||
@ -249,6 +255,10 @@ github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4az
 | 
				
			|||||||
github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
 | 
					github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
 | 
				
			||||||
github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI=
 | 
					github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI=
 | 
				
			||||||
github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
 | 
					github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
 | 
				
			||||||
 | 
					github.com/go-openapi/swag v0.19.13 h1:233UVgMy1DlmCYYfOiFpta6e2urloh+sEs5id6lyzog=
 | 
				
			||||||
 | 
					github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 | 
				
			||||||
 | 
					github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
 | 
				
			||||||
 | 
					github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 | 
				
			||||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
 | 
					github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
 | 
				
			||||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
 | 
					github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
 | 
				
			||||||
github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
 | 
					github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
 | 
				
			||||||
@ -258,10 +268,10 @@ github.com/go-openapi/validate v0.19.12 h1:mPLM/bfbd00PGOCJlU0yJL7IulkZ+q9VjPv7U
 | 
				
			|||||||
github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4=
 | 
					github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4=
 | 
				
			||||||
github.com/go-openapi/validate v0.19.15 h1:oUHZO8jD7p5oRLANlXF0U8ic9ePBUkDQyRZdN0EhL6M=
 | 
					github.com/go-openapi/validate v0.19.15 h1:oUHZO8jD7p5oRLANlXF0U8ic9ePBUkDQyRZdN0EhL6M=
 | 
				
			||||||
github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI=
 | 
					github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI=
 | 
				
			||||||
github.com/go-openapi/validate v0.20.0 h1:pzutNCCBZGZlE+u8HD3JZyWdc/TVbtVwlWUp8/vgUKk=
 | 
					 | 
				
			||||||
github.com/go-openapi/validate v0.20.0/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
 | 
					 | 
				
			||||||
github.com/go-openapi/validate v0.20.1 h1:QGQ5CvK74E28t3DkegGweKR+auemUi5IdpMc4x3UW6s=
 | 
					github.com/go-openapi/validate v0.20.1 h1:QGQ5CvK74E28t3DkegGweKR+auemUi5IdpMc4x3UW6s=
 | 
				
			||||||
github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
 | 
					github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
 | 
				
			||||||
 | 
					github.com/go-openapi/validate v0.20.2 h1:AhqDegYV3J3iQkMPJSXkvzymHKMTw0BST3RK3hTT4ts=
 | 
				
			||||||
 | 
					github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0=
 | 
				
			||||||
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
 | 
					github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
 | 
				
			||||||
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
 | 
					github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
 | 
				
			||||||
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
 | 
					github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
 | 
				
			||||||
@ -269,8 +279,8 @@ github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRf
 | 
				
			|||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
					github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
				
			||||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 | 
					github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 | 
				
			||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
					github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
				
			||||||
github.com/go-swagger/go-swagger v0.25.0 h1:FxhyrWWV8V/A9P6GtI5szWordAdbb6Y0nqdY/y9So2w=
 | 
					github.com/go-swagger/go-swagger v0.26.1 h1:1XUWLnH6hKxHzeKjJfA2gHkSqcT1Zgi4q/PZp2hDdN8=
 | 
				
			||||||
github.com/go-swagger/go-swagger v0.25.0/go.mod h1:9639ioXrPX9E6BbnbaDklGXjNz7upAXoNBwL4Ok11Vk=
 | 
					github.com/go-swagger/go-swagger v0.26.1/go.mod h1:zlf/LHplZpdtU2mYXg9Ajd3+9TgHYltv5f/pEM6LjnI=
 | 
				
			||||||
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0=
 | 
					github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0=
 | 
				
			||||||
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
 | 
					github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
 | 
				
			||||||
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
 | 
					github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
 | 
				
			||||||
@ -331,6 +341,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
 | 
				
			|||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 | 
					github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 | 
				
			||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 | 
					github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 | 
				
			||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
					github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
				
			||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
					github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
				
			||||||
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3 h1:6amM4HsNPOvMLVc2ZnyqrjeQ92YAVWn7T4WBKK87inY=
 | 
					github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3 h1:6amM4HsNPOvMLVc2ZnyqrjeQ92YAVWn7T4WBKK87inY=
 | 
				
			||||||
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 | 
					github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 | 
				
			||||||
@ -371,8 +383,6 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+
 | 
				
			|||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
					github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
				
			||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 | 
					github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 | 
				
			||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
					github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
				
			||||||
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
 | 
					 | 
				
			||||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
 | 
					 | 
				
			||||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 | 
					github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 | 
				
			||||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 | 
					github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 | 
				
			||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
					github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
				
			||||||
@ -415,7 +425,6 @@ github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/
 | 
				
			|||||||
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
 | 
					github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
 | 
				
			||||||
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
 | 
					github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
 | 
				
			||||||
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
 | 
					github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
 | 
				
			||||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
 | 
					 | 
				
			||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 | 
					github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 | 
				
			||||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
 | 
					github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
 | 
				
			||||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
 | 
					github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
 | 
				
			||||||
@ -483,6 +492,8 @@ github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8
 | 
				
			|||||||
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 | 
					github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 | 
				
			||||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
 | 
					github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
 | 
				
			||||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 | 
					github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 | 
				
			||||||
 | 
					github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 | 
				
			||||||
 | 
					github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 | 
				
			||||||
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
 | 
					github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
 | 
				
			||||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 | 
					github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 | 
				
			||||||
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
 | 
					github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
 | 
				
			||||||
@ -544,13 +555,14 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
 | 
				
			|||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 | 
					github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 | 
				
			||||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
 | 
					github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
 | 
				
			||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 | 
					github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 | 
				
			||||||
 | 
					github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
 | 
				
			||||||
 | 
					github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
 | 
				
			||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 | 
					github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 | 
				
			||||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 | 
					github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 | 
				
			||||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 | 
					github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 | 
				
			||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
					github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
				
			||||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 | 
					github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 | 
				
			||||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
 | 
					github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
 | 
				
			||||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
 | 
					 | 
				
			||||||
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
 | 
					github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
 | 
				
			||||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
 | 
					github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
 | 
				
			||||||
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
 | 
					github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
 | 
				
			||||||
@ -568,8 +580,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 | 
				
			|||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 | 
					github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 | 
				
			||||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
 | 
					github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
 | 
				
			||||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 | 
					github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 | 
				
			||||||
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f h1:JDEmUDtyiLMyMlFwiaDOv2hxUp35497fkwePcLeV7j4=
 | 
					github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e h1:BLqxdwZ6j771IpSCRx7s/GJjXHUE00Hmu7/YegCGdzA=
 | 
				
			||||||
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ=
 | 
					github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ=
 | 
				
			||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
					github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
				
			||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 | 
					github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 | 
				
			||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
					github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
				
			||||||
@ -580,8 +592,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 | 
				
			|||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
					github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
				
			||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
					github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
				
			||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 | 
					github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 | 
				
			||||||
github.com/recws-org/recws v1.2.2 h1:TkyyCEgMjsr1D2fnutY/DPhGnUKCLpJeXDAGy6rLmGE=
 | 
					github.com/recws-org/recws v1.2.1 h1:bYocRkAsS71hlQ9AMCVS+hYXHEgEyQsAbYKXf394gZ8=
 | 
				
			||||||
github.com/recws-org/recws v1.2.2/go.mod h1:SxTgwQU/jqYSzEgUh4ifDxq/7enApS150f8nZ5Sczk8=
 | 
					github.com/recws-org/recws v1.2.1/go.mod h1:SxTgwQU/jqYSzEgUh4ifDxq/7enApS150f8nZ5Sczk8=
 | 
				
			||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
					github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
					github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
					github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
				
			||||||
@ -608,8 +620,6 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
 | 
				
			|||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
					github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
				
			||||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 | 
					github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 | 
				
			||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
					github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
				
			||||||
github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
 | 
					 | 
				
			||||||
github.com/spf13/afero v1.4.1 h1:asw9sl74539yqavKaglDM5hFpdJVK0Y5Dr/JOgQ89nQ=
 | 
					 | 
				
			||||||
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
 | 
					github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
 | 
				
			||||||
github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
 | 
					github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
 | 
				
			||||||
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
 | 
					github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
 | 
				
			||||||
@ -630,7 +640,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
 | 
				
			|||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
					github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
				
			||||||
github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
 | 
					github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
 | 
				
			||||||
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
 | 
					github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
 | 
				
			||||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 | 
					 | 
				
			||||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
 | 
					github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
 | 
				
			||||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 | 
					github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
@ -643,6 +652,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
 | 
				
			|||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
					github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
				
			||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 | 
					github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 | 
				
			||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 | 
					github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 | 
				
			||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 | 
					github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 | 
				
			||||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
 | 
					github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
 | 
				
			||||||
@ -691,11 +702,13 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL
 | 
				
			|||||||
go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
 | 
					go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
 | 
				
			||||||
go.mongodb.org/mongo-driver v1.3.4 h1:zs/dKNwX0gYUtzwrN9lLiR15hCO0nDwQj5xXx+vjCdE=
 | 
					go.mongodb.org/mongo-driver v1.3.4 h1:zs/dKNwX0gYUtzwrN9lLiR15hCO0nDwQj5xXx+vjCdE=
 | 
				
			||||||
go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
 | 
					go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
 | 
				
			||||||
go.mongodb.org/mongo-driver v1.3.5/go.mod h1:Ual6Gkco7ZGQw8wE1t4tLnvBsf6yVSM60qW6TgOeJ5c=
 | 
					 | 
				
			||||||
go.mongodb.org/mongo-driver v1.4.3 h1:moga+uhicpVshTyaqY9L23E6QqwcHRUv1sqyOsoyOO8=
 | 
					go.mongodb.org/mongo-driver v1.4.3 h1:moga+uhicpVshTyaqY9L23E6QqwcHRUv1sqyOsoyOO8=
 | 
				
			||||||
go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
 | 
					go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
 | 
				
			||||||
go.mongodb.org/mongo-driver v1.4.4 h1:bsPHfODES+/yx2PCWzUYMH8xj6PVniPI8DQrsJuSXSs=
 | 
					go.mongodb.org/mongo-driver v1.4.4 h1:bsPHfODES+/yx2PCWzUYMH8xj6PVniPI8DQrsJuSXSs=
 | 
				
			||||||
go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
 | 
					go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
 | 
				
			||||||
 | 
					go.mongodb.org/mongo-driver v1.4.5/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
 | 
				
			||||||
 | 
					go.mongodb.org/mongo-driver v1.4.6 h1:rh7GdYmDrb8AQSkF8yteAus8qYOgOASWDOv1BWqBXkU=
 | 
				
			||||||
 | 
					go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
 | 
				
			||||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
 | 
					go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
 | 
				
			||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
					go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
				
			||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
					go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
				
			||||||
@ -706,7 +719,6 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 | 
				
			|||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
					go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
				
			||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
					go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
				
			||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
					go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
				
			||||||
goauthentik.io/outpost v0.0.0-20210108085217-fd6d99f4f999 h1:XYHeaZx7fm4JNx77MHMO6ek/Gdp+sZa2jIJyjC294Gw=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
					golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
					golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
					golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
				
			||||||
@ -724,9 +736,8 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
 | 
				
			|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
 | 
					golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					 | 
				
			||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
					golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
				
			||||||
@ -759,8 +770,6 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
 | 
				
			|||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
				
			||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
 | 
					golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
 | 
				
			||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
				
			||||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
 | 
					 | 
				
			||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					 | 
				
			||||||
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
 | 
					golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
 | 
				
			||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
				
			||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
@ -806,14 +815,15 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
 | 
				
			|||||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
					golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
				
			||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 | 
					golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 | 
				
			||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 | 
					golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 | 
				
			||||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
 | 
					golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 | 
				
			||||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
					golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
 | 
					golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
					golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
					golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
 | 
					golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
					golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
 | 
					golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
 | 
				
			||||||
 | 
					golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 | 
					golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 | 
				
			||||||
@ -821,6 +831,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
 | 
				
			|||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
 | 
					golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
 | 
					golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 h1:Mj83v+wSRNEar42a/MQgxk9X42TdEmrOl9i+y8WbxLo=
 | 
				
			||||||
 | 
					golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
@ -881,18 +893,14 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
				
			|||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
 | 
					golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
 | 
					golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
 | 
					golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
@ -959,17 +967,13 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
 | 
				
			|||||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
					golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
					golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
					golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 | 
					golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 | 
					golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f h1:33yHANSyO/TeglgY9rBhUpX43wtonTXoFOsMRtNB6qE=
 | 
					golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 | 
					golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b h1:Lq5JUTFhiybGVf28jB6QRpqd13/JPOaCnET17PVzYJE=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a h1:pdfjQ7VswBeGam3EpuEJ4e8EAb7JgaubV570LO/SIQM=
 | 
					golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
					golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e h1:Z2uDrs8MyXUWJbwGc4V+nGjV4Ygo+oubBbWSVQw21/I=
 | 
					golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 h1:BTs2GMGSMWpgtCpv1CE7vkJTv7XcHdcLLnAMu7UbgTY=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
					 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
 | 
					golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
@ -1004,6 +1008,8 @@ google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpC
 | 
				
			|||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
					google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
				
			||||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
 | 
					google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
 | 
				
			||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
					google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
				
			||||||
 | 
					google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
 | 
				
			||||||
 | 
					google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
					google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
					google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
					google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
				
			||||||
@ -1033,8 +1039,8 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY
 | 
				
			|||||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
 | 
					google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
					google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
					google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70 h1:wboULUXGF3c5qdUnKp+6gLAccE6PRpa/czkYvQ4UXv8=
 | 
					google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
					google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
				
			||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
					google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
				
			||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
					google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
				
			||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
					google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
				
			||||||
@ -1078,7 +1084,6 @@ gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/R
 | 
				
			|||||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
 | 
					gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
 | 
				
			||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
					gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
				
			||||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
					gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
				
			||||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
					 | 
				
			||||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
 | 
					gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
 | 
				
			||||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
					gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
				
			||||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 | 
					gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 | 
				
			||||||
@ -1108,6 +1113,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
 | 
				
			|||||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
 | 
					gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
				
			||||||
 | 
				
			|||||||
@ -69,14 +69,9 @@ func (ac *APIController) Shutdown() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ac *APIController) startWSHandler() {
 | 
					func (ac *APIController) startWSHandler() {
 | 
				
			||||||
	notConnectedBackoff := 1
 | 
					 | 
				
			||||||
	logger := ac.logger.WithField("loop", "ws-handler")
 | 
						logger := ac.logger.WithField("loop", "ws-handler")
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		if !ac.wsConn.IsConnected() {
 | 
							if !ac.wsConn.IsConnected() {
 | 
				
			||||||
			notConnectedWait := time.Duration(notConnectedBackoff) * time.Second
 | 
					 | 
				
			||||||
			logger.WithField("wait", notConnectedWait).Info("Not connected, trying again...")
 | 
					 | 
				
			||||||
			time.Sleep(notConnectedWait)
 | 
					 | 
				
			||||||
			notConnectedBackoff += notConnectedBackoff
 | 
					 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		var wsMsg websocketMessage
 | 
							var wsMsg websocketMessage
 | 
				
			||||||
@ -109,7 +104,7 @@ func (ac *APIController) startWSHealth() {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		err := ac.wsConn.WriteJSON(aliveMsg)
 | 
							err := ac.wsConn.WriteJSON(aliveMsg)
 | 
				
			||||||
		ac.logger.WithField("loop", "ws-health").Debug("hello'd")
 | 
							ac.logger.WithField("loop", "ws-health").Trace("hello'd")
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ac.logger.WithField("loop", "ws-health").Println("write:", err)
 | 
								ac.logger.WithField("loop", "ws-health").Println("write:", err)
 | 
				
			||||||
			ac.wsConn.CloseAndReconnect()
 | 
								ac.wsConn.CloseAndReconnect()
 | 
				
			||||||
 | 
				
			|||||||
@ -68,7 +68,7 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if provider.Certificate != nil {
 | 
						if provider.Certificate != nil {
 | 
				
			||||||
		pb.log.WithField("provider", provider.ClientID).Debug("Enabling TLS")
 | 
							pb.log.WithField("provider", provider.ClientID).Debug("Enabling TLS")
 | 
				
			||||||
		cert, err := pb.s.ak.Client.Crypto.CryptoCertificatekeypairsRead(&crypto.CryptoCertificatekeypairsReadParams{
 | 
							cert, err := pb.s.ak.Client.Crypto.CryptoCertificatekeypairsViewCertificate(&crypto.CryptoCertificatekeypairsViewCertificateParams{
 | 
				
			||||||
			Context: context.Background(),
 | 
								Context: context.Background(),
 | 
				
			||||||
			KpUUID:  *provider.Certificate,
 | 
								KpUUID:  *provider.Certificate,
 | 
				
			||||||
		}, pb.s.ak.Auth)
 | 
							}, pb.s.ak.Auth)
 | 
				
			||||||
@ -76,13 +76,22 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
 | 
				
			|||||||
			pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch certificate")
 | 
								pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch certificate")
 | 
				
			||||||
			return providerOpts
 | 
								return providerOpts
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		x509cert, err := tls.X509KeyPair([]byte(*cert.Payload.CertificateData), []byte(cert.Payload.KeyData))
 | 
							key, err := pb.s.ak.Client.Crypto.CryptoCertificatekeypairsViewPrivateKey(&crypto.CryptoCertificatekeypairsViewPrivateKeyParams{
 | 
				
			||||||
 | 
								Context: context.Background(),
 | 
				
			||||||
 | 
								KpUUID:  *provider.Certificate,
 | 
				
			||||||
 | 
							}, pb.s.ak.Auth)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch private key")
 | 
				
			||||||
 | 
								return providerOpts
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							x509cert, err := tls.X509KeyPair([]byte(*&cert.Payload.Data), []byte(key.Payload.Data))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to parse certificate")
 | 
								pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to parse certificate")
 | 
				
			||||||
			return providerOpts
 | 
								return providerOpts
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		pb.cert = &x509cert
 | 
							pb.cert = &x509cert
 | 
				
			||||||
		pb.log.WithField("provider", provider.ClientID).WithField("certificate-key-pair", *cert.Payload.Name).Debug("Loaded certificates")
 | 
							pb.log.WithField("provider", provider.ClientID).Debug("Loaded certificates")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return providerOpts
 | 
						return providerOpts
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,3 @@
 | 
				
			|||||||
package pkg
 | 
					package pkg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const VERSION = "2021.2.1-rc1"
 | 
					const VERSION = "2021.2.3-stable"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										470
									
								
								swagger.yaml
									
									
									
									
									
								
							
							
						
						
									
										470
									
								
								swagger.yaml
									
									
									
									
									
								
							@ -565,9 +565,9 @@ paths:
 | 
				
			|||||||
      parameters: []
 | 
					      parameters: []
 | 
				
			||||||
      responses:
 | 
					      responses:
 | 
				
			||||||
        '200':
 | 
					        '200':
 | 
				
			||||||
          description: ''
 | 
					          description: Show token's current key
 | 
				
			||||||
          schema:
 | 
					          schema:
 | 
				
			||||||
            $ref: '#/definitions/Token'
 | 
					            $ref: '#/definitions/TokenView'
 | 
				
			||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - core
 | 
					        - core
 | 
				
			||||||
    parameters:
 | 
					    parameters:
 | 
				
			||||||
@ -863,6 +863,44 @@ paths:
 | 
				
			|||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
 | 
					  /crypto/certificatekeypairs/{kp_uuid}/view_certificate/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: crypto_certificatekeypairs_view_certificate
 | 
				
			||||||
 | 
					      description: Return certificate-key pairs certificate and log access
 | 
				
			||||||
 | 
					      parameters: []
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: Get CertificateKeyPair's data
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/CertificateData'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - crypto
 | 
				
			||||||
 | 
					    parameters:
 | 
				
			||||||
 | 
					      - name: kp_uuid
 | 
				
			||||||
 | 
					        in: path
 | 
				
			||||||
 | 
					        description: A UUID string identifying this Certificate-Key Pair.
 | 
				
			||||||
 | 
					        required: true
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: uuid
 | 
				
			||||||
 | 
					  /crypto/certificatekeypairs/{kp_uuid}/view_private_key/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: crypto_certificatekeypairs_view_private_key
 | 
				
			||||||
 | 
					      description: Return certificate-key pairs private key and log access
 | 
				
			||||||
 | 
					      parameters: []
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: Get CertificateKeyPair's data
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/CertificateData'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - crypto
 | 
				
			||||||
 | 
					    parameters:
 | 
				
			||||||
 | 
					      - name: kp_uuid
 | 
				
			||||||
 | 
					        in: path
 | 
				
			||||||
 | 
					        description: A UUID string identifying this Certificate-Key Pair.
 | 
				
			||||||
 | 
					        required: true
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: uuid
 | 
				
			||||||
  /events/events/:
 | 
					  /events/events/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: events_events_list
 | 
					      operationId: events_events_list
 | 
				
			||||||
@ -1789,6 +1827,11 @@ paths:
 | 
				
			|||||||
      operationId: outposts_outposts_list
 | 
					      operationId: outposts_outposts_list
 | 
				
			||||||
      description: Outpost Viewset
 | 
					      description: Outpost Viewset
 | 
				
			||||||
      parameters:
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: providers__isnull
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
        - name: ordering
 | 
					        - name: ordering
 | 
				
			||||||
          in: query
 | 
					          in: query
 | 
				
			||||||
          description: Which field to use when ordering the results.
 | 
					          description: Which field to use when ordering the results.
 | 
				
			||||||
@ -1911,6 +1954,28 @@ paths:
 | 
				
			|||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
 | 
					  /outposts/outposts/{uuid}/health/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: outposts_outposts_health
 | 
				
			||||||
 | 
					      description: Get outposts current health
 | 
				
			||||||
 | 
					      parameters: []
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: Outpost health status
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            description: ''
 | 
				
			||||||
 | 
					            type: array
 | 
				
			||||||
 | 
					            items:
 | 
				
			||||||
 | 
					              $ref: '#/definitions/OutpostHealth'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - outposts
 | 
				
			||||||
 | 
					    parameters:
 | 
				
			||||||
 | 
					      - name: uuid
 | 
				
			||||||
 | 
					        in: path
 | 
				
			||||||
 | 
					        description: A UUID string identifying this outpost.
 | 
				
			||||||
 | 
					        required: true
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: uuid
 | 
				
			||||||
  /outposts/proxy/:
 | 
					  /outposts/proxy/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: outposts_proxy_list
 | 
					      operationId: outposts_proxy_list
 | 
				
			||||||
@ -2037,6 +2102,133 @@ paths:
 | 
				
			|||||||
        description: A unique integer value identifying this Proxy Provider.
 | 
					        description: A unique integer value identifying this Proxy Provider.
 | 
				
			||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
        type: integer
 | 
					        type: integer
 | 
				
			||||||
 | 
					  /outposts/service_connections/all/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: outposts_service_connections_all_list
 | 
				
			||||||
 | 
					      description: ServiceConnection Viewset
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: ordering
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: Which field to use when ordering the results.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					        - name: search
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: A search term.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					        - name: page
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: A page number within the paginated result set.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: integer
 | 
				
			||||||
 | 
					        - name: page_size
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: Number of results to return per page.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: integer
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            required:
 | 
				
			||||||
 | 
					              - count
 | 
				
			||||||
 | 
					              - results
 | 
				
			||||||
 | 
					            type: object
 | 
				
			||||||
 | 
					            properties:
 | 
				
			||||||
 | 
					              count:
 | 
				
			||||||
 | 
					                type: integer
 | 
				
			||||||
 | 
					              next:
 | 
				
			||||||
 | 
					                type: string
 | 
				
			||||||
 | 
					                format: uri
 | 
				
			||||||
 | 
					                x-nullable: true
 | 
				
			||||||
 | 
					              previous:
 | 
				
			||||||
 | 
					                type: string
 | 
				
			||||||
 | 
					                format: uri
 | 
				
			||||||
 | 
					                x-nullable: true
 | 
				
			||||||
 | 
					              results:
 | 
				
			||||||
 | 
					                type: array
 | 
				
			||||||
 | 
					                items:
 | 
				
			||||||
 | 
					                  $ref: '#/definitions/ServiceConnection'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - outposts
 | 
				
			||||||
 | 
					    post:
 | 
				
			||||||
 | 
					      operationId: outposts_service_connections_all_create
 | 
				
			||||||
 | 
					      description: ServiceConnection Viewset
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: data
 | 
				
			||||||
 | 
					          in: body
 | 
				
			||||||
 | 
					          required: true
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/ServiceConnection'
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '201':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/ServiceConnection'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - outposts
 | 
				
			||||||
 | 
					    parameters: []
 | 
				
			||||||
 | 
					  /outposts/service_connections/all/{uuid}/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: outposts_service_connections_all_read
 | 
				
			||||||
 | 
					      description: ServiceConnection Viewset
 | 
				
			||||||
 | 
					      parameters: []
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/ServiceConnection'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - outposts
 | 
				
			||||||
 | 
					    put:
 | 
				
			||||||
 | 
					      operationId: outposts_service_connections_all_update
 | 
				
			||||||
 | 
					      description: ServiceConnection Viewset
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: data
 | 
				
			||||||
 | 
					          in: body
 | 
				
			||||||
 | 
					          required: true
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/ServiceConnection'
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/ServiceConnection'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - outposts
 | 
				
			||||||
 | 
					    patch:
 | 
				
			||||||
 | 
					      operationId: outposts_service_connections_all_partial_update
 | 
				
			||||||
 | 
					      description: ServiceConnection Viewset
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: data
 | 
				
			||||||
 | 
					          in: body
 | 
				
			||||||
 | 
					          required: true
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/ServiceConnection'
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/ServiceConnection'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - outposts
 | 
				
			||||||
 | 
					    delete:
 | 
				
			||||||
 | 
					      operationId: outposts_service_connections_all_delete
 | 
				
			||||||
 | 
					      description: ServiceConnection Viewset
 | 
				
			||||||
 | 
					      parameters: []
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '204':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - outposts
 | 
				
			||||||
 | 
					    parameters:
 | 
				
			||||||
 | 
					      - name: uuid
 | 
				
			||||||
 | 
					        in: path
 | 
				
			||||||
 | 
					        description: A UUID string identifying this Outpost Service-Connection.
 | 
				
			||||||
 | 
					        required: true
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: uuid
 | 
				
			||||||
  /outposts/service_connections/docker/:
 | 
					  /outposts/service_connections/docker/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: outposts_service_connections_docker_list
 | 
					      operationId: outposts_service_connections_docker_list
 | 
				
			||||||
@ -4120,6 +4312,47 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - providers
 | 
					        - providers
 | 
				
			||||||
    parameters: []
 | 
					    parameters: []
 | 
				
			||||||
 | 
					  /providers/all/types/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: providers_all_types
 | 
				
			||||||
 | 
					      description: Get all creatable provider types
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: application__isnull
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					        - name: ordering
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: Which field to use when ordering the results.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					        - name: search
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: A search term.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					        - name: page
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: A page number within the paginated result set.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: integer
 | 
				
			||||||
 | 
					        - name: page_size
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: Number of results to return per page.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: integer
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: Types of an object that can be created
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            description: ''
 | 
				
			||||||
 | 
					            type: array
 | 
				
			||||||
 | 
					            items:
 | 
				
			||||||
 | 
					              $ref: '#/definitions/TypeCreate'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - providers
 | 
				
			||||||
 | 
					    parameters: []
 | 
				
			||||||
  /providers/all/{id}/:
 | 
					  /providers/all/{id}/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: providers_all_read
 | 
					      operationId: providers_all_read
 | 
				
			||||||
@ -4308,7 +4541,7 @@ paths:
 | 
				
			|||||||
  /providers/oauth2/{id}/setup_urls/:
 | 
					  /providers/oauth2/{id}/setup_urls/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: providers_oauth2_setup_urls
 | 
					      operationId: providers_oauth2_setup_urls
 | 
				
			||||||
      description: Return metadata as XML string
 | 
					      description: Get Providers setup URLs
 | 
				
			||||||
      parameters: []
 | 
					      parameters: []
 | 
				
			||||||
      responses:
 | 
					      responses:
 | 
				
			||||||
        '200':
 | 
					        '200':
 | 
				
			||||||
@ -4676,6 +4909,42 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - sources
 | 
					        - sources
 | 
				
			||||||
    parameters: []
 | 
					    parameters: []
 | 
				
			||||||
 | 
					  /sources/all/types/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: sources_all_types
 | 
				
			||||||
 | 
					      description: Get all creatable source types
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: ordering
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: Which field to use when ordering the results.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					        - name: search
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: A search term.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					        - name: page
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: A page number within the paginated result set.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: integer
 | 
				
			||||||
 | 
					        - name: page_size
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: Number of results to return per page.
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: integer
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: Types of an object that can be created
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            description: ''
 | 
				
			||||||
 | 
					            type: array
 | 
				
			||||||
 | 
					            items:
 | 
				
			||||||
 | 
					              $ref: '#/definitions/TypeCreate'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - sources
 | 
				
			||||||
 | 
					    parameters: []
 | 
				
			||||||
  /sources/all/{slug}/:
 | 
					  /sources/all/{slug}/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: sources_all_read
 | 
					      operationId: sources_all_read
 | 
				
			||||||
@ -4763,7 +5032,7 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - sources
 | 
					        - sources
 | 
				
			||||||
    parameters: []
 | 
					    parameters: []
 | 
				
			||||||
  /sources/ldap/{pbm_uuid}/:
 | 
					  /sources/ldap/{slug}/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: sources_ldap_read
 | 
					      operationId: sources_ldap_read
 | 
				
			||||||
      description: LDAP Source Viewset
 | 
					      description: LDAP Source Viewset
 | 
				
			||||||
@ -4817,12 +5086,33 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - sources
 | 
					        - sources
 | 
				
			||||||
    parameters:
 | 
					    parameters:
 | 
				
			||||||
      - name: pbm_uuid
 | 
					      - name: slug
 | 
				
			||||||
        in: path
 | 
					        in: path
 | 
				
			||||||
        description: A UUID string identifying this LDAP Source.
 | 
					        description: Internal source name, used in URLs.
 | 
				
			||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: slug
 | 
				
			||||||
 | 
					        pattern: ^[-a-zA-Z0-9_]+$
 | 
				
			||||||
 | 
					  /sources/ldap/{slug}/sync_status/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: sources_ldap_sync_status
 | 
				
			||||||
 | 
					      description: Get source's sync status
 | 
				
			||||||
 | 
					      parameters: []
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: LDAP Sync status
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/LDAPSourceSyncStatus'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - sources
 | 
				
			||||||
 | 
					    parameters:
 | 
				
			||||||
 | 
					      - name: slug
 | 
				
			||||||
 | 
					        in: path
 | 
				
			||||||
 | 
					        description: Internal source name, used in URLs.
 | 
				
			||||||
 | 
					        required: true
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: slug
 | 
				
			||||||
 | 
					        pattern: ^[-a-zA-Z0-9_]+$
 | 
				
			||||||
  /sources/oauth/:
 | 
					  /sources/oauth/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: sources_oauth_list
 | 
					      operationId: sources_oauth_list
 | 
				
			||||||
@ -4890,7 +5180,7 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - sources
 | 
					        - sources
 | 
				
			||||||
    parameters: []
 | 
					    parameters: []
 | 
				
			||||||
  /sources/oauth/{pbm_uuid}/:
 | 
					  /sources/oauth/{slug}/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: sources_oauth_read
 | 
					      operationId: sources_oauth_read
 | 
				
			||||||
      description: Source Viewset
 | 
					      description: Source Viewset
 | 
				
			||||||
@ -4944,12 +5234,13 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - sources
 | 
					        - sources
 | 
				
			||||||
    parameters:
 | 
					    parameters:
 | 
				
			||||||
      - name: pbm_uuid
 | 
					      - name: slug
 | 
				
			||||||
        in: path
 | 
					        in: path
 | 
				
			||||||
        description: A UUID string identifying this Generic OAuth Source.
 | 
					        description: Internal source name, used in URLs.
 | 
				
			||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: slug
 | 
				
			||||||
 | 
					        pattern: ^[-a-zA-Z0-9_]+$
 | 
				
			||||||
  /sources/saml/:
 | 
					  /sources/saml/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: sources_saml_list
 | 
					      operationId: sources_saml_list
 | 
				
			||||||
@ -5017,7 +5308,7 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - sources
 | 
					        - sources
 | 
				
			||||||
    parameters: []
 | 
					    parameters: []
 | 
				
			||||||
  /sources/saml/{pbm_uuid}/:
 | 
					  /sources/saml/{slug}/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: sources_saml_read
 | 
					      operationId: sources_saml_read
 | 
				
			||||||
      description: SAMLSource Viewset
 | 
					      description: SAMLSource Viewset
 | 
				
			||||||
@ -5071,12 +5362,13 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - sources
 | 
					        - sources
 | 
				
			||||||
    parameters:
 | 
					    parameters:
 | 
				
			||||||
      - name: pbm_uuid
 | 
					      - name: slug
 | 
				
			||||||
        in: path
 | 
					        in: path
 | 
				
			||||||
        description: A UUID string identifying this SAML Source.
 | 
					        description: Internal source name, used in URLs.
 | 
				
			||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: slug
 | 
				
			||||||
 | 
					        pattern: ^[-a-zA-Z0-9_]+$
 | 
				
			||||||
  /stages/all/:
 | 
					  /stages/all/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: stages_all_list
 | 
					      operationId: stages_all_list
 | 
				
			||||||
@ -7536,6 +7828,15 @@ definitions:
 | 
				
			|||||||
      description:
 | 
					      description:
 | 
				
			||||||
        title: Description
 | 
					        title: Description
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
 | 
					  TokenView:
 | 
				
			||||||
 | 
					    description: Show token's current key
 | 
				
			||||||
 | 
					    type: object
 | 
				
			||||||
 | 
					    properties:
 | 
				
			||||||
 | 
					      key:
 | 
				
			||||||
 | 
					        title: Key
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					        minLength: 1
 | 
				
			||||||
  User:
 | 
					  User:
 | 
				
			||||||
    description: User Serializer
 | 
					    description: User Serializer
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
@ -7589,6 +7890,10 @@ definitions:
 | 
				
			|||||||
        title: Name
 | 
					        title: Name
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        minLength: 1
 | 
					        minLength: 1
 | 
				
			||||||
 | 
					      fingerprint:
 | 
				
			||||||
 | 
					        title: Fingerprint
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
      certificate_data:
 | 
					      certificate_data:
 | 
				
			||||||
        title: Certificate data
 | 
					        title: Certificate data
 | 
				
			||||||
        description: PEM-encoded Certificate data
 | 
					        description: PEM-encoded Certificate data
 | 
				
			||||||
@ -7599,6 +7904,24 @@ definitions:
 | 
				
			|||||||
        description: Optional Private Key. If this is set, you can use this keypair
 | 
					        description: Optional Private Key. If this is set, you can use this keypair
 | 
				
			||||||
          for encryption.
 | 
					          for encryption.
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
 | 
					      cert_expiry:
 | 
				
			||||||
 | 
					        title: Cert expiry
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: date-time
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					      cert_subject:
 | 
				
			||||||
 | 
					        title: Cert subject
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					  CertificateData:
 | 
				
			||||||
 | 
					    description: Get CertificateKeyPair's data
 | 
				
			||||||
 | 
					    type: object
 | 
				
			||||||
 | 
					    properties:
 | 
				
			||||||
 | 
					      data:
 | 
				
			||||||
 | 
					        title: Data
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					        minLength: 1
 | 
				
			||||||
  Event:
 | 
					  Event:
 | 
				
			||||||
    description: Event Serializer
 | 
					    description: Event Serializer
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
@ -7624,7 +7947,7 @@ definitions:
 | 
				
			|||||||
          - user_write
 | 
					          - user_write
 | 
				
			||||||
          - suspicious_request
 | 
					          - suspicious_request
 | 
				
			||||||
          - password_set
 | 
					          - password_set
 | 
				
			||||||
          - token_view
 | 
					          - secret_view
 | 
				
			||||||
          - invitation_used
 | 
					          - invitation_used
 | 
				
			||||||
          - authorize_application
 | 
					          - authorize_application
 | 
				
			||||||
          - source_linked
 | 
					          - source_linked
 | 
				
			||||||
@ -8023,6 +8346,12 @@ definitions:
 | 
				
			|||||||
        items:
 | 
					        items:
 | 
				
			||||||
          type: integer
 | 
					          type: integer
 | 
				
			||||||
        uniqueItems: true
 | 
					        uniqueItems: true
 | 
				
			||||||
 | 
					      providers_obj:
 | 
				
			||||||
 | 
					        description: ''
 | 
				
			||||||
 | 
					        type: array
 | 
				
			||||||
 | 
					        items:
 | 
				
			||||||
 | 
					          $ref: '#/definitions/Provider'
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
      service_connection:
 | 
					      service_connection:
 | 
				
			||||||
        title: Service connection
 | 
					        title: Service connection
 | 
				
			||||||
        description: Select Service-Connection authentik should use to manage this
 | 
					        description: Select Service-Connection authentik should use to manage this
 | 
				
			||||||
@ -8030,9 +8359,36 @@ definitions:
 | 
				
			|||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
        x-nullable: true
 | 
					        x-nullable: true
 | 
				
			||||||
 | 
					      token_identifier:
 | 
				
			||||||
 | 
					        title: Token identifier
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
      _config:
 | 
					      _config:
 | 
				
			||||||
        title: config
 | 
					        title: config
 | 
				
			||||||
        type: object
 | 
					        type: object
 | 
				
			||||||
 | 
					  OutpostHealth:
 | 
				
			||||||
 | 
					    description: Outpost health status
 | 
				
			||||||
 | 
					    type: object
 | 
				
			||||||
 | 
					    properties:
 | 
				
			||||||
 | 
					      last_seen:
 | 
				
			||||||
 | 
					        title: Last seen
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: date-time
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					      version:
 | 
				
			||||||
 | 
					        title: Version
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					        minLength: 1
 | 
				
			||||||
 | 
					      version_should:
 | 
				
			||||||
 | 
					        title: Version should
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					        minLength: 1
 | 
				
			||||||
 | 
					      version_outdated:
 | 
				
			||||||
 | 
					        title: Version outdated
 | 
				
			||||||
 | 
					        type: boolean
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
  OpenIDConnectConfiguration:
 | 
					  OpenIDConnectConfiguration:
 | 
				
			||||||
    title: Oidc configuration
 | 
					    title: Oidc configuration
 | 
				
			||||||
    description: rest_framework Serializer for OIDC Configuration
 | 
					    description: rest_framework Serializer for OIDC Configuration
 | 
				
			||||||
@ -8170,6 +8526,21 @@ definitions:
 | 
				
			|||||||
        description: User/Group Attribute used for the user part of the HTTP-Basic
 | 
					        description: User/Group Attribute used for the user part of the HTTP-Basic
 | 
				
			||||||
          Header. If not set, the user's Email address is used.
 | 
					          Header. If not set, the user's Email address is used.
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
 | 
					  ServiceConnection:
 | 
				
			||||||
 | 
					    description: ServiceConnection Serializer
 | 
				
			||||||
 | 
					    required:
 | 
				
			||||||
 | 
					      - name
 | 
				
			||||||
 | 
					    type: object
 | 
				
			||||||
 | 
					    properties:
 | 
				
			||||||
 | 
					      pk:
 | 
				
			||||||
 | 
					        title: Uuid
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: uuid
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					      name:
 | 
				
			||||||
 | 
					        title: Name
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        minLength: 1
 | 
				
			||||||
  DockerServiceConnection:
 | 
					  DockerServiceConnection:
 | 
				
			||||||
    description: DockerServiceConnection Serializer
 | 
					    description: DockerServiceConnection Serializer
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
@ -8347,7 +8718,7 @@ definitions:
 | 
				
			|||||||
          - user_write
 | 
					          - user_write
 | 
				
			||||||
          - suspicious_request
 | 
					          - suspicious_request
 | 
				
			||||||
          - password_set
 | 
					          - password_set
 | 
				
			||||||
          - token_view
 | 
					          - secret_view
 | 
				
			||||||
          - invitation_used
 | 
					          - invitation_used
 | 
				
			||||||
          - authorize_application
 | 
					          - authorize_application
 | 
				
			||||||
          - source_linked
 | 
					          - source_linked
 | 
				
			||||||
@ -8711,6 +9082,25 @@ definitions:
 | 
				
			|||||||
        title: Verbose name plural
 | 
					        title: Verbose name plural
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        readOnly: true
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					  TypeCreate:
 | 
				
			||||||
 | 
					    description: Types of an object that can be created
 | 
				
			||||||
 | 
					    type: object
 | 
				
			||||||
 | 
					    properties:
 | 
				
			||||||
 | 
					      name:
 | 
				
			||||||
 | 
					        title: Name
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					        minLength: 1
 | 
				
			||||||
 | 
					      description:
 | 
				
			||||||
 | 
					        title: Description
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					        minLength: 1
 | 
				
			||||||
 | 
					      link:
 | 
				
			||||||
 | 
					        title: Link
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					        minLength: 1
 | 
				
			||||||
  OAuth2Provider:
 | 
					  OAuth2Provider:
 | 
				
			||||||
    description: OAuth2Provider Serializer
 | 
					    description: OAuth2Provider Serializer
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
@ -9157,6 +9547,10 @@ definitions:
 | 
				
			|||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
        x-nullable: true
 | 
					        x-nullable: true
 | 
				
			||||||
 | 
					      object_type:
 | 
				
			||||||
 | 
					        title: Object type
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
      verbose_name:
 | 
					      verbose_name:
 | 
				
			||||||
        title: Verbose name
 | 
					        title: Verbose name
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
@ -9165,10 +9559,6 @@ definitions:
 | 
				
			|||||||
        title: Verbose name plural
 | 
					        title: Verbose name plural
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        readOnly: true
 | 
					        readOnly: true
 | 
				
			||||||
      __type__:
 | 
					 | 
				
			||||||
        title: 'type  '
 | 
					 | 
				
			||||||
        type: string
 | 
					 | 
				
			||||||
        readOnly: true
 | 
					 | 
				
			||||||
  LDAPSource:
 | 
					  LDAPSource:
 | 
				
			||||||
    description: LDAP Source Serializer
 | 
					    description: LDAP Source Serializer
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
@ -9213,6 +9603,10 @@ definitions:
 | 
				
			|||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
        x-nullable: true
 | 
					        x-nullable: true
 | 
				
			||||||
 | 
					      object_type:
 | 
				
			||||||
 | 
					        title: Object type
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
      verbose_name:
 | 
					      verbose_name:
 | 
				
			||||||
        title: Verbose name
 | 
					        title: Verbose name
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
@ -9298,6 +9692,15 @@ definitions:
 | 
				
			|||||||
          type: string
 | 
					          type: string
 | 
				
			||||||
          format: uuid
 | 
					          format: uuid
 | 
				
			||||||
        uniqueItems: true
 | 
					        uniqueItems: true
 | 
				
			||||||
 | 
					  LDAPSourceSyncStatus:
 | 
				
			||||||
 | 
					    description: LDAP Sync status
 | 
				
			||||||
 | 
					    type: object
 | 
				
			||||||
 | 
					    properties:
 | 
				
			||||||
 | 
					      last_sync:
 | 
				
			||||||
 | 
					        title: Last sync
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: date-time
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
  OAuthSource:
 | 
					  OAuthSource:
 | 
				
			||||||
    description: OAuth Source Serializer
 | 
					    description: OAuth Source Serializer
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
@ -9344,6 +9747,10 @@ definitions:
 | 
				
			|||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
        x-nullable: true
 | 
					        x-nullable: true
 | 
				
			||||||
 | 
					      object_type:
 | 
				
			||||||
 | 
					        title: Object type
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
      verbose_name:
 | 
					      verbose_name:
 | 
				
			||||||
        title: Verbose name
 | 
					        title: Verbose name
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
@ -9389,6 +9796,10 @@ definitions:
 | 
				
			|||||||
        title: Consumer secret
 | 
					        title: Consumer secret
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        minLength: 1
 | 
					        minLength: 1
 | 
				
			||||||
 | 
					      callback_url:
 | 
				
			||||||
 | 
					        title: Callback url
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
  SAMLSource:
 | 
					  SAMLSource:
 | 
				
			||||||
    description: SAMLSource Serializer
 | 
					    description: SAMLSource Serializer
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
@ -9397,6 +9808,11 @@ definitions:
 | 
				
			|||||||
      - sso_url
 | 
					      - sso_url
 | 
				
			||||||
    type: object
 | 
					    type: object
 | 
				
			||||||
    properties:
 | 
					    properties:
 | 
				
			||||||
 | 
					      pk:
 | 
				
			||||||
 | 
					        title: Pbm uuid
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        format: uuid
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
      name:
 | 
					      name:
 | 
				
			||||||
        title: Name
 | 
					        title: Name
 | 
				
			||||||
        description: Source's display Name.
 | 
					        description: Source's display Name.
 | 
				
			||||||
@ -9425,6 +9841,18 @@ definitions:
 | 
				
			|||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
        x-nullable: true
 | 
					        x-nullable: true
 | 
				
			||||||
 | 
					      object_type:
 | 
				
			||||||
 | 
					        title: Object type
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					      verbose_name:
 | 
				
			||||||
 | 
					        title: Verbose name
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					      verbose_name_plural:
 | 
				
			||||||
 | 
					        title: Verbose name plural
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
      issuer:
 | 
					      issuer:
 | 
				
			||||||
        title: Issuer
 | 
					        title: Issuer
 | 
				
			||||||
        description: Also known as Entity ID. Defaults the Metadata URL.
 | 
					        description: Also known as Entity ID. Defaults the Metadata URL.
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										150
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										150
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -409,13 +409,13 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@typescript-eslint/eslint-plugin": {
 | 
					        "@typescript-eslint/eslint-plugin": {
 | 
				
			||||||
            "version": "4.14.2",
 | 
					            "version": "4.15.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-uMGfG7GFYK/nYutK/iqYJv6K/Xuog/vrRRZX9aEP4Zv1jsYXuvFUMDFLhUnc8WFv3D2R5QhNQL3VYKmvLS5zsQ==",
 | 
					            "integrity": "sha512-DJgdGZW+8CFUTz5C/dnn4ONcUm2h2T0itWD85Ob5/V27Ndie8hUoX5HKyGssvR8sUMkAIlUc/AMK67Lqa3kBIQ==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@typescript-eslint/experimental-utils": "4.14.2",
 | 
					                "@typescript-eslint/experimental-utils": "4.15.0",
 | 
				
			||||||
                "@typescript-eslint/scope-manager": "4.14.2",
 | 
					                "@typescript-eslint/scope-manager": "4.15.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",
 | 
				
			||||||
@ -425,59 +425,115 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@typescript-eslint/experimental-utils": {
 | 
					        "@typescript-eslint/experimental-utils": {
 | 
				
			||||||
            "version": "4.14.2",
 | 
					            "version": "4.15.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-mV9pmET4C2y2WlyHmD+Iun8SAEqkLahHGBkGqDVslHkmoj3VnxnGP4ANlwuxxfq1BsKdl/MPieDbohCEQgKrwA==",
 | 
					            "integrity": "sha512-V4vaDWvxA2zgesg4KPgEGiomWEBpJXvY4ZX34Y3qxK8LUm5I87L+qGIOTd9tHZOARXNRt9pLbblSKiYBlGMawg==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@types/json-schema": "^7.0.3",
 | 
					                "@types/json-schema": "^7.0.3",
 | 
				
			||||||
                "@typescript-eslint/scope-manager": "4.14.2",
 | 
					                "@typescript-eslint/scope-manager": "4.15.0",
 | 
				
			||||||
                "@typescript-eslint/types": "4.14.2",
 | 
					                "@typescript-eslint/types": "4.15.0",
 | 
				
			||||||
                "@typescript-eslint/typescript-estree": "4.14.2",
 | 
					                "@typescript-eslint/typescript-estree": "4.15.0",
 | 
				
			||||||
                "eslint-scope": "^5.0.0",
 | 
					                "eslint-scope": "^5.0.0",
 | 
				
			||||||
                "eslint-utils": "^2.0.0"
 | 
					                "eslint-utils": "^2.0.0"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@typescript-eslint/parser": {
 | 
					        "@typescript-eslint/parser": {
 | 
				
			||||||
            "version": "4.14.2",
 | 
					            "version": "4.15.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-ipqSP6EuUsMu3E10EZIApOJgWSpcNXeKZaFeNKQyzqxnQl8eQCbV+TSNsl+s2GViX2d18m1rq3CWgnpOxDPgHg==",
 | 
					            "integrity": "sha512-L6Dtbq8Bc7g2aZwnIBETpmUa9XDKCMzKVwAArnGp5Mn7PRNFjf3mUzq8UeBjL3K8t311hvevnyqXAMSmxO8Gpg==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@typescript-eslint/scope-manager": "4.14.2",
 | 
					                "@typescript-eslint/scope-manager": "4.15.0",
 | 
				
			||||||
                "@typescript-eslint/types": "4.14.2",
 | 
					                "@typescript-eslint/types": "4.15.0",
 | 
				
			||||||
                "@typescript-eslint/typescript-estree": "4.14.2",
 | 
					                "@typescript-eslint/typescript-estree": "4.15.0",
 | 
				
			||||||
                "debug": "^4.1.1"
 | 
					                "debug": "^4.1.1"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "dependencies": {
 | 
				
			||||||
 | 
					                "@typescript-eslint/scope-manager": {
 | 
				
			||||||
 | 
					                    "version": "4.15.0",
 | 
				
			||||||
 | 
					                    "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz",
 | 
				
			||||||
 | 
					                    "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==",
 | 
				
			||||||
 | 
					                    "dev": true,
 | 
				
			||||||
 | 
					                    "requires": {
 | 
				
			||||||
 | 
					                        "@typescript-eslint/types": "4.15.0",
 | 
				
			||||||
 | 
					                        "@typescript-eslint/visitor-keys": "4.15.0"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "@typescript-eslint/types": {
 | 
				
			||||||
 | 
					                    "version": "4.15.0",
 | 
				
			||||||
 | 
					                    "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz",
 | 
				
			||||||
 | 
					                    "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==",
 | 
				
			||||||
 | 
					                    "dev": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "@typescript-eslint/typescript-estree": {
 | 
				
			||||||
 | 
					                    "version": "4.15.0",
 | 
				
			||||||
 | 
					                    "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz",
 | 
				
			||||||
 | 
					                    "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==",
 | 
				
			||||||
 | 
					                    "dev": true,
 | 
				
			||||||
 | 
					                    "requires": {
 | 
				
			||||||
 | 
					                        "@typescript-eslint/types": "4.15.0",
 | 
				
			||||||
 | 
					                        "@typescript-eslint/visitor-keys": "4.15.0",
 | 
				
			||||||
 | 
					                        "debug": "^4.1.1",
 | 
				
			||||||
 | 
					                        "globby": "^11.0.1",
 | 
				
			||||||
 | 
					                        "is-glob": "^4.0.1",
 | 
				
			||||||
 | 
					                        "semver": "^7.3.2",
 | 
				
			||||||
 | 
					                        "tsutils": "^3.17.1"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "@typescript-eslint/visitor-keys": {
 | 
				
			||||||
 | 
					                    "version": "4.15.0",
 | 
				
			||||||
 | 
					                    "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz",
 | 
				
			||||||
 | 
					                    "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==",
 | 
				
			||||||
 | 
					                    "dev": true,
 | 
				
			||||||
 | 
					                    "requires": {
 | 
				
			||||||
 | 
					                        "@typescript-eslint/types": "4.15.0",
 | 
				
			||||||
 | 
					                        "eslint-visitor-keys": "^2.0.0"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "globby": {
 | 
				
			||||||
 | 
					                    "version": "11.0.2",
 | 
				
			||||||
 | 
					                    "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz",
 | 
				
			||||||
 | 
					                    "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==",
 | 
				
			||||||
 | 
					                    "dev": true,
 | 
				
			||||||
 | 
					                    "requires": {
 | 
				
			||||||
 | 
					                        "array-union": "^2.1.0",
 | 
				
			||||||
 | 
					                        "dir-glob": "^3.0.1",
 | 
				
			||||||
 | 
					                        "fast-glob": "^3.1.1",
 | 
				
			||||||
 | 
					                        "ignore": "^5.1.4",
 | 
				
			||||||
 | 
					                        "merge2": "^1.3.0",
 | 
				
			||||||
 | 
					                        "slash": "^3.0.0"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@typescript-eslint/scope-manager": {
 | 
					        "@typescript-eslint/scope-manager": {
 | 
				
			||||||
            "version": "4.14.2",
 | 
					            "version": "4.15.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-cuV9wMrzKm6yIuV48aTPfIeqErt5xceTheAgk70N1V4/2Ecj+fhl34iro/vIssJlb7XtzcaD07hWk7Jk0nKghg==",
 | 
					            "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@typescript-eslint/types": "4.14.2",
 | 
					                "@typescript-eslint/types": "4.15.0",
 | 
				
			||||||
                "@typescript-eslint/visitor-keys": "4.14.2"
 | 
					                "@typescript-eslint/visitor-keys": "4.15.0"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@typescript-eslint/types": {
 | 
					        "@typescript-eslint/types": {
 | 
				
			||||||
            "version": "4.14.2",
 | 
					            "version": "4.15.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-LltxawRW6wXy4Gck6ZKlBD05tCHQUj4KLn4iR69IyRiDHX3d3NCAhO+ix5OR2Q+q9bjCrHE/HKt+riZkd1At8Q==",
 | 
					            "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@typescript-eslint/typescript-estree": {
 | 
					        "@typescript-eslint/typescript-estree": {
 | 
				
			||||||
            "version": "4.14.2",
 | 
					            "version": "4.15.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-ESiFl8afXxt1dNj8ENEZT12p+jl9PqRur+Y19m0Z/SPikGL6rqq4e7Me60SU9a2M28uz48/8yct97VQYaGl0Vg==",
 | 
					            "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@typescript-eslint/types": "4.14.2",
 | 
					                "@typescript-eslint/types": "4.15.0",
 | 
				
			||||||
                "@typescript-eslint/visitor-keys": "4.14.2",
 | 
					                "@typescript-eslint/visitor-keys": "4.15.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",
 | 
				
			||||||
                "lodash": "^4.17.15",
 | 
					 | 
				
			||||||
                "semver": "^7.3.2",
 | 
					                "semver": "^7.3.2",
 | 
				
			||||||
                "tsutils": "^3.17.1"
 | 
					                "tsutils": "^3.17.1"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -499,12 +555,12 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@typescript-eslint/visitor-keys": {
 | 
					        "@typescript-eslint/visitor-keys": {
 | 
				
			||||||
            "version": "4.14.2",
 | 
					            "version": "4.15.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-KBB+xLBxnBdTENs/rUgeUKO0UkPBRs2vD09oMRRIkj5BEN8PX1ToXV532desXfpQnZsYTyLLviS7JrPhdL154w==",
 | 
					            "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@typescript-eslint/types": "4.14.2",
 | 
					                "@typescript-eslint/types": "4.15.0",
 | 
				
			||||||
                "eslint-visitor-keys": "^2.0.0"
 | 
					                "eslint-visitor-keys": "^2.0.0"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -899,9 +955,9 @@
 | 
				
			|||||||
            "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
 | 
					            "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "construct-style-sheets-polyfill": {
 | 
					        "construct-style-sheets-polyfill": {
 | 
				
			||||||
            "version": "2.4.6",
 | 
					            "version": "2.4.9",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.6.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.9.tgz",
 | 
				
			||||||
            "integrity": "sha512-lU0to7dFDjKslMF+M5NUa4s0RQMBRVyZMXvD/vp7vmjdEPgziTkHSfZHQxfoIvVWajWRJUVJMLfrMwcx8fTh4A=="
 | 
					            "integrity": "sha512-kPXZXxsp7CTr/Vs29+omUA29wTrFplkdY6jqxyv0DDWC5Ro79WmwpboH2M9KiOclbtn8r81GCFtc7+t7OjRnCw=="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "copy-descriptor": {
 | 
					        "copy-descriptor": {
 | 
				
			||||||
            "version": "0.1.1",
 | 
					            "version": "0.1.1",
 | 
				
			||||||
@ -1537,9 +1593,9 @@
 | 
				
			|||||||
            "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
 | 
					            "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "fsevents": {
 | 
					        "fsevents": {
 | 
				
			||||||
            "version": "2.3.1",
 | 
					            "version": "2.3.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
 | 
				
			||||||
            "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==",
 | 
					            "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
 | 
				
			||||||
            "optional": true
 | 
					            "optional": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "functional-red-black-tree": {
 | 
					        "functional-red-black-tree": {
 | 
				
			||||||
@ -2638,9 +2694,9 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "rollup": {
 | 
					        "rollup": {
 | 
				
			||||||
            "version": "2.38.4",
 | 
					            "version": "2.38.5",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.4.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.5.tgz",
 | 
				
			||||||
            "integrity": "sha512-B0LcJhjiwKkTl79aGVF/u5KdzsH8IylVfV56Ut6c9ouWLJcUK17T83aZBetNYSnZtXf2OHD4+2PbmRW+Fp5ulg==",
 | 
					            "integrity": "sha512-VoWt8DysFGDVRGWuHTqZzT02J0ASgjVq/hPs9QcBOGMd7B+jfTr/iqMVEyOi901rE3xq+Deq66GzIT1yt7sGwQ==",
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "fsevents": "~2.3.1"
 | 
					                "fsevents": "~2.3.1"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -3304,9 +3360,9 @@
 | 
				
			|||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "typescript": {
 | 
					        "typescript": {
 | 
				
			||||||
            "version": "4.1.3",
 | 
					            "version": "4.1.4",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.4.tgz",
 | 
				
			||||||
            "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
 | 
					            "integrity": "sha512-+Uru0t8qIRgjuCpiSPpfGuhHecMllk5Zsazj5LZvVsEStEjmIRRBZe+jHjGQvsgS7M1wONy2PQXd67EMyV6acg==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "uglify-js": {
 | 
					        "uglify-js": {
 | 
				
			||||||
 | 
				
			|||||||
@ -18,11 +18,11 @@
 | 
				
			|||||||
        "@types/codemirror": "0.0.108",
 | 
					        "@types/codemirror": "0.0.108",
 | 
				
			||||||
        "chart.js": "^2.9.4",
 | 
					        "chart.js": "^2.9.4",
 | 
				
			||||||
        "codemirror": "^5.59.2",
 | 
					        "codemirror": "^5.59.2",
 | 
				
			||||||
        "construct-style-sheets-polyfill": "^2.4.6",
 | 
					        "construct-style-sheets-polyfill": "^2.4.9",
 | 
				
			||||||
        "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.38.4",
 | 
					        "rollup": "^2.38.5",
 | 
				
			||||||
        "rollup-plugin-copy": "^3.3.0",
 | 
					        "rollup-plugin-copy": "^3.3.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",
 | 
				
			||||||
@ -30,8 +30,8 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
        "@rollup/plugin-typescript": "^8.1.1",
 | 
					        "@rollup/plugin-typescript": "^8.1.1",
 | 
				
			||||||
        "@typescript-eslint/eslint-plugin": "^4.14.2",
 | 
					        "@typescript-eslint/eslint-plugin": "^4.15.0",
 | 
				
			||||||
        "@typescript-eslint/parser": "^4.14.2",
 | 
					        "@typescript-eslint/parser": "^4.15.0",
 | 
				
			||||||
        "eslint": "^7.19.0",
 | 
					        "eslint": "^7.19.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",
 | 
				
			||||||
@ -41,6 +41,6 @@
 | 
				
			|||||||
        "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.1.3"
 | 
					        "typescript": "^4.1.4"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
					import { DefaultClient, AKResponse, QueryArguments } from "./Client";
 | 
				
			||||||
import { Provider } from "./Providers";
 | 
					import { Provider } from "./Providers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Application {
 | 
					export class Application {
 | 
				
			||||||
@ -22,8 +22,8 @@ export class Application {
 | 
				
			|||||||
        return DefaultClient.fetch<Application>(["core", "applications", slug]);
 | 
					        return DefaultClient.fetch<Application>(["core", "applications", slug]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Application>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<Application>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Application>>(["core", "applications"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<Application>>(["core", "applications"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static adminUrl(rest: string): string {
 | 
					    static adminUrl(rest: string): string {
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,15 @@ export interface QueryArguments {
 | 
				
			|||||||
    [key: string]: number | string | boolean | null;
 | 
					    [key: string]: number | string | boolean | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface BaseInheritanceModel {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    object_type: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    verbose_name: string;
 | 
				
			||||||
 | 
					    verbose_name_plural: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Client {
 | 
					export class Client {
 | 
				
			||||||
    makeUrl(url: string[], query?: QueryArguments): string {
 | 
					    makeUrl(url: string[], query?: QueryArguments): string {
 | 
				
			||||||
        let builtUrl = `/api/${VERSION}/${url.join("/")}/`;
 | 
					        let builtUrl = `/api/${VERSION}/${url.join("/")}/`;
 | 
				
			||||||
@ -85,7 +94,7 @@ export interface PBPagination {
 | 
				
			|||||||
    end_index: number;
 | 
					    end_index: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface PBResponse<T> {
 | 
					export interface AKResponse<T> {
 | 
				
			||||||
    pagination: PBPagination;
 | 
					    pagination: PBPagination;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    results: Array<T>;
 | 
					    results: Array<T>;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, QueryArguments, PBResponse } from "./Client";
 | 
					import { DefaultClient, QueryArguments, AKResponse } from "./Client";
 | 
				
			||||||
import { Event } from "./Events";
 | 
					import { Event } from "./Events";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Notification {
 | 
					export class Notification {
 | 
				
			||||||
@ -17,8 +17,8 @@ export class Notification {
 | 
				
			|||||||
        return DefaultClient.fetch<Notification>(["events", "notifications", pk]);
 | 
					        return DefaultClient.fetch<Notification>(["events", "notifications", pk]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Notification>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<Notification>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Notification>>(["events", "notifications"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<Notification>>(["events", "notifications"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static markSeen(pk: string): Promise<{seen: boolean}> {
 | 
					    static markSeen(pk: string): Promise<{seen: boolean}> {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, QueryArguments, PBResponse } from "./Client";
 | 
					import { DefaultClient, QueryArguments, AKResponse } from "./Client";
 | 
				
			||||||
import { Group } from "./Groups";
 | 
					import { Group } from "./Groups";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Rule {
 | 
					export class Rule {
 | 
				
			||||||
@ -16,8 +16,8 @@ export class Rule {
 | 
				
			|||||||
        return DefaultClient.fetch<Rule>(["events", "rules", pk]);
 | 
					        return DefaultClient.fetch<Rule>(["events", "rules", pk]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Rule>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<Rule>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Rule>>(["events", "rules"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<Rule>>(["events", "rules"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static adminUrl(rest: string): string {
 | 
					    static adminUrl(rest: string): string {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, QueryArguments, PBResponse } from "./Client";
 | 
					import { DefaultClient, QueryArguments, AKResponse } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Transport {
 | 
					export class Transport {
 | 
				
			||||||
    pk: string;
 | 
					    pk: string;
 | 
				
			||||||
@ -15,8 +15,8 @@ export class Transport {
 | 
				
			|||||||
        return DefaultClient.fetch<Transport>(["events", "transports", pk]);
 | 
					        return DefaultClient.fetch<Transport>(["events", "transports", pk]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Transport>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<Transport>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Transport>>(["events", "transports"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<Transport>>(["events", "transports"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static adminUrl(rest: string): string {
 | 
					    static adminUrl(rest: string): string {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
					import { DefaultClient, AKResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface EventUser {
 | 
					export interface EventUser {
 | 
				
			||||||
    pk: number;
 | 
					    pk: number;
 | 
				
			||||||
@ -28,8 +28,8 @@ export class Event {
 | 
				
			|||||||
        return DefaultClient.fetch<Event>(["events", "events", pk]);
 | 
					        return DefaultClient.fetch<Event>(["events", "events", pk]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Event>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<Event>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Event>>(["events", "events"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<Event>>(["events", "events"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // events/events/top_per_user/?filter_action=authorize_application
 | 
					    // events/events/top_per_user/?filter_action=authorize_application
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
					import { DefaultClient, AKResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum FlowDesignation {
 | 
					export enum FlowDesignation {
 | 
				
			||||||
    Authentication = "authentication",
 | 
					    Authentication = "authentication",
 | 
				
			||||||
@ -34,12 +34,12 @@ export class Flow {
 | 
				
			|||||||
        return DefaultClient.fetch<{ diagram: string }>(["flows", "instances", slug, "diagram"]);
 | 
					        return DefaultClient.fetch<{ diagram: string }>(["flows", "instances", slug, "diagram"]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Flow>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<Flow>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Flow>>(["flows", "instances"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<Flow>>(["flows", "instances"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static cached(): Promise<number> {
 | 
					    static cached(): Promise<number> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Flow>>(["flows", "cached"]).then(r => {
 | 
					        return DefaultClient.fetch<AKResponse<Flow>>(["flows", "cached"]).then(r => {
 | 
				
			||||||
            return r.pagination.count;
 | 
					            return r.pagination.count;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -76,8 +76,8 @@ export class FlowStageBinding {
 | 
				
			|||||||
        return DefaultClient.fetch<FlowStageBinding>(["flows", "bindings", slug]);
 | 
					        return DefaultClient.fetch<FlowStageBinding>(["flows", "bindings", slug]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<FlowStageBinding>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<FlowStageBinding>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<FlowStageBinding>>(["flows", "bindings"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<FlowStageBinding>>(["flows", "bindings"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static adminUrl(rest: string): string {
 | 
					    static adminUrl(rest: string): string {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										40
									
								
								web/src/api/Outposts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								web/src/api/Outposts.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					import { DefaultClient, AKResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					import { Provider } 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}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,23 +1,26 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
					import { DefaultClient, BaseInheritanceModel, AKResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Policy {
 | 
					export class Policy implements BaseInheritanceModel {
 | 
				
			||||||
    pk: string;
 | 
					    pk: string;
 | 
				
			||||||
    name: string;
 | 
					    name: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        throw Error();
 | 
					        throw Error();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    object_type: string;
 | 
				
			||||||
 | 
					    verbose_name: string;
 | 
				
			||||||
 | 
					    verbose_name_plural: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static get(pk: string): Promise<Policy> {
 | 
					    static get(pk: string): Promise<Policy> {
 | 
				
			||||||
        return DefaultClient.fetch<Policy>(["policies", "all", pk]);
 | 
					        return DefaultClient.fetch<Policy>(["policies", "all", pk]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Policy>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<Policy>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Policy>>(["policies", "all"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<Policy>>(["policies", "all"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static cached(): Promise<number> {
 | 
					    static cached(): Promise<number> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Policy>>(["policies", "cached"]).then(r => {
 | 
					        return DefaultClient.fetch<AKResponse<Policy>>(["policies", "cached"]).then(r => {
 | 
				
			||||||
            return r.pagination.count;
 | 
					            return r.pagination.count;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
					import { DefaultClient, AKResponse, QueryArguments } from "./Client";
 | 
				
			||||||
import { Policy } from "./Policies";
 | 
					import { Policy } from "./Policies";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class PolicyBinding {
 | 
					export class PolicyBinding {
 | 
				
			||||||
@ -18,8 +18,8 @@ export class PolicyBinding {
 | 
				
			|||||||
        return DefaultClient.fetch<PolicyBinding>(["policies", "bindings", pk]);
 | 
					        return DefaultClient.fetch<PolicyBinding>(["policies", "bindings", pk]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<PolicyBinding>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<PolicyBinding>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<PolicyBinding>>(["policies", "bindings"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<PolicyBinding>>(["policies", "bindings"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static adminUrl(rest: string): string {
 | 
					    static adminUrl(rest: string): string {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
					import { DefaultClient, AKResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class PropertyMapping {
 | 
					export class PropertyMapping {
 | 
				
			||||||
    pk: string;
 | 
					    pk: string;
 | 
				
			||||||
@ -16,8 +16,8 @@ export class PropertyMapping {
 | 
				
			|||||||
        return DefaultClient.fetch<PropertyMapping>(["propertymappings", "all", pk]);
 | 
					        return DefaultClient.fetch<PropertyMapping>(["propertymappings", "all", pk]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<PropertyMapping>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<PropertyMapping>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<PropertyMapping>>(["propertymappings", "all"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<PropertyMapping>>(["propertymappings", "all"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static adminUrl(rest: string): string {
 | 
					    static adminUrl(rest: string): string {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,12 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
					import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Provider {
 | 
					export interface TypeCreate {
 | 
				
			||||||
 | 
					    name: string;
 | 
				
			||||||
 | 
					    description: string;
 | 
				
			||||||
 | 
					    link: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Provider implements BaseInheritanceModel {
 | 
				
			||||||
    pk: number;
 | 
					    pk: number;
 | 
				
			||||||
    name: string;
 | 
					    name: string;
 | 
				
			||||||
    authorization_flow: string;
 | 
					    authorization_flow: string;
 | 
				
			||||||
@ -20,8 +26,12 @@ export class Provider {
 | 
				
			|||||||
        return DefaultClient.fetch<Provider>(["providers", "all", id.toString()]);
 | 
					        return DefaultClient.fetch<Provider>(["providers", "all", id.toString()]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Provider>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<Provider>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Provider>>(["providers", "all"], filter);
 | 
					        return DefaultClient.fetch<AKResponse<Provider>>(["providers", "all"], filter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static getTypes(): Promise<TypeCreate[]> {
 | 
				
			||||||
 | 
					        return DefaultClient.fetch<TypeCreate[]>(["providers", "all", "types"]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static adminUrl(rest: string): string {
 | 
					    static adminUrl(rest: string): string {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
					import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					import { TypeCreate } from "./Providers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Source {
 | 
					export class Source implements BaseInheritanceModel {
 | 
				
			||||||
    pk: string;
 | 
					    pk: string;
 | 
				
			||||||
    name: string;
 | 
					    name: string;
 | 
				
			||||||
    slug: string;
 | 
					    slug: string;
 | 
				
			||||||
@ -11,12 +12,23 @@ export class Source {
 | 
				
			|||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        throw Error();
 | 
					        throw Error();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    object_type: string;
 | 
				
			||||||
 | 
					    verbose_name: string;
 | 
				
			||||||
 | 
					    verbose_name_plural: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static get(slug: string): Promise<Source> {
 | 
					    static get(slug: string): Promise<Source> {
 | 
				
			||||||
        return DefaultClient.fetch<Source>(["sources", "all", slug]);
 | 
					        return DefaultClient.fetch<Source>(["sources", "all", slug]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Source>> {
 | 
					    static list(filter?: QueryArguments): Promise<AKResponse<Source>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Source>>(["sources", "all"], filter);
 | 
					        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}`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user