stages/identification: add show_matched_user to optionally hide user details

This commit is contained in:
Jens Langhammer 2020-12-06 13:12:32 +01:00
parent 58497bb63f
commit ff15514d5b
8 changed files with 68 additions and 4 deletions

View File

@ -1,4 +1,5 @@
"""authentik stage Base view""" """authentik stage Base view"""
from collections import namedtuple
from typing import Any, Dict from typing import Any, Dict
from django.http import HttpRequest 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.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.views import FlowExecutorView from authentik.flows.views import FlowExecutorView
PLAN_CONTEXT_PENDING_USER_IDENTIFIER = "pending_user_identifier"
FakeUser = namedtuple("User", ["username", "email"])
class StageView(TemplateView): class StageView(TemplateView):
"""Abstract Stage, inherits TemplateView but can be combined with FormView""" """Abstract Stage, inherits TemplateView but can be combined with FormView"""
@ -21,9 +26,20 @@ class StageView(TemplateView):
def __init__(self, executor: FlowExecutorView): def __init__(self, executor: FlowExecutorView):
self.executor = executor 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 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: if PLAN_CONTEXT_PENDING_USER in self.executor.plan.context:
kwargs["user"] = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] 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") kwargs["primary_action"] = _("Continue")
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

View File

@ -18,6 +18,7 @@ register = template.Library()
LOGGER = get_logger() LOGGER = get_logger()
GRAVATAR_URL = "https://secure.gravatar.com" GRAVATAR_URL = "https://secure.gravatar.com"
DEFAULT_AVATAR = static("authentik/user_default.png")
@register.simple_tag(takes_context=True) @register.simple_tag(takes_context=True)
@ -62,7 +63,7 @@ def avatar(user: User) -> str:
"""Get avatar, depending on authentik.avatar setting""" """Get avatar, depending on authentik.avatar setting"""
mode = CONFIG.raw.get("authentik").get("avatars") mode = CONFIG.raw.get("authentik").get("avatars")
if mode == "none": if mode == "none":
return static("authentik/user_default.png") return DEFAULT_AVATAR
if mode == "gravatar": if mode == "gravatar":
parameters = [ parameters = [
("s", "158"), ("s", "158"),

View File

@ -16,6 +16,7 @@ class IdentificationStageSerializer(ModelSerializer):
"name", "name",
"user_fields", "user_fields",
"case_insensitive_matching", "case_insensitive_matching",
"show_matched_user",
"template", "template",
"enrollment_flow", "enrollment_flow",
"recovery_flow", "recovery_flow",

View File

@ -31,6 +31,7 @@ class IdentificationStageForm(forms.ModelForm):
"name", "name",
"user_fields", "user_fields",
"case_insensitive_matching", "case_insensitive_matching",
"show_matched_user",
"template", "template",
"enrollment_flow", "enrollment_flow",
"recovery_flow", "recovery_flow",

View File

@ -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",
),
),
]

View File

@ -45,6 +45,16 @@ class IdentificationStage(Stage):
"When enabled, user fields are matched regardless of their casing." "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( enrollment_flow = models.ForeignKey(
Flow, Flow,

View File

@ -11,7 +11,7 @@ from structlog import get_logger
from authentik.core.models import Source, User from authentik.core.models import Source, User
from authentik.flows.planner import PLAN_CONTEXT_PENDING_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.flows.views import SESSION_KEY_APPLICATION_PRE
from authentik.stages.identification.forms import IdentificationForm from authentik.stages.identification.forms import IdentificationForm
from authentik.stages.identification.models import IdentificationStage from authentik.stages.identification.models import IdentificationStage
@ -84,10 +84,18 @@ class IdentificationStageView(FormView, StageView):
def form_valid(self, form: IdentificationForm) -> HttpResponse: def form_valid(self, form: IdentificationForm) -> HttpResponse:
"""Form data is valid""" """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: if not pre_user:
LOGGER.debug("invalid_login") LOGGER.debug("invalid_login")
messages.error(self.request, _("Failed to authenticate.")) messages.error(self.request, _("Failed to authenticate."))
return self.form_invalid(form) return self.form_invalid(form)
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = pre_user 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() return self.executor.stage_ok()

View File

@ -8278,6 +8278,12 @@ definitions:
title: Case insensitive matching title: Case insensitive matching
description: When enabled, user fields are matched regardless of their casing. description: When enabled, user fields are matched regardless of their casing.
type: boolean 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: template:
title: Template title: Template
type: string type: string