Compare commits
	
		
			40 Commits
		
	
	
		
			version/0.
			...
			version/0.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e23afd18e4 | |||
| c2a30b760a | |||
| 6e24856d45 | |||
| 98a58b74e3 | |||
| 5f3ab22bea | |||
| 1ed5d5da35 | |||
| 76193e0031 | |||
| 50109ca7ad | |||
| e4b66d991c | |||
| 68adc2d5a5 | |||
| 349a3a67d5 | |||
| e1394207e7 | |||
| f265c1f10b | |||
| 1aecdc7f8f | |||
| a18edaf62b | |||
| c91abe448c | |||
| e531e52403 | |||
| cae536fa65 | |||
| 316b15b8a9 | |||
| e6ccd4fa76 | |||
| 86aabba3ed | |||
| 0b36aad5c8 | |||
| 64d2a216f0 | |||
| a5e5e140d6 | |||
| 29f98abd00 | |||
| 7b5ce4e98a | |||
| d7fa52ebf3 | |||
| 2ffaa94825 | |||
| b80b2626a6 | |||
| 3b7bba5a62 | |||
| 2d9efe035e | |||
| 48438e28fd | |||
| 885a2f0a58 | |||
| cf46ee06b7 | |||
| 9e33b49d29 | |||
| 1179ba4ef2 | |||
| 3c12c8b3ff | |||
| 4d22659b6e | |||
| 2c0709eeee | |||
| c24d1b6b84 | 
@ -1,5 +1,5 @@
 | 
				
			|||||||
[bumpversion]
 | 
					[bumpversion]
 | 
				
			||||||
current_version = 0.13.1-stable
 | 
					current_version = 0.13.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>.*)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										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:0.13.1-stable
 | 
					          -t beryju/authentik:0.13.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:0.13.1-stable
 | 
					        run: docker push beryju/authentik:0.13.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 proxy/
 | 
					          cd proxy/
 | 
				
			||||||
          docker build \
 | 
					          docker build \
 | 
				
			||||||
          --no-cache \
 | 
					          --no-cache \
 | 
				
			||||||
          -t beryju/authentik-proxy:0.13.1-stable \
 | 
					          -t beryju/authentik-proxy:0.13.3-stable \
 | 
				
			||||||
          -t beryju/authentik-proxy:latest \
 | 
					          -t beryju/authentik-proxy:latest \
 | 
				
			||||||
          -f Dockerfile .
 | 
					          -f Dockerfile .
 | 
				
			||||||
      - name: Push Docker Container to Registry (versioned)
 | 
					      - name: Push Docker Container to Registry (versioned)
 | 
				
			||||||
        run: docker push beryju/authentik-proxy:0.13.1-stable
 | 
					        run: docker push beryju/authentik-proxy:0.13.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:0.13.1-stable \
 | 
					          -t beryju/authentik-static:0.13.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:0.13.1-stable
 | 
					        run: docker push beryju/authentik-static:0.13.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: 0.13.1-stable
 | 
					          tagName: 0.13.3-stable
 | 
				
			||||||
          environment: beryjuorg-prod
 | 
					          environment: beryjuorg-prod
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										36
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										36
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							@ -74,18 +74,18 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "boto3": {
 | 
					        "boto3": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:ad0e8dbd934d97b5228252785c0236c3ee4d464c14138f568e371bf43c6ea584",
 | 
					                "sha256:a05614300fd404c7952a55ae92e106b9400ae65886425aaab3104527be833848",
 | 
				
			||||||
                "sha256:ee86c26b3d457aa4d0256d0535d13107c32aa33bb5eb2a0b2dac9d81c3aca405"
 | 
					                "sha256:c7556b0861d982b71043fbc0df024644320c817ad796391c442d0c2f15a77223"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "index": "pypi",
 | 
					            "index": "pypi",
 | 
				
			||||||
            "version": "==1.16.37"
 | 
					            "version": "==1.16.39"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "botocore": {
 | 
					        "botocore": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:5605c250f6f7c72ca50e45eab6186dfda03cb84296ca5b05f7416defcd3fcbc5",
 | 
					                "sha256:449e4196160ff58ee27d2a626a7ce4cfff2640fe1806d7a279e73a30ad286347",
 | 
				
			||||||
                "sha256:67bf1285455d79336ce7061da1768206b78f7a0efc13c8b4033fd348a74e7491"
 | 
					                "sha256:e0d0386098a072abd7b6c087e6149d997377c969a823ebe01b3f5bfabe9bfac0"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "version": "==1.19.37"
 | 
					            "version": "==1.19.39"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "cachetools": {
 | 
					        "cachetools": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
@ -96,11 +96,11 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "celery": {
 | 
					        "celery": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:45bb7909061862305cefec94289fabc1b89ac004680f4dc7d9dea642a2507e53",
 | 
					                "sha256:5e8d364e058554e83bbb116e8377d90c79be254785f357cb2cec026e79febe13",
 | 
				
			||||||
                "sha256:533f3635065b7ed362ffc04228635b4c82d53a9ab812118ccdedb5eae281fb97"
 | 
					                "sha256:f4efebe6f8629b0da2b8e529424de376494f5b7a743c321c8a2ddc2b1414921c"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "index": "pypi",
 | 
					            "index": "pypi",
 | 
				
			||||||
            "version": "==5.0.4"
 | 
					            "version": "==5.0.5"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "certifi": {
 | 
					        "certifi": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
@ -168,10 +168,10 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "chardet": {
 | 
					        "chardet": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
 | 
					                "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
 | 
				
			||||||
                "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
 | 
					                "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "version": "==3.0.4"
 | 
					            "version": "==4.0.0"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "click": {
 | 
					        "click": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
@ -343,11 +343,11 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "django-storages": {
 | 
					        "django-storages": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:12de8fb2605b9b57bfaf54b075280d7cbb3b3ee1ca4bc9b9add147af87fe3a2c",
 | 
					                "sha256:056ec3e9e2b0c6f363913976072ffba2923e79e4859578047da139ba1637497e",
 | 
				
			||||||
                "sha256:652275ab7844538c462b62810276c0244866f345878256a9e0e86f5b1283ae18"
 | 
					                "sha256:7af56611c62a1c174aab4e862efb7fdd98296dccf76f42135f5b6851fc313c97"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "index": "pypi",
 | 
					            "index": "pypi",
 | 
				
			||||||
            "version": "==1.10.1"
 | 
					            "version": "==1.11"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "djangorestframework": {
 | 
					        "djangorestframework": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
@ -950,10 +950,10 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "requests": {
 | 
					        "requests": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
                "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8",
 | 
					                "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
 | 
				
			||||||
                "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"
 | 
					                "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "version": "==2.25.0"
 | 
					            "version": "==2.25.1"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "requests-oauthlib": {
 | 
					        "requests-oauthlib": {
 | 
				
			||||||
            "hashes": [
 | 
					            "hashes": [
 | 
				
			||||||
 | 
				
			|||||||
@ -1,2 +1,2 @@
 | 
				
			|||||||
"""authentik"""
 | 
					"""authentik"""
 | 
				
			||||||
__version__ = "0.13.1-stable"
 | 
					__version__ = "0.13.3-stable"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
"""authentik administration overview"""
 | 
					"""authentik administration metrics"""
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
from collections import Counter
 | 
					from collections import Counter
 | 
				
			||||||
from datetime import timedelta
 | 
					from datetime import timedelta
 | 
				
			||||||
@ -47,7 +47,7 @@ def get_events_per_1h(**filter_kwargs) -> List[Dict[str, int]]:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AdministrationMetricsSerializer(Serializer):
 | 
					class AdministrationMetricsSerializer(Serializer):
 | 
				
			||||||
    """Overview View"""
 | 
					    """Login Metrics per 1h"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    logins_per_1h = SerializerMethodField()
 | 
					    logins_per_1h = SerializerMethodField()
 | 
				
			||||||
    logins_failed_per_1h = SerializerMethodField()
 | 
					    logins_failed_per_1h = SerializerMethodField()
 | 
				
			||||||
@ -68,12 +68,12 @@ class AdministrationMetricsSerializer(Serializer):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AdministrationMetricsViewSet(ViewSet):
 | 
					class AdministrationMetricsViewSet(ViewSet):
 | 
				
			||||||
    """Return single instance of AdministrationMetricsSerializer"""
 | 
					    """Login Metrics per 1h"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    permission_classes = [IsAdminUser]
 | 
					    permission_classes = [IsAdminUser]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @swagger_auto_schema(responses={200: AdministrationMetricsSerializer(many=True)})
 | 
					    @swagger_auto_schema(responses={200: AdministrationMetricsSerializer(many=True)})
 | 
				
			||||||
    def list(self, request: Request) -> Response:
 | 
					    def list(self, request: Request) -> Response:
 | 
				
			||||||
        """Return single instance of AdministrationMetricsSerializer"""
 | 
					        """Login Metrics per 1h"""
 | 
				
			||||||
        serializer = AdministrationMetricsSerializer(True)
 | 
					        serializer = AdministrationMetricsSerializer(True)
 | 
				
			||||||
        return Response(serializer.data)
 | 
					        return Response(serializer.data)
 | 
				
			||||||
@ -1,79 +0,0 @@
 | 
				
			|||||||
"""authentik administration overview"""
 | 
					 | 
				
			||||||
from django.core.cache import cache
 | 
					 | 
				
			||||||
from drf_yasg2.utils import swagger_auto_schema
 | 
					 | 
				
			||||||
from rest_framework.fields import SerializerMethodField
 | 
					 | 
				
			||||||
from rest_framework.permissions import IsAdminUser
 | 
					 | 
				
			||||||
from rest_framework.request import Request
 | 
					 | 
				
			||||||
from rest_framework.response import Response
 | 
					 | 
				
			||||||
from rest_framework.serializers import Serializer
 | 
					 | 
				
			||||||
from rest_framework.viewsets import ViewSet
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from authentik import __version__
 | 
					 | 
				
			||||||
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
 | 
					 | 
				
			||||||
from authentik.core.models import Provider
 | 
					 | 
				
			||||||
from authentik.policies.models import Policy
 | 
					 | 
				
			||||||
from authentik.root.celery import CELERY_APP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class AdministrationOverviewSerializer(Serializer):
 | 
					 | 
				
			||||||
    """Overview View"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    version = SerializerMethodField()
 | 
					 | 
				
			||||||
    version_latest = SerializerMethodField()
 | 
					 | 
				
			||||||
    worker_count = SerializerMethodField()
 | 
					 | 
				
			||||||
    providers_without_application = SerializerMethodField()
 | 
					 | 
				
			||||||
    policies_without_binding = SerializerMethodField()
 | 
					 | 
				
			||||||
    cached_policies = SerializerMethodField()
 | 
					 | 
				
			||||||
    cached_flows = SerializerMethodField()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_version(self, _) -> str:
 | 
					 | 
				
			||||||
        """Get current version"""
 | 
					 | 
				
			||||||
        return __version__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_version_latest(self, _) -> str:
 | 
					 | 
				
			||||||
        """Get latest version from cache"""
 | 
					 | 
				
			||||||
        version_in_cache = cache.get(VERSION_CACHE_KEY)
 | 
					 | 
				
			||||||
        if not version_in_cache:
 | 
					 | 
				
			||||||
            update_latest_version.delay()
 | 
					 | 
				
			||||||
            return __version__
 | 
					 | 
				
			||||||
        return version_in_cache
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_worker_count(self, _) -> int:
 | 
					 | 
				
			||||||
        """Ping workers"""
 | 
					 | 
				
			||||||
        return len(CELERY_APP.control.ping(timeout=0.5))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_providers_without_application(self, _) -> int:
 | 
					 | 
				
			||||||
        """Count of providers without application"""
 | 
					 | 
				
			||||||
        return len(Provider.objects.filter(application=None))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_policies_without_binding(self, _) -> int:
 | 
					 | 
				
			||||||
        """Count of policies not bound or use in prompt stages"""
 | 
					 | 
				
			||||||
        return len(
 | 
					 | 
				
			||||||
            Policy.objects.filter(bindings__isnull=True, promptstage__isnull=True)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_cached_policies(self, _) -> int:
 | 
					 | 
				
			||||||
        """Get cached policy count"""
 | 
					 | 
				
			||||||
        return len(cache.keys("policy_*"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_cached_flows(self, _) -> int:
 | 
					 | 
				
			||||||
        """Get cached flow count"""
 | 
					 | 
				
			||||||
        return len(cache.keys("flow_*"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def create(self, request: Request) -> Response:
 | 
					 | 
				
			||||||
        raise NotImplementedError
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def update(self, request: Request) -> Response:
 | 
					 | 
				
			||||||
        raise NotImplementedError
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class AdministrationOverviewViewSet(ViewSet):
 | 
					 | 
				
			||||||
    """Return single instance of AdministrationOverviewSerializer"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    permission_classes = [IsAdminUser]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @swagger_auto_schema(responses={200: AdministrationOverviewSerializer(many=True)})
 | 
					 | 
				
			||||||
    def list(self, request: Request) -> Response:
 | 
					 | 
				
			||||||
        """Return single instance of AdministrationOverviewSerializer"""
 | 
					 | 
				
			||||||
        serializer = AdministrationOverviewSerializer(True)
 | 
					 | 
				
			||||||
        return Response(serializer.data)
 | 
					 | 
				
			||||||
@ -66,7 +66,7 @@ class TaskViewSet(ViewSet):
 | 
				
			|||||||
                    "successful": True,
 | 
					                    "successful": True,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        except ImportError:
 | 
					        except ImportError:  # pragma: no cover
 | 
				
			||||||
            # if we get an import error, the module path has probably changed
 | 
					            # if we get an import error, the module path has probably changed
 | 
				
			||||||
            task.delete()
 | 
					            task.delete()
 | 
				
			||||||
            return Response({"successful": False})
 | 
					            return Response({"successful": False})
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										60
									
								
								authentik/admin/api/version.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								authentik/admin/api/version.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					"""authentik administration overview"""
 | 
				
			||||||
 | 
					from django.core.cache import cache
 | 
				
			||||||
 | 
					from drf_yasg2.utils import swagger_auto_schema
 | 
				
			||||||
 | 
					from packaging.version import parse
 | 
				
			||||||
 | 
					from rest_framework.fields import SerializerMethodField
 | 
				
			||||||
 | 
					from rest_framework.mixins import ListModelMixin
 | 
				
			||||||
 | 
					from rest_framework.permissions import IsAdminUser
 | 
				
			||||||
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.response import Response
 | 
				
			||||||
 | 
					from rest_framework.serializers import Serializer
 | 
				
			||||||
 | 
					from rest_framework.viewsets import GenericViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik import __version__
 | 
				
			||||||
 | 
					from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VersionSerializer(Serializer):
 | 
				
			||||||
 | 
					    """Get running and latest version."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    version_current = SerializerMethodField()
 | 
				
			||||||
 | 
					    version_latest = SerializerMethodField()
 | 
				
			||||||
 | 
					    outdated = SerializerMethodField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_version_current(self, _) -> str:
 | 
				
			||||||
 | 
					        """Get current version"""
 | 
				
			||||||
 | 
					        return __version__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_version_latest(self, _) -> str:
 | 
				
			||||||
 | 
					        """Get latest version from cache"""
 | 
				
			||||||
 | 
					        version_in_cache = cache.get(VERSION_CACHE_KEY)
 | 
				
			||||||
 | 
					        if not version_in_cache:  # pragma: no cover
 | 
				
			||||||
 | 
					            update_latest_version.delay()
 | 
				
			||||||
 | 
					            return __version__
 | 
				
			||||||
 | 
					        return version_in_cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_outdated(self, instance) -> bool:
 | 
				
			||||||
 | 
					        """Check if we're running the latest version"""
 | 
				
			||||||
 | 
					        return parse(self.get_version_current(instance)) < parse(
 | 
				
			||||||
 | 
					            self.get_version_latest(instance)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create(self, request: Request) -> Response:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, request: Request) -> Response:
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VersionViewSet(ListModelMixin, GenericViewSet):
 | 
				
			||||||
 | 
					    """Get running and latest version."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    permission_classes = [IsAdminUser]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_queryset(self):
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @swagger_auto_schema(responses={200: VersionSerializer(many=True)})
 | 
				
			||||||
 | 
					    def list(self, request: Request) -> Response:
 | 
				
			||||||
 | 
					        """Get running and latest version."""
 | 
				
			||||||
 | 
					        return Response(VersionSerializer(True).data)
 | 
				
			||||||
							
								
								
									
										25
									
								
								authentik/admin/api/workers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								authentik/admin/api/workers.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					"""authentik administration overview"""
 | 
				
			||||||
 | 
					from rest_framework.mixins import ListModelMixin
 | 
				
			||||||
 | 
					from rest_framework.permissions import IsAdminUser
 | 
				
			||||||
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.response import Response
 | 
				
			||||||
 | 
					from rest_framework.serializers import Serializer
 | 
				
			||||||
 | 
					from rest_framework.viewsets import GenericViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.root.celery import CELERY_APP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WorkerViewSet(ListModelMixin, GenericViewSet):
 | 
				
			||||||
 | 
					    """Get currently connected worker count."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializer_class = Serializer
 | 
				
			||||||
 | 
					    permission_classes = [IsAdminUser]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_queryset(self):
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def list(self, request: Request) -> Response:
 | 
				
			||||||
 | 
					        """Get currently connected worker count."""
 | 
				
			||||||
 | 
					        return Response(
 | 
				
			||||||
 | 
					            {"pagination": {"count": len(CELERY_APP.control.ping(timeout=0.5))}}
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
@ -1,230 +0,0 @@
 | 
				
			|||||||
{% extends "administration/base.html" %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% load i18n %}
 | 
					 | 
				
			||||||
{% load static %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% block content %}
 | 
					 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-light">
 | 
					 | 
				
			||||||
    <div class="pf-c-content">
 | 
					 | 
				
			||||||
        <h1>{% trans 'System Overview' %}</h1>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
<section class="pf-c-page__main-section">
 | 
					 | 
				
			||||||
    <div class="pf-l-gallery pf-m-gutter">
 | 
					 | 
				
			||||||
        <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" style="grid-column-end: span 3;grid-row-end: span 2;">
 | 
					 | 
				
			||||||
            <div class="pf-c-card__header">
 | 
					 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					 | 
				
			||||||
                    <i class="pf-icon pf-icon-server"></i> {% trans 'Logins over the last 24 hours' %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="pf-c-card__body">
 | 
					 | 
				
			||||||
                <ak-admin-logins-chart url="{% url 'authentik_api:admin_metrics-list' %}"></ak-admin-logins-chart>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" style="grid-column-end: span 2;grid-row-end: span 3;">
 | 
					 | 
				
			||||||
            <div class="pf-c-card__header">
 | 
					 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					 | 
				
			||||||
                    <i class="pf-icon pf-icon-server"></i> {% trans 'Apps with most usage' %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="pf-c-card__body">
 | 
					 | 
				
			||||||
                <table class="pf-c-table pf-m-compact" role="grid">
 | 
					 | 
				
			||||||
                    <thead>
 | 
					 | 
				
			||||||
                        <tr role="row">
 | 
					 | 
				
			||||||
                            <th role="columnheader" scope="col">{% trans 'Application' %}</th>
 | 
					 | 
				
			||||||
                            <th role="columnheader" scope="col">{% trans 'Logins' %}</th>
 | 
					 | 
				
			||||||
                            <th role="columnheader" scope="col"></th>
 | 
					 | 
				
			||||||
                        </tr>
 | 
					 | 
				
			||||||
                    </thead>
 | 
					 | 
				
			||||||
                    <tbody role="rowgroup">
 | 
					 | 
				
			||||||
                        {% for app in most_used_applications %}
 | 
					 | 
				
			||||||
                        <tr role="row">
 | 
					 | 
				
			||||||
                            <td role="cell">
 | 
					 | 
				
			||||||
                                {{ app.application.name }}
 | 
					 | 
				
			||||||
                            </td>
 | 
					 | 
				
			||||||
                            <td role="cell">
 | 
					 | 
				
			||||||
                                {{ app.total_logins }}
 | 
					 | 
				
			||||||
                            </td>
 | 
					 | 
				
			||||||
                            <td role="cell">
 | 
					 | 
				
			||||||
                                <progress value="{{ app.total_logins }}" max="{{ most_used_applications.0.total_logins }}"></progress>
 | 
					 | 
				
			||||||
                            </td>
 | 
					 | 
				
			||||||
                        </tr>
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                    </tbody>
 | 
					 | 
				
			||||||
                </table>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
 | 
					 | 
				
			||||||
            <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
 | 
					 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					 | 
				
			||||||
                    <i class="pf-icon pf-icon-plugged"></i> {% trans 'Providers' %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <a href="{% url 'authentik_admin:providers' %}">
 | 
					 | 
				
			||||||
                    <i class="fa fa-external-link-alt"> </i>
 | 
					 | 
				
			||||||
                </a>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="pf-c-card__body">
 | 
					 | 
				
			||||||
                {% if providers_without_application.exists %}
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    <i class="fa fa-exclamation-triangle"></i> {{ provider_count }}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                <p>{% trans 'Warning: At least one Provider has no application assigned.' %}</p>
 | 
					 | 
				
			||||||
                {% else %}
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    <i class="fa fa-check-circle"></i> {{ provider_count }}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                {% endif %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
 | 
					 | 
				
			||||||
            <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
 | 
					 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					 | 
				
			||||||
                    <i class="pf-icon pf-icon-infrastructure"></i> {% trans 'Policies' %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <a href="{% url 'authentik_admin:policies' %}">
 | 
					 | 
				
			||||||
                    <i class="fa fa-external-link-alt"> </i>
 | 
					 | 
				
			||||||
                </a>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="pf-c-card__body">
 | 
					 | 
				
			||||||
                {% if policies_without_binding %}
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    <i class="fa fa-exclamation-triangle"></i> {{ policy_count }}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                <p>{% trans 'Policies without binding exist.' %}</p>
 | 
					 | 
				
			||||||
                {% else %}
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    <i class="fa fa-check-circle"></i> {{ policy_count }}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                {% endif %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
 | 
					 | 
				
			||||||
            <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
 | 
					 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					 | 
				
			||||||
                    <i class="pf-icon pf-icon-user"></i> {% trans 'Users' %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <a href="{% url 'authentik_admin:users' %}">
 | 
					 | 
				
			||||||
                    <i class="fa fa-external-link-alt"> </i>
 | 
					 | 
				
			||||||
                </a>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="pf-c-card__body">
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    <i class="fa fa-check-circle"></i> {{ user_count }}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
 | 
					 | 
				
			||||||
            <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
 | 
					 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					 | 
				
			||||||
                    <i class="pf-icon pf-icon-bundle"></i> {% trans 'Version' %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <a href="https://github.com/BeryJu/authentik/releases" target="_blank">
 | 
					 | 
				
			||||||
                    <i class="fa fa-external-link-alt"> </i>
 | 
					 | 
				
			||||||
                </a>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="pf-c-card__body">
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    {% if version >= version_latest %}
 | 
					 | 
				
			||||||
                    <i class="fa fa-check-circle"></i> {{ version }}
 | 
					 | 
				
			||||||
                    {% else %}
 | 
					 | 
				
			||||||
                    <i class="fa fa-exclamation-triangle"></i> {{ version }}
 | 
					 | 
				
			||||||
                    {% endif %}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                {% if version >= version_latest %}
 | 
					 | 
				
			||||||
                    {% blocktrans %}
 | 
					 | 
				
			||||||
                    Up-to-date!
 | 
					 | 
				
			||||||
                    {% endblocktrans %}
 | 
					 | 
				
			||||||
                {% else %}
 | 
					 | 
				
			||||||
                    {% blocktrans with latest=version_latest %}
 | 
					 | 
				
			||||||
                    {{ latest }} is available!
 | 
					 | 
				
			||||||
                    {% endblocktrans %}
 | 
					 | 
				
			||||||
                {% endif %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
 | 
					 | 
				
			||||||
            <div class="pf-c-card__header">
 | 
					 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					 | 
				
			||||||
                    <i class="pf-icon pf-icon-server"></i> {% trans 'Workers' %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <fetch-fill-slot class="pf-c-card__body" url="{% url 'authentik_api:admin_overview-list' %}" key="worker_count">
 | 
					 | 
				
			||||||
                <div slot="value < 1">
 | 
					 | 
				
			||||||
                    <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                        <i class="fa fa-exclamation-triangle"></i> <span data-value></span>
 | 
					 | 
				
			||||||
                    </p>
 | 
					 | 
				
			||||||
                    <p>{% trans 'No workers connected.' %}</p>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <div slot="value >= 1">
 | 
					 | 
				
			||||||
                    <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                        <i class="fa fa-check-circle"></i> <span data-value></span>
 | 
					 | 
				
			||||||
                    </p>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <div>
 | 
					 | 
				
			||||||
                    <span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
 | 
					 | 
				
			||||||
                        <span class="pf-c-spinner__clipper"></span>
 | 
					 | 
				
			||||||
                        <span class="pf-c-spinner__lead-ball"></span>
 | 
					 | 
				
			||||||
                        <span class="pf-c-spinner__tail-ball"></span>
 | 
					 | 
				
			||||||
                    </span>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </fetch-fill-slot>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
 | 
					 | 
				
			||||||
            <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
 | 
					 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					 | 
				
			||||||
                    <i class="pf-icon pf-icon-server"></i> {% trans 'Cached Policies' %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <ak-modal-button href="{% url 'authentik_admin:overview-clear-policy-cache' %}">
 | 
					 | 
				
			||||||
                    <a slot="trigger">
 | 
					 | 
				
			||||||
                        <i class="fa fa-trash"> </i>
 | 
					 | 
				
			||||||
                    </a>
 | 
					 | 
				
			||||||
                    <div slot="modal"></div>
 | 
					 | 
				
			||||||
                </ak-modal-button>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="pf-c-card__body">
 | 
					 | 
				
			||||||
                {% if cached_policies < 1 %}
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    <i class="fa fa-exclamation-triangle"></i> {{ cached_policies }}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                <p>{% trans 'No policies cached. Users may experience slow response times.' %}</p>
 | 
					 | 
				
			||||||
                {% else %}
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    <i class="fa fa-check-circle"></i> {{ cached_policies }}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                {% endif %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact">
 | 
					 | 
				
			||||||
            <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
 | 
					 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					 | 
				
			||||||
                    <i class="pf-icon pf-icon-server"></i> {% trans 'Cached Flows' %}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <ak-modal-button href="{% url 'authentik_admin:overview-clear-flow-cache' %}">
 | 
					 | 
				
			||||||
                    <a slot="trigger">
 | 
					 | 
				
			||||||
                        <i class="fa fa-trash"> </i>
 | 
					 | 
				
			||||||
                    </a>
 | 
					 | 
				
			||||||
                    <div slot="modal"></div>
 | 
					 | 
				
			||||||
                </ak-modal-button>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="pf-c-card__body">
 | 
					 | 
				
			||||||
                {% if cached_flows < 1 %}
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    <span class="fa fa-exclamation-triangle"></span> {{ cached_flows }}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                <p>{% trans 'No flows cached.' %}</p>
 | 
					 | 
				
			||||||
                {% else %}
 | 
					 | 
				
			||||||
                <p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                    <i class="fa fa-check-circle"></i> {{ cached_flows }}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                {% endif %}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					 | 
				
			||||||
@ -6,6 +6,7 @@ from django.test import TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from authentik import __version__
 | 
					from authentik import __version__
 | 
				
			||||||
from authentik.core.models import Group, User
 | 
					from authentik.core.models import Group, User
 | 
				
			||||||
 | 
					from authentik.core.tasks import clean_expired_models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestAdminAPI(TestCase):
 | 
					class TestAdminAPI(TestCase):
 | 
				
			||||||
@ -19,19 +20,54 @@ class TestAdminAPI(TestCase):
 | 
				
			|||||||
        self.group.save()
 | 
					        self.group.save()
 | 
				
			||||||
        self.client.force_login(self.user)
 | 
					        self.client.force_login(self.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_overview(self):
 | 
					    def test_tasks(self):
 | 
				
			||||||
        """Test Overview API"""
 | 
					        """Test Task API"""
 | 
				
			||||||
        response = self.client.get(reverse("authentik_api:admin_overview-list"))
 | 
					        clean_expired_models.delay()
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("authentik_api:admin_system_tasks-list"))
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
        body = loads(response.content)
 | 
					        body = loads(response.content)
 | 
				
			||||||
        self.assertEqual(body["version"], __version__)
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            any([task["task_name"] == "clean_expired_models" for task in body])
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_tasks_retry(self):
 | 
				
			||||||
 | 
					        """Test Task API (retry)"""
 | 
				
			||||||
 | 
					        clean_expired_models.delay()
 | 
				
			||||||
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            reverse(
 | 
				
			||||||
 | 
					                "authentik_api:admin_system_tasks-retry",
 | 
				
			||||||
 | 
					                kwargs={"pk": "clean_expired_models"},
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					        body = loads(response.content)
 | 
				
			||||||
 | 
					        self.assertTrue(body["successful"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_tasks_retry_404(self):
 | 
				
			||||||
 | 
					        """Test Task API (retry, 404)"""
 | 
				
			||||||
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            reverse(
 | 
				
			||||||
 | 
					                "authentik_api:admin_system_tasks-retry",
 | 
				
			||||||
 | 
					                kwargs={"pk": "qwerqewrqrqewrqewr"},
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_version(self):
 | 
				
			||||||
 | 
					        """Test Version API"""
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("authentik_api:admin_version-list"))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					        body = loads(response.content)
 | 
				
			||||||
 | 
					        self.assertEqual(body["version_current"], __version__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_workers(self):
 | 
				
			||||||
 | 
					        """Test Workers API"""
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("authentik_api:admin_workers-list"))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					        body = loads(response.content)
 | 
				
			||||||
 | 
					        self.assertEqual(body["pagination"]["count"], 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_metrics(self):
 | 
					    def test_metrics(self):
 | 
				
			||||||
        """Test metrics API"""
 | 
					        """Test metrics API"""
 | 
				
			||||||
        response = self.client.get(reverse("authentik_api:admin_metrics-list"))
 | 
					        response = self.client.get(reverse("authentik_api:admin_metrics-list"))
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_tasks(self):
 | 
					 | 
				
			||||||
        """Test tasks metrics API"""
 | 
					 | 
				
			||||||
        response = self.client.get(reverse("authentik_api:admin_system_tasks-list"))
 | 
					 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,6 @@ urlpatterns = [
 | 
				
			|||||||
        overview.PolicyCacheClearView.as_view(),
 | 
					        overview.PolicyCacheClearView.as_view(),
 | 
				
			||||||
        name="overview-clear-policy-cache",
 | 
					        name="overview-clear-policy-cache",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path("overview/", overview.AdministrationOverviewView.as_view(), name="overview"),
 | 
					 | 
				
			||||||
    # Applications
 | 
					    # Applications
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "applications/", applications.ApplicationListView.as_view(), name="applications"
 | 
					        "applications/", applications.ApplicationListView.as_view(), name="applications"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,65 +1,25 @@
 | 
				
			|||||||
"""authentik administration overview"""
 | 
					"""authentik administration overview"""
 | 
				
			||||||
from typing import Union
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.conf import settings
 | 
					 | 
				
			||||||
from django.contrib.messages.views import SuccessMessageMixin
 | 
					from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			||||||
from django.core.cache import cache
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.http.request import HttpRequest
 | 
					from django.http.request import HttpRequest
 | 
				
			||||||
from django.http.response import HttpResponse
 | 
					from django.http.response import HttpResponse
 | 
				
			||||||
from django.urls import reverse_lazy
 | 
					 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from django.views.generic import FormView, TemplateView
 | 
					from django.views.generic import FormView
 | 
				
			||||||
from packaging.version import LegacyVersion, Version, parse
 | 
					 | 
				
			||||||
from structlog import get_logger
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik import __version__
 | 
					 | 
				
			||||||
from authentik.admin.forms.overview import FlowCacheClearForm, PolicyCacheClearForm
 | 
					from authentik.admin.forms.overview import FlowCacheClearForm, PolicyCacheClearForm
 | 
				
			||||||
from authentik.admin.mixins import AdminRequiredMixin
 | 
					from authentik.admin.mixins import AdminRequiredMixin
 | 
				
			||||||
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
 | 
					 | 
				
			||||||
from authentik.core.models import Provider, User
 | 
					 | 
				
			||||||
from authentik.policies.models import Policy
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = get_logger()
 | 
					LOGGER = get_logger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
 | 
					 | 
				
			||||||
    """Overview View"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    template_name = "administration/overview.html"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_latest_version(self) -> Union[LegacyVersion, Version]:
 | 
					 | 
				
			||||||
        """Get latest version from cache"""
 | 
					 | 
				
			||||||
        version_in_cache = cache.get(VERSION_CACHE_KEY)
 | 
					 | 
				
			||||||
        if not version_in_cache:
 | 
					 | 
				
			||||||
            if not settings.DEBUG:
 | 
					 | 
				
			||||||
                update_latest_version.delay()
 | 
					 | 
				
			||||||
            return parse(__version__)
 | 
					 | 
				
			||||||
        return parse(version_in_cache)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					 | 
				
			||||||
        kwargs["policy_count"] = len(Policy.objects.all())
 | 
					 | 
				
			||||||
        kwargs["user_count"] = len(User.objects.all()) - 1  # Remove anonymous user
 | 
					 | 
				
			||||||
        kwargs["provider_count"] = len(Provider.objects.all())
 | 
					 | 
				
			||||||
        kwargs["version"] = parse(__version__)
 | 
					 | 
				
			||||||
        kwargs["version_latest"] = self.get_latest_version()
 | 
					 | 
				
			||||||
        kwargs["providers_without_application"] = Provider.objects.filter(
 | 
					 | 
				
			||||||
            application=None
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        kwargs["policies_without_binding"] = len(
 | 
					 | 
				
			||||||
            Policy.objects.filter(bindings__isnull=True, promptstage__isnull=True)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        kwargs["cached_policies"] = len(cache.keys("policy_*"))
 | 
					 | 
				
			||||||
        kwargs["cached_flows"] = len(cache.keys("flow_*"))
 | 
					 | 
				
			||||||
        return super().get_context_data(**kwargs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
 | 
					class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
 | 
				
			||||||
    """View to clear Policy cache"""
 | 
					    """View to clear Policy cache"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    form_class = PolicyCacheClearForm
 | 
					    form_class = PolicyCacheClearForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_name = "generic/form_non_model.html"
 | 
					    template_name = "generic/form_non_model.html"
 | 
				
			||||||
    success_url = reverse_lazy("authentik_admin:overview")
 | 
					    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:
 | 
				
			||||||
@ -75,7 +35,7 @@ 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 = reverse_lazy("authentik_admin:overview")
 | 
					    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:
 | 
				
			||||||
 | 
				
			|||||||
@ -5,9 +5,10 @@ from drf_yasg2.views import get_schema_view
 | 
				
			|||||||
from rest_framework import routers
 | 
					from rest_framework import routers
 | 
				
			||||||
from rest_framework.permissions import AllowAny
 | 
					from rest_framework.permissions import AllowAny
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.api.overview import AdministrationOverviewViewSet
 | 
					from authentik.admin.api.metrics import AdministrationMetricsViewSet
 | 
				
			||||||
from authentik.admin.api.overview_metrics import AdministrationMetricsViewSet
 | 
					 | 
				
			||||||
from authentik.admin.api.tasks import TaskViewSet
 | 
					from authentik.admin.api.tasks import TaskViewSet
 | 
				
			||||||
 | 
					from authentik.admin.api.version import VersionViewSet
 | 
				
			||||||
 | 
					from authentik.admin.api.workers import WorkerViewSet
 | 
				
			||||||
from authentik.api.v2.config import ConfigsViewSet
 | 
					from authentik.api.v2.config import ConfigsViewSet
 | 
				
			||||||
from authentik.api.v2.messages import MessagesViewSet
 | 
					from authentik.api.v2.messages import MessagesViewSet
 | 
				
			||||||
from authentik.audit.api import EventViewSet
 | 
					from authentik.audit.api import EventViewSet
 | 
				
			||||||
@ -19,13 +20,22 @@ from authentik.core.api.sources import SourceViewSet
 | 
				
			|||||||
from authentik.core.api.tokens import TokenViewSet
 | 
					from authentik.core.api.tokens import TokenViewSet
 | 
				
			||||||
from authentik.core.api.users import UserViewSet
 | 
					from authentik.core.api.users import UserViewSet
 | 
				
			||||||
from authentik.crypto.api import CertificateKeyPairViewSet
 | 
					from authentik.crypto.api import CertificateKeyPairViewSet
 | 
				
			||||||
from authentik.flows.api import FlowStageBindingViewSet, FlowViewSet, StageViewSet
 | 
					from authentik.flows.api import (
 | 
				
			||||||
 | 
					    FlowCacheViewSet,
 | 
				
			||||||
 | 
					    FlowStageBindingViewSet,
 | 
				
			||||||
 | 
					    FlowViewSet,
 | 
				
			||||||
 | 
					    StageViewSet,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from authentik.outposts.api import (
 | 
					from authentik.outposts.api import (
 | 
				
			||||||
    DockerServiceConnectionViewSet,
 | 
					    DockerServiceConnectionViewSet,
 | 
				
			||||||
    KubernetesServiceConnectionViewSet,
 | 
					    KubernetesServiceConnectionViewSet,
 | 
				
			||||||
    OutpostViewSet,
 | 
					    OutpostViewSet,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.policies.api import PolicyBindingViewSet, PolicyViewSet
 | 
					from authentik.policies.api import (
 | 
				
			||||||
 | 
					    PolicyBindingViewSet,
 | 
				
			||||||
 | 
					    PolicyCacheViewSet,
 | 
				
			||||||
 | 
					    PolicyViewSet,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from authentik.policies.dummy.api import DummyPolicyViewSet
 | 
					from authentik.policies.dummy.api import DummyPolicyViewSet
 | 
				
			||||||
from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet
 | 
					from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet
 | 
				
			||||||
from authentik.policies.expression.api import ExpressionPolicyViewSet
 | 
					from authentik.policies.expression.api import ExpressionPolicyViewSet
 | 
				
			||||||
@ -63,9 +73,8 @@ router = routers.DefaultRouter()
 | 
				
			|||||||
router.register("root/messages", MessagesViewSet, basename="messages")
 | 
					router.register("root/messages", MessagesViewSet, basename="messages")
 | 
				
			||||||
router.register("root/config", ConfigsViewSet, basename="configs")
 | 
					router.register("root/config", ConfigsViewSet, basename="configs")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.register(
 | 
					router.register("admin/version", VersionViewSet, basename="admin_version")
 | 
				
			||||||
    "admin/overview", AdministrationOverviewViewSet, basename="admin_overview"
 | 
					router.register("admin/workers", WorkerViewSet, basename="admin_workers")
 | 
				
			||||||
)
 | 
					 | 
				
			||||||
router.register("admin/metrics", AdministrationMetricsViewSet, basename="admin_metrics")
 | 
					router.register("admin/metrics", AdministrationMetricsViewSet, basename="admin_metrics")
 | 
				
			||||||
router.register("admin/system_tasks", TaskViewSet, basename="admin_system_tasks")
 | 
					router.register("admin/system_tasks", TaskViewSet, basename="admin_system_tasks")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -82,6 +91,7 @@ router.register(
 | 
				
			|||||||
router.register("outposts/proxy", ProxyOutpostConfigViewSet)
 | 
					router.register("outposts/proxy", ProxyOutpostConfigViewSet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.register("flows/instances", FlowViewSet)
 | 
					router.register("flows/instances", FlowViewSet)
 | 
				
			||||||
 | 
					router.register("flows/cached", FlowCacheViewSet, basename="flows_cache")
 | 
				
			||||||
router.register("flows/bindings", FlowStageBindingViewSet)
 | 
					router.register("flows/bindings", FlowStageBindingViewSet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.register("crypto/certificatekeypairs", CertificateKeyPairViewSet)
 | 
					router.register("crypto/certificatekeypairs", CertificateKeyPairViewSet)
 | 
				
			||||||
@ -94,6 +104,7 @@ router.register("sources/saml", SAMLSourceViewSet)
 | 
				
			|||||||
router.register("sources/oauth", OAuthSourceViewSet)
 | 
					router.register("sources/oauth", OAuthSourceViewSet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.register("policies/all", PolicyViewSet)
 | 
					router.register("policies/all", PolicyViewSet)
 | 
				
			||||||
 | 
					router.register("policies/cached", PolicyCacheViewSet, basename="policies_cache")
 | 
				
			||||||
router.register("policies/bindings", PolicyBindingViewSet)
 | 
					router.register("policies/bindings", PolicyBindingViewSet)
 | 
				
			||||||
router.register("policies/expression", ExpressionPolicyViewSet)
 | 
					router.register("policies/expression", ExpressionPolicyViewSet)
 | 
				
			||||||
router.register("policies/group_membership", GroupMembershipPolicyViewSet)
 | 
					router.register("policies/group_membership", GroupMembershipPolicyViewSet)
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ from rest_framework.serializers import ModelSerializer
 | 
				
			|||||||
from rest_framework.viewsets import ModelViewSet
 | 
					from rest_framework.viewsets import ModelViewSet
 | 
				
			||||||
from rest_framework_guardian.filters import ObjectPermissionsFilter
 | 
					from rest_framework_guardian.filters import ObjectPermissionsFilter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.api.overview_metrics import get_events_per_1h
 | 
					from authentik.admin.api.metrics import get_events_per_1h
 | 
				
			||||||
from authentik.audit.models import EventAction
 | 
					from authentik.audit.models import EventAction
 | 
				
			||||||
from authentik.core.models import Application
 | 
					from authentik.core.models import Application
 | 
				
			||||||
from authentik.policies.engine import PolicyEngine
 | 
					from authentik.policies.engine import PolicyEngine
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
"""Provider API Views"""
 | 
					"""Provider API Views"""
 | 
				
			||||||
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 ModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import Provider
 | 
					from authentik.core.models import Provider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -14,17 +14,33 @@ class ProviderSerializer(ModelSerializer):
 | 
				
			|||||||
        """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("provider", "")
 | 
					        return obj._meta.object_name.lower().replace("provider", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_representation(self, instance: Provider):
 | 
				
			||||||
 | 
					        # pyright: reportGeneralTypeIssues=false
 | 
				
			||||||
 | 
					        if instance.__class__ == Provider:
 | 
				
			||||||
 | 
					            return super().to_representation(instance)
 | 
				
			||||||
 | 
					        return instance.serializer(instance=instance).data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model = Provider
 | 
					        model = Provider
 | 
				
			||||||
        fields = ["pk", "name", "authorization_flow", "property_mappings", "__type__"]
 | 
					        fields = [
 | 
				
			||||||
 | 
					            "pk",
 | 
				
			||||||
 | 
					            "name",
 | 
				
			||||||
 | 
					            "application",
 | 
				
			||||||
 | 
					            "authorization_flow",
 | 
				
			||||||
 | 
					            "property_mappings",
 | 
				
			||||||
 | 
					            "__type__",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProviderViewSet(ReadOnlyModelViewSet):
 | 
					class ProviderViewSet(ModelViewSet):
 | 
				
			||||||
    """Provider Viewset"""
 | 
					    """Provider Viewset"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    queryset = Provider.objects.all()
 | 
					    queryset = Provider.objects.all()
 | 
				
			||||||
    serializer_class = ProviderSerializer
 | 
					    serializer_class = ProviderSerializer
 | 
				
			||||||
 | 
					    filterset_fields = {
 | 
				
			||||||
 | 
					        "application": ["isnull"],
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_queryset(self):
 | 
					    def get_queryset(self):
 | 
				
			||||||
        return Provider.objects.select_subclasses()
 | 
					        return Provider.objects.select_subclasses()
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ from django.utils.timezone import now
 | 
				
			|||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from guardian.mixins import GuardianUserMixin
 | 
					from guardian.mixins import GuardianUserMixin
 | 
				
			||||||
from model_utils.managers import InheritanceManager
 | 
					from model_utils.managers import InheritanceManager
 | 
				
			||||||
 | 
					from rest_framework.serializers import Serializer
 | 
				
			||||||
from structlog import get_logger
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.exceptions import PropertyMappingExpressionException
 | 
					from authentik.core.exceptions import PropertyMappingExpressionException
 | 
				
			||||||
@ -127,7 +128,7 @@ class User(GuardianUserMixin, AbstractUser):
 | 
				
			|||||||
        verbose_name_plural = _("Users")
 | 
					        verbose_name_plural = _("Users")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Provider(models.Model):
 | 
					class Provider(SerializerModel):
 | 
				
			||||||
    """Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application"""
 | 
					    """Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    name = models.TextField()
 | 
					    name = models.TextField()
 | 
				
			||||||
@ -156,6 +157,11 @@ class Provider(models.Model):
 | 
				
			|||||||
        """Return Form class used to edit this object"""
 | 
					        """Return Form class used to edit this object"""
 | 
				
			||||||
        raise NotImplementedError
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def serializer(self) -> Type[Serializer]:
 | 
				
			||||||
 | 
					        """Get serializer for this model"""
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return self.name
 | 
					        return self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,15 @@
 | 
				
			|||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
{% load authentik_utils %}
 | 
					{% load authentik_utils %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block head %}
 | 
				
			||||||
 | 
					{{ block.super }}
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					    .pf-c-empty-state {
 | 
				
			||||||
 | 
					        height: 100vh;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block body %}
 | 
					{% block body %}
 | 
				
			||||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
 | 
					<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
 | 
				
			||||||
    <div class="pf-c-empty-state">
 | 
					    <div class="pf-c-empty-state">
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,14 @@
 | 
				
			|||||||
"""Flow API Views"""
 | 
					"""Flow API Views"""
 | 
				
			||||||
from django.core.cache import cache
 | 
					from django.core.cache import cache
 | 
				
			||||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
 | 
					from rest_framework.mixins import ListModelMixin
 | 
				
			||||||
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.response import Response
 | 
				
			||||||
 | 
					from rest_framework.serializers import (
 | 
				
			||||||
 | 
					    ModelSerializer,
 | 
				
			||||||
 | 
					    Serializer,
 | 
				
			||||||
 | 
					    SerializerMethodField,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.flows.models import Flow, FlowStageBinding, Stage
 | 
					from authentik.flows.models import Flow, FlowStageBinding, Stage
 | 
				
			||||||
from authentik.flows.planner import cache_key
 | 
					from authentik.flows.planner import cache_key
 | 
				
			||||||
@ -98,3 +105,14 @@ class FlowStageBindingViewSet(ModelViewSet):
 | 
				
			|||||||
    queryset = FlowStageBinding.objects.all()
 | 
					    queryset = FlowStageBinding.objects.all()
 | 
				
			||||||
    serializer_class = FlowStageBindingSerializer
 | 
					    serializer_class = FlowStageBindingSerializer
 | 
				
			||||||
    filterset_fields = "__all__"
 | 
					    filterset_fields = "__all__"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FlowCacheViewSet(ListModelMixin, GenericViewSet):
 | 
				
			||||||
 | 
					    """Info about cached flows"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    queryset = Flow.objects.none()
 | 
				
			||||||
 | 
					    serializer_class = Serializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def list(self, request: Request) -> Response:
 | 
				
			||||||
 | 
					        """Info about cached flows"""
 | 
				
			||||||
 | 
					        return Response(data={"pagination": {"count": len(cache.keys("flow_*"))}})
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,7 @@ LOGGER = get_logger()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
PLAN_CONTEXT_PENDING_USER = "pending_user"
 | 
					PLAN_CONTEXT_PENDING_USER = "pending_user"
 | 
				
			||||||
PLAN_CONTEXT_SSO = "is_sso"
 | 
					PLAN_CONTEXT_SSO = "is_sso"
 | 
				
			||||||
 | 
					PLAN_CONTEXT_REDIRECT = "redirect"
 | 
				
			||||||
PLAN_CONTEXT_APPLICATION = "application"
 | 
					PLAN_CONTEXT_APPLICATION = "application"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ from django.test.client import RequestFactory
 | 
				
			|||||||
from django.utils.encoding import force_str
 | 
					from django.utils.encoding import force_str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import User
 | 
					from authentik.core.models import User
 | 
				
			||||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
 | 
					from authentik.flows.exceptions import FlowNonApplicableException
 | 
				
			||||||
from authentik.flows.markers import ReevaluateMarker, StageMarker
 | 
					from authentik.flows.markers import ReevaluateMarker, StageMarker
 | 
				
			||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
 | 
					from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
 | 
				
			||||||
from authentik.flows.planner import FlowPlan, FlowPlanner
 | 
					from authentik.flows.planner import FlowPlan, FlowPlanner
 | 
				
			||||||
@ -40,6 +40,10 @@ class TestFlowExecutor(TestCase):
 | 
				
			|||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        self.request_factory = RequestFactory()
 | 
					        self.request_factory = RequestFactory()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @patch(
 | 
				
			||||||
 | 
					        "authentik.flows.views.to_stage_response",
 | 
				
			||||||
 | 
					        TO_STAGE_RESPONSE_MOCK,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    def test_existing_plan_diff_flow(self):
 | 
					    def test_existing_plan_diff_flow(self):
 | 
				
			||||||
        """Check that a plan for a different flow cancels the current plan"""
 | 
					        """Check that a plan for a different flow cancels the current plan"""
 | 
				
			||||||
        flow = Flow.objects.create(
 | 
					        flow = Flow.objects.create(
 | 
				
			||||||
@ -62,7 +66,7 @@ class TestFlowExecutor(TestCase):
 | 
				
			|||||||
                    "authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug}
 | 
					                    "authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug}
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            self.assertEqual(response.status_code, 200)
 | 
					            self.assertEqual(response.status_code, 302)
 | 
				
			||||||
            self.assertEqual(cancel_mock.call_count, 2)
 | 
					            self.assertEqual(cancel_mock.call_count, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @patch(
 | 
					    @patch(
 | 
				
			||||||
@ -105,10 +109,13 @@ class TestFlowExecutor(TestCase):
 | 
				
			|||||||
        response = self.client.get(
 | 
					        response = self.client.get(
 | 
				
			||||||
            reverse("authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug}),
 | 
					            reverse("authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug}),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, 302)
 | 
				
			||||||
        self.assertIsInstance(response, AccessDeniedResponse)
 | 
					        self.assertEqual(response.url, reverse("authentik_core:shell"))
 | 
				
			||||||
        self.assertInHTML(EmptyFlowException.__doc__, response.rendered_content)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @patch(
 | 
				
			||||||
 | 
					        "authentik.flows.views.to_stage_response",
 | 
				
			||||||
 | 
					        TO_STAGE_RESPONSE_MOCK,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    def test_invalid_flow_redirect(self):
 | 
					    def test_invalid_flow_redirect(self):
 | 
				
			||||||
        """Tests that an invalid flow still redirects"""
 | 
					        """Tests that an invalid flow still redirects"""
 | 
				
			||||||
        flow = Flow.objects.create(
 | 
					        flow = Flow.objects.create(
 | 
				
			||||||
@ -121,11 +128,8 @@ class TestFlowExecutor(TestCase):
 | 
				
			|||||||
        dest = "/unique-string"
 | 
					        dest = "/unique-string"
 | 
				
			||||||
        url = reverse("authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug})
 | 
					        url = reverse("authentik_flows:flow-executor", kwargs={"flow_slug": flow.slug})
 | 
				
			||||||
        response = self.client.get(url + f"?{NEXT_ARG_NAME}={dest}")
 | 
					        response = self.client.get(url + f"?{NEXT_ARG_NAME}={dest}")
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, 302)
 | 
				
			||||||
        self.assertJSONEqual(
 | 
					        self.assertEqual(response.url, reverse("authentik_core:shell"))
 | 
				
			||||||
            force_str(response.content),
 | 
					 | 
				
			||||||
            {"type": "redirect", "to": dest},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_multi_stage_flow(self):
 | 
					    def test_multi_stage_flow(self):
 | 
				
			||||||
        """Test a full flow with multiple stages"""
 | 
					        """Test a full flow with multiple stages"""
 | 
				
			||||||
@ -161,6 +165,10 @@ class TestFlowExecutor(TestCase):
 | 
				
			|||||||
        plan: FlowPlan = session[SESSION_KEY_PLAN]
 | 
					        plan: FlowPlan = session[SESSION_KEY_PLAN]
 | 
				
			||||||
        self.assertEqual(len(plan.stages), 1)
 | 
					        self.assertEqual(len(plan.stages), 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @patch(
 | 
				
			||||||
 | 
					        "authentik.flows.views.to_stage_response",
 | 
				
			||||||
 | 
					        TO_STAGE_RESPONSE_MOCK,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    def test_reevaluate_remove_last(self):
 | 
					    def test_reevaluate_remove_last(self):
 | 
				
			||||||
        """Test planner with re-evaluate (last stage is removed)"""
 | 
					        """Test planner with re-evaluate (last stage is removed)"""
 | 
				
			||||||
        flow = Flow.objects.create(
 | 
					        flow = Flow.objects.create(
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,12 @@ from authentik.audit.models import cleanse_dict
 | 
				
			|||||||
from authentik.core.models import USER_ATTRIBUTE_DEBUG
 | 
					from authentik.core.models import USER_ATTRIBUTE_DEBUG
 | 
				
			||||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
 | 
					from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
 | 
				
			||||||
from authentik.flows.models import ConfigurableStage, Flow, FlowDesignation, Stage
 | 
					from authentik.flows.models import ConfigurableStage, Flow, FlowDesignation, Stage
 | 
				
			||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan, FlowPlanner
 | 
					from authentik.flows.planner import (
 | 
				
			||||||
 | 
					    PLAN_CONTEXT_PENDING_USER,
 | 
				
			||||||
 | 
					    PLAN_CONTEXT_REDIRECT,
 | 
				
			||||||
 | 
					    FlowPlan,
 | 
				
			||||||
 | 
					    FlowPlanner,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from authentik.lib.utils.reflection import class_to_path
 | 
					from authentik.lib.utils.reflection import class_to_path
 | 
				
			||||||
from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs
 | 
					from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs
 | 
				
			||||||
from authentik.policies.http import AccessDeniedResponse
 | 
					from authentik.policies.http import AccessDeniedResponse
 | 
				
			||||||
@ -83,7 +88,9 @@ class FlowExecutorView(View):
 | 
				
			|||||||
                return to_stage_response(self.request, self.handle_invalid_flow(exc))
 | 
					                return to_stage_response(self.request, self.handle_invalid_flow(exc))
 | 
				
			||||||
            except EmptyFlowException as exc:
 | 
					            except EmptyFlowException as exc:
 | 
				
			||||||
                LOGGER.warning("f(exec): Flow is empty", exc=exc)
 | 
					                LOGGER.warning("f(exec): Flow is empty", exc=exc)
 | 
				
			||||||
                return to_stage_response(self.request, self.handle_invalid_flow(exc))
 | 
					                # To match behaviour with loading an empty flow plan from cache,
 | 
				
			||||||
 | 
					                # we don't show an error message here, but rather call _flow_done()
 | 
				
			||||||
 | 
					                return self._flow_done()
 | 
				
			||||||
        # We don't save the Plan after getting the next stage
 | 
					        # We don't save the Plan after getting the next stage
 | 
				
			||||||
        # as it hasn't been successfully passed yet
 | 
					        # as it hasn't been successfully passed yet
 | 
				
			||||||
        next_stage = self.plan.next(self.request)
 | 
					        next_stage = self.plan.next(self.request)
 | 
				
			||||||
@ -143,11 +150,15 @@ class FlowExecutorView(View):
 | 
				
			|||||||
        """User Successfully passed all stages"""
 | 
					        """User Successfully passed all stages"""
 | 
				
			||||||
        # Since this is wrapped by the ExecutorShell, the next argument is saved in the session
 | 
					        # Since this is wrapped by the ExecutorShell, the next argument is saved in the session
 | 
				
			||||||
        # extract the next param before cancel as that cleans it
 | 
					        # extract the next param before cancel as that cleans it
 | 
				
			||||||
 | 
					        next_param = None
 | 
				
			||||||
 | 
					        if self.plan:
 | 
				
			||||||
 | 
					            next_param = self.plan.context.get(PLAN_CONTEXT_REDIRECT)
 | 
				
			||||||
 | 
					        if not next_param:
 | 
				
			||||||
            next_param = self.request.session.get(SESSION_KEY_GET, {}).get(
 | 
					            next_param = self.request.session.get(SESSION_KEY_GET, {}).get(
 | 
				
			||||||
                NEXT_ARG_NAME, "authentik_core:shell"
 | 
					                NEXT_ARG_NAME, "authentik_core:shell"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        self.cancel()
 | 
					        self.cancel()
 | 
				
			||||||
        return redirect_with_qs(next_param)
 | 
					        return to_stage_response(self.request, redirect_with_qs(next_param))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def stage_ok(self) -> HttpResponse:
 | 
					    def stage_ok(self) -> HttpResponse:
 | 
				
			||||||
        """Callback called by stages upon successful completion.
 | 
					        """Callback called by stages upon successful completion.
 | 
				
			||||||
 | 
				
			|||||||
@ -42,9 +42,8 @@ def outpost_service_connection_state(connection_pk: Any):
 | 
				
			|||||||
        .select_subclasses()
 | 
					        .select_subclasses()
 | 
				
			||||||
        .first()
 | 
					        .first()
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    cache.delete(f"outpost_service_connection_{connection.pk.hex}")
 | 
					 | 
				
			||||||
    state = connection.fetch_state()
 | 
					    state = connection.fetch_state()
 | 
				
			||||||
    cache.set(connection.state_key, state, timeout=0)
 | 
					    cache.set(connection.state_key, state, timeout=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CELERY_APP.task(bind=True, base=MonitoredTask)
 | 
					@CELERY_APP.task(bind=True, base=MonitoredTask)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,16 @@
 | 
				
			|||||||
"""policy API Views"""
 | 
					"""policy API Views"""
 | 
				
			||||||
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.core.exceptions import ObjectDoesNotExist
 | 
					from django.core.exceptions import ObjectDoesNotExist
 | 
				
			||||||
 | 
					from rest_framework.mixins import ListModelMixin
 | 
				
			||||||
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.response import Response
 | 
				
			||||||
from rest_framework.serializers import (
 | 
					from rest_framework.serializers import (
 | 
				
			||||||
    ModelSerializer,
 | 
					    ModelSerializer,
 | 
				
			||||||
    PrimaryKeyRelatedField,
 | 
					    PrimaryKeyRelatedField,
 | 
				
			||||||
 | 
					    Serializer,
 | 
				
			||||||
    SerializerMethodField,
 | 
					    SerializerMethodField,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
 | 
					from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.policies.forms import GENERAL_FIELDS
 | 
					from authentik.policies.forms import GENERAL_FIELDS
 | 
				
			||||||
from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel
 | 
					from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel
 | 
				
			||||||
@ -68,6 +73,10 @@ class PolicyViewSet(ReadOnlyModelViewSet):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    queryset = Policy.objects.all()
 | 
					    queryset = Policy.objects.all()
 | 
				
			||||||
    serializer_class = PolicySerializer
 | 
					    serializer_class = PolicySerializer
 | 
				
			||||||
 | 
					    filterset_fields = {
 | 
				
			||||||
 | 
					        "bindings": ["isnull"],
 | 
				
			||||||
 | 
					        "promptstage": ["isnull"],
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_queryset(self):
 | 
					    def get_queryset(self):
 | 
				
			||||||
        return Policy.objects.select_subclasses()
 | 
					        return Policy.objects.select_subclasses()
 | 
				
			||||||
@ -98,3 +107,14 @@ class PolicyBindingViewSet(ModelViewSet):
 | 
				
			|||||||
    serializer_class = PolicyBindingSerializer
 | 
					    serializer_class = PolicyBindingSerializer
 | 
				
			||||||
    filterset_fields = ["policy", "target", "enabled", "order", "timeout"]
 | 
					    filterset_fields = ["policy", "target", "enabled", "order", "timeout"]
 | 
				
			||||||
    search_fields = ["policy__name"]
 | 
					    search_fields = ["policy__name"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PolicyCacheViewSet(ListModelMixin, GenericViewSet):
 | 
				
			||||||
 | 
					    """Info about cached policies"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    queryset = Policy.objects.none()
 | 
				
			||||||
 | 
					    serializer_class = Serializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def list(self, request: Request) -> Response:
 | 
				
			||||||
 | 
					        """Info about cached policies"""
 | 
				
			||||||
 | 
					        return Response(data={"pagination": {"count": len(cache.keys("policy_*"))}})
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,7 @@ from django.utils import dateformat, timezone
 | 
				
			|||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from jwkest.jwk import Key, RSAKey, SYMKey, import_rsa_key
 | 
					from jwkest.jwk import Key, RSAKey, SYMKey, import_rsa_key
 | 
				
			||||||
from jwkest.jws import JWS
 | 
					from jwkest.jws import JWS
 | 
				
			||||||
 | 
					from rest_framework.serializers import Serializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import ExpiringModel, PropertyMapping, Provider, User
 | 
					from authentik.core.models import ExpiringModel, PropertyMapping, Provider, User
 | 
				
			||||||
from authentik.crypto.models import CertificateKeyPair
 | 
					from authentik.crypto.models import CertificateKeyPair
 | 
				
			||||||
@ -263,6 +264,12 @@ class OAuth2Provider(Provider):
 | 
				
			|||||||
        launch_url = urlparse(main_url)
 | 
					        launch_url = urlparse(main_url)
 | 
				
			||||||
        return main_url.replace(launch_url.path, "")
 | 
					        return main_url.replace(launch_url.path, "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def serializer(self) -> Type[Serializer]:
 | 
				
			||||||
 | 
					        from authentik.providers.oauth2.api import OAuth2ProviderSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return OAuth2ProviderSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def form(self) -> Type[ModelForm]:
 | 
					    def form(self) -> Type[ModelForm]:
 | 
				
			||||||
        from authentik.providers.oauth2.forms import OAuth2ProviderForm
 | 
					        from authentik.providers.oauth2.forms import OAuth2ProviderForm
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,16 @@
 | 
				
			|||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
{% load authentik_utils %}
 | 
					{% load authentik_utils %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block head %}
 | 
				
			||||||
 | 
					{{ block.super }}
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					.pf-c-background-image::before {
 | 
				
			||||||
 | 
					    background-image: url("{% static 'dist/assets/images/flow_background.jpg' %}");
 | 
				
			||||||
 | 
					    background-position: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block title %}
 | 
					{% block title %}
 | 
				
			||||||
{% trans 'End session' %}
 | 
					{% trans 'End session' %}
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ from django.db import models
 | 
				
			|||||||
from django.forms import ModelForm
 | 
					from django.forms import ModelForm
 | 
				
			||||||
from django.http import HttpRequest
 | 
					from django.http import HttpRequest
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
 | 
					from rest_framework.serializers import Serializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.crypto.models import CertificateKeyPair
 | 
					from authentik.crypto.models import CertificateKeyPair
 | 
				
			||||||
from authentik.lib.models import DomainlessURLValidator
 | 
					from authentik.lib.models import DomainlessURLValidator
 | 
				
			||||||
@ -108,6 +109,12 @@ class ProxyProvider(OutpostModel, OAuth2Provider):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return ProxyProviderForm
 | 
					        return ProxyProviderForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def serializer(self) -> Type[Serializer]:
 | 
				
			||||||
 | 
					        from authentik.providers.proxy.api import ProxyProviderSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return ProxyProviderSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def launch_url(self) -> Optional[str]:
 | 
					    def launch_url(self) -> Optional[str]:
 | 
				
			||||||
        """Use external_host as launch URL"""
 | 
					        """Use external_host as launch URL"""
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@ from django.forms import ModelForm
 | 
				
			|||||||
from django.http import HttpRequest
 | 
					from django.http import HttpRequest
 | 
				
			||||||
from django.shortcuts import reverse
 | 
					from django.shortcuts import reverse
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from rest_framework.serializers import Serializer
 | 
				
			||||||
from structlog import get_logger
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import PropertyMapping, Provider
 | 
					from authentik.core.models import PropertyMapping, Provider
 | 
				
			||||||
@ -145,6 +146,12 @@ class SAMLProvider(Provider):
 | 
				
			|||||||
        launch_url = urlparse(self.acs_url)
 | 
					        launch_url = urlparse(self.acs_url)
 | 
				
			||||||
        return self.acs_url.replace(launch_url.path, "")
 | 
					        return self.acs_url.replace(launch_url.path, "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def serializer(self) -> Type[Serializer]:
 | 
				
			||||||
 | 
					        from authentik.providers.saml.api import SAMLPropertyMappingSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return SAMLPropertyMappingSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def form(self) -> Type[ModelForm]:
 | 
					    def form(self) -> Type[ModelForm]:
 | 
				
			||||||
        from authentik.providers.saml.forms import SAMLProviderForm
 | 
					        from authentik.providers.saml.forms import SAMLProviderForm
 | 
				
			||||||
 | 
				
			|||||||
@ -15,10 +15,11 @@ from authentik.core.models import User
 | 
				
			|||||||
from authentik.flows.models import Flow, in_memory_stage
 | 
					from authentik.flows.models import Flow, in_memory_stage
 | 
				
			||||||
from authentik.flows.planner import (
 | 
					from authentik.flows.planner import (
 | 
				
			||||||
    PLAN_CONTEXT_PENDING_USER,
 | 
					    PLAN_CONTEXT_PENDING_USER,
 | 
				
			||||||
 | 
					    PLAN_CONTEXT_REDIRECT,
 | 
				
			||||||
    PLAN_CONTEXT_SSO,
 | 
					    PLAN_CONTEXT_SSO,
 | 
				
			||||||
    FlowPlanner,
 | 
					    FlowPlanner,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.flows.views import SESSION_KEY_PLAN
 | 
					from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
 | 
				
			||||||
from authentik.lib.utils.urls import redirect_with_qs
 | 
					from authentik.lib.utils.urls import redirect_with_qs
 | 
				
			||||||
from authentik.policies.utils import delete_none_keys
 | 
					from authentik.policies.utils import delete_none_keys
 | 
				
			||||||
from authentik.sources.oauth.auth import AuthorizedServiceBackend
 | 
					from authentik.sources.oauth.auth import AuthorizedServiceBackend
 | 
				
			||||||
@ -135,11 +136,17 @@ class OAuthCallback(OAuthClientMixin, View):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def handle_login_flow(self, flow: Flow, **kwargs) -> HttpResponse:
 | 
					    def handle_login_flow(self, flow: Flow, **kwargs) -> HttpResponse:
 | 
				
			||||||
        """Prepare Authentication Plan, redirect user FlowExecutor"""
 | 
					        """Prepare Authentication Plan, redirect user FlowExecutor"""
 | 
				
			||||||
 | 
					        # Ensure redirect is carried through when user was trying to
 | 
				
			||||||
 | 
					        # authorize application
 | 
				
			||||||
 | 
					        final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
 | 
				
			||||||
 | 
					            NEXT_ARG_NAME, "authentik_core:shell"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        kwargs.update(
 | 
					        kwargs.update(
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                # Since we authenticate the user by their token, they have no backend set
 | 
					                # Since we authenticate the user by their token, they have no backend set
 | 
				
			||||||
                PLAN_CONTEXT_AUTHENTICATION_BACKEND: "django.contrib.auth.backends.ModelBackend",
 | 
					                PLAN_CONTEXT_AUTHENTICATION_BACKEND: "django.contrib.auth.backends.ModelBackend",
 | 
				
			||||||
                PLAN_CONTEXT_SSO: True,
 | 
					                PLAN_CONTEXT_SSO: True,
 | 
				
			||||||
 | 
					                PLAN_CONTEXT_REDIRECT: final_redirect,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        # We run the Flow planner here so we can pass the Pending user in the context
 | 
					        # We run the Flow planner here so we can pass the Pending user in the context
 | 
				
			||||||
 | 
				
			|||||||
@ -13,10 +13,11 @@ from authentik.core.models import User
 | 
				
			|||||||
from authentik.flows.models import Flow
 | 
					from authentik.flows.models import Flow
 | 
				
			||||||
from authentik.flows.planner import (
 | 
					from authentik.flows.planner import (
 | 
				
			||||||
    PLAN_CONTEXT_PENDING_USER,
 | 
					    PLAN_CONTEXT_PENDING_USER,
 | 
				
			||||||
 | 
					    PLAN_CONTEXT_REDIRECT,
 | 
				
			||||||
    PLAN_CONTEXT_SSO,
 | 
					    PLAN_CONTEXT_SSO,
 | 
				
			||||||
    FlowPlanner,
 | 
					    FlowPlanner,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.flows.views import SESSION_KEY_PLAN
 | 
					from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
 | 
				
			||||||
from authentik.lib.utils.urls import redirect_with_qs
 | 
					from authentik.lib.utils.urls import redirect_with_qs
 | 
				
			||||||
from authentik.policies.utils import delete_none_keys
 | 
					from authentik.policies.utils import delete_none_keys
 | 
				
			||||||
from authentik.sources.saml.exceptions import (
 | 
					from authentik.sources.saml.exceptions import (
 | 
				
			||||||
@ -54,11 +55,14 @@ class ResponseProcessor:
 | 
				
			|||||||
    _root: Any
 | 
					    _root: Any
 | 
				
			||||||
    _root_xml: str
 | 
					    _root_xml: str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _http_request: HttpRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, source: SAMLSource):
 | 
					    def __init__(self, source: SAMLSource):
 | 
				
			||||||
        self._source = source
 | 
					        self._source = source
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def parse(self, request: HttpRequest):
 | 
					    def parse(self, request: HttpRequest):
 | 
				
			||||||
        """Check if `request` contains SAML Response data, parse and validate it."""
 | 
					        """Check if `request` contains SAML Response data, parse and validate it."""
 | 
				
			||||||
 | 
					        self._http_request = request
 | 
				
			||||||
        # First off, check if we have any SAML Data at all.
 | 
					        # First off, check if we have any SAML Data at all.
 | 
				
			||||||
        raw_response = request.POST.get("SAMLResponse", None)
 | 
					        raw_response = request.POST.get("SAMLResponse", None)
 | 
				
			||||||
        if not raw_response:
 | 
					        if not raw_response:
 | 
				
			||||||
@ -187,6 +191,11 @@ class ResponseProcessor:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        name_id_filter = self._get_name_id_filter()
 | 
					        name_id_filter = self._get_name_id_filter()
 | 
				
			||||||
        matching_users = User.objects.filter(**name_id_filter)
 | 
					        matching_users = User.objects.filter(**name_id_filter)
 | 
				
			||||||
 | 
					        # Ensure redirect is carried through when user was trying to
 | 
				
			||||||
 | 
					        # authorize application
 | 
				
			||||||
 | 
					        final_redirect = self._http_request.session.get(SESSION_KEY_GET, {}).get(
 | 
				
			||||||
 | 
					            NEXT_ARG_NAME, "authentik_core:shell"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        if matching_users.exists():
 | 
					        if matching_users.exists():
 | 
				
			||||||
            # User exists already, switch to authentication flow
 | 
					            # User exists already, switch to authentication flow
 | 
				
			||||||
            return self._flow_response(
 | 
					            return self._flow_response(
 | 
				
			||||||
@ -195,6 +204,7 @@ class ResponseProcessor:
 | 
				
			|||||||
                **{
 | 
					                **{
 | 
				
			||||||
                    PLAN_CONTEXT_PENDING_USER: matching_users.first(),
 | 
					                    PLAN_CONTEXT_PENDING_USER: matching_users.first(),
 | 
				
			||||||
                    PLAN_CONTEXT_AUTHENTICATION_BACKEND: DEFAULT_BACKEND,
 | 
					                    PLAN_CONTEXT_AUTHENTICATION_BACKEND: DEFAULT_BACKEND,
 | 
				
			||||||
 | 
					                    PLAN_CONTEXT_REDIRECT: final_redirect,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        return self._flow_response(
 | 
					        return self._flow_response(
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ services:
 | 
				
			|||||||
    networks:
 | 
					    networks:
 | 
				
			||||||
      - internal
 | 
					      - internal
 | 
				
			||||||
  server:
 | 
					  server:
 | 
				
			||||||
    image: beryju/authentik:${AUTHENTIK_TAG:-0.13.1-stable}
 | 
					    image: beryju/authentik:${AUTHENTIK_TAG:-0.13.3-stable}
 | 
				
			||||||
    command: server
 | 
					    command: server
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      AUTHENTIK_REDIS__HOST: redis
 | 
					      AUTHENTIK_REDIS__HOST: redis
 | 
				
			||||||
@ -44,7 +44,7 @@ services:
 | 
				
			|||||||
    env_file:
 | 
					    env_file:
 | 
				
			||||||
      - .env
 | 
					      - .env
 | 
				
			||||||
  worker:
 | 
					  worker:
 | 
				
			||||||
    image: beryju/authentik:${AUTHENTIK_TAG:-0.13.1-stable}
 | 
					    image: beryju/authentik:${AUTHENTIK_TAG:-0.13.3-stable}
 | 
				
			||||||
    command: worker
 | 
					    command: worker
 | 
				
			||||||
    networks:
 | 
					    networks:
 | 
				
			||||||
      - internal
 | 
					      - internal
 | 
				
			||||||
@ -60,7 +60,7 @@ services:
 | 
				
			|||||||
    env_file:
 | 
					    env_file:
 | 
				
			||||||
      - .env
 | 
					      - .env
 | 
				
			||||||
  static:
 | 
					  static:
 | 
				
			||||||
    image: beryju/authentik-static:${AUTHENTIK_TAG:-0.13.1-stable}
 | 
					    image: beryju/authentik-static:${AUTHENTIK_TAG:-0.13.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: "0.13.1-stable"
 | 
					version: "0.13.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                         | 0.13.1-stable              | Image tag |
 | 
					| image.tag                         | 0.13.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 |
 | 
				
			||||||
 | 
				
			|||||||
@ -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: 0.13.1-stable
 | 
					  tag: 0.13.3-stable
 | 
				
			||||||
  pullPolicy: IfNotPresent
 | 
					  pullPolicy: IfNotPresent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
serverReplicas: 1
 | 
					serverReplicas: 1
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,3 @@
 | 
				
			|||||||
package pkg
 | 
					package pkg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const VERSION = "0.13.1-stable"
 | 
					const VERSION = "0.13.3-stable"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										341
									
								
								swagger.yaml
									
									
									
									
									
								
							
							
						
						
									
										341
									
								
								swagger.yaml
									
									
									
									
									
								
							@ -22,11 +22,11 @@ paths:
 | 
				
			|||||||
  /admin/metrics/:
 | 
					  /admin/metrics/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: admin_metrics_list
 | 
					      operationId: admin_metrics_list
 | 
				
			||||||
      description: Return single instance of AdministrationMetricsSerializer
 | 
					      description: Login Metrics per 1h
 | 
				
			||||||
      parameters: []
 | 
					      parameters: []
 | 
				
			||||||
      responses:
 | 
					      responses:
 | 
				
			||||||
        '200':
 | 
					        '200':
 | 
				
			||||||
          description: Overview View
 | 
					          description: Login Metrics per 1h
 | 
				
			||||||
          schema:
 | 
					          schema:
 | 
				
			||||||
            description: ''
 | 
					            description: ''
 | 
				
			||||||
            type: array
 | 
					            type: array
 | 
				
			||||||
@ -35,22 +35,6 @@ paths:
 | 
				
			|||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - admin
 | 
					        - admin
 | 
				
			||||||
    parameters: []
 | 
					    parameters: []
 | 
				
			||||||
  /admin/overview/:
 | 
					 | 
				
			||||||
    get:
 | 
					 | 
				
			||||||
      operationId: admin_overview_list
 | 
					 | 
				
			||||||
      description: Return single instance of AdministrationOverviewSerializer
 | 
					 | 
				
			||||||
      parameters: []
 | 
					 | 
				
			||||||
      responses:
 | 
					 | 
				
			||||||
        '200':
 | 
					 | 
				
			||||||
          description: Overview View
 | 
					 | 
				
			||||||
          schema:
 | 
					 | 
				
			||||||
            description: ''
 | 
					 | 
				
			||||||
            type: array
 | 
					 | 
				
			||||||
            items:
 | 
					 | 
				
			||||||
              $ref: '#/definitions/AdministrationOverview'
 | 
					 | 
				
			||||||
      tags:
 | 
					 | 
				
			||||||
        - admin
 | 
					 | 
				
			||||||
    parameters: []
 | 
					 | 
				
			||||||
  /admin/system_tasks/:
 | 
					  /admin/system_tasks/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: admin_system_tasks_list
 | 
					      operationId: admin_system_tasks_list
 | 
				
			||||||
@ -82,6 +66,95 @@ paths:
 | 
				
			|||||||
        in: path
 | 
					        in: path
 | 
				
			||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
 | 
					  /admin/version/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: admin_version_list
 | 
				
			||||||
 | 
					      description: Get running and latest version.
 | 
				
			||||||
 | 
					      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: Get running and latest version.
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            description: ''
 | 
				
			||||||
 | 
					            type: array
 | 
				
			||||||
 | 
					            items:
 | 
				
			||||||
 | 
					              $ref: '#/definitions/Version'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - admin
 | 
				
			||||||
 | 
					    parameters: []
 | 
				
			||||||
 | 
					  /admin/workers/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: admin_workers_list
 | 
				
			||||||
 | 
					      description: Get currently connected worker count.
 | 
				
			||||||
 | 
					      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:
 | 
				
			||||||
 | 
					                  description: ''
 | 
				
			||||||
 | 
					                  type: object
 | 
				
			||||||
 | 
					                  properties: {}
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - admin
 | 
				
			||||||
 | 
					    parameters: []
 | 
				
			||||||
  /audit/events/:
 | 
					  /audit/events/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: audit_events_list
 | 
					      operationId: audit_events_list
 | 
				
			||||||
@ -1062,6 +1135,59 @@ paths:
 | 
				
			|||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
 | 
					  /flows/cached/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: flows_cached_list
 | 
				
			||||||
 | 
					      description: Info about cached flows
 | 
				
			||||||
 | 
					      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:
 | 
				
			||||||
 | 
					                  description: ''
 | 
				
			||||||
 | 
					                  type: object
 | 
				
			||||||
 | 
					                  properties: {}
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - flows
 | 
				
			||||||
 | 
					    parameters: []
 | 
				
			||||||
  /flows/instances/:
 | 
					  /flows/instances/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: flows_instances_list
 | 
					      operationId: flows_instances_list
 | 
				
			||||||
@ -1702,6 +1828,16 @@ paths:
 | 
				
			|||||||
      operationId: policies_all_list
 | 
					      operationId: policies_all_list
 | 
				
			||||||
      description: Policy Viewset
 | 
					      description: Policy Viewset
 | 
				
			||||||
      parameters:
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: bindings__isnull
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					          type: string
 | 
				
			||||||
 | 
					        - name: promptstage__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.
 | 
				
			||||||
@ -1919,6 +2055,59 @@ paths:
 | 
				
			|||||||
        required: true
 | 
					        required: true
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
 | 
					  /policies/cached/:
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: policies_cached_list
 | 
				
			||||||
 | 
					      description: Info about cached policies
 | 
				
			||||||
 | 
					      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:
 | 
				
			||||||
 | 
					                  description: ''
 | 
				
			||||||
 | 
					                  type: object
 | 
				
			||||||
 | 
					                  properties: {}
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - policies
 | 
				
			||||||
 | 
					    parameters: []
 | 
				
			||||||
  /policies/dummy/:
 | 
					  /policies/dummy/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: policies_dummy_list
 | 
					      operationId: policies_dummy_list
 | 
				
			||||||
@ -3264,6 +3453,11 @@ paths:
 | 
				
			|||||||
      operationId: providers_all_list
 | 
					      operationId: providers_all_list
 | 
				
			||||||
      description: Provider Viewset
 | 
					      description: Provider Viewset
 | 
				
			||||||
      parameters:
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: application__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.
 | 
				
			||||||
@ -3309,6 +3503,22 @@ paths:
 | 
				
			|||||||
                  $ref: '#/definitions/Provider'
 | 
					                  $ref: '#/definitions/Provider'
 | 
				
			||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - providers
 | 
					        - providers
 | 
				
			||||||
 | 
					    post:
 | 
				
			||||||
 | 
					      operationId: providers_all_create
 | 
				
			||||||
 | 
					      description: Provider Viewset
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: data
 | 
				
			||||||
 | 
					          in: body
 | 
				
			||||||
 | 
					          required: true
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/Provider'
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '201':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/Provider'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - providers
 | 
				
			||||||
    parameters: []
 | 
					    parameters: []
 | 
				
			||||||
  /providers/all/{id}/:
 | 
					  /providers/all/{id}/:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
@ -3322,6 +3532,47 @@ paths:
 | 
				
			|||||||
            $ref: '#/definitions/Provider'
 | 
					            $ref: '#/definitions/Provider'
 | 
				
			||||||
      tags:
 | 
					      tags:
 | 
				
			||||||
        - providers
 | 
					        - providers
 | 
				
			||||||
 | 
					    put:
 | 
				
			||||||
 | 
					      operationId: providers_all_update
 | 
				
			||||||
 | 
					      description: Provider Viewset
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: data
 | 
				
			||||||
 | 
					          in: body
 | 
				
			||||||
 | 
					          required: true
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/Provider'
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/Provider'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - providers
 | 
				
			||||||
 | 
					    patch:
 | 
				
			||||||
 | 
					      operationId: providers_all_partial_update
 | 
				
			||||||
 | 
					      description: Provider Viewset
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - name: data
 | 
				
			||||||
 | 
					          in: body
 | 
				
			||||||
 | 
					          required: true
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/Provider'
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '200':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            $ref: '#/definitions/Provider'
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - providers
 | 
				
			||||||
 | 
					    delete:
 | 
				
			||||||
 | 
					      operationId: providers_all_delete
 | 
				
			||||||
 | 
					      description: Provider Viewset
 | 
				
			||||||
 | 
					      parameters: []
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        '204':
 | 
				
			||||||
 | 
					          description: ''
 | 
				
			||||||
 | 
					      tags:
 | 
				
			||||||
 | 
					        - providers
 | 
				
			||||||
    parameters:
 | 
					    parameters:
 | 
				
			||||||
      - name: id
 | 
					      - name: id
 | 
				
			||||||
        in: path
 | 
					        in: path
 | 
				
			||||||
@ -6421,7 +6672,7 @@ paths:
 | 
				
			|||||||
        format: uuid
 | 
					        format: uuid
 | 
				
			||||||
definitions:
 | 
					definitions:
 | 
				
			||||||
  AdministrationMetrics:
 | 
					  AdministrationMetrics:
 | 
				
			||||||
    description: Overview View
 | 
					    description: Login Metrics per 1h
 | 
				
			||||||
    type: object
 | 
					    type: object
 | 
				
			||||||
    properties:
 | 
					    properties:
 | 
				
			||||||
      logins_per_1h:
 | 
					      logins_per_1h:
 | 
				
			||||||
@ -6432,38 +6683,6 @@ definitions:
 | 
				
			|||||||
        title: Logins failed per 1h
 | 
					        title: Logins failed per 1h
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        readOnly: true
 | 
					        readOnly: true
 | 
				
			||||||
  AdministrationOverview:
 | 
					 | 
				
			||||||
    description: Overview View
 | 
					 | 
				
			||||||
    type: object
 | 
					 | 
				
			||||||
    properties:
 | 
					 | 
				
			||||||
      version:
 | 
					 | 
				
			||||||
        title: Version
 | 
					 | 
				
			||||||
        type: string
 | 
					 | 
				
			||||||
        readOnly: true
 | 
					 | 
				
			||||||
      version_latest:
 | 
					 | 
				
			||||||
        title: Version latest
 | 
					 | 
				
			||||||
        type: string
 | 
					 | 
				
			||||||
        readOnly: true
 | 
					 | 
				
			||||||
      worker_count:
 | 
					 | 
				
			||||||
        title: Worker count
 | 
					 | 
				
			||||||
        type: integer
 | 
					 | 
				
			||||||
        readOnly: true
 | 
					 | 
				
			||||||
      providers_without_application:
 | 
					 | 
				
			||||||
        title: Providers without application
 | 
					 | 
				
			||||||
        type: integer
 | 
					 | 
				
			||||||
        readOnly: true
 | 
					 | 
				
			||||||
      policies_without_binding:
 | 
					 | 
				
			||||||
        title: Policies without binding
 | 
					 | 
				
			||||||
        type: integer
 | 
					 | 
				
			||||||
        readOnly: true
 | 
					 | 
				
			||||||
      cached_policies:
 | 
					 | 
				
			||||||
        title: Cached policies
 | 
					 | 
				
			||||||
        type: integer
 | 
					 | 
				
			||||||
        readOnly: true
 | 
					 | 
				
			||||||
      cached_flows:
 | 
					 | 
				
			||||||
        title: Cached flows
 | 
					 | 
				
			||||||
        type: integer
 | 
					 | 
				
			||||||
        readOnly: true
 | 
					 | 
				
			||||||
  Task:
 | 
					  Task:
 | 
				
			||||||
    description: Serialize TaskInfo and TaskResult
 | 
					    description: Serialize TaskInfo and TaskResult
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
@ -6494,6 +6713,22 @@ definitions:
 | 
				
			|||||||
        type: array
 | 
					        type: array
 | 
				
			||||||
        items:
 | 
					        items:
 | 
				
			||||||
          type: string
 | 
					          type: string
 | 
				
			||||||
 | 
					  Version:
 | 
				
			||||||
 | 
					    description: Get running and latest version.
 | 
				
			||||||
 | 
					    type: object
 | 
				
			||||||
 | 
					    properties:
 | 
				
			||||||
 | 
					      version_current:
 | 
				
			||||||
 | 
					        title: Version current
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					      version_latest:
 | 
				
			||||||
 | 
					        title: Version latest
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
 | 
					      outdated:
 | 
				
			||||||
 | 
					        title: Outdated
 | 
				
			||||||
 | 
					        type: boolean
 | 
				
			||||||
 | 
					        readOnly: true
 | 
				
			||||||
  Event:
 | 
					  Event:
 | 
				
			||||||
    description: Event Serializer
 | 
					    description: Event Serializer
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
@ -7480,6 +7715,7 @@ definitions:
 | 
				
			|||||||
    description: Provider Serializer
 | 
					    description: Provider Serializer
 | 
				
			||||||
    required:
 | 
					    required:
 | 
				
			||||||
      - name
 | 
					      - name
 | 
				
			||||||
 | 
					      - application
 | 
				
			||||||
      - authorization_flow
 | 
					      - authorization_flow
 | 
				
			||||||
    type: object
 | 
					    type: object
 | 
				
			||||||
    properties:
 | 
					    properties:
 | 
				
			||||||
@ -7491,6 +7727,9 @@ definitions:
 | 
				
			|||||||
        title: Name
 | 
					        title: Name
 | 
				
			||||||
        type: string
 | 
					        type: string
 | 
				
			||||||
        minLength: 1
 | 
					        minLength: 1
 | 
				
			||||||
 | 
					      application:
 | 
				
			||||||
 | 
					        title: Application
 | 
				
			||||||
 | 
					        type: string
 | 
				
			||||||
      authorization_flow:
 | 
					      authorization_flow:
 | 
				
			||||||
        title: Authorization flow
 | 
					        title: Authorization flow
 | 
				
			||||||
        description: Flow used when authorizing this provider.
 | 
					        description: Flow used when authorizing this provider.
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										108
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										108
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -142,13 +142,13 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@sentry/browser": {
 | 
					        "@sentry/browser": {
 | 
				
			||||||
            "version": "5.29.0",
 | 
					            "version": "5.29.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.29.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.29.2.tgz",
 | 
				
			||||||
            "integrity": "sha512-kRlt1mE2wrYjspnIupNnPxqsUrRuy02SuXhbpP7J6uu8QasoEmJ78hk0hHz4jOZRmuWwfs2zIXD4tLGgWOKq8A==",
 | 
					            "integrity": "sha512-uxZ7y7rp85tJll+RZtXRhXPbnFnOaxZqJEv05vJlXBtBNLQtlczV5iCtU9mZRLVHDtmZ5VVKUV8IKXntEqqDpQ==",
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@sentry/core": "5.29.0",
 | 
					                "@sentry/core": "5.29.2",
 | 
				
			||||||
                "@sentry/types": "5.29.0",
 | 
					                "@sentry/types": "5.29.2",
 | 
				
			||||||
                "@sentry/utils": "5.29.0",
 | 
					                "@sentry/utils": "5.29.2",
 | 
				
			||||||
                "tslib": "^1.9.3"
 | 
					                "tslib": "^1.9.3"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
@ -160,14 +160,14 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@sentry/core": {
 | 
					        "@sentry/core": {
 | 
				
			||||||
            "version": "5.29.0",
 | 
					            "version": "5.29.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.29.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.29.2.tgz",
 | 
				
			||||||
            "integrity": "sha512-a1sZBJ2u3NG0YDlGvOTwUCWiNjhfmDtAQiKK1o6RIIbcrWy9TlSps7CYDkBP239Y3A4pnvohjEEKEP3v3L3LZQ==",
 | 
					            "integrity": "sha512-7WYkoxB5IdlNEbwOwqSU64erUKH4laavPsM0/yQ+jojM76ErxlgEF0u//p5WaLPRzh3iDSt6BH+9TL45oNZeZw==",
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@sentry/hub": "5.29.0",
 | 
					                "@sentry/hub": "5.29.2",
 | 
				
			||||||
                "@sentry/minimal": "5.29.0",
 | 
					                "@sentry/minimal": "5.29.2",
 | 
				
			||||||
                "@sentry/types": "5.29.0",
 | 
					                "@sentry/types": "5.29.2",
 | 
				
			||||||
                "@sentry/utils": "5.29.0",
 | 
					                "@sentry/utils": "5.29.2",
 | 
				
			||||||
                "tslib": "^1.9.3"
 | 
					                "tslib": "^1.9.3"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
@ -179,12 +179,12 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@sentry/hub": {
 | 
					        "@sentry/hub": {
 | 
				
			||||||
            "version": "5.29.0",
 | 
					            "version": "5.29.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.2.tgz",
 | 
				
			||||||
            "integrity": "sha512-kcDPQsRG4cFdmqDh+TzjeO7lWYxU8s1dZYAbbl1J4uGKmhNB0J7I4ak4SGwTsXLY6fhbierxr6PRaoNojCxjPw==",
 | 
					            "integrity": "sha512-LaAIo2hwUk9ykeh9RF0cwLy6IRw+DjEee8l1HfEaDFUM6TPGlNNGObMJNXb9/95jzWp7jWwOpQjoIE3jepdQJQ==",
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@sentry/types": "5.29.0",
 | 
					                "@sentry/types": "5.29.2",
 | 
				
			||||||
                "@sentry/utils": "5.29.0",
 | 
					                "@sentry/utils": "5.29.2",
 | 
				
			||||||
                "tslib": "^1.9.3"
 | 
					                "tslib": "^1.9.3"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
@ -196,12 +196,12 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@sentry/minimal": {
 | 
					        "@sentry/minimal": {
 | 
				
			||||||
            "version": "5.29.0",
 | 
					            "version": "5.29.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.2.tgz",
 | 
				
			||||||
            "integrity": "sha512-nhXofdjtO41/caiF1wk1oT3p/QuhOZDYdF/b29DoD2MiAMK9IjhhOXI/gqaRpDKkXlDvd95fDTcx4t/MqqcKXA==",
 | 
					            "integrity": "sha512-0aINSm8fGA1KyM7PavOBe1GDZDxrvnKt+oFnU0L+bTcw8Lr+of+v6Kwd97rkLRNOLw621xP076dL/7LSIzMuhw==",
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@sentry/hub": "5.29.0",
 | 
					                "@sentry/hub": "5.29.2",
 | 
				
			||||||
                "@sentry/types": "5.29.0",
 | 
					                "@sentry/types": "5.29.2",
 | 
				
			||||||
                "tslib": "^1.9.3"
 | 
					                "tslib": "^1.9.3"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
@ -213,48 +213,48 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@sentry/tracing": {
 | 
					        "@sentry/tracing": {
 | 
				
			||||||
            "version": "5.29.0",
 | 
					            "version": "5.29.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.29.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.29.2.tgz",
 | 
				
			||||||
            "integrity": "sha512-2ZITUH7Eur7IkmRAd5gw8Xt2Sfc28btCnT7o2P2J8ZPD65e99ATqjxXPokx0+6zEkTsstIDD3mbyuwkpbuvuTA==",
 | 
					            "integrity": "sha512-iumYbVRpvoU3BUuIooxibydeaOOjl5ysc+mzsqhRs2NGW/C3uKAsFXdvyNfqt3bxtRQwJEhwJByLP2u3pLThpw==",
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@sentry/hub": "5.29.0",
 | 
					                "@sentry/hub": "5.29.2",
 | 
				
			||||||
                "@sentry/minimal": "5.29.0",
 | 
					                "@sentry/minimal": "5.29.2",
 | 
				
			||||||
                "@sentry/types": "5.29.0",
 | 
					                "@sentry/types": "5.29.2",
 | 
				
			||||||
                "@sentry/utils": "5.29.0",
 | 
					                "@sentry/utils": "5.29.2",
 | 
				
			||||||
                "tslib": "^1.9.3"
 | 
					                "tslib": "^1.9.3"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@sentry/hub": {
 | 
					                "@sentry/hub": {
 | 
				
			||||||
                    "version": "5.29.0",
 | 
					                    "version": "5.29.2",
 | 
				
			||||||
                    "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.0.tgz",
 | 
					                    "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.2.tgz",
 | 
				
			||||||
                    "integrity": "sha512-kcDPQsRG4cFdmqDh+TzjeO7lWYxU8s1dZYAbbl1J4uGKmhNB0J7I4ak4SGwTsXLY6fhbierxr6PRaoNojCxjPw==",
 | 
					                    "integrity": "sha512-LaAIo2hwUk9ykeh9RF0cwLy6IRw+DjEee8l1HfEaDFUM6TPGlNNGObMJNXb9/95jzWp7jWwOpQjoIE3jepdQJQ==",
 | 
				
			||||||
                    "requires": {
 | 
					                    "requires": {
 | 
				
			||||||
                        "@sentry/types": "5.29.0",
 | 
					                        "@sentry/types": "5.29.2",
 | 
				
			||||||
                        "@sentry/utils": "5.29.0",
 | 
					                        "@sentry/utils": "5.29.2",
 | 
				
			||||||
                        "tslib": "^1.9.3"
 | 
					                        "tslib": "^1.9.3"
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "@sentry/minimal": {
 | 
					                "@sentry/minimal": {
 | 
				
			||||||
                    "version": "5.29.0",
 | 
					                    "version": "5.29.2",
 | 
				
			||||||
                    "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.0.tgz",
 | 
					                    "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.2.tgz",
 | 
				
			||||||
                    "integrity": "sha512-nhXofdjtO41/caiF1wk1oT3p/QuhOZDYdF/b29DoD2MiAMK9IjhhOXI/gqaRpDKkXlDvd95fDTcx4t/MqqcKXA==",
 | 
					                    "integrity": "sha512-0aINSm8fGA1KyM7PavOBe1GDZDxrvnKt+oFnU0L+bTcw8Lr+of+v6Kwd97rkLRNOLw621xP076dL/7LSIzMuhw==",
 | 
				
			||||||
                    "requires": {
 | 
					                    "requires": {
 | 
				
			||||||
                        "@sentry/hub": "5.29.0",
 | 
					                        "@sentry/hub": "5.29.2",
 | 
				
			||||||
                        "@sentry/types": "5.29.0",
 | 
					                        "@sentry/types": "5.29.2",
 | 
				
			||||||
                        "tslib": "^1.9.3"
 | 
					                        "tslib": "^1.9.3"
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "@sentry/types": {
 | 
					                "@sentry/types": {
 | 
				
			||||||
                    "version": "5.29.0",
 | 
					                    "version": "5.29.2",
 | 
				
			||||||
                    "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.0.tgz",
 | 
					                    "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.2.tgz",
 | 
				
			||||||
                    "integrity": "sha512-iDkxT/9sT3UF+Xb+JyLjZ5caMXsgLfRyV9VXQEiR2J6mgpMielj184d9jeF3bm/VMuAf/VFFqrHlcVsVgmrrMw=="
 | 
					                    "integrity": "sha512-dM9wgt8wy4WRty75QkqQgrw9FV9F+BOMfmc0iaX13Qos7i6Qs2Q0dxtJ83SoR4YGtW8URaHzlDtWlGs5egBiMA=="
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "@sentry/utils": {
 | 
					                "@sentry/utils": {
 | 
				
			||||||
                    "version": "5.29.0",
 | 
					                    "version": "5.29.2",
 | 
				
			||||||
                    "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.0.tgz",
 | 
					                    "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.2.tgz",
 | 
				
			||||||
                    "integrity": "sha512-b2B1gshw2u3EHlAi84PuI5sfmLKXW1z9enMMhNuuNT/CoRp+g5kMAcUv/qYTws7UNnYSvTuVGuZG30v1e0hP9A==",
 | 
					                    "integrity": "sha512-nEwQIDjtFkeE4k6yIk4Ka5XjGRklNLThWLs2xfXlL7uwrYOH2B9UBBOOIRUraBm/g/Xrra3xsam/kRxuiwtXZQ==",
 | 
				
			||||||
                    "requires": {
 | 
					                    "requires": {
 | 
				
			||||||
                        "@sentry/types": "5.29.0",
 | 
					                        "@sentry/types": "5.29.2",
 | 
				
			||||||
                        "tslib": "^1.9.3"
 | 
					                        "tslib": "^1.9.3"
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@ -266,16 +266,16 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@sentry/types": {
 | 
					        "@sentry/types": {
 | 
				
			||||||
            "version": "5.29.0",
 | 
					            "version": "5.29.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.2.tgz",
 | 
				
			||||||
            "integrity": "sha512-iDkxT/9sT3UF+Xb+JyLjZ5caMXsgLfRyV9VXQEiR2J6mgpMielj184d9jeF3bm/VMuAf/VFFqrHlcVsVgmrrMw=="
 | 
					            "integrity": "sha512-dM9wgt8wy4WRty75QkqQgrw9FV9F+BOMfmc0iaX13Qos7i6Qs2Q0dxtJ83SoR4YGtW8URaHzlDtWlGs5egBiMA=="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "@sentry/utils": {
 | 
					        "@sentry/utils": {
 | 
				
			||||||
            "version": "5.29.0",
 | 
					            "version": "5.29.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.2.tgz",
 | 
				
			||||||
            "integrity": "sha512-b2B1gshw2u3EHlAi84PuI5sfmLKXW1z9enMMhNuuNT/CoRp+g5kMAcUv/qYTws7UNnYSvTuVGuZG30v1e0hP9A==",
 | 
					            "integrity": "sha512-nEwQIDjtFkeE4k6yIk4Ka5XjGRklNLThWLs2xfXlL7uwrYOH2B9UBBOOIRUraBm/g/Xrra3xsam/kRxuiwtXZQ==",
 | 
				
			||||||
            "requires": {
 | 
					            "requires": {
 | 
				
			||||||
                "@sentry/types": "5.29.0",
 | 
					                "@sentry/types": "5.29.2",
 | 
				
			||||||
                "tslib": "^1.9.3"
 | 
					                "tslib": "^1.9.3"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
 | 
				
			|||||||
@ -9,8 +9,8 @@
 | 
				
			|||||||
    "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
        "@fortawesome/fontawesome-free": "^5.15.1",
 | 
					        "@fortawesome/fontawesome-free": "^5.15.1",
 | 
				
			||||||
        "@patternfly/patternfly": "^4.70.2",
 | 
					        "@patternfly/patternfly": "^4.70.2",
 | 
				
			||||||
        "@sentry/browser": "^5.29.0",
 | 
					        "@sentry/browser": "^5.29.2",
 | 
				
			||||||
        "@sentry/tracing": "^5.29.0",
 | 
					        "@sentry/tracing": "^5.29.2",
 | 
				
			||||||
        "@types/chart.js": "^2.9.29",
 | 
					        "@types/chart.js": "^2.9.29",
 | 
				
			||||||
        "@types/codemirror": "0.0.102",
 | 
					        "@types/codemirror": "0.0.102",
 | 
				
			||||||
        "chart.js": "^2.9.4",
 | 
					        "chart.js": "^2.9.4",
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./client";
 | 
					import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Application {
 | 
					export class Application {
 | 
				
			||||||
    pk: string;
 | 
					    pk: string;
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { NotFoundError, RequestError } from "./errors";
 | 
					import { NotFoundError, RequestError } from "./Error";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const VERSION = "v2beta";
 | 
					export const VERSION = "v2beta";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient } from "./client";
 | 
					import { DefaultClient } from "./Client";
 | 
				
			||||||
import * as Sentry from "@sentry/browser";
 | 
					import * as Sentry from "@sentry/browser";
 | 
				
			||||||
import { Integrations } from "@sentry/tracing";
 | 
					import { Integrations } from "@sentry/tracing";
 | 
				
			||||||
import { VERSION } from "../constants";
 | 
					import { VERSION } from "../constants";
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient } from "./client";
 | 
					import { DefaultClient } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AuditEvent {
 | 
					export class AuditEvent {
 | 
				
			||||||
    //audit/events/top_per_user/?filter_action=authorize_application
 | 
					    //audit/events/top_per_user/?filter_action=authorize_application
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./client";
 | 
					import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum FlowDesignation {
 | 
					export enum FlowDesignation {
 | 
				
			||||||
    Authentication = "authentication",
 | 
					    Authentication = "authentication",
 | 
				
			||||||
@ -33,6 +33,12 @@ export class Flow {
 | 
				
			|||||||
    static list(filter?: QueryArguments): Promise<PBResponse<Flow>> {
 | 
					    static list(filter?: QueryArguments): Promise<PBResponse<Flow>> {
 | 
				
			||||||
        return DefaultClient.fetch<PBResponse<Flow>>(["flows", "instances"], filter);
 | 
					        return DefaultClient.fetch<PBResponse<Flow>>(["flows", "instances"], filter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static cached(): Promise<number> {
 | 
				
			||||||
 | 
					        return DefaultClient.fetch<PBResponse<Flow>>(["flows", "cached"]).then(r => {
 | 
				
			||||||
 | 
					            return r.pagination.count;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Stage {
 | 
					export class Stage {
 | 
				
			||||||
							
								
								
									
										24
									
								
								web/src/api/Policies.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								web/src/api/Policies.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Policy {
 | 
				
			||||||
 | 
					    pk: string;
 | 
				
			||||||
 | 
					    name: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        throw Error();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static get(pk: string): Promise<Policy> {
 | 
				
			||||||
 | 
					        return DefaultClient.fetch<Policy>(["policies", "all", pk]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static list(filter?: QueryArguments): Promise<PBResponse<Policy>> {
 | 
				
			||||||
 | 
					        return DefaultClient.fetch<PBResponse<Policy>>(["policies", "all"], filter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static cached(): Promise<number> {
 | 
				
			||||||
 | 
					        return DefaultClient.fetch<PBResponse<Policy>>(["policies", "cached"]).then(r => {
 | 
				
			||||||
 | 
					            return r.pagination.count;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,10 +1,5 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./client";
 | 
					import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					import { Policy } from "./Policies";
 | 
				
			||||||
export interface Policy {
 | 
					 | 
				
			||||||
    pk: string;
 | 
					 | 
				
			||||||
    name: string;
 | 
					 | 
				
			||||||
    [key: string]: unknown;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class PolicyBinding {
 | 
					export class PolicyBinding {
 | 
				
			||||||
    pk: string;
 | 
					    pk: string;
 | 
				
			||||||
							
								
								
									
										19
									
								
								web/src/api/Providers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								web/src/api/Providers.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Provider {
 | 
				
			||||||
 | 
					    pk: number;
 | 
				
			||||||
 | 
					    name: string;
 | 
				
			||||||
 | 
					    authorization_flow: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        throw Error();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static get(slug: string): Promise<Provider> {
 | 
				
			||||||
 | 
					        return DefaultClient.fetch<Provider>(["providers", "all", slug]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static list(filter?: QueryArguments): Promise<PBResponse<Provider>> {
 | 
				
			||||||
 | 
					        return DefaultClient.fetch<PBResponse<Provider>>(["providers", "all"], filter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse, QueryArguments } from "./client";
 | 
					import { DefaultClient, PBResponse, QueryArguments } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Source {
 | 
					export class Source {
 | 
				
			||||||
    pk: string;
 | 
					    pk: string;
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient } from "./client";
 | 
					import { DefaultClient } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface TokenResponse {
 | 
					interface TokenResponse {
 | 
				
			||||||
    key: string;
 | 
					    key: string;
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { DefaultClient, PBResponse } from "./client";
 | 
					import { DefaultClient, PBResponse } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let _globalMePromise: Promise<User>;
 | 
					let _globalMePromise: Promise<User>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								web/src/api/Versions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								web/src/api/Versions.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import { DefaultClient } from "./Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Version {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    version_current: string;
 | 
				
			||||||
 | 
					    version_latest: string;
 | 
				
			||||||
 | 
					    outdated: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        throw Error();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static get(): Promise<Version> {
 | 
				
			||||||
 | 
					        return DefaultClient.fetch<Version>(["admin", "version"]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,20 +0,0 @@
 | 
				
			|||||||
import { DefaultClient } from "./client";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class AdminOverview {
 | 
					 | 
				
			||||||
    version: string;
 | 
					 | 
				
			||||||
    version_latest: string;
 | 
					 | 
				
			||||||
    worker_count: number;
 | 
					 | 
				
			||||||
    providers_without_application: number;
 | 
					 | 
				
			||||||
    policies_without_binding: number;
 | 
					 | 
				
			||||||
    cached_policies: number;
 | 
					 | 
				
			||||||
    cached_flows: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor() {
 | 
					 | 
				
			||||||
        throw Error();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static get(): Promise<AdminOverview> {
 | 
					 | 
				
			||||||
        return DefaultClient.fetch<AdminOverview>(["admin", "overview"]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -5,11 +5,6 @@ html {
 | 
				
			|||||||
    --pf-c-nav__link--PaddingLeft: 0.5rem;
 | 
					    --pf-c-nav__link--PaddingLeft: 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Fix patternfly sidebar and header with open Modal */
 | 
					 | 
				
			||||||
.pf-c-page__sidebar {
 | 
					 | 
				
			||||||
    z-index: 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.pf-c-page__header {
 | 
					.pf-c-page__header {
 | 
				
			||||||
    z-index: 0;
 | 
					    z-index: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -162,10 +157,12 @@ select[multiple] {
 | 
				
			|||||||
        background-color: var(--ak-dark-background-light);
 | 
					        background-color: var(--ak-dark-background-light);
 | 
				
			||||||
        color: var(--ak-dark-foreground);
 | 
					        color: var(--ak-dark-foreground);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    .pf-m-tertiary,
 | 
				
			||||||
    .pf-c-button.pf-m-tertiary {
 | 
					    .pf-c-button.pf-m-tertiary {
 | 
				
			||||||
        --pf-c-button--after--BorderColor: var(--ak-dark-foreground-darker);
 | 
					        --pf-c-button--after--BorderColor: var(--ak-dark-foreground-darker);
 | 
				
			||||||
        color: var(--ak-dark-foreground-darker);
 | 
					        color: var(--ak-dark-foreground-darker);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    .pf-m-tertiary:hover,
 | 
				
			||||||
    .pf-c-button.pf-m-tertiary:hover {
 | 
					    .pf-c-button.pf-m-tertiary:hover {
 | 
				
			||||||
        --pf-c-button--after--BorderColor: var(--ak-dark-background-lighter);
 | 
					        --pf-c-button--after--BorderColor: var(--ak-dark-background-lighter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -197,6 +194,9 @@ select[multiple] {
 | 
				
			|||||||
    .pf-c-login__main {
 | 
					    .pf-c-login__main {
 | 
				
			||||||
        background-color: var(--ak-dark-background);
 | 
					        background-color: var(--ak-dark-background);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    .pf-c-login__main-body {
 | 
				
			||||||
 | 
					        color: var(--ak-dark-foreground);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    .pf-c-login__main-footer-links-item-link > img {
 | 
					    .pf-c-login__main-footer-links-item-link > img {
 | 
				
			||||||
        filter: invert(1);
 | 
					        filter: invert(1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -28,4 +28,4 @@ export const ColorStyles = css`
 | 
				
			|||||||
        background-color: var(--pf-global--danger-color--100);
 | 
					        background-color: var(--pf-global--danger-color--100);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
export const VERSION = "0.13.1-stable";
 | 
					export const VERSION = "0.13.3-stable";
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu
 | 
				
			|||||||
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
 | 
					import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
 | 
				
			||||||
// @ts-ignore
 | 
					// @ts-ignore
 | 
				
			||||||
import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
 | 
					import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
 | 
				
			||||||
import { tokenByIdentifier } from "../../api/token";
 | 
					import { tokenByIdentifier } from "../../api/Tokens";
 | 
				
			||||||
import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants";
 | 
					import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-token-copy-button")
 | 
					@customElement("ak-token-copy-button")
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ import { gettext } from "django";
 | 
				
			|||||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { ifDefined } from "lit-html/directives/if-defined";
 | 
					import { ifDefined } from "lit-html/directives/if-defined";
 | 
				
			||||||
import { COMMON_STYLES } from "../../common/styles";
 | 
					import { COMMON_STYLES } from "../../common/styles";
 | 
				
			||||||
 | 
					import { ColorStyles } from "../../constants";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-aggregate-card")
 | 
					@customElement("ak-aggregate-card")
 | 
				
			||||||
export class AggregateCard extends LitElement {
 | 
					export class AggregateCard extends LitElement {
 | 
				
			||||||
@ -24,22 +25,26 @@ export class AggregateCard extends LitElement {
 | 
				
			|||||||
                text-align: center;
 | 
					                text-align: center;
 | 
				
			||||||
                color: var(--pf-global--Color--100);
 | 
					                color: var(--pf-global--Color--100);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        `]);
 | 
					        `, ColorStyles]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderInner(): TemplateResult {
 | 
					    renderInner(): TemplateResult {
 | 
				
			||||||
        return html`<slot></slot>`;
 | 
					        return html`<slot></slot>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderHeaderLink(): TemplateResult {
 | 
				
			||||||
 | 
					        return html`${this.headerLink ? html`<a href="${this.headerLink}">
 | 
				
			||||||
 | 
					            <i class="fa fa-external-link-alt"> </i>
 | 
				
			||||||
 | 
					        </a>` : ""}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render(): TemplateResult {
 | 
					    render(): TemplateResult {
 | 
				
			||||||
        return html`<div class="pf-c-card pf-c-card-aggregate">
 | 
					        return html`<div class="pf-c-card pf-c-card-aggregate">
 | 
				
			||||||
            <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
 | 
					            <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between">
 | 
				
			||||||
                <div class="pf-c-card__header-main">
 | 
					                <div class="pf-c-card__header-main">
 | 
				
			||||||
                    <i class="${ifDefined(this.icon)}"></i> ${this.header ? gettext(this.header) : ""}
 | 
					                    <i class="${ifDefined(this.icon)}"></i> ${this.header ? gettext(this.header) : ""}
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                ${this.headerLink ? html`<a href="${this.headerLink}">
 | 
					                ${this.renderHeaderLink()}
 | 
				
			||||||
                    <i class="fa fa-external-link-alt"> </i>
 | 
					 | 
				
			||||||
                </a>` : ""}
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div class="pf-c-card__body center-value">
 | 
					            <div class="pf-c-card__body center-value">
 | 
				
			||||||
                ${this.renderInner()}
 | 
					                ${this.renderInner()}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { LitElement, html, customElement, TemplateResult, property } from "lit-element";
 | 
					import { LitElement, html, customElement, TemplateResult, property } from "lit-element";
 | 
				
			||||||
import { DefaultClient } from "../../api/client";
 | 
					import { DefaultClient } from "../../api/Client";
 | 
				
			||||||
import "./Message";
 | 
					import "./Message";
 | 
				
			||||||
import { APIMessage } from "./Message";
 | 
					import { APIMessage } from "./Message";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { customElement, html, property, TemplateResult } from "lit-element";
 | 
					import { customElement, html, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { PBResponse } from "../../api/client";
 | 
					import { PBResponse } from "../../api/Client";
 | 
				
			||||||
import { PolicyBinding } from "../../api/policy_binding";
 | 
					 | 
				
			||||||
import { Table } from "../../elements/table/Table";
 | 
					import { Table } from "../../elements/table/Table";
 | 
				
			||||||
 | 
					import { PolicyBinding } from "../../api/PolicyBindings";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../../elements/Tabs";
 | 
					import "../../elements/Tabs";
 | 
				
			||||||
import "../../elements/AdminLoginsChart";
 | 
					import "../../elements/AdminLoginsChart";
 | 
				
			||||||
 | 
				
			|||||||
@ -65,6 +65,32 @@ export class SidebarItem {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async render(activePath: string): Promise<TemplateResult> {
 | 
				
			||||||
 | 
					        if (this.condition) {
 | 
				
			||||||
 | 
					            const result = await this.condition();
 | 
				
			||||||
 | 
					            if (!result) {
 | 
				
			||||||
 | 
					                return html``;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return html` <li class="pf-c-nav__item ${this.hasChildren() ? "pf-m-expandable pf-m-expanded" : ""}">
 | 
				
			||||||
 | 
					            ${this.path ?
 | 
				
			||||||
 | 
					        html`<a href="#${this.path}" class="pf-c-nav__link ${this.isActive(activePath) ? "pf-m-current" : ""}">
 | 
				
			||||||
 | 
					                        ${this.name}
 | 
				
			||||||
 | 
					                    </a>` :
 | 
				
			||||||
 | 
					        html`<a class="pf-c-nav__link" aria-expanded="true">
 | 
				
			||||||
 | 
					                        ${this.name}
 | 
				
			||||||
 | 
					                        <span class="pf-c-nav__toggle">
 | 
				
			||||||
 | 
					                            <i class="fas fa-angle-right" aria-hidden="true"></i>
 | 
				
			||||||
 | 
					                        </span>
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                    <section class="pf-c-nav__subnav">
 | 
				
			||||||
 | 
					                        <ul class="pf-c-nav__simple-list">
 | 
				
			||||||
 | 
					                            ${this._children.map((i) => until(i.render(activePath), html``))}
 | 
				
			||||||
 | 
					                        </ul>
 | 
				
			||||||
 | 
					                    </section>`}
 | 
				
			||||||
 | 
					        </li>`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-sidebar")
 | 
					@customElement("ak-sidebar")
 | 
				
			||||||
@ -118,37 +144,11 @@ export class Sidebar extends LitElement {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async renderItem(item: SidebarItem): Promise<TemplateResult> {
 | 
					 | 
				
			||||||
        if (item.condition) {
 | 
					 | 
				
			||||||
            const result = await item.condition();
 | 
					 | 
				
			||||||
            if (!result) {
 | 
					 | 
				
			||||||
                return html``;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return html` <li class="pf-c-nav__item ${item.hasChildren() ? "pf-m-expandable pf-m-expanded" : ""}">
 | 
					 | 
				
			||||||
            ${item.path ?
 | 
					 | 
				
			||||||
        html`<a href="#${item.path}" class="pf-c-nav__link ${item.isActive(this.activePath) ? "pf-m-current": ""}">
 | 
					 | 
				
			||||||
                        ${item.name}
 | 
					 | 
				
			||||||
                    </a>` :
 | 
					 | 
				
			||||||
        html`<a class="pf-c-nav__link" aria-expanded="true">
 | 
					 | 
				
			||||||
                        ${item.name}
 | 
					 | 
				
			||||||
                        <span class="pf-c-nav__toggle">
 | 
					 | 
				
			||||||
                            <i class="fas fa-angle-right" aria-hidden="true"></i>
 | 
					 | 
				
			||||||
                        </span>
 | 
					 | 
				
			||||||
                    </a>
 | 
					 | 
				
			||||||
                    <section class="pf-c-nav__subnav">
 | 
					 | 
				
			||||||
                        <ul class="pf-c-nav__simple-list">
 | 
					 | 
				
			||||||
                            ${item._children.map((i) => until(this.renderItem(i), html``))}
 | 
					 | 
				
			||||||
                        </ul>
 | 
					 | 
				
			||||||
                    </section>`}
 | 
					 | 
				
			||||||
        </li>`;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    render(): TemplateResult {
 | 
					    render(): TemplateResult {
 | 
				
			||||||
        return html`<nav class="pf-c-nav" aria-label="Global">
 | 
					        return html`<nav class="pf-c-nav" aria-label="Global">
 | 
				
			||||||
            <ak-sidebar-brand></ak-sidebar-brand>
 | 
					            <ak-sidebar-brand></ak-sidebar-brand>
 | 
				
			||||||
            <ul class="pf-c-nav__list">
 | 
					            <ul class="pf-c-nav__list">
 | 
				
			||||||
                ${this.items.map((i) => until(this.renderItem(i), html``))}
 | 
					                ${this.items.map((i) => until(i.render(this.activePath), html``))}
 | 
				
			||||||
            </ul>
 | 
					            </ul>
 | 
				
			||||||
            <ak-sidebar-user></ak-sidebar-user>
 | 
					            <ak-sidebar-user></ak-sidebar-user>
 | 
				
			||||||
        </nav>`;
 | 
					        </nav>`;
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu
 | 
				
			|||||||
import PageStyle from "@patternfly/patternfly/components/Page/page.css";
 | 
					import PageStyle from "@patternfly/patternfly/components/Page/page.css";
 | 
				
			||||||
// @ts-ignore
 | 
					// @ts-ignore
 | 
				
			||||||
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
 | 
					import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
 | 
				
			||||||
import { Config } from "../../api/config";
 | 
					import { Config } from "../../api/Config";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const DefaultConfig: Config = {
 | 
					export const DefaultConfig: Config = {
 | 
				
			||||||
    branding_logo: " /static/dist/assets/icons/icon_left_brand.svg",
 | 
					    branding_logo: " /static/dist/assets/icons/icon_left_brand.svg",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										35
									
								
								web/src/elements/sidebar/SidebarHamburger.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								web/src/elements/sidebar/SidebarHamburger.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import { css, CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element";
 | 
				
			||||||
 | 
					import { COMMON_STYLES } from "../../common/styles";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@customElement("ak-sidebar-hamburger")
 | 
				
			||||||
 | 
					export class SidebarHamburger extends LitElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static get styles(): CSSResult[] {
 | 
				
			||||||
 | 
					        return COMMON_STYLES.concat(
 | 
				
			||||||
 | 
					            css`
 | 
				
			||||||
 | 
					                :host {
 | 
				
			||||||
 | 
					                    position: absolute;
 | 
				
			||||||
 | 
					                    top: var(--pf-c-page__main-section--PaddingTop);
 | 
				
			||||||
 | 
					                    right: var(--pf-c-page__main-section--PaddingRight);
 | 
				
			||||||
 | 
					                    z-index: 250;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            `
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    onClick(): void {
 | 
				
			||||||
 | 
					        this.dispatchEvent(
 | 
				
			||||||
 | 
					            new CustomEvent("ak-sidebar-toggle", {
 | 
				
			||||||
 | 
					                bubbles: true,
 | 
				
			||||||
 | 
					                composed: true,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render(): TemplateResult {
 | 
				
			||||||
 | 
					        return html`<button @click=${() => (this.onClick())} class="pf-c-button pf-m-plain" type="button">
 | 
				
			||||||
 | 
					            <i class="fas fa-bars" aria-hidden="true"></i>
 | 
				
			||||||
 | 
					        </button>`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -5,7 +5,7 @@ import NavStyle from "@patternfly/patternfly/components/Nav/nav.css";
 | 
				
			|||||||
import fa from "@fortawesome/fontawesome-free/css/all.css";
 | 
					import fa from "@fortawesome/fontawesome-free/css/all.css";
 | 
				
			||||||
// @ts-ignore
 | 
					// @ts-ignore
 | 
				
			||||||
import AvatarStyle from "@patternfly/patternfly/components/Avatar/avatar.css";
 | 
					import AvatarStyle from "@patternfly/patternfly/components/Avatar/avatar.css";
 | 
				
			||||||
import { User } from "../../api/user";
 | 
					import { User } from "../../api/Users";
 | 
				
			||||||
import { until } from "lit-html/directives/until";
 | 
					import { until } from "lit-html/directives/until";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-sidebar-user")
 | 
					@customElement("ak-sidebar-user")
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { CSSResult, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import { CSSResult, html, LitElement, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { PBResponse } from "../../api/client";
 | 
					import { PBResponse } from "../../api/Client";
 | 
				
			||||||
import { COMMON_STYLES } from "../../common/styles";
 | 
					import { COMMON_STYLES } from "../../common/styles";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "./TablePagination";
 | 
					import "./TablePagination";
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { COMMON_STYLES } from "../../common/styles";
 | 
					import { COMMON_STYLES } from "../../common/styles";
 | 
				
			||||||
import { PBPagination } from "../../api/client";
 | 
					import { PBPagination } from "../../api/Client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-table-pagination")
 | 
					@customElement("ak-table-pagination")
 | 
				
			||||||
export class TablePagination extends LitElement {
 | 
					export class TablePagination extends LitElement {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,17 +1,19 @@
 | 
				
			|||||||
import { customElement } from "lit-element";
 | 
					import { customElement } from "lit-element";
 | 
				
			||||||
import { User } from "../api/user";
 | 
					import { User } from "../api/Users";
 | 
				
			||||||
import { SidebarItem } from "../elements/sidebar/Sidebar";
 | 
					import { SidebarItem } from "../elements/sidebar/Sidebar";
 | 
				
			||||||
import { SLUG_REGEX } from "../elements/router/Route";
 | 
					import { SLUG_REGEX } from "../elements/router/Route";
 | 
				
			||||||
import { Interface } from "./Interface";
 | 
					import { Interface } from "./Interface";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SIDEBAR_ITEMS: SidebarItem[] = [
 | 
					export const SIDEBAR_ITEMS: SidebarItem[] = [
 | 
				
			||||||
    new SidebarItem("Library", "/library/"),
 | 
					    new SidebarItem("Library", "/library/"),
 | 
				
			||||||
    new SidebarItem("Monitor", "/audit/audit").when((): Promise<boolean> => {
 | 
					    new SidebarItem("Monitor").children(
 | 
				
			||||||
 | 
					        new SidebarItem("Overview", "/administration/overview/"),
 | 
				
			||||||
 | 
					        new SidebarItem("System Tasks", "/administration/tasks/"),
 | 
				
			||||||
 | 
					        new SidebarItem("Events", "/audit/audit"),
 | 
				
			||||||
 | 
					    ).when((): Promise<boolean> => {
 | 
				
			||||||
        return User.me().then(u => u.is_superuser);
 | 
					        return User.me().then(u => u.is_superuser);
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
    new SidebarItem("Administration").children(
 | 
					    new SidebarItem("Administration").children(
 | 
				
			||||||
        new SidebarItem("Overview", "/administration/overview-ng/"),
 | 
					 | 
				
			||||||
        new SidebarItem("System Tasks", "/administration/tasks/"),
 | 
					 | 
				
			||||||
        new SidebarItem("Applications", "/administration/applications/").activeWhen(
 | 
					        new SidebarItem("Applications", "/administration/applications/").activeWhen(
 | 
				
			||||||
            `^/applications/(?<slug>${SLUG_REGEX})/$`
 | 
					            `^/applications/(?<slug>${SLUG_REGEX})/$`
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
@ -19,27 +21,29 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [
 | 
				
			|||||||
            `^/sources/(?<slug>${SLUG_REGEX})/$`,
 | 
					            `^/sources/(?<slug>${SLUG_REGEX})/$`,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        new SidebarItem("Providers", "/administration/providers/"),
 | 
					        new SidebarItem("Providers", "/administration/providers/"),
 | 
				
			||||||
        new SidebarItem("Flows").children(
 | 
					 | 
				
			||||||
            new SidebarItem("Flows", "/administration/flows/").activeWhen(`^/flows/(?<slug>${SLUG_REGEX})/$`),
 | 
					 | 
				
			||||||
            new SidebarItem("Stages", "/administration/stages/"),
 | 
					 | 
				
			||||||
            new SidebarItem("Prompts", "/administration/stages/prompts/"),
 | 
					 | 
				
			||||||
            new SidebarItem("Invitations", "/administration/stages/invitations/"),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        new SidebarItem("User Management").children(
 | 
					 | 
				
			||||||
            new SidebarItem("User", "/administration/users/"),
 | 
					 | 
				
			||||||
            new SidebarItem("Groups", "/administration/groups/")
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        new SidebarItem("Outposts").children(
 | 
					 | 
				
			||||||
        new SidebarItem("Outposts", "/administration/outposts/"),
 | 
					        new SidebarItem("Outposts", "/administration/outposts/"),
 | 
				
			||||||
            new SidebarItem("Service Connections", "/administration/outposts/service_connections/")
 | 
					        new SidebarItem("Outpost Service Connections", "/administration/outposts/service_connections/"),
 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        new SidebarItem("Policies", "/administration/policies/"),
 | 
					        new SidebarItem("Policies", "/administration/policies/"),
 | 
				
			||||||
        new SidebarItem("Property Mappings", "/administration/property-mappings"),
 | 
					        new SidebarItem("Property Mappings", "/administration/property-mappings"),
 | 
				
			||||||
        new SidebarItem("Certificates", "/administration/crypto/certificates"),
 | 
					        new SidebarItem("Certificates", "/administration/crypto/certificates"),
 | 
				
			||||||
        new SidebarItem("Tokens", "/administration/tokens/"),
 | 
					        new SidebarItem("Tokens", "/administration/tokens/"),
 | 
				
			||||||
    ).when((): Promise<boolean> => {
 | 
					    ).when((): Promise<boolean> => {
 | 
				
			||||||
        return User.me().then(u => u.is_superuser);
 | 
					        return User.me().then(u => u.is_superuser);
 | 
				
			||||||
    })
 | 
					    }),
 | 
				
			||||||
 | 
					    new SidebarItem("Flows").children(
 | 
				
			||||||
 | 
					        new SidebarItem("Flows", "/administration/flows/").activeWhen(`^/flows/(?<slug>${SLUG_REGEX})/$`),
 | 
				
			||||||
 | 
					        new SidebarItem("Stages", "/administration/stages/"),
 | 
				
			||||||
 | 
					        new SidebarItem("Prompts", "/administration/stages/prompts/"),
 | 
				
			||||||
 | 
					        new SidebarItem("Invitations", "/administration/stages/invitations/"),
 | 
				
			||||||
 | 
					    ).when((): Promise<boolean> => {
 | 
				
			||||||
 | 
					        return User.me().then(u => u.is_superuser);
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    new SidebarItem("User Management").children(
 | 
				
			||||||
 | 
					        new SidebarItem("User", "/administration/users/"),
 | 
				
			||||||
 | 
					        new SidebarItem("Groups", "/administration/groups/")
 | 
				
			||||||
 | 
					    ).when((): Promise<boolean> => {
 | 
				
			||||||
 | 
					        return User.me().then(u => u.is_superuser);
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-interface-admin")
 | 
					@customElement("ak-interface-admin")
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,14 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { html, LitElement, TemplateResult } from "lit-element";
 | 
					import { html, LitElement, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { SidebarItem } from "../elements/sidebar/Sidebar";
 | 
					import { SidebarItem } from "../elements/sidebar/Sidebar";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../elements/router/RouterOutlet";
 | 
					import "../elements/router/RouterOutlet";
 | 
				
			||||||
import "../elements/messages/MessageContainer";
 | 
					import "../elements/messages/MessageContainer";
 | 
				
			||||||
 | 
					import "../elements/sidebar/SidebarHamburger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export abstract class Interface extends LitElement {
 | 
					export abstract class Interface extends LitElement {
 | 
				
			||||||
 | 
					    @property({type: Boolean})
 | 
				
			||||||
 | 
					    sidebarOpen?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract get sidebar(): SidebarItem[];
 | 
					    abstract get sidebar(): SidebarItem[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,11 +16,20 @@ export abstract class Interface extends LitElement {
 | 
				
			|||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        window.addEventListener("ak-sidebar-toggle", () => {
 | 
				
			||||||
 | 
					            this.sidebarOpen = !this.sidebarOpen;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render(): TemplateResult {
 | 
					    render(): TemplateResult {
 | 
				
			||||||
        return html`<ak-message-container></ak-message-container>
 | 
					        return html`<ak-message-container></ak-message-container>
 | 
				
			||||||
            <div class="pf-c-page">
 | 
					            <div class="pf-c-page">
 | 
				
			||||||
                <a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content">${gettext("Skip to content")}</a>
 | 
					                <a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content">${gettext("Skip to content")}</a>
 | 
				
			||||||
                <ak-sidebar class="pf-c-page__sidebar" .items=${this.sidebar}>
 | 
					                <ak-sidebar-hamburger>
 | 
				
			||||||
 | 
					                </ak-sidebar-hamburger>
 | 
				
			||||||
 | 
					                <ak-sidebar class="pf-c-page__sidebar ${this.sidebarOpen ? "pf-m-expanded" : "pf-m-collapsed"}" .items=${this.sidebar}>
 | 
				
			||||||
                </ak-sidebar>
 | 
					                </ak-sidebar>
 | 
				
			||||||
                <main class="pf-c-page__main">
 | 
					                <main class="pf-c-page__main">
 | 
				
			||||||
                    <ak-router-outlet role="main" class="pf-c-page__main" tabindex="-1" id="main-content" defaultUrl="/library/">
 | 
					                    <ak-router-outlet role="main" class="pf-c-page__main" tabindex="-1" id="main-content" defaultUrl="/library/">
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { ifDefined } from "lit-html/directives/if-defined";
 | 
					import { ifDefined } from "lit-html/directives/if-defined";
 | 
				
			||||||
import { Application } from "../api/application";
 | 
					import { Application } from "../api/Applications";
 | 
				
			||||||
import { PBResponse } from "../api/client";
 | 
					import { PBResponse } from "../api/Client";
 | 
				
			||||||
import { COMMON_STYLES } from "../common/styles";
 | 
					import { COMMON_STYLES } from "../common/styles";
 | 
				
			||||||
import { loading, truncate } from "../utils";
 | 
					import { loading, truncate } from "../utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,71 +1,27 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element";
 | 
				
			||||||
import { AdminOverview } from "../../api/admin_overview";
 | 
					import { DefaultClient } from "../../api/Client";
 | 
				
			||||||
import { DefaultClient } from "../../api/client";
 | 
					 | 
				
			||||||
import { User } from "../../api/user";
 | 
					 | 
				
			||||||
import { COMMON_STYLES } from "../../common/styles";
 | 
					import { COMMON_STYLES } from "../../common/styles";
 | 
				
			||||||
import { AggregatePromiseCard } from "../../elements/cards/AggregatePromiseCard";
 | 
					 | 
				
			||||||
import { SpinnerSize } from "../../elements/Spinner";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../../elements/AdminLoginsChart";
 | 
					import "../../elements/AdminLoginsChart";
 | 
				
			||||||
 | 
					import "../../elements/cards/AggregatePromiseCard";
 | 
				
			||||||
import "./TopApplicationsTable";
 | 
					import "./TopApplicationsTable";
 | 
				
			||||||
 | 
					import "./cards/AdminStatusCard";
 | 
				
			||||||
@customElement("ak-admin-status-card")
 | 
					import "./cards/FlowCacheStatusCard";
 | 
				
			||||||
export class AdminStatusCard extends AggregatePromiseCard {
 | 
					import "./cards/PolicyCacheStatusCard";
 | 
				
			||||||
 | 
					import "./cards/PolicyUnboundStatusCard";
 | 
				
			||||||
    @property({type: Number})
 | 
					import "./cards/ProviderStatusCard";
 | 
				
			||||||
    value?: number;
 | 
					import "./cards/UserCountStatusCard";
 | 
				
			||||||
 | 
					import "./cards/VersionStatusCard";
 | 
				
			||||||
    @property()
 | 
					import "./cards/WorkerStatusCard";
 | 
				
			||||||
    warningText?: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property({type: Number})
 | 
					 | 
				
			||||||
    lessThanThreshold?: number;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    renderNone(): TemplateResult {
 | 
					 | 
				
			||||||
        return html`<ak-spinner size=${SpinnerSize.Large}></ak-spinner>`;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    renderGood(): TemplateResult {
 | 
					 | 
				
			||||||
        return html`<p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
            <i class="fa fa-check-circle"></i> ${this.value}
 | 
					 | 
				
			||||||
        </p>`;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    renderBad(): TemplateResult {
 | 
					 | 
				
			||||||
        return html`<p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
            <i class="fa fa-exclamation-triangle"></i> ${this.value}
 | 
					 | 
				
			||||||
        </p>
 | 
					 | 
				
			||||||
        <p class="subtext">${this.warningText ? gettext(this.warningText) : ""}</p>`;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    renderInner(): TemplateResult {
 | 
					 | 
				
			||||||
        if (!this.value) {
 | 
					 | 
				
			||||||
            return this.renderNone();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return html``;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-admin-overview")
 | 
					@customElement("ak-admin-overview")
 | 
				
			||||||
export class AdminOverviewPage extends LitElement {
 | 
					export class AdminOverviewPage extends LitElement {
 | 
				
			||||||
    @property({attribute: false})
 | 
					 | 
				
			||||||
    data?: AdminOverview;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property({attribute: false})
 | 
					 | 
				
			||||||
    users?: Promise<number>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static get styles(): CSSResult[] {
 | 
					    static get styles(): CSSResult[] {
 | 
				
			||||||
        return COMMON_STYLES;
 | 
					        return COMMON_STYLES;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    firstUpdated(): void {
 | 
					 | 
				
			||||||
        AdminOverview.get().then(value => this.data = value);
 | 
					 | 
				
			||||||
        this.users = User.count();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    render(): TemplateResult {
 | 
					    render(): TemplateResult {
 | 
				
			||||||
        return html`<section class="pf-c-page__main-section pf-m-light">
 | 
					        return html`<section class="pf-c-page__main-section pf-m-light">
 | 
				
			||||||
            <div class="pf-c-content">
 | 
					            <div class="pf-c-content">
 | 
				
			||||||
@ -80,48 +36,20 @@ export class AdminOverviewPage extends LitElement {
 | 
				
			|||||||
                <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;">
 | 
					                <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;">
 | 
				
			||||||
                    <ak-top-applications-table></ak-top-applications-table>
 | 
					                    <ak-top-applications-table></ak-top-applications-table>
 | 
				
			||||||
                </ak-aggregate-card>
 | 
					                </ak-aggregate-card>
 | 
				
			||||||
                <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Workers">
 | 
					                <ak-admin-status-card-provider class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Providers" headerLink="#/administration/providers/">
 | 
				
			||||||
                    ${this.data ?
 | 
					                </ak-admin-status-card-provider>
 | 
				
			||||||
        this.data?.worker_count < 1 ?
 | 
					                <ak-admin-status-card-policy-unbound class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-infrastructure" header="Policies" headerLink="#/administration/policies/">
 | 
				
			||||||
            html`<p class="ak-aggregate-card">
 | 
					                </ak-admin-status-card-policy-unbound>
 | 
				
			||||||
                                    <i class="fa fa-exclamation-triangle"></i> ${this.data?.worker_count}
 | 
					                <ak-admin-status-card-user-count class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-user" header="Users" headerLink="#/administration/users/">
 | 
				
			||||||
                                </p>
 | 
					                </ak-admin-status-card-user-count>
 | 
				
			||||||
                                <p class="subtext">${gettext("No workers connected.")}</p>` :
 | 
					                <ak-admin-status-version class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-bundle" header="Version" headerLink="https://github.com/BeryJu/authentik/releases">
 | 
				
			||||||
            html`<p class="ak-aggregate-card">
 | 
					                </ak-admin-status-version>
 | 
				
			||||||
                                    <i class="fa fa-check-circle"></i> ${this.data?.worker_count}
 | 
					                <ak-admin-status-card-workers class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Workers">
 | 
				
			||||||
                                </p>`
 | 
					                </ak-admin-status-card-workers>
 | 
				
			||||||
        : html`<ak-spinner size=${SpinnerSize.Large}></ak-spinner>`}
 | 
					                <ak-admin-status-card-policy-cache class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Cached Policies">
 | 
				
			||||||
                </ak-aggregate-card>
 | 
					                </ak-admin-status-card-policy-cache>
 | 
				
			||||||
                <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Providers" headerLink="#/administration/providers/">
 | 
					                <ak-admin-status-card-flow-cache class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Cached Flows">
 | 
				
			||||||
                    ${this.data ?
 | 
					                </ak-admin-status-card-flow-cache>
 | 
				
			||||||
        this.data?.providers_without_application > 1 ?
 | 
					 | 
				
			||||||
            html`<p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                                    <i class="fa fa-exclamation-triangle"></i> 0
 | 
					 | 
				
			||||||
                                </p>
 | 
					 | 
				
			||||||
                                <p class="subtext">${gettext("At least one Provider has no application assigned.")}</p>` :
 | 
					 | 
				
			||||||
            html`<p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                                    <i class="fa fa-check-circle"></i> 0
 | 
					 | 
				
			||||||
                                </p>`
 | 
					 | 
				
			||||||
        : html`<ak-spinner size=${SpinnerSize.Large}></ak-spinner>`}
 | 
					 | 
				
			||||||
                </ak-aggregate-card>
 | 
					 | 
				
			||||||
                <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Policies" headerLink="#/administration/policies/">
 | 
					 | 
				
			||||||
                    ${this.data ?
 | 
					 | 
				
			||||||
        this.data?.policies_without_binding > 1 ?
 | 
					 | 
				
			||||||
            html`<p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                                    <i class="fa fa-exclamation-triangle"></i> 0
 | 
					 | 
				
			||||||
                                </p>
 | 
					 | 
				
			||||||
                                <p class="subtext">${gettext("Policies without binding exist.")}</p>` :
 | 
					 | 
				
			||||||
            html`<p class="ak-aggregate-card">
 | 
					 | 
				
			||||||
                                    <i class="fa fa-check-circle"></i> 0
 | 
					 | 
				
			||||||
                                </p>`
 | 
					 | 
				
			||||||
        : html`<ak-spinner size=${SpinnerSize.Large}></ak-spinner>`}
 | 
					 | 
				
			||||||
                </ak-aggregate-card>
 | 
					 | 
				
			||||||
                <ak-aggregate-card-promise
 | 
					 | 
				
			||||||
                    icon="pf-icon pf-icon-user"
 | 
					 | 
				
			||||||
                    header="Users"
 | 
					 | 
				
			||||||
                    headerLink="#/administration/users/"
 | 
					 | 
				
			||||||
                    .promise=${this.users}>
 | 
					 | 
				
			||||||
                </ak-aggregate-card-promise>
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </section>`;
 | 
					        </section>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { AuditEvent, TopNEvent } from "../../api/events";
 | 
					import { AuditEvent, TopNEvent } from "../../api/Events";
 | 
				
			||||||
import { COMMON_STYLES } from "../../common/styles";
 | 
					import { COMMON_STYLES } from "../../common/styles";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../../elements/Spinner";
 | 
					import "../../elements/Spinner";
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										37
									
								
								web/src/pages/admin-overview/cards/AdminStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								web/src/pages/admin-overview/cards/AdminStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					import { html, TemplateResult } from "lit-html";
 | 
				
			||||||
 | 
					import { until } from "lit-html/directives/until";
 | 
				
			||||||
 | 
					import { AggregateCard } from "../../../elements/cards/AggregateCard";
 | 
				
			||||||
 | 
					import { SpinnerSize } from "../../../elements/Spinner";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface AdminStatus {
 | 
				
			||||||
 | 
					    icon: string;
 | 
				
			||||||
 | 
					    message?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export abstract class AdminStatusCard<T> extends AggregateCard {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    abstract getPrimaryValue(): Promise<T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    abstract getStatus(value: T): Promise<AdminStatus>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    value?: T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderValue(): TemplateResult {
 | 
				
			||||||
 | 
					        return html`${this.value}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderInner(): TemplateResult {
 | 
				
			||||||
 | 
					        return html`<p class="center-value">
 | 
				
			||||||
 | 
					            ${until(this.getPrimaryValue().then((v) => {
 | 
				
			||||||
 | 
					        this.value = v;
 | 
				
			||||||
 | 
					        return this.getStatus(v);
 | 
				
			||||||
 | 
					    }).then((status) => {
 | 
				
			||||||
 | 
					        return html`<p class="ak-aggregate-card">
 | 
				
			||||||
 | 
					                    <i class="${status.icon}"></i> ${this.renderValue()}
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
 | 
					                ${status.message ? html`<p class="subtext">${status.message}</p>` : html``}`;
 | 
				
			||||||
 | 
					    }), html`<ak-spinner size="${SpinnerSize.Large}"></ak-spinner>`)}
 | 
				
			||||||
 | 
					        </p>`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										36
									
								
								web/src/pages/admin-overview/cards/FlowCacheStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web/src/pages/admin-overview/cards/FlowCacheStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import { gettext } from "django";
 | 
				
			||||||
 | 
					import { customElement, html, TemplateResult } from "lit-element";
 | 
				
			||||||
 | 
					import { Flow } from "../../../api/Flows";
 | 
				
			||||||
 | 
					import { AdminStatus, AdminStatusCard } from "./AdminStatusCard";
 | 
				
			||||||
 | 
					import "../../../elements/buttons/ModalButton";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@customElement("ak-admin-status-card-flow-cache")
 | 
				
			||||||
 | 
					export class FlowCacheStatusCard extends AdminStatusCard<number> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getPrimaryValue(): Promise<number> {
 | 
				
			||||||
 | 
					        return Flow.cached();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getStatus(value: number): Promise<AdminStatus> {
 | 
				
			||||||
 | 
					        if (value < 1) {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-exclamation-triangle pf-m-warning",
 | 
				
			||||||
 | 
					                message: gettext("No flows cached."),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-check-circle pf-m-success"
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderHeaderLink(): TemplateResult {
 | 
				
			||||||
 | 
					        return html`<ak-modal-button href="/administration/overview/cache/flow/">
 | 
				
			||||||
 | 
					            <a slot="trigger">
 | 
				
			||||||
 | 
					                <i class="fa fa-trash"> </i>
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					            <div slot="modal"></div>
 | 
				
			||||||
 | 
					        </ak-modal-button>`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								web/src/pages/admin-overview/cards/PolicyCacheStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								web/src/pages/admin-overview/cards/PolicyCacheStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					import { gettext } from "django";
 | 
				
			||||||
 | 
					import { customElement } from "lit-element";
 | 
				
			||||||
 | 
					import { TemplateResult, html } from "lit-html";
 | 
				
			||||||
 | 
					import { Policy } from "../../../api/Policies";
 | 
				
			||||||
 | 
					import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
 | 
				
			||||||
 | 
					import "../../../elements/buttons/ModalButton";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@customElement("ak-admin-status-card-policy-cache")
 | 
				
			||||||
 | 
					export class PolicyCacheStatusCard extends AdminStatusCard<number> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getPrimaryValue(): Promise<number> {
 | 
				
			||||||
 | 
					        return Policy.cached();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getStatus(value: number): Promise<AdminStatus> {
 | 
				
			||||||
 | 
					        if (value < 1) {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-exclamation-triangle pf-m-warning",
 | 
				
			||||||
 | 
					                message: gettext("No policies cached. Users may experience slow response times."),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-check-circle pf-m-success"
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderHeaderLink(): TemplateResult {
 | 
				
			||||||
 | 
					        return html`<ak-modal-button href="/administration/overview/cache/policy/">
 | 
				
			||||||
 | 
					            <a slot="trigger">
 | 
				
			||||||
 | 
					                <i class="fa fa-trash"> </i>
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					            <div slot="modal"></div>
 | 
				
			||||||
 | 
					        </ak-modal-button>`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					import { gettext } from "django";
 | 
				
			||||||
 | 
					import { customElement } from "lit-element";
 | 
				
			||||||
 | 
					import { Policy } from "../../../api/Policies";
 | 
				
			||||||
 | 
					import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@customElement("ak-admin-status-card-policy-unbound")
 | 
				
			||||||
 | 
					export class PolicyUnboundStatusCard extends AdminStatusCard<number> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getPrimaryValue(): Promise<number> {
 | 
				
			||||||
 | 
					        return Policy.list({
 | 
				
			||||||
 | 
					            "bindings__isnull": true,
 | 
				
			||||||
 | 
					            "promptstage__isnull": true,
 | 
				
			||||||
 | 
					        }).then((response) => {
 | 
				
			||||||
 | 
					            return response.pagination.count;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getStatus(value: number): Promise<AdminStatus> {
 | 
				
			||||||
 | 
					        if (value > 0) {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-exclamation-triangle pf-m-warning",
 | 
				
			||||||
 | 
					                message: gettext("Policies without binding exist."),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-check-circle pf-m-success"
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								web/src/pages/admin-overview/cards/ProviderStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								web/src/pages/admin-overview/cards/ProviderStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					import { gettext } from "django";
 | 
				
			||||||
 | 
					import { customElement } from "lit-element";
 | 
				
			||||||
 | 
					import { Provider } from "../../../api/Providers";
 | 
				
			||||||
 | 
					import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@customElement("ak-admin-status-card-provider")
 | 
				
			||||||
 | 
					export class ProviderStatusCard extends AdminStatusCard<number> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getPrimaryValue(): Promise<number> {
 | 
				
			||||||
 | 
					        return Provider.list({
 | 
				
			||||||
 | 
					            "application__isnull": true
 | 
				
			||||||
 | 
					        }).then((response) => {
 | 
				
			||||||
 | 
					            return response.pagination.count;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getStatus(value: number): Promise<AdminStatus> {
 | 
				
			||||||
 | 
					        if (value > 0) {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-exclamation-triangle pf-m-warning",
 | 
				
			||||||
 | 
					                message: gettext("Warning: At least one Provider has no application assigned."),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-check-circle pf-m-success"
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								web/src/pages/admin-overview/cards/UserCountStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								web/src/pages/admin-overview/cards/UserCountStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { customElement } from "lit-element";
 | 
				
			||||||
 | 
					import { User } from "../../../api/Users";
 | 
				
			||||||
 | 
					import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@customElement("ak-admin-status-card-user-count")
 | 
				
			||||||
 | 
					export class UserCountStatusCard extends AdminStatusCard<number> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getPrimaryValue(): Promise<number> {
 | 
				
			||||||
 | 
					        return User.count();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
 | 
					    getStatus(value: number): Promise<AdminStatus> {
 | 
				
			||||||
 | 
					        return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					            icon: "fa fa-check-circle pf-m-success"
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								web/src/pages/admin-overview/cards/VersionStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								web/src/pages/admin-overview/cards/VersionStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					import { gettext } from "django";
 | 
				
			||||||
 | 
					import { customElement, html, TemplateResult } from "lit-element";
 | 
				
			||||||
 | 
					import { Version } from "../../../api/Versions";
 | 
				
			||||||
 | 
					import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@customElement("ak-admin-status-version")
 | 
				
			||||||
 | 
					export class VersionStatusCard extends AdminStatusCard<Version> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getPrimaryValue(): Promise<Version> {
 | 
				
			||||||
 | 
					        return Version.get();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getStatus(value: Version): Promise<AdminStatus> {
 | 
				
			||||||
 | 
					        if (value.outdated) {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-exclamation-triangle pf-m-warning",
 | 
				
			||||||
 | 
					                message: gettext(`${value.version_latest} is available!`),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-check-circle pf-m-success",
 | 
				
			||||||
 | 
					                message: gettext("Up-to-date!")
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderValue(): TemplateResult {
 | 
				
			||||||
 | 
					        return html`${this.value?.version_current}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								web/src/pages/admin-overview/cards/WorkerStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								web/src/pages/admin-overview/cards/WorkerStatusCard.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import { gettext } from "django";
 | 
				
			||||||
 | 
					import { customElement } from "lit-element";
 | 
				
			||||||
 | 
					import { DefaultClient, PBResponse } from "../../../api/Client";
 | 
				
			||||||
 | 
					import { AdminStatus, AdminStatusCard } from "./AdminStatusCard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@customElement("ak-admin-status-card-workers")
 | 
				
			||||||
 | 
					export class WorkersStatusCard extends AdminStatusCard<number> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getPrimaryValue(): Promise<number> {
 | 
				
			||||||
 | 
					        return DefaultClient.fetch<PBResponse<number>>(["admin", "workers"]).then((r) => {
 | 
				
			||||||
 | 
					            return r.pagination.count;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getStatus(value: number): Promise<AdminStatus> {
 | 
				
			||||||
 | 
					        if (value < 1) {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-exclamation-triangle pf-m-warning",
 | 
				
			||||||
 | 
					                message: gettext("No workers connected. Background tasks will not run."),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return Promise.resolve<AdminStatus>({
 | 
				
			||||||
 | 
					                icon: "fa fa-check-circle pf-m-success"
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { customElement, html, TemplateResult } from "lit-element";
 | 
					import { customElement, html, TemplateResult } from "lit-element";
 | 
				
			||||||
import { Application } from "../../api/application";
 | 
					import { Application } from "../../api/Applications";
 | 
				
			||||||
import { PBResponse } from "../../api/client";
 | 
					import { PBResponse } from "../../api/Client";
 | 
				
			||||||
import { TablePage } from "../../elements/table/TablePage";
 | 
					import { TablePage } from "../../elements/table/TablePage";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../../elements/buttons/ModalButton";
 | 
					import "../../elements/buttons/ModalButton";
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { Application } from "../../api/application";
 | 
					import { Application } from "../../api/Applications";
 | 
				
			||||||
import { DefaultClient } from "../../api/client";
 | 
					import { DefaultClient } from "../../api/Client";
 | 
				
			||||||
import { COMMON_STYLES } from "../../common/styles";
 | 
					import { COMMON_STYLES } from "../../common/styles";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../../elements/Tabs";
 | 
					import "../../elements/Tabs";
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { customElement, html, property, TemplateResult } from "lit-element";
 | 
					import { customElement, html, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { PBResponse } from "../../api/client";
 | 
					import { PBResponse } from "../../api/Client";
 | 
				
			||||||
import { Table } from "../../elements/table/Table";
 | 
					import { Table } from "../../elements/table/Table";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../../elements/Tabs";
 | 
					import "../../elements/Tabs";
 | 
				
			||||||
@ -8,7 +8,7 @@ import "../../elements/AdminLoginsChart";
 | 
				
			|||||||
import "../../elements/buttons/ModalButton";
 | 
					import "../../elements/buttons/ModalButton";
 | 
				
			||||||
import "../../elements/buttons/SpinnerButton";
 | 
					import "../../elements/buttons/SpinnerButton";
 | 
				
			||||||
import "../../elements/policies/BoundPoliciesList";
 | 
					import "../../elements/policies/BoundPoliciesList";
 | 
				
			||||||
import { FlowStageBinding } from "../../api/flow";
 | 
					import { FlowStageBinding } from "../../api/Flows";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-bound-stages-list")
 | 
					@customElement("ak-bound-stages-list")
 | 
				
			||||||
export class BoundStagesList extends Table<FlowStageBinding> {
 | 
					export class BoundStagesList extends Table<FlowStageBinding> {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { COMMON_STYLES } from "../../common/styles";
 | 
					import { COMMON_STYLES } from "../../common/styles";
 | 
				
			||||||
import { Flow } from "../../api/flow";
 | 
					import { Flow } from "../../api/Flows";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../../elements/Tabs";
 | 
					import "../../elements/Tabs";
 | 
				
			||||||
import "../../elements/AdminLoginsChart";
 | 
					import "../../elements/AdminLoginsChart";
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ import "../../elements/AdminLoginsChart";
 | 
				
			|||||||
import "../../elements/buttons/ModalButton";
 | 
					import "../../elements/buttons/ModalButton";
 | 
				
			||||||
import "../../elements/buttons/SpinnerButton";
 | 
					import "../../elements/buttons/SpinnerButton";
 | 
				
			||||||
import "../../elements/policies/BoundPoliciesList";
 | 
					import "../../elements/policies/BoundPoliciesList";
 | 
				
			||||||
import { Source } from "../../api/source";
 | 
					import { Source } from "../../api/Sources";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-source-view")
 | 
					@customElement("ak-source-view")
 | 
				
			||||||
export class SourceViewPage extends LitElement {
 | 
					export class SourceViewPage extends LitElement {
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ export const ROUTES: Route[] = [
 | 
				
			|||||||
    new Route(new RegExp("^/$")).redirect("/library/"),
 | 
					    new Route(new RegExp("^/$")).redirect("/library/"),
 | 
				
			||||||
    new Route(new RegExp("^#.*")).redirect("/library/"),
 | 
					    new Route(new RegExp("^#.*")).redirect("/library/"),
 | 
				
			||||||
    new Route(new RegExp("^/library/$"), html`<ak-library></ak-library>`),
 | 
					    new Route(new RegExp("^/library/$"), html`<ak-library></ak-library>`),
 | 
				
			||||||
    new Route(new RegExp("^/administration/overview-ng/$"), html`<ak-admin-overview></ak-admin-overview>`),
 | 
					    new Route(new RegExp("^/administration/overview/$"), html`<ak-admin-overview></ak-admin-overview>`),
 | 
				
			||||||
    new Route(new RegExp("^/applications/$"), html`<ak-application-list></ak-application-list>`),
 | 
					    new Route(new RegExp("^/applications/$"), html`<ak-application-list></ak-application-list>`),
 | 
				
			||||||
    new Route(new RegExp(`^/applications/(?<slug>${SLUG_REGEX})/$`)).then((args) => {
 | 
					    new Route(new RegExp(`^/applications/(?<slug>${SLUG_REGEX})/$`)).then((args) => {
 | 
				
			||||||
        return html`<ak-application-view .args=${args}></ak-application-view>`;
 | 
					        return html`<ak-application-view .args=${args}></ak-application-view>`;
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@ Download the latest `docker-compose.yml` from [here](https://raw.githubuserconte
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env`
 | 
					To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To optionally deploy a different version run `echo AUTHENTIK_TAG=0.13.1-stable >> .env`
 | 
					To optionally deploy a different version run `echo AUTHENTIK_TAG=0.13.3-stable >> .env`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If this is a fresh authentik install run the following commands to generate a password:
 | 
					If this is a fresh authentik install run the following commands to generate a password:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,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: 0.13.1-stable
 | 
					    tag: 0.13.3-stable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
serverReplicas: 1
 | 
					serverReplicas: 1
 | 
				
			||||||
workerReplicas: 1
 | 
					workerReplicas: 1
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user