Compare commits
51 Commits
version/0.
...
version/0.
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 040e148a73 | |||
| b85d550ee0 | |||
| ce95139d66 | |||
| 46436a5780 | |||
| 835a9aaaf2 | |||
| 42005e7def | |||
| d9956e1e9c | |||
| 4b1e73251a | |||
| 736dbdca33 | |||
| 789b8e5d3e | |||
| 074b55f66b | |||
| d9bc5ea4d1 | |||
| 716bb9f188 | |||
| dd496619a2 | |||
| 51d07f7913 | |||
| 5c4163579b | |||
| 5a73413d58 | |||
| 51a5d4bf49 | |||
| 8bbb854073 |
@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.13.0-stable
|
current_version = 0.13.2-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.0-stable
|
-t beryju/authentik:0.13.2-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.0-stable
|
run: docker push beryju/authentik:0.13.2-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.0-stable \
|
-t beryju/authentik-proxy:0.13.2-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.0-stable
|
run: docker push beryju/authentik-proxy:0.13.2-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.0-stable \
|
-t beryju/authentik-static:0.13.2-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.0-stable
|
run: docker push beryju/authentik-static:0.13.2-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.0-stable
|
tagName: 0.13.2-stable
|
||||||
environment: beryjuorg-prod
|
environment: beryjuorg-prod
|
||||||
|
|||||||
54
Pipfile.lock
generated
54
Pipfile.lock
generated
@ -53,10 +53,10 @@
|
|||||||
},
|
},
|
||||||
"autobahn": {
|
"autobahn": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:74ca21d3552825615a65d47ec38d0aa5961a1345f7639f5b0e2abfff40cbfd07",
|
"sha256:491238c31f78721eaa9d0593909ab455a4ea68127aadd76ecf67185143f5f298",
|
||||||
"sha256:85c14b4a404146339ffd171e1ea1f65bf71e2f777d810aaa8a36119273869e3d"
|
"sha256:72b68a1ce1e10e3cbcc3b280aae86d5b2e7a1f409febab1ab91a8a3274113f6e"
|
||||||
],
|
],
|
||||||
"version": "==20.12.1"
|
"version": "==20.12.2"
|
||||||
},
|
},
|
||||||
"automat": {
|
"automat": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -74,18 +74,18 @@
|
|||||||
},
|
},
|
||||||
"boto3": {
|
"boto3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2a6e92194bd6f2341908dc9b133af057ea1ff20b7d7e54674f48cdb531d93ca5",
|
"sha256:18d7ba5d623d4794f439201ab900c9c14a50019bc52d9113b0a2bb2e1ef9af2c",
|
||||||
"sha256:a35e0915547ea659ddd832c9aaf55038c56fa894c4cc2a2a46cd6c642494012a"
|
"sha256:1ddfd307d409e7bc792bd12923078f59c2f56fbba4065c320b3f768481bbbbf7"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.16.35"
|
"version": "==1.16.38"
|
||||||
},
|
},
|
||||||
"botocore": {
|
"botocore": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:633aa910509b060717df4130f7e2841f1101c0c47fd5871f4903b4b1dbab7e23",
|
"sha256:1f1ecb1b0c6ffc8fcdd5eeb40f33e986dfe9724dc66c83017014a0506af6378a",
|
||||||
"sha256:d31dce56799edb5796085d5296931faae201e28e14e568d9db4dac237a135fe3"
|
"sha256:38ccc132c5b9d1e7a4dd37af78061fd2dd0e4fd611f527b409a4e9a679a85cdb"
|
||||||
],
|
],
|
||||||
"version": "==1.19.35"
|
"version": "==1.19.38"
|
||||||
},
|
},
|
||||||
"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": [
|
||||||
@ -875,10 +875,10 @@
|
|||||||
},
|
},
|
||||||
"pyopenssl": {
|
"pyopenssl": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:898aefbde331ba718570244c3b01dcddb1b31a3b336613436a45e52e27d9a82d",
|
"sha256:4c231c759543ba02560fcd2480c48dcec4dae34c9da7d3747c508227e0624b51",
|
||||||
"sha256:92f08eccbd73701cf744e8ffd6989aa7842d48cbe3fea8a7c031c5647f590ac5"
|
"sha256:818ae18e06922c066f777a33f1fca45786d85edfe71cd043de6379337a7f274b"
|
||||||
],
|
],
|
||||||
"version": "==20.0.0"
|
"version": "==20.0.1"
|
||||||
},
|
},
|
||||||
"pyparsing": {
|
"pyparsing": {
|
||||||
"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": [
|
||||||
@ -1586,11 +1586,11 @@
|
|||||||
},
|
},
|
||||||
"pytest": {
|
"pytest": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b12e09409c5bdedc28d308469e156127004a436b41e9b44f9bff6446cbab9152",
|
"sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8",
|
||||||
"sha256:d69e1a80b34fe4d596c9142f35d9e523d98a2838976f1a68419a8f051b24cec6"
|
"sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==6.2.0"
|
"version": "==6.2.1"
|
||||||
},
|
},
|
||||||
"pytest-django": {
|
"pytest-django": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
"""authentik"""
|
"""authentik"""
|
||||||
__version__ = "0.13.0-stable"
|
__version__ = "0.13.2-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
|
||||||
|
|
||||||
|
|||||||
@ -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_*"))}})
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -83,7 +83,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)
|
||||||
@ -147,7 +149,7 @@ class FlowExecutorView(View):
|
|||||||
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.
|
||||||
|
|||||||
@ -113,17 +113,24 @@ class OutpostServiceConnection(models.Model):
|
|||||||
|
|
||||||
objects = InheritanceManager()
|
objects = InheritanceManager()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_key(self) -> str:
|
||||||
|
"""Key used to save connection state in cache"""
|
||||||
|
return f"outpost_service_connection_{self.pk.hex}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self) -> OutpostServiceConnectionState:
|
def state(self) -> OutpostServiceConnectionState:
|
||||||
"""Get state of service connection"""
|
"""Get state of service connection"""
|
||||||
state_key = f"outpost_service_connection_{self.pk.hex}"
|
from authentik.outposts.tasks import outpost_service_connection_state
|
||||||
state = cache.get(state_key, None)
|
|
||||||
|
state = cache.get(self.state_key, None)
|
||||||
if not state:
|
if not state:
|
||||||
state = self._get_state()
|
outpost_service_connection_state.delay(self.pk)
|
||||||
cache.set(state_key, state, timeout=0)
|
return OutpostServiceConnectionState("", False)
|
||||||
return state
|
return state
|
||||||
|
|
||||||
def _get_state(self) -> OutpostServiceConnectionState:
|
def fetch_state(self) -> OutpostServiceConnectionState:
|
||||||
|
"""Fetch current Service Connection state"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -203,7 +210,7 @@ class DockerServiceConnection(OutpostServiceConnection):
|
|||||||
raise ServiceConnectionInvalid from exc
|
raise ServiceConnectionInvalid from exc
|
||||||
return client
|
return client
|
||||||
|
|
||||||
def _get_state(self) -> OutpostServiceConnectionState:
|
def fetch_state(self) -> OutpostServiceConnectionState:
|
||||||
try:
|
try:
|
||||||
client = self.client()
|
client = self.client()
|
||||||
return OutpostServiceConnectionState(
|
return OutpostServiceConnectionState(
|
||||||
@ -239,7 +246,7 @@ class KubernetesServiceConnection(OutpostServiceConnection):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"Kubernetes Service-Connection {self.name}"
|
return f"Kubernetes Service-Connection {self.name}"
|
||||||
|
|
||||||
def _get_state(self) -> OutpostServiceConnectionState:
|
def fetch_state(self) -> OutpostServiceConnectionState:
|
||||||
try:
|
try:
|
||||||
client = self.client()
|
client = self.client()
|
||||||
api_instance = VersionApi(client)
|
api_instance = VersionApi(client)
|
||||||
@ -247,7 +254,7 @@ class KubernetesServiceConnection(OutpostServiceConnection):
|
|||||||
return OutpostServiceConnectionState(
|
return OutpostServiceConnectionState(
|
||||||
version=version.git_version, healthy=True
|
version=version.git_version, healthy=True
|
||||||
)
|
)
|
||||||
except (OpenApiException, HTTPError):
|
except (OpenApiException, HTTPError, ServiceConnectionInvalid):
|
||||||
return OutpostServiceConnectionState(version="", healthy=False)
|
return OutpostServiceConnectionState(version="", healthy=False)
|
||||||
|
|
||||||
def client(self) -> ApiClient:
|
def client(self) -> ApiClient:
|
||||||
|
|||||||
@ -35,21 +35,22 @@ def outpost_controller_all():
|
|||||||
|
|
||||||
|
|
||||||
@CELERY_APP.task()
|
@CELERY_APP.task()
|
||||||
def outpost_service_connection_state(state_pk: Any):
|
def outpost_service_connection_state(connection_pk: Any):
|
||||||
"""Update cached state of a service connection"""
|
"""Update cached state of a service connection"""
|
||||||
connection: OutpostServiceConnection = (
|
connection: OutpostServiceConnection = (
|
||||||
OutpostServiceConnection.objects.filter(pk=state_pk).select_subclasses().first()
|
OutpostServiceConnection.objects.filter(pk=connection_pk)
|
||||||
|
.select_subclasses()
|
||||||
|
.first()
|
||||||
)
|
)
|
||||||
cache.delete(f"outpost_service_connection_{connection.pk.hex}")
|
state = connection.fetch_state()
|
||||||
_ = connection.state
|
cache.set(connection.state_key, state, timeout=None)
|
||||||
|
|
||||||
|
|
||||||
@CELERY_APP.task(bind=True, base=MonitoredTask)
|
@CELERY_APP.task(bind=True, base=MonitoredTask)
|
||||||
def outpost_service_connection_monitor(self: MonitoredTask):
|
def outpost_service_connection_monitor(self: MonitoredTask):
|
||||||
"""Regularly check the state of Outpost Service Connections"""
|
"""Regularly check the state of Outpost Service Connections"""
|
||||||
for connection in OutpostServiceConnection.objects.select_subclasses():
|
for connection in OutpostServiceConnection.objects.all():
|
||||||
cache.delete(f"outpost_service_connection_{connection.pk.hex}")
|
outpost_service_connection_state.delay(connection.pk)
|
||||||
_ = connection.state
|
|
||||||
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL))
|
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -7,13 +7,16 @@ class MessageConsumer(JsonWebsocketConsumer):
|
|||||||
"""Consumer which sends django.contrib.messages Messages over WS.
|
"""Consumer which sends django.contrib.messages Messages over WS.
|
||||||
channel_name is saved into cache with user_id, and when a add_message is called"""
|
channel_name is saved into cache with user_id, and when a add_message is called"""
|
||||||
|
|
||||||
|
session_key: str
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
self.accept()
|
self.accept()
|
||||||
cache.set(f"user_{self.scope['user'].pk}_messages_{self.channel_name}", True)
|
self.session_key = self.scope["session"].session_key
|
||||||
|
cache.set(f"user_{self.session_key}_messages_{self.channel_name}", True, None)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def disconnect(self, close_code):
|
def disconnect(self, close_code):
|
||||||
cache.delete(f"user_{self.scope['user'].pk}_messages_{self.channel_name}")
|
cache.delete(f"user_{self.session_key}_messages_{self.channel_name}")
|
||||||
|
|
||||||
def event_update(self, event: dict):
|
def event_update(self, event: dict):
|
||||||
"""Event handler which is called by Messages Storage backend"""
|
"""Event handler which is called by Messages Storage backend"""
|
||||||
|
|||||||
@ -16,7 +16,7 @@ class ChannelsStorage(FallbackStorage):
|
|||||||
self.channel = get_channel_layer()
|
self.channel = get_channel_layer()
|
||||||
|
|
||||||
def _store(self, messages: list[Message], response, *args, **kwargs):
|
def _store(self, messages: list[Message], response, *args, **kwargs):
|
||||||
prefix = f"user_{self.request.user.pk}_messages_"
|
prefix = f"user_{self.request.session.session_key}_messages_"
|
||||||
keys = cache.keys(f"{prefix}*")
|
keys = cache.keys(f"{prefix}*")
|
||||||
if len(keys) < 1:
|
if len(keys) < 1:
|
||||||
return super()._store(messages, response, *args, **kwargs)
|
return super()._store(messages, response, *args, **kwargs)
|
||||||
|
|||||||
@ -10,8 +10,8 @@ services:
|
|||||||
- internal
|
- internal
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=${PG_PASS:-thisisnotagoodpassword}
|
- POSTGRES_PASSWORD=${PG_PASS:-thisisnotagoodpassword}
|
||||||
- POSTGRES_USER=authentik
|
- POSTGRES_USER=${PG_USER:-authentik}
|
||||||
- POSTGRES_DB=authentik
|
- POSTGRES_DB=${PG_DB:-authentik}
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
redis:
|
redis:
|
||||||
@ -19,11 +19,13 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
server:
|
server:
|
||||||
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.0-stable}
|
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.2-stable}
|
||||||
command: server
|
command: server
|
||||||
environment:
|
environment:
|
||||||
AUTHENTIK_REDIS__HOST: redis
|
AUTHENTIK_REDIS__HOST: redis
|
||||||
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
||||||
|
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
|
||||||
|
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
|
||||||
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
||||||
volumes:
|
volumes:
|
||||||
- ./media:/media
|
- ./media:/media
|
||||||
@ -42,13 +44,15 @@ services:
|
|||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
worker:
|
worker:
|
||||||
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.0-stable}
|
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.2-stable}
|
||||||
command: worker
|
command: worker
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
environment:
|
environment:
|
||||||
AUTHENTIK_REDIS__HOST: redis
|
AUTHENTIK_REDIS__HOST: redis
|
||||||
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
||||||
|
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
|
||||||
|
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
|
||||||
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
||||||
volumes:
|
volumes:
|
||||||
- ./backups:/backups
|
- ./backups:/backups
|
||||||
@ -56,7 +60,7 @@ services:
|
|||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
static:
|
static:
|
||||||
image: beryju/authentik-static:${AUTHENTIK_TAG:-0.13.0-stable}
|
image: beryju/authentik-static:${AUTHENTIK_TAG:-0.13.2-stable}
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
labels:
|
labels:
|
||||||
@ -68,7 +72,7 @@ services:
|
|||||||
traefik.http.services.static-service.loadbalancer.healthcheck.path: /
|
traefik.http.services.static-service.loadbalancer.healthcheck.path: /
|
||||||
traefik.http.services.static-service.loadbalancer.server.port: '80'
|
traefik.http.services.static-service.loadbalancer.server.port: '80'
|
||||||
volumes:
|
volumes:
|
||||||
- ./media:/media
|
- ./media:/usr/share/nginx/html/media
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:2.3
|
image: traefik:2.3
|
||||||
command:
|
command:
|
||||||
@ -81,7 +85,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
ports:
|
ports:
|
||||||
- "0.0.0.0:80:80"
|
|
||||||
- "0.0.0.0:443:443"
|
- "0.0.0.0:443:443"
|
||||||
- "127.0.0.1:8080:8080"
|
- "127.0.0.1:8080:8080"
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@ -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.0-stable"
|
version: "0.13.2-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.0-stable | Image tag |
|
| image.tag | 0.13.2-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.0-stable
|
tag: 0.13.2-stable
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
serverReplicas: 1
|
serverReplicas: 1
|
||||||
|
|||||||
@ -59,6 +59,7 @@ func getCommonOptions() *options.Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func doGlobalSetup(config map[string]interface{}) {
|
func doGlobalSetup(config map[string]interface{}) {
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
switch config[ConfigLogLevel].(string) {
|
switch config[ConfigLogLevel].(string) {
|
||||||
case "debug":
|
case "debug":
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
const VERSION = "0.13.0-stable"
|
const VERSION = "0.13.2-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.
|
||||||
|
|||||||
286
web/package-lock.json
generated
286
web/package-lock.json
generated
@ -102,14 +102,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@patternfly/patternfly": {
|
"@patternfly/patternfly": {
|
||||||
"version": "4.65.6",
|
"version": "4.70.2",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.65.6.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.70.2.tgz",
|
||||||
"integrity": "sha512-dENO2nZbf5SoEH68coW9U+6FpZmdVnFVjztl7rUeWUPSBUuF1eWld5LT03Q6PVoZuWqqbJxFJodyFKwLb+L9vw=="
|
"integrity": "sha512-XKCHnOjx1JThY3s98AJhsApSsGHPvEdlY7r+b18OecqUnmThVGw3nslzYYrwfCGlJ/xQtV5so29SduH2/uhHzA=="
|
||||||
},
|
},
|
||||||
"@rollup/plugin-typescript": {
|
"@rollup/plugin-typescript": {
|
||||||
"version": "8.0.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.1.0.tgz",
|
||||||
"integrity": "sha512-2L/kKvM5U4VOm+yVMvPIBF3yMZtQUyopf4YIT+KQbqZBZ8Fsdm7X6yeezy92PMyvvHQG1Pa322MVwxPojQvukA==",
|
"integrity": "sha512-pyQlcGQYRsONUDwXK3ckGPHjPzmjlq4sinzr7emW8ZMb2oZjg9WLcdcP8wyHSvBjvHrLzMayyPy079RROqb4vw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@rollup/pluginutils": "^3.1.0",
|
"@rollup/pluginutils": "^3.1.0",
|
||||||
@ -142,13 +142,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/browser": {
|
"@sentry/browser": {
|
||||||
"version": "5.29.0",
|
"version": "5.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.29.1.tgz",
|
||||||
"integrity": "sha512-kRlt1mE2wrYjspnIupNnPxqsUrRuy02SuXhbpP7J6uu8QasoEmJ78hk0hHz4jOZRmuWwfs2zIXD4tLGgWOKq8A==",
|
"integrity": "sha512-cVlXoQBJ64eNNkQuOB+bS6sK5KWV+Fw+ZYxT+XqjpeXkOPaxh8aeoi9CHz2DsFfbLV91P4AnXZEUdDl+7ktQNg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/core": "5.29.0",
|
"@sentry/core": "5.29.1",
|
||||||
"@sentry/types": "5.29.0",
|
"@sentry/types": "5.29.1",
|
||||||
"@sentry/utils": "5.29.0",
|
"@sentry/utils": "5.29.1",
|
||||||
"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.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.29.1.tgz",
|
||||||
"integrity": "sha512-a1sZBJ2u3NG0YDlGvOTwUCWiNjhfmDtAQiKK1o6RIIbcrWy9TlSps7CYDkBP239Y3A4pnvohjEEKEP3v3L3LZQ==",
|
"integrity": "sha512-SMybIx9IlswkJ7a61ez/zjdiMdAo51Adpo4nVrzke2k84U/t726/EbJj0FJ4vVgsGdLCvSSZ6v7BQlINcwWupg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/hub": "5.29.0",
|
"@sentry/hub": "5.29.1",
|
||||||
"@sentry/minimal": "5.29.0",
|
"@sentry/minimal": "5.29.1",
|
||||||
"@sentry/types": "5.29.0",
|
"@sentry/types": "5.29.1",
|
||||||
"@sentry/utils": "5.29.0",
|
"@sentry/utils": "5.29.1",
|
||||||
"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.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.1.tgz",
|
||||||
"integrity": "sha512-kcDPQsRG4cFdmqDh+TzjeO7lWYxU8s1dZYAbbl1J4uGKmhNB0J7I4ak4SGwTsXLY6fhbierxr6PRaoNojCxjPw==",
|
"integrity": "sha512-Ig/vqCiJcsnGaWajkWRFH+5IKeo50ZtsjM0zJb8IfTadLjQuF/gTQst0aXO3l6q4HzveeGsELY8jlm6WVcq9Aw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/types": "5.29.0",
|
"@sentry/types": "5.29.1",
|
||||||
"@sentry/utils": "5.29.0",
|
"@sentry/utils": "5.29.1",
|
||||||
"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.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.1.tgz",
|
||||||
"integrity": "sha512-nhXofdjtO41/caiF1wk1oT3p/QuhOZDYdF/b29DoD2MiAMK9IjhhOXI/gqaRpDKkXlDvd95fDTcx4t/MqqcKXA==",
|
"integrity": "sha512-lAa3+Duxum1qQvR0tKiBUsH6Ehit3g/vO53SqBib7YK3qdvIUWHacmkJvfz/AeSvVnpJ9bsBMCVRJNSVe8BPVA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/hub": "5.29.0",
|
"@sentry/hub": "5.29.1",
|
||||||
"@sentry/types": "5.29.0",
|
"@sentry/types": "5.29.1",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -213,51 +213,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/tracing": {
|
"@sentry/tracing": {
|
||||||
"version": "5.29.0",
|
"version": "5.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.29.1.tgz",
|
||||||
"integrity": "sha512-2ZITUH7Eur7IkmRAd5gw8Xt2Sfc28btCnT7o2P2J8ZPD65e99ATqjxXPokx0+6zEkTsstIDD3mbyuwkpbuvuTA==",
|
"integrity": "sha512-iWfPtDhf5X7N9R5WB3vX/wlyFVsGG8iMx4hLIP+6bj8EcPYnZfeP6Sxn65a0ACT/FKv7SMBoZ1qPDzmvk0bviw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/hub": "5.29.0",
|
"@sentry/hub": "5.29.1",
|
||||||
"@sentry/minimal": "5.29.0",
|
"@sentry/minimal": "5.29.1",
|
||||||
"@sentry/types": "5.29.0",
|
"@sentry/types": "5.29.1",
|
||||||
"@sentry/utils": "5.29.0",
|
"@sentry/utils": "5.29.1",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/hub": {
|
|
||||||
"version": "5.29.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.29.0.tgz",
|
|
||||||
"integrity": "sha512-kcDPQsRG4cFdmqDh+TzjeO7lWYxU8s1dZYAbbl1J4uGKmhNB0J7I4ak4SGwTsXLY6fhbierxr6PRaoNojCxjPw==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/types": "5.29.0",
|
|
||||||
"@sentry/utils": "5.29.0",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sentry/minimal": {
|
|
||||||
"version": "5.29.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.29.0.tgz",
|
|
||||||
"integrity": "sha512-nhXofdjtO41/caiF1wk1oT3p/QuhOZDYdF/b29DoD2MiAMK9IjhhOXI/gqaRpDKkXlDvd95fDTcx4t/MqqcKXA==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/hub": "5.29.0",
|
|
||||||
"@sentry/types": "5.29.0",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@sentry/types": {
|
|
||||||
"version": "5.29.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.0.tgz",
|
|
||||||
"integrity": "sha512-iDkxT/9sT3UF+Xb+JyLjZ5caMXsgLfRyV9VXQEiR2J6mgpMielj184d9jeF3bm/VMuAf/VFFqrHlcVsVgmrrMw=="
|
|
||||||
},
|
|
||||||
"@sentry/utils": {
|
|
||||||
"version": "5.29.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.0.tgz",
|
|
||||||
"integrity": "sha512-b2B1gshw2u3EHlAi84PuI5sfmLKXW1z9enMMhNuuNT/CoRp+g5kMAcUv/qYTws7UNnYSvTuVGuZG30v1e0hP9A==",
|
|
||||||
"requires": {
|
|
||||||
"@sentry/types": "5.29.0",
|
|
||||||
"tslib": "^1.9.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
@ -266,16 +232,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/types": {
|
"@sentry/types": {
|
||||||
"version": "5.29.0",
|
"version": "5.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.29.1.tgz",
|
||||||
"integrity": "sha512-iDkxT/9sT3UF+Xb+JyLjZ5caMXsgLfRyV9VXQEiR2J6mgpMielj184d9jeF3bm/VMuAf/VFFqrHlcVsVgmrrMw=="
|
"integrity": "sha512-QXZBA1gJheMYTGFV+UUhr3+jKpGZqPx8kEJABs8htlKabCDJlEeoFNmeqPuVxCxukoy5ZaaHACoE+2Z87T0g2A=="
|
||||||
},
|
},
|
||||||
"@sentry/utils": {
|
"@sentry/utils": {
|
||||||
"version": "5.29.0",
|
"version": "5.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.29.1.tgz",
|
||||||
"integrity": "sha512-b2B1gshw2u3EHlAi84PuI5sfmLKXW1z9enMMhNuuNT/CoRp+g5kMAcUv/qYTws7UNnYSvTuVGuZG30v1e0hP9A==",
|
"integrity": "sha512-FOhWxASvIQREAlSuWf3Vmb4uIkG0fmRdHkULpuv5dFmrMX2PpudYAppQtS8K9V4BYxFy6KFdUht1Qz5zYTecMw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/types": "5.29.0",
|
"@sentry/types": "5.29.1",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -287,9 +253,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/chart.js": {
|
"@types/chart.js": {
|
||||||
"version": "2.9.28",
|
"version": "2.9.29",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.28.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.29.tgz",
|
||||||
"integrity": "sha512-9YYhsxRngRJb0dkuaU5BezkF+zvvVHnwdRw+rtlahtFb4zqNf9YSgWsOq+dLYeh0fqsWmHUYLR64eNigh02F+w==",
|
"integrity": "sha512-WOZMitUU3gHDM0oQsCsVivX+oDsIki93szcTmmUPBm39cCvAELBjokjSDVOoA3xiIEbb+jp17z/3S2tIqruwOQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"moment": "^2.10.2"
|
"moment": "^2.10.2"
|
||||||
}
|
}
|
||||||
@ -393,156 +359,70 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/eslint-plugin": {
|
"@typescript-eslint/eslint-plugin": {
|
||||||
"version": "4.9.1",
|
"version": "4.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.10.0.tgz",
|
||||||
"integrity": "sha512-QRLDSvIPeI1pz5tVuurD+cStNR4sle4avtHhxA+2uyixWGFjKzJ+EaFVRW6dA/jOgjV5DTAjOxboQkRDE8cRlQ==",
|
"integrity": "sha512-h6/V46o6aXpKRlarP1AiJEXuCJ7cMQdlpfMDrcllIgX3dFkLwEBTXAoNP98ZoOmqd1xvymMVRAI4e7yVvlzWEg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/experimental-utils": "4.9.1",
|
"@typescript-eslint/experimental-utils": "4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "4.9.1",
|
"@typescript-eslint/scope-manager": "4.10.0",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"functional-red-black-tree": "^1.0.1",
|
"functional-red-black-tree": "^1.0.1",
|
||||||
"regexpp": "^3.0.0",
|
"regexpp": "^3.0.0",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
"tsutils": "^3.17.1"
|
"tsutils": "^3.17.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@typescript-eslint/scope-manager": {
|
|
||||||
"version": "4.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz",
|
|
||||||
"integrity": "sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@typescript-eslint/types": "4.9.1",
|
|
||||||
"@typescript-eslint/visitor-keys": "4.9.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@typescript-eslint/types": {
|
|
||||||
"version": "4.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.1.tgz",
|
|
||||||
"integrity": "sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@typescript-eslint/visitor-keys": {
|
|
||||||
"version": "4.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz",
|
|
||||||
"integrity": "sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@typescript-eslint/types": "4.9.1",
|
|
||||||
"eslint-visitor-keys": "^2.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/experimental-utils": {
|
"@typescript-eslint/experimental-utils": {
|
||||||
"version": "4.9.1",
|
"version": "4.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.10.0.tgz",
|
||||||
"integrity": "sha512-c3k/xJqk0exLFs+cWSJxIjqLYwdHCuLWhnpnikmPQD2+NGAx9KjLYlBDcSI81EArh9FDYSL6dslAUSwILeWOxg==",
|
"integrity": "sha512-opX+7ai1sdWBOIoBgpVJrH5e89ra1KoLrJTz0UtWAa4IekkKmqDosk5r6xqRaNJfCXEfteW4HXQAwMdx+jjEmw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/json-schema": "^7.0.3",
|
"@types/json-schema": "^7.0.3",
|
||||||
"@typescript-eslint/scope-manager": "4.9.1",
|
"@typescript-eslint/scope-manager": "4.10.0",
|
||||||
"@typescript-eslint/types": "4.9.1",
|
"@typescript-eslint/types": "4.10.0",
|
||||||
"@typescript-eslint/typescript-estree": "4.9.1",
|
"@typescript-eslint/typescript-estree": "4.10.0",
|
||||||
"eslint-scope": "^5.0.0",
|
"eslint-scope": "^5.0.0",
|
||||||
"eslint-utils": "^2.0.0"
|
"eslint-utils": "^2.0.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@typescript-eslint/scope-manager": {
|
|
||||||
"version": "4.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz",
|
|
||||||
"integrity": "sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@typescript-eslint/types": "4.9.1",
|
|
||||||
"@typescript-eslint/visitor-keys": "4.9.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@typescript-eslint/types": {
|
|
||||||
"version": "4.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.1.tgz",
|
|
||||||
"integrity": "sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@typescript-eslint/typescript-estree": {
|
|
||||||
"version": "4.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz",
|
|
||||||
"integrity": "sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@typescript-eslint/types": "4.9.1",
|
|
||||||
"@typescript-eslint/visitor-keys": "4.9.1",
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"globby": "^11.0.1",
|
|
||||||
"is-glob": "^4.0.1",
|
|
||||||
"lodash": "^4.17.15",
|
|
||||||
"semver": "^7.3.2",
|
|
||||||
"tsutils": "^3.17.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@typescript-eslint/visitor-keys": {
|
|
||||||
"version": "4.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz",
|
|
||||||
"integrity": "sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@typescript-eslint/types": "4.9.1",
|
|
||||||
"eslint-visitor-keys": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"globby": {
|
|
||||||
"version": "11.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
|
|
||||||
"integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"array-union": "^2.1.0",
|
|
||||||
"dir-glob": "^3.0.1",
|
|
||||||
"fast-glob": "^3.1.1",
|
|
||||||
"ignore": "^5.1.4",
|
|
||||||
"merge2": "^1.3.0",
|
|
||||||
"slash": "^3.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/parser": {
|
"@typescript-eslint/parser": {
|
||||||
"version": "4.9.1",
|
"version": "4.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.10.0.tgz",
|
||||||
"integrity": "sha512-Gv2VpqiomvQ2v4UL+dXlQcZ8zCX4eTkoIW+1aGVWT6yTO+6jbxsw7yQl2z2pPl/4B9qa5JXeIbhJpONKjXIy3g==",
|
"integrity": "sha512-amBvUUGBMadzCW6c/qaZmfr3t9PyevcSWw7hY2FuevdZVp5QPw/K76VSQ5Sw3BxlgYCHZcK6DjIhSZK0PQNsQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/scope-manager": "4.9.1",
|
"@typescript-eslint/scope-manager": "4.10.0",
|
||||||
"@typescript-eslint/types": "4.9.1",
|
"@typescript-eslint/types": "4.10.0",
|
||||||
"@typescript-eslint/typescript-estree": "4.9.1",
|
"@typescript-eslint/typescript-estree": "4.10.0",
|
||||||
"debug": "^4.1.1"
|
"debug": "^4.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/scope-manager": {
|
"@typescript-eslint/scope-manager": {
|
||||||
"version": "4.9.1",
|
"version": "4.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.10.0.tgz",
|
||||||
"integrity": "sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==",
|
"integrity": "sha512-WAPVw35P+fcnOa8DEic0tQUhoJJsgt+g6DEcz257G7vHFMwmag58EfowdVbiNcdfcV27EFR0tUBVXkDoIvfisQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.9.1",
|
"@typescript-eslint/types": "4.10.0",
|
||||||
"@typescript-eslint/visitor-keys": "4.9.1"
|
"@typescript-eslint/visitor-keys": "4.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/types": {
|
"@typescript-eslint/types": {
|
||||||
"version": "4.9.1",
|
"version": "4.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.10.0.tgz",
|
||||||
"integrity": "sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==",
|
"integrity": "sha512-+dt5w1+Lqyd7wIPMa4XhJxUuE8+YF+vxQ6zxHyhLGHJjHiunPf0wSV8LtQwkpmAsRi1lEOoOIR30FG5S2HS33g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@typescript-eslint/typescript-estree": {
|
"@typescript-eslint/typescript-estree": {
|
||||||
"version": "4.9.1",
|
"version": "4.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.10.0.tgz",
|
||||||
"integrity": "sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw==",
|
"integrity": "sha512-mGK0YRp9TOk6ZqZ98F++bW6X5kMTzCRROJkGXH62d2azhghmq+1LNLylkGe6uGUOQzD452NOAEth5VAF6PDo5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.9.1",
|
"@typescript-eslint/types": "4.10.0",
|
||||||
"@typescript-eslint/visitor-keys": "4.9.1",
|
"@typescript-eslint/visitor-keys": "4.10.0",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"globby": "^11.0.1",
|
"globby": "^11.0.1",
|
||||||
"is-glob": "^4.0.1",
|
"is-glob": "^4.0.1",
|
||||||
@ -568,12 +448,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/visitor-keys": {
|
"@typescript-eslint/visitor-keys": {
|
||||||
"version": "4.9.1",
|
"version": "4.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.10.0.tgz",
|
||||||
"integrity": "sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==",
|
"integrity": "sha512-hPyz5qmDMuZWFtHZkjcCpkAKHX8vdu1G3YsCLEd25ryZgnJfj6FQuJ5/O7R+dB1ueszilJmAFMtlU4CA6se3Jg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.9.1",
|
"@typescript-eslint/types": "4.10.0",
|
||||||
"eslint-visitor-keys": "^2.0.0"
|
"eslint-visitor-keys": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2680,9 +2560,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rollup": {
|
"rollup": {
|
||||||
"version": "2.34.2",
|
"version": "2.35.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.34.2.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.35.1.tgz",
|
||||||
"integrity": "sha512-mvtQLqu3cNeoctS+kZ09iOPxrc1P1/Bt1z15enuQ5feyKOdM3MJAVFjjsygurDpSWn530xB4AlA83TWIzRstXA==",
|
"integrity": "sha512-q5KxEyWpprAIcainhVy6HfRttD9kutQpHbeqDTWnqAFNJotiojetK6uqmcydNMymBEtC4I8bCYR+J3mTMqeaUA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fsevents": "~2.1.2"
|
"fsevents": "~2.1.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,26 +8,26 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.15.1",
|
"@fortawesome/fontawesome-free": "^5.15.1",
|
||||||
"@patternfly/patternfly": "^4.65.6",
|
"@patternfly/patternfly": "^4.70.2",
|
||||||
"@sentry/browser": "^5.29.0",
|
"@sentry/browser": "^5.29.1",
|
||||||
"@sentry/tracing": "^5.29.0",
|
"@sentry/tracing": "^5.29.1",
|
||||||
"@types/chart.js": "^2.9.28",
|
"@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",
|
||||||
"codemirror": "^5.58.3",
|
"codemirror": "^5.58.3",
|
||||||
"construct-style-sheets-polyfill": "^2.4.3",
|
"construct-style-sheets-polyfill": "^2.4.3",
|
||||||
"lit-element": "^2.4.0",
|
"lit-element": "^2.4.0",
|
||||||
"lit-html": "^1.3.0",
|
"lit-html": "^1.3.0",
|
||||||
"rollup": "^2.34.2",
|
"rollup": "^2.35.1",
|
||||||
"rollup-plugin-copy": "^3.3.0",
|
"rollup-plugin-copy": "^3.3.0",
|
||||||
"rollup-plugin-cssimport": "^1.0.2",
|
"rollup-plugin-cssimport": "^1.0.2",
|
||||||
"rollup-plugin-external-globals": "^0.6.1",
|
"rollup-plugin-external-globals": "^0.6.1",
|
||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-typescript": "^8.0.0",
|
"@rollup/plugin-typescript": "^8.1.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.9.1",
|
"@typescript-eslint/eslint-plugin": "^4.10.0",
|
||||||
"@typescript-eslint/parser": "^4.9.1",
|
"@typescript-eslint/parser": "^4.10.0",
|
||||||
"eslint": "^7.15.0",
|
"eslint": "^7.15.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-lit": "^1.3.0",
|
"eslint-plugin-lit": "^1.3.0",
|
||||||
|
|||||||
@ -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"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -162,10 +162,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 +199,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.0-stable";
|
export const VERSION = "0.13.2-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",
|
||||||
@ -33,6 +33,7 @@ export class SidebarBrand extends LitElement {
|
|||||||
.pf-c-brand img {
|
.pf-c-brand img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 .5rem;
|
padding: 0 .5rem;
|
||||||
|
height: 42px;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -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("Outposts", "/administration/outposts/"),
|
||||||
new SidebarItem("Flows", "/administration/flows/").activeWhen(`^/flows/(?<slug>${SLUG_REGEX})/$`),
|
new SidebarItem("Outpost Service Connections", "/administration/outposts/service_connections/"),
|
||||||
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("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,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.0-stable >> .env`
|
To optionally deploy a different version run `echo AUTHENTIK_TAG=0.13.2-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.0-stable
|
tag: 0.13.2-stable
|
||||||
|
|
||||||
serverReplicas: 1
|
serverReplicas: 1
|
||||||
workerReplicas: 1
|
workerReplicas: 1
|
||||||
|
|||||||
@ -35,6 +35,13 @@ If you decided to rename the folder you're running the docker-compose file from,
|
|||||||
|
|
||||||
The only manual change you have to do is replace the `PASSBOOK_` prefix in your `.env` file, so `PASSBOOK_SECRET_KEY` gets changed to `AUTHENTIK_SECRET_KEY`.
|
The only manual change you have to do is replace the `PASSBOOK_` prefix in your `.env` file, so `PASSBOOK_SECRET_KEY` gets changed to `AUTHENTIK_SECRET_KEY`.
|
||||||
|
|
||||||
|
Additionally, the database name and username have to be changed, so add this block to your `.env` file:
|
||||||
|
|
||||||
|
```
|
||||||
|
PG_USER=passbook
|
||||||
|
PG_DB=passbook
|
||||||
|
```
|
||||||
|
|
||||||
Afterwards, you can simply run `docker-compose up -d` and then the normal upgrade command of `docker-compose run --rm server migrate`.
|
Afterwards, you can simply run `docker-compose up -d` and then the normal upgrade command of `docker-compose run --rm server migrate`.
|
||||||
|
|
||||||
### Kubernetes
|
### Kubernetes
|
||||||
|
|||||||
@ -14,7 +14,6 @@ module.exports = {
|
|||||||
alt: "authentik logo",
|
alt: "authentik logo",
|
||||||
src: "img/icon_left_brand.svg",
|
src: "img/icon_left_brand.svg",
|
||||||
},
|
},
|
||||||
hideOnScroll: true,
|
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
to: "docs/",
|
to: "docs/",
|
||||||
|
|||||||
Reference in New Issue
Block a user