diff --git a/authentik/flows/api/flows.py b/authentik/flows/api/flows.py index 670128c26..9838dd114 100644 --- a/authentik/flows/api/flows.py +++ b/authentik/flows/api/flows.py @@ -23,7 +23,7 @@ from authentik.events.utils import sanitize_dict from authentik.flows.api.flows_diagram import FlowDiagram, FlowDiagramSerializer from authentik.flows.exceptions import FlowNonApplicableException from authentik.flows.models import Flow -from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key +from authentik.flows.planner import CACHE_PREFIX, PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key from authentik.flows.views.executor import SESSION_KEY_HISTORY, SESSION_KEY_PLAN from authentik.lib.utils.file import ( FilePathSerializer, @@ -121,7 +121,7 @@ class FlowViewSet(UsedByMixin, ModelViewSet): @action(detail=False, pagination_class=None, filter_backends=[]) def cache_info(self, request: Request) -> Response: """Info about cached flows""" - return Response(data={"count": len(cache.keys("flow_*"))}) + return Response(data={"count": len(cache.keys(f"{CACHE_PREFIX}*"))}) @permission_required(None, ["authentik_flows.clear_flow_cache"]) @extend_schema( @@ -134,7 +134,7 @@ class FlowViewSet(UsedByMixin, ModelViewSet): @action(detail=False, methods=["POST"]) def cache_clear(self, request: Request) -> Response: """Clear flow cache""" - keys = cache.keys("flow_*") + keys = cache.keys(f"{CACHE_PREFIX}*") cache.delete_many(keys) LOGGER.debug("Cleared flow cache", keys=len(keys)) return Response(status=204) diff --git a/authentik/flows/planner.py b/authentik/flows/planner.py index 35f56f468..547803dd2 100644 --- a/authentik/flows/planner.py +++ b/authentik/flows/planner.py @@ -27,11 +27,12 @@ PLAN_CONTEXT_SOURCE = "source" # was restored. PLAN_CONTEXT_IS_RESTORED = "is_restored" CACHE_TIMEOUT = int(CONFIG.y("redis.cache_timeout_flows")) +CACHE_PREFIX = "goauthentik.io/flows/planner/" def cache_key(flow: Flow, user: Optional[User] = None) -> str: """Generate Cache key for flow""" - prefix = f"goauthentik.io/flows/planner/{flow.pk}" + prefix = CACHE_PREFIX + str(flow.pk) if user: prefix += f"#{user.pk}" return prefix @@ -141,6 +142,7 @@ class FlowPlanner: # First off, check the flow's direct policy bindings # to make sure the user even has access to the flow engine = PolicyEngine(self.flow, user, request) + engine.use_cache = self.use_cache if default_context: span.set_data("default_context", cleanse_dict(default_context)) engine.request.context.update(default_context) @@ -206,6 +208,7 @@ class FlowPlanner: stage=stage, ) engine = PolicyEngine(binding, user, request) + engine.use_cache = self.use_cache engine.request.context["flow_plan"] = plan engine.request.context.update(plan.context) engine.build() diff --git a/authentik/flows/signals.py b/authentik/flows/signals.py index 8338aca6b..0268fa869 100644 --- a/authentik/flows/signals.py +++ b/authentik/flows/signals.py @@ -5,6 +5,7 @@ from django.dispatch import receiver from structlog.stdlib import get_logger from authentik.flows.apps import GAUGE_FLOWS_CACHED +from authentik.flows.planner import CACHE_PREFIX from authentik.root.monitoring import monitoring_set LOGGER = get_logger() @@ -21,7 +22,7 @@ def delete_cache_prefix(prefix: str) -> int: # pylint: disable=unused-argument def monitoring_set_flows(sender, **kwargs): """set flow gauges""" - GAUGE_FLOWS_CACHED.set(len(cache.keys("flow_*") or [])) + GAUGE_FLOWS_CACHED.set(len(cache.keys(f"{CACHE_PREFIX}*") or [])) @receiver(post_save) diff --git a/authentik/flows/views/executor.py b/authentik/flows/views/executor.py index 4b55fcbb6..cb85fd93d 100644 --- a/authentik/flows/views/executor.py +++ b/authentik/flows/views/executor.py @@ -44,6 +44,7 @@ from authentik.flows.models import ( Stage, ) from authentik.flows.planner import ( + CACHE_PREFIX, PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDING_USER, PLAN_CONTEXT_REDIRECT, @@ -216,7 +217,7 @@ class FlowExecutorView(APIView): self._logger.warning( "f(exec): found incompatible flow plan, invalidating run", exc=exc ) - keys = cache.keys("flow_*") + keys = cache.keys(f"{CACHE_PREFIX}*") cache.delete_many(keys) return self.stage_invalid() if not next_binding: @@ -351,7 +352,7 @@ class FlowExecutorView(APIView): # from the cache. If there are errors, just delete all cached flows _ = plan.has_stages except Exception: # pylint: disable=broad-except - keys = cache.keys("flow_*") + keys = cache.keys(f"{CACHE_PREFIX}*") cache.delete_many(keys) return self._initiate_plan() return plan diff --git a/authentik/policies/api/policies.py b/authentik/policies/api/policies.py index 55bd29ddf..7a4161c47 100644 --- a/authentik/policies/api/policies.py +++ b/authentik/policies/api/policies.py @@ -21,7 +21,7 @@ from authentik.lib.utils.reflection import all_subclasses from authentik.policies.api.exec import PolicyTestResultSerializer, PolicyTestSerializer from authentik.policies.models import Policy, PolicyBinding from authentik.policies.process import PolicyProcess -from authentik.policies.types import PolicyRequest +from authentik.policies.types import CACHE_PREFIX, PolicyRequest LOGGER = get_logger() @@ -114,7 +114,7 @@ class PolicyViewSet( @action(detail=False, pagination_class=None, filter_backends=[]) def cache_info(self, request: Request) -> Response: """Info about cached policies""" - return Response(data={"count": len(cache.keys("policy_*"))}) + return Response(data={"count": len(cache.keys(f"{CACHE_PREFIX}*"))}) @permission_required(None, ["authentik_policies.clear_policy_cache"]) @extend_schema( @@ -127,7 +127,7 @@ class PolicyViewSet( @action(detail=False, methods=["POST"]) def cache_clear(self, request: Request) -> Response: """Clear policy cache""" - keys = cache.keys("policy_*") + keys = cache.keys(f"{CACHE_PREFIX}*") cache.delete_many(keys) LOGGER.debug("Cleared Policy cache", keys=len(keys)) # Also delete user application cache diff --git a/authentik/policies/tests/test_process.py b/authentik/policies/tests/test_process.py index 0ae1ffefa..3d55a4c70 100644 --- a/authentik/policies/tests/test_process.py +++ b/authentik/policies/tests/test_process.py @@ -10,12 +10,12 @@ from authentik.policies.dummy.models import DummyPolicy from authentik.policies.expression.models import ExpressionPolicy from authentik.policies.models import Policy, PolicyBinding from authentik.policies.process import PolicyProcess -from authentik.policies.types import PolicyRequest +from authentik.policies.types import CACHE_PREFIX, PolicyRequest def clear_policy_cache(): """Ensure no policy-related keys are still cached""" - keys = cache.keys("policy_*") + keys = cache.keys(f"{CACHE_PREFIX}*") cache.delete(keys)