core: add error handling in source flow manager when flow isn't applicable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
e434321f7c
commit
7d3d17acb9
|
@ -14,6 +14,7 @@ from structlog.stdlib import get_logger
|
||||||
from authentik.core.models import Source, SourceUserMatchingModes, User, UserSourceConnection
|
from authentik.core.models import Source, SourceUserMatchingModes, User, UserSourceConnection
|
||||||
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION, PostUserEnrollmentStage
|
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION, PostUserEnrollmentStage
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
|
from authentik.flows.exceptions import FlowNonApplicableException
|
||||||
from authentik.flows.models import Flow, Stage, in_memory_stage
|
from authentik.flows.models import Flow, Stage, in_memory_stage
|
||||||
from authentik.flows.planner import (
|
from authentik.flows.planner import (
|
||||||
PLAN_CONTEXT_PENDING_USER,
|
PLAN_CONTEXT_PENDING_USER,
|
||||||
|
@ -24,6 +25,8 @@ from authentik.flows.planner import (
|
||||||
)
|
)
|
||||||
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
|
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
|
||||||
from authentik.lib.utils.urls import redirect_with_qs
|
from authentik.lib.utils.urls import redirect_with_qs
|
||||||
|
from authentik.policies.denied import AccessDeniedResponse
|
||||||
|
from authentik.policies.types import PolicyResult
|
||||||
from authentik.policies.utils import delete_none_keys
|
from authentik.policies.utils import delete_none_keys
|
||||||
from authentik.stages.password import BACKEND_INBUILT
|
from authentik.stages.password import BACKEND_INBUILT
|
||||||
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
||||||
|
@ -149,6 +152,7 @@ class SourceFlowManager:
|
||||||
self._logger.warning("failed to get action", exc=exc)
|
self._logger.warning("failed to get action", exc=exc)
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
self._logger.debug("get_action", action=action, connection=connection)
|
self._logger.debug("get_action", action=action, connection=connection)
|
||||||
|
try:
|
||||||
if connection:
|
if connection:
|
||||||
if action == Action.LINK:
|
if action == Action.LINK:
|
||||||
self._logger.debug("Linking existing user")
|
self._logger.debug("Linking existing user")
|
||||||
|
@ -159,9 +163,11 @@ class SourceFlowManager:
|
||||||
if action == Action.ENROLL:
|
if action == Action.ENROLL:
|
||||||
self._logger.debug("Handling enrollment of new user")
|
self._logger.debug("Handling enrollment of new user")
|
||||||
return self.handle_enroll(connection)
|
return self.handle_enroll(connection)
|
||||||
|
except FlowNonApplicableException as exc:
|
||||||
|
self._logger.warning("Flow non applicable", exc=exc)
|
||||||
|
return self.error_handler(exc, exc.policy_result)
|
||||||
# Default case, assume deny
|
# Default case, assume deny
|
||||||
messages.error(
|
error = (
|
||||||
self.request,
|
|
||||||
_(
|
_(
|
||||||
(
|
(
|
||||||
"Request to authenticate with %(source)s has been denied. Please authenticate "
|
"Request to authenticate with %(source)s has been denied. Please authenticate "
|
||||||
|
@ -170,7 +176,17 @@ class SourceFlowManager:
|
||||||
% {"source": self.source.name}
|
% {"source": self.source.name}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return redirect(reverse("authentik_core:root-redirect"))
|
return self.error_handler(error)
|
||||||
|
|
||||||
|
def error_handler(
|
||||||
|
self, error: Exception, policy_result: Optional[PolicyResult] = None
|
||||||
|
) -> HttpResponse:
|
||||||
|
"""Handle any errors by returning an access denied stage"""
|
||||||
|
response = AccessDeniedResponse(self.request)
|
||||||
|
response.error_message = str(error)
|
||||||
|
if policy_result:
|
||||||
|
response.policy_result = policy_result
|
||||||
|
return response
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_stages_to_append(self, flow: Flow) -> list[Stage]:
|
def get_stages_to_append(self, flow: Flow) -> list[Stage]:
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
"""flow exceptions"""
|
"""flow exceptions"""
|
||||||
|
|
||||||
from authentik.lib.sentry import SentryIgnoredException
|
from authentik.lib.sentry import SentryIgnoredException
|
||||||
|
from authentik.policies.types import PolicyResult
|
||||||
|
|
||||||
|
|
||||||
class FlowNonApplicableException(SentryIgnoredException):
|
class FlowNonApplicableException(SentryIgnoredException):
|
||||||
"""Flow does not apply to current user (denied by policy)."""
|
"""Flow does not apply to current user (denied by policy)."""
|
||||||
|
|
||||||
|
policy_result: PolicyResult
|
||||||
|
|
||||||
|
|
||||||
class EmptyFlowException(SentryIgnoredException):
|
class EmptyFlowException(SentryIgnoredException):
|
||||||
"""Flow has no stages."""
|
"""Flow has no stages."""
|
||||||
|
|
|
@ -152,7 +152,9 @@ class FlowPlanner:
|
||||||
engine.build()
|
engine.build()
|
||||||
result = engine.result
|
result = engine.result
|
||||||
if not result.passing:
|
if not result.passing:
|
||||||
raise FlowNonApplicableException(",".join(result.messages))
|
exc = FlowNonApplicableException(",".join(result.messages))
|
||||||
|
exc.policy_result = result
|
||||||
|
raise exc
|
||||||
# User is passing so far, check if we have a cached plan
|
# User is passing so far, check if we have a cached plan
|
||||||
cached_plan_key = cache_key(self.flow, user)
|
cached_plan_key = cache_key(self.flow, user)
|
||||||
cached_plan = cache.get(cached_plan_key, None)
|
cached_plan = cache.get(cached_plan_key, None)
|
||||||
|
|
Reference in New Issue