core: show success message when authenticating/enrolling after flow is finished

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2022-10-25 22:46:15 +02:00
parent 2206b71f6f
commit dd65862bf2

View file

@ -5,7 +5,7 @@ from typing import Any, Optional
from django.contrib import messages
from django.db import IntegrityError
from django.db.models.query_utils import Q
from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest
from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import gettext as _
@ -23,8 +23,10 @@ from authentik.flows.planner import (
PLAN_CONTEXT_SSO,
FlowPlanner,
)
from authentik.flows.stage import StageView
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.views import bad_request_message
from authentik.policies.denied import AccessDeniedResponse
from authentik.policies.utils import delete_none_keys
from authentik.stages.password import BACKEND_INBUILT
@ -43,6 +45,34 @@ class Action(Enum):
DENY = "deny"
def message_stage(message: str, level: int) -> StageView:
"""Show a pre-configured message after the flow is done"""
class MessageStage(StageView):
"""Show a pre-configured message after the flow is done"""
message: str
level: int
# pylint: disable=unused-argument
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""Show a pre-configured message after the flow is done"""
messages.add_message(
self.request,
self.level,
self.message,
)
return self.executor.stage_ok()
def post(self, request: HttpRequest) -> HttpResponse:
"""Wrapper for post requests"""
return self.get(request)
MessageStage.message = message
MessageStage.level = level
return MessageStage
class SourceFlowManager:
"""Help sources decide what they should do after authorization. Based on source settings and
previous connections, authenticate the user, enroll a new user, link to an existing user
@ -156,10 +186,10 @@ class SourceFlowManager:
if connection:
if action == Action.LINK:
self._logger.debug("Linking existing user")
return self.handle_existing_user_link(connection)
return self.handle_existing_link(connection)
if action == Action.AUTH:
self._logger.debug("Handling auth user")
return self.handle_auth_user(connection)
return self.handle_auth(connection)
if action == Action.ENROLL:
self._logger.debug("Handling enrollment of new user")
return self.handle_enroll(connection)
@ -199,7 +229,11 @@ class SourceFlowManager:
return []
def _handle_login_flow(
self, flow: Flow, connection: UserSourceConnection, **kwargs
self,
flow: Flow,
connection: UserSourceConnection,
stages: Optional[list[StageView]] = None,
**kwargs,
) -> HttpResponse:
"""Prepare Authentication Plan, redirect user FlowExecutor"""
# Ensure redirect is carried through when user was trying to
@ -219,12 +253,18 @@ class SourceFlowManager:
)
kwargs.update(self.policy_context)
if not flow:
return HttpResponseBadRequest()
return bad_request_message(
self.request,
_("Configured flow does not exist."),
)
# We run the Flow planner here so we can pass the Pending user in the context
planner = FlowPlanner(flow)
plan = planner.plan(self.request, kwargs)
for stage in self.get_stages_to_append(flow):
plan.append_stage(stage=stage)
plan.append_stage(stage)
if stages:
for stage in stages:
plan.append_stage(stage)
self.request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
@ -233,19 +273,30 @@ class SourceFlowManager:
)
# pylint: disable=unused-argument
def handle_auth_user(
def handle_auth(
self,
connection: UserSourceConnection,
) -> HttpResponse:
"""Login user and redirect."""
messages.success(
self.request,
_("Successfully authenticated with %(source)s!" % {"source": self.source.name}),
)
flow_kwargs = {PLAN_CONTEXT_PENDING_USER: connection.user}
return self._handle_login_flow(self.source.authentication_flow, connection, **flow_kwargs)
return self._handle_login_flow(
self.source.authentication_flow,
connection,
stages=[
in_memory_stage(
message_stage(
messages.SUCCESS,
_(
"Successfully authenticated with %(source)s!"
% {"source": self.source.name}
),
)
)
],
**flow_kwargs,
)
def handle_existing_user_link(
def handle_existing_link(
self,
connection: UserSourceConnection,
) -> HttpResponse:
@ -263,7 +314,7 @@ class SourceFlowManager:
)
# When request isn't authenticated we jump straight to auth
if not self.request.user.is_authenticated:
return self.handle_auth_user(connection)
return self.handle_auth(connection)
return redirect(
reverse(
"authentik_core:if-user",
@ -276,18 +327,27 @@ class SourceFlowManager:
connection: UserSourceConnection,
) -> HttpResponse:
"""User was not authenticated and previous request was not authenticated."""
messages.success(
self.request,
_("Successfully authenticated with %(source)s!" % {"source": self.source.name}),
)
# We run the Flow planner here so we can pass the Pending user in the context
if not self.source.enrollment_flow:
self._logger.warning("source has no enrollment flow")
return HttpResponseBadRequest()
return bad_request_message(
self.request,
_("Source is not configured for enrollment."),
)
return self._handle_login_flow(
self.source.enrollment_flow,
connection,
stages=[
in_memory_stage(
message_stage(
messages.SUCCESS,
_(
"Successfully authenticated with %(source)s!"
% {"source": self.source.name}
),
)
)
],
**{
PLAN_CONTEXT_PROMPT: delete_none_keys(self.enroll_info),
PLAN_CONTEXT_USER_PATH: self.source.get_user_path(),