From ff15514d5b94b2e01acaca16c3ef317c2ec14681 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 6 Dec 2020 13:12:32 +0100 Subject: [PATCH] stages/identification: add show_matched_user to optionally hide user details --- authentik/flows/stage.py | 18 +++++++++++++++- authentik/lib/templatetags/authentik_utils.py | 3 ++- authentik/stages/identification/api.py | 1 + authentik/stages/identification/forms.py | 1 + ...6_identificationstage_show_matched_user.py | 21 +++++++++++++++++++ authentik/stages/identification/models.py | 10 +++++++++ authentik/stages/identification/stage.py | 12 +++++++++-- swagger.yaml | 6 ++++++ 8 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 authentik/stages/identification/migrations/0006_identificationstage_show_matched_user.py diff --git a/authentik/flows/stage.py b/authentik/flows/stage.py index edef963d5..c4e41e1b1 100644 --- a/authentik/flows/stage.py +++ b/authentik/flows/stage.py @@ -1,4 +1,5 @@ """authentik stage Base view""" +from collections import namedtuple from typing import Any, Dict from django.http import HttpRequest @@ -8,6 +9,10 @@ from django.views.generic import TemplateView from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER from authentik.flows.views import FlowExecutorView +PLAN_CONTEXT_PENDING_USER_IDENTIFIER = "pending_user_identifier" + +FakeUser = namedtuple("User", ["username", "email"]) + class StageView(TemplateView): """Abstract Stage, inherits TemplateView but can be combined with FormView""" @@ -21,9 +26,20 @@ class StageView(TemplateView): def __init__(self, executor: FlowExecutorView): self.executor = executor - def get_context_data(self, **kwargs: Dict[str, Any]) -> Dict[str, Any]: + def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: kwargs["title"] = self.executor.flow.title + # Either show the matched User object or show what the user entered, + # based on what the earlier stage (mostly IdentificationStage) set. + # _USER_IDENTIFIER overrides the first User, as PENDING_USER is used for + # other things besides the form display if PLAN_CONTEXT_PENDING_USER in self.executor.plan.context: kwargs["user"] = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] + if PLAN_CONTEXT_PENDING_USER_IDENTIFIER in self.executor.plan.context: + kwargs["user"] = FakeUser( + username=self.executor.plan.context.get( + PLAN_CONTEXT_PENDING_USER_IDENTIFIER + ), + email="", + ) kwargs["primary_action"] = _("Continue") return super().get_context_data(**kwargs) diff --git a/authentik/lib/templatetags/authentik_utils.py b/authentik/lib/templatetags/authentik_utils.py index 16cd0e153..d2c45af3c 100644 --- a/authentik/lib/templatetags/authentik_utils.py +++ b/authentik/lib/templatetags/authentik_utils.py @@ -18,6 +18,7 @@ register = template.Library() LOGGER = get_logger() GRAVATAR_URL = "https://secure.gravatar.com" +DEFAULT_AVATAR = static("authentik/user_default.png") @register.simple_tag(takes_context=True) @@ -62,7 +63,7 @@ def avatar(user: User) -> str: """Get avatar, depending on authentik.avatar setting""" mode = CONFIG.raw.get("authentik").get("avatars") if mode == "none": - return static("authentik/user_default.png") + return DEFAULT_AVATAR if mode == "gravatar": parameters = [ ("s", "158"), diff --git a/authentik/stages/identification/api.py b/authentik/stages/identification/api.py index 7c463fa21..be08013fc 100644 --- a/authentik/stages/identification/api.py +++ b/authentik/stages/identification/api.py @@ -16,6 +16,7 @@ class IdentificationStageSerializer(ModelSerializer): "name", "user_fields", "case_insensitive_matching", + "show_matched_user", "template", "enrollment_flow", "recovery_flow", diff --git a/authentik/stages/identification/forms.py b/authentik/stages/identification/forms.py index d371545ff..d431f544e 100644 --- a/authentik/stages/identification/forms.py +++ b/authentik/stages/identification/forms.py @@ -31,6 +31,7 @@ class IdentificationStageForm(forms.ModelForm): "name", "user_fields", "case_insensitive_matching", + "show_matched_user", "template", "enrollment_flow", "recovery_flow", diff --git a/authentik/stages/identification/migrations/0006_identificationstage_show_matched_user.py b/authentik/stages/identification/migrations/0006_identificationstage_show_matched_user.py new file mode 100644 index 000000000..239d1f905 --- /dev/null +++ b/authentik/stages/identification/migrations/0006_identificationstage_show_matched_user.py @@ -0,0 +1,21 @@ +# Generated by Django 3.1.4 on 2020-12-06 11:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_stages_identification", "0005_auto_20201003_1734"), + ] + + operations = [ + migrations.AddField( + model_name="identificationstage", + name="show_matched_user", + field=models.BooleanField( + default=True, + help_text="When a valid username/email has been entered, and this option is enabled, the user's username and avatar will be shown. Otherwise, the text that the user entered will be shown", + ), + ), + ] diff --git a/authentik/stages/identification/models.py b/authentik/stages/identification/models.py index 94e28cbe8..cf6513e97 100644 --- a/authentik/stages/identification/models.py +++ b/authentik/stages/identification/models.py @@ -45,6 +45,16 @@ class IdentificationStage(Stage): "When enabled, user fields are matched regardless of their casing." ), ) + show_matched_user = models.BooleanField( + default=True, + help_text=_( + ( + "When a valid username/email has been entered, and this option is enabled, " + "the user's username and avatar will be shown. Otherwise, the text that the user " + "entered will be shown" + ) + ), + ) enrollment_flow = models.ForeignKey( Flow, diff --git a/authentik/stages/identification/stage.py b/authentik/stages/identification/stage.py index 41a8a8952..1bcef47e1 100644 --- a/authentik/stages/identification/stage.py +++ b/authentik/stages/identification/stage.py @@ -11,7 +11,7 @@ from structlog import get_logger from authentik.core.models import Source, User from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER -from authentik.flows.stage import StageView +from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView from authentik.flows.views import SESSION_KEY_APPLICATION_PRE from authentik.stages.identification.forms import IdentificationForm from authentik.stages.identification.models import IdentificationStage @@ -84,10 +84,18 @@ class IdentificationStageView(FormView, StageView): def form_valid(self, form: IdentificationForm) -> HttpResponse: """Form data is valid""" - pre_user = self.get_user(form.cleaned_data.get("uid_field")) + user_identifier = form.cleaned_data.get("uid_field") + pre_user = self.get_user(user_identifier) if not pre_user: LOGGER.debug("invalid_login") messages.error(self.request, _("Failed to authenticate.")) return self.form_invalid(form) self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = pre_user + + current_stage: IdentificationStage = self.executor.current_stage + if not current_stage.show_matched_user: + self.executor.plan.context[ + PLAN_CONTEXT_PENDING_USER_IDENTIFIER + ] = user_identifier + return self.executor.stage_ok() diff --git a/swagger.yaml b/swagger.yaml index 5b008e4a8..7f0c49926 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -8278,6 +8278,12 @@ definitions: title: Case insensitive matching description: When enabled, user fields are matched regardless of their casing. type: boolean + show_matched_user: + title: Show matched user + description: When a valid username/email has been entered, and this option + is enabled, the user's username and avatar will be shown. Otherwise, the + text that the user entered will be shown + type: boolean template: title: Template type: string