stages/authenticator_duo: fix error when enrolling an existing user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
b53c94d76a
commit
b20a8b7c17
|
@ -18,27 +18,11 @@ from authentik.flows.challenge import (
|
||||||
)
|
)
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from authentik.flows.views import FlowExecutorView
|
from authentik.flows.views import FlowExecutorView
|
||||||
from authentik.lib.sentry import SentryIgnoredException
|
|
||||||
|
|
||||||
PLAN_CONTEXT_PENDING_USER_IDENTIFIER = "pending_user_identifier"
|
PLAN_CONTEXT_PENDING_USER_IDENTIFIER = "pending_user_identifier"
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
||||||
class InvalidChallengeError(SentryIgnoredException):
|
|
||||||
"""Error raised when a challenge from a stage is not valid"""
|
|
||||||
|
|
||||||
def __init__(self, errors, stage_view: View, challenge: Challenge) -> None:
|
|
||||||
super().__init__()
|
|
||||||
self.errors = errors
|
|
||||||
self.stage_view = stage_view
|
|
||||||
self.challenge = challenge
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return (
|
|
||||||
f"Invalid challenge from {self.stage_view}: {self.errors}\n{self.challenge}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class StageView(View):
|
class StageView(View):
|
||||||
"""Abstract Stage, inherits TemplateView but can be combined with FormView"""
|
"""Abstract Stage, inherits TemplateView but can be combined with FormView"""
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ from authentik.flows.planner import (
|
||||||
FlowPlan,
|
FlowPlan,
|
||||||
FlowPlanner,
|
FlowPlanner,
|
||||||
)
|
)
|
||||||
|
from authentik.lib.sentry import SentryIgnoredException
|
||||||
from authentik.lib.utils.reflection import all_subclasses, class_to_path
|
from authentik.lib.utils.reflection import all_subclasses, class_to_path
|
||||||
from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs
|
from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.models import Tenant
|
||||||
|
@ -93,6 +94,10 @@ def challenge_response_types():
|
||||||
return Inner()
|
return Inner()
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidStageError(SentryIgnoredException):
|
||||||
|
"""Error raised when a challenge from a stage is not valid"""
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(xframe_options_sameorigin, name="dispatch")
|
@method_decorator(xframe_options_sameorigin, name="dispatch")
|
||||||
class FlowExecutorView(APIView):
|
class FlowExecutorView(APIView):
|
||||||
"""Stage 1 Flow executor, passing requests to Stage Views"""
|
"""Stage 1 Flow executor, passing requests to Stage Views"""
|
||||||
|
@ -173,7 +178,10 @@ class FlowExecutorView(APIView):
|
||||||
self.current_stage_view.args = self.args
|
self.current_stage_view.args = self.args
|
||||||
self.current_stage_view.kwargs = self.kwargs
|
self.current_stage_view.kwargs = self.kwargs
|
||||||
self.current_stage_view.request = request
|
self.current_stage_view.request = request
|
||||||
|
try:
|
||||||
return super().dispatch(request)
|
return super().dispatch(request)
|
||||||
|
except InvalidStageError as exc:
|
||||||
|
return self.stage_invalid(str(exc))
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
responses={
|
responses={
|
||||||
|
|
|
@ -3,6 +3,7 @@ from django.http import HttpRequest, HttpResponse
|
||||||
from rest_framework.fields import CharField
|
from rest_framework.fields import CharField
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.flows.challenge import (
|
from authentik.flows.challenge import (
|
||||||
Challenge,
|
Challenge,
|
||||||
ChallengeResponse,
|
ChallengeResponse,
|
||||||
|
@ -11,6 +12,7 @@ from authentik.flows.challenge import (
|
||||||
)
|
)
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from authentik.flows.stage import ChallengeStageView
|
from authentik.flows.stage import ChallengeStageView
|
||||||
|
from authentik.flows.views import InvalidStageError
|
||||||
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
@ -42,7 +44,15 @@ class AuthenticatorDuoStageView(ChallengeStageView):
|
||||||
def get_challenge(self, *args, **kwargs) -> Challenge:
|
def get_challenge(self, *args, **kwargs) -> Challenge:
|
||||||
user = self.get_pending_user()
|
user = self.get_pending_user()
|
||||||
stage: AuthenticatorDuoStage = self.executor.current_stage
|
stage: AuthenticatorDuoStage = self.executor.current_stage
|
||||||
|
try:
|
||||||
enroll = stage.client.enroll(user.username)
|
enroll = stage.client.enroll(user.username)
|
||||||
|
except RuntimeError as exc:
|
||||||
|
Event.new(
|
||||||
|
EventAction.CONFIGURATION_ERROR,
|
||||||
|
message=f"Failed to enroll user: {str(exc)}",
|
||||||
|
user=user,
|
||||||
|
).from_http(self.request).set_user(user).save()
|
||||||
|
raise InvalidStageError(str(exc)) from exc
|
||||||
user_id = enroll["user_id"]
|
user_id = enroll["user_id"]
|
||||||
self.request.session[SESSION_KEY_DUO_USER_ID] = user_id
|
self.request.session[SESSION_KEY_DUO_USER_ID] = user_id
|
||||||
self.request.session[SESSION_KEY_DUO_ACTIVATION_CODE] = enroll[
|
self.request.session[SESSION_KEY_DUO_ACTIVATION_CODE] = enroll[
|
||||||
|
|
Reference in New Issue