stages/authenticator_duo: fix error when enrolling an existing user

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-06-16 22:59:16 +02:00
parent b53c94d76a
commit b20a8b7c17
3 changed files with 20 additions and 18 deletions

View File

@ -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"""

View File

@ -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
return super().dispatch(request) try:
return super().dispatch(request)
except InvalidStageError as exc:
return self.stage_invalid(str(exc))
@extend_schema( @extend_schema(
responses={ responses={

View File

@ -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
enroll = stage.client.enroll(user.username) try:
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[