flows: fix post-email continuation not working
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
1d641b2432
commit
dce869b566
|
@ -46,7 +46,7 @@ class Challenge(Serializer):
|
|||
background = CharField(required=False)
|
||||
|
||||
response_errors = DictField(
|
||||
child=ErrorDetailSerializer(many=True), allow_empty=False, required=False
|
||||
child=ErrorDetailSerializer(many=True), allow_empty=True, required=False
|
||||
)
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
|
|
|
@ -16,11 +16,27 @@ from authentik.flows.challenge import (
|
|||
)
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.views import FlowExecutorView
|
||||
from authentik.lib.sentry import SentryIgnoredException
|
||||
|
||||
PLAN_CONTEXT_PENDING_USER_IDENTIFIER = "pending_user_identifier"
|
||||
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):
|
||||
"""Abstract Stage, inherits TemplateView but can be combined with FormView"""
|
||||
|
||||
|
@ -64,7 +80,7 @@ class ChallengeStageView(StageView):
|
|||
"""Return a challenge for the frontend to solve"""
|
||||
challenge = self._get_challenge(*args, **kwargs)
|
||||
if not challenge.is_valid():
|
||||
LOGGER.warning(challenge.errors)
|
||||
LOGGER.warning(challenge.errors, stage_view=self, challenge=challenge)
|
||||
return HttpChallengeResponse(challenge)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
|
|
@ -103,8 +103,8 @@ class FlowExecutorView(APIView):
|
|||
# To match behaviour with loading an empty flow plan from cache,
|
||||
# we don't show an error message here, but rather call _flow_done()
|
||||
return self._flow_done()
|
||||
# Initial flow request, check if we have an upstream query string passed in
|
||||
request.session[SESSION_KEY_GET] = QueryDict(request.GET.get("query", ""))
|
||||
# Initial flow request, check if we have an upstream query string passed in
|
||||
request.session[SESSION_KEY_GET] = QueryDict(request.GET.get("query", ""))
|
||||
# We don't save the Plan after getting the next stage
|
||||
# as it hasn't been successfully passed yet
|
||||
next_stage = self.plan.next(self.request)
|
||||
|
|
|
@ -84,6 +84,7 @@ class EmailStageView(ChallengeStageView):
|
|||
messages.success(request, _("Successfully verified Email."))
|
||||
return self.executor.stage_ok()
|
||||
if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
|
||||
LOGGER.debug("No pending user")
|
||||
messages.error(self.request, _("No pending user."))
|
||||
return self.executor.stage_invalid()
|
||||
# Check if we've already sent the initial e-mail
|
||||
|
@ -94,7 +95,11 @@ class EmailStageView(ChallengeStageView):
|
|||
|
||||
def get_challenge(self) -> Challenge:
|
||||
challenge = EmailChallenge(
|
||||
data={"type": ChallengeTypes.native.value, "component": "ak-stage-email"}
|
||||
data={
|
||||
"type": ChallengeTypes.native.value,
|
||||
"component": "ak-stage-email",
|
||||
"title": "Email sent.",
|
||||
}
|
||||
)
|
||||
return challenge
|
||||
|
||||
|
|
|
@ -5,12 +5,13 @@ from django.core import mail
|
|||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.http import urlencode
|
||||
|
||||
from authentik.core.models import Token, User
|
||||
from authentik.flows.markers import StageMarker
|
||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||
from authentik.flows.views import SESSION_KEY_GET, SESSION_KEY_PLAN
|
||||
from authentik.flows.views import SESSION_KEY_PLAN
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.stage import QS_KEY_TOKEN
|
||||
|
||||
|
@ -104,11 +105,23 @@ class TestEmailStage(TestCase):
|
|||
)
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
token: Token = Token.objects.get(user=self.user)
|
||||
session[SESSION_KEY_GET] = {QS_KEY_TOKEN: token.key}
|
||||
session.save()
|
||||
token: Token = Token.objects.get(user=self.user)
|
||||
|
||||
with patch("authentik.flows.views.FlowExecutorView.cancel", MagicMock()):
|
||||
# Call the executor shell to preseed the session
|
||||
url = reverse(
|
||||
"authentik_api:flow-executor",
|
||||
kwargs={"flow_slug": self.flow.slug},
|
||||
)
|
||||
url_query = urlencode(
|
||||
{
|
||||
QS_KEY_TOKEN: token.key,
|
||||
}
|
||||
)
|
||||
url += f"?query={url_query}"
|
||||
self.client.get(url)
|
||||
|
||||
# Call the actual executor to get the JSON Response
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
|
|
|
@ -89,10 +89,12 @@ class SeleniumTestCase(StaticLiveServerTestCase):
|
|||
)
|
||||
self.driver.save_screenshot(screenshot_file)
|
||||
self.logger.warning("Saved screenshot", file=screenshot_file)
|
||||
self.logger.debug("--------browser logs")
|
||||
for line in self.driver.get_log("browser"):
|
||||
self.logger.warning(
|
||||
self.logger.debug(
|
||||
line["message"], source=line["source"], level=line["level"]
|
||||
)
|
||||
self.logger.debug("--------end browser logs")
|
||||
if self.container:
|
||||
self.container.kill()
|
||||
self.driver.quit()
|
||||
|
|
Reference in a new issue