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)
|
planner = FlowPlanner(flow)
|
||||||
plan = planner.plan(self.request, kwargs)
|
plan = planner.plan(self.request, kwargs)
|
||||||
for stage in self.get_stages_to_append(flow):
|
for stage in self.get_stages_to_append(flow):
|
||||||
plan.append(stage)
|
plan.append_stage(stage=stage)
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
self.request.session[SESSION_KEY_PLAN] = plan
|
||||||
return redirect_with_qs(
|
return redirect_with_qs(
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
|
|
|
@ -13,7 +13,7 @@ from authentik.core.models import User
|
||||||
from authentik.events.models import cleanse_dict
|
from authentik.events.models import cleanse_dict
|
||||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
||||||
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
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.lib.config import CONFIG
|
||||||
from authentik.policies.engine import PolicyEngine
|
from authentik.policies.engine import PolicyEngine
|
||||||
from authentik.root.monitoring import UpdatingGauge
|
from authentik.root.monitoring import UpdatingGauge
|
||||||
|
@ -56,14 +56,18 @@ class FlowPlan:
|
||||||
context: dict[str, Any] = field(default_factory=dict)
|
context: dict[str, Any] = field(default_factory=dict)
|
||||||
markers: list[StageMarker] = field(default_factory=list)
|
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):
|
def append(self, binding: FlowStageBinding, marker: Optional[StageMarker] = None):
|
||||||
"""Append `stage` to all stages, optionall with stage marker"""
|
"""Append `stage` to all stages, optionall with stage marker"""
|
||||||
self.bindings.append(binding)
|
self.bindings.append(binding)
|
||||||
self.markers.append(marker or StageMarker())
|
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"""
|
"""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())
|
self.markers.insert(1, marker or StageMarker())
|
||||||
|
|
||||||
def next(self, http_request: Optional[HttpRequest]) -> Optional[FlowStageBinding]:
|
def next(self, http_request: Optional[HttpRequest]) -> Optional[FlowStageBinding]:
|
||||||
|
|
|
@ -52,7 +52,7 @@ class TestFlowExecutor(TestCase):
|
||||||
designation=FlowDesignation.AUTHENTICATION,
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
)
|
)
|
||||||
stage = DummyStage.objects.create(name="dummy")
|
stage = DummyStage.objects.create(name="dummy")
|
||||||
binding = FlowStageBinding.objects.create(target=flow, stage=stage)
|
binding = FlowStageBinding(target=flow, stage=stage, order=0)
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=flow.pk.hex + "a", bindings=[binding], markers=[StageMarker()]
|
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.conf import settings
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.core.cache import cache
|
||||||
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
|
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
|
||||||
from django.http.request import QueryDict
|
from django.http.request import QueryDict
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
@ -276,6 +277,15 @@ class FlowExecutorView(APIView):
|
||||||
planner = FlowPlanner(self.flow)
|
planner = FlowPlanner(self.flow)
|
||||||
plan = planner.plan(self.request)
|
plan = planner.plan(self.request)
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
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
|
return plan
|
||||||
|
|
||||||
def _flow_done(self) -> HttpResponse:
|
def _flow_done(self) -> HttpResponse:
|
||||||
|
|
|
@ -474,8 +474,8 @@ class AuthorizationFlowInitView(PolicyAccessView):
|
||||||
name="OAuth2 Provider In-memory consent stage",
|
name="OAuth2 Provider In-memory consent stage",
|
||||||
mode=ConsentMode.ALWAYS_REQUIRE,
|
mode=ConsentMode.ALWAYS_REQUIRE,
|
||||||
)
|
)
|
||||||
plan.append(stage)
|
plan.append_stage(stage)
|
||||||
plan.append(in_memory_stage(OAuthFulfillmentStage))
|
plan.append_stage(in_memory_stage(OAuthFulfillmentStage))
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
self.request.session[SESSION_KEY_PLAN] = plan
|
||||||
return redirect_with_qs(
|
return redirect_with_qs(
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
|
|
|
@ -79,7 +79,7 @@ class SAMLSSOView(PolicyAccessView):
|
||||||
PLAN_CONTEXT_CONSENT_PERMISSIONS: [],
|
PLAN_CONTEXT_CONSENT_PERMISSIONS: [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
plan.append(in_memory_stage(SAMLFlowFinalView))
|
plan.append_stage(in_memory_stage(SAMLFlowFinalView))
|
||||||
request.session[SESSION_KEY_PLAN] = plan
|
request.session[SESSION_KEY_PLAN] = plan
|
||||||
return redirect_with_qs(
|
return redirect_with_qs(
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
|
|
|
@ -90,7 +90,7 @@ class InitiateView(View):
|
||||||
planner.allow_empty_flows = True
|
planner.allow_empty_flows = True
|
||||||
plan = planner.plan(self.request, kwargs)
|
plan = planner.plan(self.request, kwargs)
|
||||||
for stage in stages_to_append:
|
for stage in stages_to_append:
|
||||||
plan.append(stage)
|
plan.append_stage(stage)
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
self.request.session[SESSION_KEY_PLAN] = plan
|
||||||
return redirect_with_qs(
|
return redirect_with_qs(
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
|
|
|
@ -148,7 +148,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
||||||
stage = Stage.objects.get_subclass(pk=stage.configuration_stage.pk)
|
stage = Stage.objects.get_subclass(pk=stage.configuration_stage.pk)
|
||||||
# plan.insert inserts at 1 index, so when stage_ok pops 0,
|
# plan.insert inserts at 1 index, so when stage_ok pops 0,
|
||||||
# the configuration stage is next
|
# the configuration stage is next
|
||||||
self.executor.plan.insert(stage)
|
self.executor.plan.insert_stage(stage)
|
||||||
return self.executor.stage_ok()
|
return self.executor.stage_ok()
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
Reference in New Issue