From 48438e28fd2b918dd5eda8d549a80a1b9bd242a0 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Wed, 16 Dec 2020 22:40:22 +0100 Subject: [PATCH] admin: separate overview API into WorkerAPI and VersionAPI --- .../api/{overview_metrics.py => metrics.py} | 8 ++--- .../admin/api/{overview.py => version.py} | 32 +++++++++---------- authentik/admin/api/workers.py | 23 +++++++++++++ authentik/api/v2/urls.py | 23 +++++++++---- authentik/core/api/applications.py | 2 +- authentik/core/api/providers.py | 2 +- authentik/flows/api.py | 6 +++- authentik/policies/api.py | 7 ++-- 8 files changed, 70 insertions(+), 33 deletions(-) rename authentik/admin/api/{overview_metrics.py => metrics.py} (92%) rename authentik/admin/api/{overview.py => version.py} (58%) create mode 100644 authentik/admin/api/workers.py diff --git a/authentik/admin/api/overview_metrics.py b/authentik/admin/api/metrics.py similarity index 92% rename from authentik/admin/api/overview_metrics.py rename to authentik/admin/api/metrics.py index e1c29e54f..6909fd8bd 100644 --- a/authentik/admin/api/overview_metrics.py +++ b/authentik/admin/api/metrics.py @@ -1,4 +1,4 @@ -"""authentik administration overview""" +"""authentik administration metrics""" import time from collections import Counter from datetime import timedelta @@ -47,7 +47,7 @@ def get_events_per_1h(**filter_kwargs) -> List[Dict[str, int]]: class AdministrationMetricsSerializer(Serializer): - """Overview View""" + """Login Metrics per 1h""" logins_per_1h = SerializerMethodField() logins_failed_per_1h = SerializerMethodField() @@ -68,12 +68,12 @@ class AdministrationMetricsSerializer(Serializer): class AdministrationMetricsViewSet(ViewSet): - """Return single instance of AdministrationMetricsSerializer""" + """Login Metrics per 1h""" permission_classes = [IsAdminUser] @swagger_auto_schema(responses={200: AdministrationMetricsSerializer(many=True)}) def list(self, request: Request) -> Response: - """Return single instance of AdministrationMetricsSerializer""" + """Login Metrics per 1h""" serializer = AdministrationMetricsSerializer(True) return Response(serializer.data) diff --git a/authentik/admin/api/overview.py b/authentik/admin/api/version.py similarity index 58% rename from authentik/admin/api/overview.py rename to authentik/admin/api/version.py index bca286104..74a5dfbd0 100644 --- a/authentik/admin/api/overview.py +++ b/authentik/admin/api/version.py @@ -2,25 +2,26 @@ from django.core.cache import cache from drf_yasg2.utils import swagger_auto_schema 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 ViewSet +from rest_framework.viewsets import GenericViewSet +from packaging.version import parse from authentik import __version__ from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version -from authentik.root.celery import CELERY_APP -class AdministrationOverviewSerializer(Serializer): - """Overview View""" +class VersionSerializer(Serializer): + """Get running and latest version.""" - version = SerializerMethodField() + version_current = SerializerMethodField() version_latest = SerializerMethodField() - worker_count = SerializerMethodField() + outdated = SerializerMethodField() - def get_version(self, _) -> str: + def get_version_current(self, _) -> str: """Get current version""" return __version__ @@ -32,9 +33,9 @@ class AdministrationOverviewSerializer(Serializer): 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_outdated(self, instance) -> str: + """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 @@ -43,13 +44,12 @@ class AdministrationOverviewSerializer(Serializer): raise NotImplementedError -class AdministrationOverviewViewSet(ViewSet): - """General overview information about authentik.""" +class VersionViewSet(ListModelMixin, GenericViewSet): + """Get running and latest version.""" permission_classes = [IsAdminUser] - @swagger_auto_schema(responses={200: AdministrationOverviewSerializer(many=True)}) + @swagger_auto_schema(responses={200: VersionSerializer(many=True)}) def list(self, request: Request) -> Response: - """General overview information about authentik.""" - serializer = AdministrationOverviewSerializer(True) - return Response(serializer.data) + """Get running and latest version.""" + return Response(VersionSerializer(True).data) diff --git a/authentik/admin/api/workers.py b/authentik/admin/api/workers.py new file mode 100644 index 000000000..334222738 --- /dev/null +++ b/authentik/admin/api/workers.py @@ -0,0 +1,23 @@ +"""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 import __version__ +from authentik.root.celery import CELERY_APP + + +class WorkerViewSet(ListModelMixin, GenericViewSet): + """Get currently connected worker count.""" + + serializer_class = Serializer + permission_classes = [IsAdminUser] + + def list(self, request: Request) -> Response: + """Get currently connected worker count.""" + return Response( + {"pagination": {"count": len(CELERY_APP.control.ping(timeout=0.5))}} + ) diff --git a/authentik/api/v2/urls.py b/authentik/api/v2/urls.py index 649c4ca14..8e7e636a3 100644 --- a/authentik/api/v2/urls.py +++ b/authentik/api/v2/urls.py @@ -5,9 +5,10 @@ from drf_yasg2.views import get_schema_view from rest_framework import routers from rest_framework.permissions import AllowAny -from authentik.admin.api.overview import AdministrationOverviewViewSet -from authentik.admin.api.overview_metrics import AdministrationMetricsViewSet +from authentik.admin.api.metrics import AdministrationMetricsViewSet 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.messages import MessagesViewSet 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.users import UserViewSet from authentik.crypto.api import CertificateKeyPairViewSet -from authentik.flows.api import FlowStageBindingViewSet, FlowViewSet, StageViewSet, FlowCacheViewSet +from authentik.flows.api import ( + FlowCacheViewSet, + FlowStageBindingViewSet, + FlowViewSet, + StageViewSet, +) from authentik.outposts.api import ( DockerServiceConnectionViewSet, KubernetesServiceConnectionViewSet, OutpostViewSet, ) -from authentik.policies.api import PolicyBindingViewSet, PolicyViewSet, PolicyCacheViewSet +from authentik.policies.api import ( + PolicyBindingViewSet, + PolicyCacheViewSet, + PolicyViewSet, +) from authentik.policies.dummy.api import DummyPolicyViewSet from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet from authentik.policies.expression.api import ExpressionPolicyViewSet @@ -63,9 +73,8 @@ router = routers.DefaultRouter() router.register("root/messages", MessagesViewSet, basename="messages") router.register("root/config", ConfigsViewSet, basename="configs") -router.register( - "admin/overview", AdministrationOverviewViewSet, basename="admin_overview" -) +router.register("admin/version", VersionViewSet, basename="admin_version") +router.register("admin/workers", WorkerViewSet, basename="admin_workers") router.register("admin/metrics", AdministrationMetricsViewSet, basename="admin_metrics") router.register("admin/system_tasks", TaskViewSet, basename="admin_system_tasks") diff --git a/authentik/core/api/applications.py b/authentik/core/api/applications.py index b528fac54..9f481261a 100644 --- a/authentik/core/api/applications.py +++ b/authentik/core/api/applications.py @@ -11,7 +11,7 @@ from rest_framework.serializers import ModelSerializer from rest_framework.viewsets import ModelViewSet 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.core.models import Application from authentik.policies.engine import PolicyEngine diff --git a/authentik/core/api/providers.py b/authentik/core/api/providers.py index 20af65733..29272c224 100644 --- a/authentik/core/api/providers.py +++ b/authentik/core/api/providers.py @@ -39,7 +39,7 @@ class ProviderViewSet(ModelViewSet): queryset = Provider.objects.all() serializer_class = ProviderSerializer filterset_fields = { - 'application': ['isnull'], + "application": ["isnull"], } def get_queryset(self): diff --git a/authentik/flows/api.py b/authentik/flows/api.py index 0460c746c..f8de34cc4 100644 --- a/authentik/flows/api.py +++ b/authentik/flows/api.py @@ -3,7 +3,11 @@ from django.core.cache import cache from rest_framework.mixins import ListModelMixin from rest_framework.request import Request from rest_framework.response import Response -from rest_framework.serializers import ModelSerializer, Serializer, SerializerMethodField +from rest_framework.serializers import ( + ModelSerializer, + Serializer, + SerializerMethodField, +) from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet from authentik.flows.models import Flow, FlowStageBinding, Stage diff --git a/authentik/policies/api.py b/authentik/policies/api.py index 757f3d327..65533cf13 100644 --- a/authentik/policies/api.py +++ b/authentik/policies/api.py @@ -7,7 +7,8 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.serializers import ( ModelSerializer, - PrimaryKeyRelatedField, Serializer, + PrimaryKeyRelatedField, + Serializer, SerializerMethodField, ) from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet @@ -74,8 +75,8 @@ class PolicyViewSet(ReadOnlyModelViewSet): queryset = Policy.objects.all() serializer_class = PolicySerializer filterset_fields = { - 'bindings': ['isnull'], - 'promptstage': ['isnull'], + "bindings": ["isnull"], + "promptstage": ["isnull"], } def get_queryset(self):