From 6961089425c723281bebb54fadf9a606437d8b04 Mon Sep 17 00:00:00 2001
From: Jens Langhammer <jens.langhammer@beryju.org>
Date: Tue, 23 Mar 2021 10:37:41 +0100
Subject: [PATCH] flows: add API to clear cache

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
---
 .../templates/generic/form_non_model.html     | 20 --------
 authentik/admin/urls.py                       | 11 -----
 authentik/admin/views/overview.py             | 47 -------------------
 authentik/flows/api/flows.py                  | 23 +++++++--
 authentik/policies/api.py                     | 26 +++++++++-
 swagger.yaml                                  | 34 ++++++++++++--
 .../cards/FlowCacheStatusCard.ts              |  2 +-
 .../cards/PolicyCacheStatusCard.ts            |  2 +-
 8 files changed, 76 insertions(+), 89 deletions(-)
 delete mode 100644 authentik/admin/templates/generic/form_non_model.html
 delete mode 100644 authentik/admin/views/overview.py

diff --git a/authentik/admin/templates/generic/form_non_model.html b/authentik/admin/templates/generic/form_non_model.html
deleted file mode 100644
index 6223e33c7..000000000
--- a/authentik/admin/templates/generic/form_non_model.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends base_template|default:"generic/form.html" %}
-
-{% load authentik_utils %}
-{% load i18n %}
-
-{% block above_form %}
-<h1>
-    {% trans form.title %}
-</h1>
-{% endblock %}
-
-{% block beneath_form %}
-<p>
-    {% trans form.body %}
-</p>
-{% endblock %}
-
-{% block action %}
-{% trans 'Confirm' %}
-{% endblock %}
diff --git a/authentik/admin/urls.py b/authentik/admin/urls.py
index 732cf956e..33d2e0a58 100644
--- a/authentik/admin/urls.py
+++ b/authentik/admin/urls.py
@@ -10,7 +10,6 @@ from authentik.admin.views import (
     groups,
     outposts,
     outposts_service_connections,
-    overview,
     policies,
     policies_bindings,
     property_mappings,
@@ -25,16 +24,6 @@ from authentik.admin.views import (
 from authentik.providers.saml.views.metadata import MetadataImportView
 
 urlpatterns = [
-    path(
-        "overview/cache/flow/",
-        overview.FlowCacheClearView.as_view(),
-        name="overview-clear-flow-cache",
-    ),
-    path(
-        "overview/cache/policy/",
-        overview.PolicyCacheClearView.as_view(),
-        name="overview-clear-policy-cache",
-    ),
     # Applications
     path(
         "applications/create/",
diff --git a/authentik/admin/views/overview.py b/authentik/admin/views/overview.py
deleted file mode 100644
index 021154f9a..000000000
--- a/authentik/admin/views/overview.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""authentik administration overview"""
-from django.contrib.messages.views import SuccessMessageMixin
-from django.core.cache import cache
-from django.http.request import HttpRequest
-from django.http.response import HttpResponse
-from django.utils.translation import gettext as _
-from django.views.generic import FormView
-from structlog.stdlib import get_logger
-
-from authentik.admin.forms.overview import FlowCacheClearForm, PolicyCacheClearForm
-from authentik.admin.mixins import AdminRequiredMixin
-from authentik.core.api.applications import user_app_cache_key
-
-LOGGER = get_logger()
-
-
-class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
-    """View to clear Policy cache"""
-
-    form_class = PolicyCacheClearForm
-    success_url = "/"
-    template_name = "generic/form_non_model.html"
-    success_message = _("Successfully cleared Policy cache")
-
-    def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
-        keys = cache.keys("policy_*")
-        cache.delete_many(keys)
-        LOGGER.debug("Cleared Policy cache", keys=len(keys))
-        # Also delete user application cache
-        keys = cache.keys(user_app_cache_key("*"))
-        cache.delete_many(keys)
-        return super().post(request, *args, **kwargs)
-
-
-class FlowCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
-    """View to clear Flow cache"""
-
-    form_class = FlowCacheClearForm
-    success_url = "/"
-    template_name = "generic/form_non_model.html"
-    success_message = _("Successfully cleared Flow cache")
-
-    def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
-        keys = cache.keys("flow_*")
-        cache.delete_many(keys)
-        LOGGER.debug("Cleared flow cache", keys=len(keys))
-        return super().post(request, *args, **kwargs)
diff --git a/authentik/flows/api/flows.py b/authentik/flows/api/flows.py
index 6cccbfd7f..850a80734 100644
--- a/authentik/flows/api/flows.py
+++ b/authentik/flows/api/flows.py
@@ -3,10 +3,10 @@ from dataclasses import dataclass
 
 from django.core.cache import cache
 from django.db.models import Model
-from django.http.response import JsonResponse
+from django.http.response import HttpResponseBadRequest, JsonResponse
 from django.shortcuts import get_object_or_404
 from drf_yasg2 import openapi
-from drf_yasg2.utils import swagger_auto_schema
+from drf_yasg2.utils import no_body, swagger_auto_schema, unset
 from guardian.shortcuts import get_objects_for_user
 from rest_framework.decorators import action
 from rest_framework.exceptions import PermissionDenied
@@ -19,6 +19,7 @@ from rest_framework.serializers import (
     SerializerMethodField,
 )
 from rest_framework.viewsets import ModelViewSet
+from structlog.stdlib import get_logger
 
 from authentik.core.api.utils import CacheSerializer
 from authentik.flows.models import Flow
@@ -26,6 +27,8 @@ from authentik.flows.planner import cache_key
 from authentik.flows.transfer.common import DataclassEncoder
 from authentik.flows.transfer.exporter import FlowExporter
 
+LOGGER = get_logger()
+
 
 class FlowSerializer(ModelSerializer):
     """Flow Serializer"""
@@ -88,10 +91,24 @@ class FlowViewSet(ModelViewSet):
 
     @swagger_auto_schema(responses={200: CacheSerializer(many=False)})
     @action(detail=False)
-    def cached(self, request: Request) -> Response:
+    def cache_info(self, request: Request) -> Response:
         """Info about cached flows"""
         return Response(data={"count": len(cache.keys("flow_*"))})
 
+    @swagger_auto_schema(
+        request_body=no_body,
+        responses={204: "Successfully cleared cache", 400: "Bad request"},
+    )
+    @action(detail=False, methods=["POST"])
+    def cache_clear(self, request: Request) -> Response:
+        """Clear flow cache"""
+        if not request.user.is_superuser:
+            return HttpResponseBadRequest()
+        keys = cache.keys("flow_*")
+        cache.delete_many(keys)
+        LOGGER.debug("Cleared flow cache", keys=len(keys))
+        return Response(status=204)
+
     @swagger_auto_schema(
         responses={
             "200": openapi.Response(
diff --git a/authentik/policies/api.py b/authentik/policies/api.py
index e550cdb77..42ce8bc9f 100644
--- a/authentik/policies/api.py
+++ b/authentik/policies/api.py
@@ -1,8 +1,9 @@
 """policy API Views"""
 from django.core.cache import cache
 from django.core.exceptions import ObjectDoesNotExist
+from django.http.response import HttpResponseBadRequest
 from django.urls import reverse
-from drf_yasg2.utils import swagger_auto_schema
+from drf_yasg2.utils import no_body, swagger_auto_schema
 from rest_framework import mixins
 from rest_framework.decorators import action
 from rest_framework.request import Request
@@ -13,7 +14,9 @@ from rest_framework.serializers import (
     SerializerMethodField,
 )
 from rest_framework.viewsets import GenericViewSet, ModelViewSet
+from structlog.stdlib import get_logger
 
+from authentik.core.api.applications import user_app_cache_key
 from authentik.core.api.utils import (
     CacheSerializer,
     MetaNameSerializer,
@@ -23,6 +26,8 @@ from authentik.lib.templatetags.authentik_utils import verbose_name
 from authentik.lib.utils.reflection import all_subclasses
 from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel
 
+LOGGER = get_logger()
+
 
 class PolicyBindingModelForeignKey(PrimaryKeyRelatedField):
     """rest_framework PrimaryKeyRelatedField which resolves
@@ -139,10 +144,27 @@ class PolicyViewSet(
 
     @swagger_auto_schema(responses={200: CacheSerializer(many=False)})
     @action(detail=False)
-    def cached(self, request: Request) -> Response:
+    def cache_info(self, request: Request) -> Response:
         """Info about cached policies"""
         return Response(data={"count": len(cache.keys("policy_*"))})
 
+    @swagger_auto_schema(
+        request_body=no_body,
+        responses={204: "Successfully cleared cache", 400: "Bad request"},
+    )
+    @action(detail=False, methods=["POST"])
+    def cache_clear(self, request: Request) -> Response:
+        """Clear policy cache"""
+        if not request.user.is_superuser:
+            return HttpResponseBadRequest()
+        keys = cache.keys("policy_*")
+        cache.delete_many(keys)
+        LOGGER.debug("Cleared Policy cache", keys=len(keys))
+        # Also delete user application cache
+        keys = cache.keys(user_app_cache_key("*"))
+        cache.delete_many(keys)
+        return Response(status=204)
+
 
 class PolicyBindingSerializer(ModelSerializer):
     """PolicyBinding Serializer"""
diff --git a/swagger.yaml b/swagger.yaml
index 89b19bd8f..7e2443740 100755
--- a/swagger.yaml
+++ b/swagger.yaml
@@ -2878,9 +2878,22 @@ paths:
       tags:
         - flows
     parameters: []
-  /flows/instances/cached/:
+  /flows/instances/cache_clear/:
+    post:
+      operationId: flows_instances_cache_clear
+      description: Clear flow cache
+      parameters: []
+      responses:
+        '204':
+          description: Successfully cleared cache
+        '400':
+          description: Bad request
+      tags:
+        - flows
+    parameters: []
+  /flows/instances/cache_info/:
     get:
-      operationId: flows_instances_cached
+      operationId: flows_instances_cache_info
       description: Info about cached flows
       parameters:
         - name: flow_uuid
@@ -4116,9 +4129,22 @@ paths:
       tags:
         - policies
     parameters: []
-  /policies/all/cached/:
+  /policies/all/cache_clear/:
+    post:
+      operationId: policies_all_cache_clear
+      description: Clear policy cache
+      parameters: []
+      responses:
+        '204':
+          description: Successfully cleared cache
+        '400':
+          description: Bad request
+      tags:
+        - policies
+    parameters: []
+  /policies/all/cache_info/:
     get:
-      operationId: policies_all_cached
+      operationId: policies_all_cache_info
       description: Info about cached policies
       parameters:
         - name: bindings__isnull
diff --git a/web/src/pages/admin-overview/cards/FlowCacheStatusCard.ts b/web/src/pages/admin-overview/cards/FlowCacheStatusCard.ts
index d4fa394c5..fd338021a 100644
--- a/web/src/pages/admin-overview/cards/FlowCacheStatusCard.ts
+++ b/web/src/pages/admin-overview/cards/FlowCacheStatusCard.ts
@@ -9,7 +9,7 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
 export class FlowCacheStatusCard extends AdminStatusCard<number> {
 
     getPrimaryValue(): Promise<number> {
-        return new FlowsApi(DEFAULT_CONFIG).flowsInstancesCached({}).then((value) => {
+        return new FlowsApi(DEFAULT_CONFIG).flowsInstancesCacheInfo({}).then((value) => {
             return value.count || 0;
         });
     }
diff --git a/web/src/pages/admin-overview/cards/PolicyCacheStatusCard.ts b/web/src/pages/admin-overview/cards/PolicyCacheStatusCard.ts
index 13fba5bd1..a98dff22b 100644
--- a/web/src/pages/admin-overview/cards/PolicyCacheStatusCard.ts
+++ b/web/src/pages/admin-overview/cards/PolicyCacheStatusCard.ts
@@ -10,7 +10,7 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
 export class PolicyCacheStatusCard extends AdminStatusCard<number> {
 
     getPrimaryValue(): Promise<number> {
-        return new PoliciesApi(DEFAULT_CONFIG).policiesAllCached({}).then((value) => {
+        return new PoliciesApi(DEFAULT_CONFIG).policiesAllCacheInfo({}).then((value) => {
             return value.count || 0;
         });
     }