flows: handle possible errors with FlowPlans received from cache
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
3b2b3262d7
commit
ba9edd6c44
|
@ -213,7 +213,7 @@ class SourceFlowManager:
|
|||
planner = FlowPlanner(flow)
|
||||
plan = planner.plan(self.request, kwargs)
|
||||
for stage in self.get_stages_to_append(flow):
|
||||
plan.append(stage)
|
||||
plan.append_stage(stage=stage)
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
return redirect_with_qs(
|
||||
"authentik_core:if-flow",
|
||||
|
|
|
@ -13,7 +13,7 @@ from authentik.core.models import User
|
|||
from authentik.events.models import cleanse_dict
|
||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
||||
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
||||
from authentik.flows.models import Flow, FlowStageBinding
|
||||
from authentik.flows.models import Flow, FlowStageBinding, Stage
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.policies.engine import PolicyEngine
|
||||
from authentik.root.monitoring import UpdatingGauge
|
||||
|
@ -56,14 +56,18 @@ class FlowPlan:
|
|||
context: dict[str, Any] = field(default_factory=dict)
|
||||
markers: list[StageMarker] = field(default_factory=list)
|
||||
|
||||
def append_stage(self, stage: Stage, marker: Optional[StageMarker] = None):
|
||||
"""Append `stage` to all stages, optionall with stage marker"""
|
||||
return self.append(FlowStageBinding(stage=stage), marker)
|
||||
|
||||
def append(self, binding: FlowStageBinding, marker: Optional[StageMarker] = None):
|
||||
"""Append `stage` to all stages, optionall with stage marker"""
|
||||
self.bindings.append(binding)
|
||||
self.markers.append(marker or StageMarker())
|
||||
|
||||
def insert(self, binding: FlowStageBinding, marker: Optional[StageMarker] = None):
|
||||
def insert_stage(self, stage: Stage, marker: Optional[StageMarker] = None):
|
||||
"""Insert stage into plan, as immediate next stage"""
|
||||
self.bindings.insert(1, binding)
|
||||
self.bindings.insert(1, FlowStageBinding(stage=stage, order=0))
|
||||
self.markers.insert(1, marker or StageMarker())
|
||||
|
||||
def next(self, http_request: Optional[HttpRequest]) -> Optional[FlowStageBinding]:
|
||||
|
|
|
@ -52,7 +52,7 @@ class TestFlowExecutor(TestCase):
|
|||
designation=FlowDesignation.AUTHENTICATION,
|
||||
)
|
||||
stage = DummyStage.objects.create(name="dummy")
|
||||
binding = FlowStageBinding.objects.create(target=flow, stage=stage)
|
||||
binding = FlowStageBinding(target=flow, stage=stage, order=0)
|
||||
plan = FlowPlan(
|
||||
flow_pk=flow.pk.hex + "a", bindings=[binding], markers=[StageMarker()]
|
||||
)
|
||||
|
|
|
@ -4,6 +4,7 @@ from typing import Any, Optional
|
|||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.cache import cache
|
||||
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
|
||||
from django.http.request import QueryDict
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
|
@ -276,6 +277,15 @@ class FlowExecutorView(APIView):
|
|||
planner = FlowPlanner(self.flow)
|
||||
plan = planner.plan(self.request)
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
try:
|
||||
# Call the has_stages getter to check that
|
||||
# there are no issues with the class we might've gotten
|
||||
# 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_*")
|
||||
cache.delete_many(keys)
|
||||
return self._initiate_plan()
|
||||
return plan
|
||||
|
||||
def _flow_done(self) -> HttpResponse:
|
||||
|
|
|
@ -474,8 +474,8 @@ class AuthorizationFlowInitView(PolicyAccessView):
|
|||
name="OAuth2 Provider In-memory consent stage",
|
||||
mode=ConsentMode.ALWAYS_REQUIRE,
|
||||
)
|
||||
plan.append(stage)
|
||||
plan.append(in_memory_stage(OAuthFulfillmentStage))
|
||||
plan.append_stage(stage)
|
||||
plan.append_stage(in_memory_stage(OAuthFulfillmentStage))
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
return redirect_with_qs(
|
||||
"authentik_core:if-flow",
|
||||
|
|
|
@ -79,7 +79,7 @@ class SAMLSSOView(PolicyAccessView):
|
|||
PLAN_CONTEXT_CONSENT_PERMISSIONS: [],
|
||||
},
|
||||
)
|
||||
plan.append(in_memory_stage(SAMLFlowFinalView))
|
||||
plan.append_stage(in_memory_stage(SAMLFlowFinalView))
|
||||
request.session[SESSION_KEY_PLAN] = plan
|
||||
return redirect_with_qs(
|
||||
"authentik_core:if-flow",
|
||||
|
|
|
@ -90,7 +90,7 @@ class InitiateView(View):
|
|||
planner.allow_empty_flows = True
|
||||
plan = planner.plan(self.request, kwargs)
|
||||
for stage in stages_to_append:
|
||||
plan.append(stage)
|
||||
plan.append_stage(stage)
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
return redirect_with_qs(
|
||||
"authentik_core:if-flow",
|
||||
|
|
|
@ -148,7 +148,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
stage = Stage.objects.get_subclass(pk=stage.configuration_stage.pk)
|
||||
# plan.insert inserts at 1 index, so when stage_ok pops 0,
|
||||
# the configuration stage is next
|
||||
self.executor.plan.insert(stage)
|
||||
self.executor.plan.insert_stage(stage)
|
||||
return self.executor.stage_ok()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
|
|
Reference in a new issue