From cf46ee06b7e30e910494244b38fa47f934431cb3 Mon Sep 17 00:00:00 2001
From: Jens Langhammer <jens.langhammer@beryju.org>
Date: Wed, 16 Dec 2020 22:18:36 +0100
Subject: [PATCH] api: create dedicated api for cached flows and policies

---
 authentik/admin/api/overview.py | 14 ++------------
 authentik/api/v2/urls.py        |  6 ++++--
 authentik/flows/api.py          | 18 ++++++++++++++++--
 authentik/policies/api.py       | 20 ++++++++++++++++++--
 4 files changed, 40 insertions(+), 18 deletions(-)

diff --git a/authentik/admin/api/overview.py b/authentik/admin/api/overview.py
index 65411b1ad..bca286104 100644
--- a/authentik/admin/api/overview.py
+++ b/authentik/admin/api/overview.py
@@ -19,8 +19,6 @@ class AdministrationOverviewSerializer(Serializer):
     version = SerializerMethodField()
     version_latest = SerializerMethodField()
     worker_count = SerializerMethodField()
-    cached_policies = SerializerMethodField()
-    cached_flows = SerializerMethodField()
 
     def get_version(self, _) -> str:
         """Get current version"""
@@ -38,14 +36,6 @@ class AdministrationOverviewSerializer(Serializer):
         """Ping workers"""
         return len(CELERY_APP.control.ping(timeout=0.5))
 
-    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
 
@@ -54,12 +44,12 @@ class AdministrationOverviewSerializer(Serializer):
 
 
 class AdministrationOverviewViewSet(ViewSet):
-    """Return single instance of AdministrationOverviewSerializer"""
+    """General overview information about authentik."""
 
     permission_classes = [IsAdminUser]
 
     @swagger_auto_schema(responses={200: AdministrationOverviewSerializer(many=True)})
     def list(self, request: Request) -> Response:
-        """Return single instance of AdministrationOverviewSerializer"""
+        """General overview information about authentik."""
         serializer = AdministrationOverviewSerializer(True)
         return Response(serializer.data)
diff --git a/authentik/api/v2/urls.py b/authentik/api/v2/urls.py
index bfbbc92ac..649c4ca14 100644
--- a/authentik/api/v2/urls.py
+++ b/authentik/api/v2/urls.py
@@ -19,13 +19,13 @@ 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
+from authentik.flows.api import FlowStageBindingViewSet, FlowViewSet, StageViewSet, FlowCacheViewSet
 from authentik.outposts.api import (
     DockerServiceConnectionViewSet,
     KubernetesServiceConnectionViewSet,
     OutpostViewSet,
 )
-from authentik.policies.api import PolicyBindingViewSet, PolicyViewSet
+from authentik.policies.api import PolicyBindingViewSet, PolicyViewSet, PolicyCacheViewSet
 from authentik.policies.dummy.api import DummyPolicyViewSet
 from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet
 from authentik.policies.expression.api import ExpressionPolicyViewSet
@@ -82,6 +82,7 @@ router.register(
 router.register("outposts/proxy", ProxyOutpostConfigViewSet)
 
 router.register("flows/instances", FlowViewSet)
+router.register("flows/cached", FlowCacheViewSet, basename="flows_cache")
 router.register("flows/bindings", FlowStageBindingViewSet)
 
 router.register("crypto/certificatekeypairs", CertificateKeyPairViewSet)
@@ -94,6 +95,7 @@ router.register("sources/saml", SAMLSourceViewSet)
 router.register("sources/oauth", OAuthSourceViewSet)
 
 router.register("policies/all", PolicyViewSet)
+router.register("policies/cached", PolicyCacheViewSet, basename="policies_cache")
 router.register("policies/bindings", PolicyBindingViewSet)
 router.register("policies/expression", ExpressionPolicyViewSet)
 router.register("policies/group_membership", GroupMembershipPolicyViewSet)
diff --git a/authentik/flows/api.py b/authentik/flows/api.py
index 4c468d646..0460c746c 100644
--- a/authentik/flows/api.py
+++ b/authentik/flows/api.py
@@ -1,7 +1,10 @@
 """Flow API Views"""
 from django.core.cache import cache
-from rest_framework.serializers import ModelSerializer, SerializerMethodField
-from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
+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.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
 
 from authentik.flows.models import Flow, FlowStageBinding, Stage
 from authentik.flows.planner import cache_key
@@ -98,3 +101,14 @@ class FlowStageBindingViewSet(ModelViewSet):
     queryset = FlowStageBinding.objects.all()
     serializer_class = FlowStageBindingSerializer
     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_*"))}})
diff --git a/authentik/policies/api.py b/authentik/policies/api.py
index fed8a2885..757f3d327 100644
--- a/authentik/policies/api.py
+++ b/authentik/policies/api.py
@@ -1,11 +1,16 @@
 """policy API Views"""
+from django.core.cache import cache
 from django.core.exceptions import ObjectDoesNotExist
+from drf_yasg2.utils import swagger_auto_schema
+from rest_framework.mixins import ListModelMixin
+from rest_framework.request import Request
+from rest_framework.response import Response
 from rest_framework.serializers import (
     ModelSerializer,
-    PrimaryKeyRelatedField,
+    PrimaryKeyRelatedField, Serializer,
     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.models import Policy, PolicyBinding, PolicyBindingModel
@@ -102,3 +107,14 @@ class PolicyBindingViewSet(ModelViewSet):
     serializer_class = PolicyBindingSerializer
     filterset_fields = ["policy", "target", "enabled", "order", "timeout"]
     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_*"))}})