feat(authentik): reject colliding usernames case-insensitively at registration
Authentik's own uniqueness is case-sensitive, so 'Boje' and 'boje' pass as distinct while Matrix treats them as the same localpart. ADR-0011 closed the takeover vector with on_conflict:fail, but that only bites at login: the user registers happily and fails later with no explanation. This policy answers where the mistake is made. Deliberately reads only prompt_data and never request.user — the stage runs in an anonymous enrollment context, which is exactly what the previously attached system policies crashed on. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b4650dcef8
commit
afc4ad3f20
@@ -28,12 +28,45 @@ data:
|
||||
# from manual UI setup, likely a "select all" slip in the policy picker.
|
||||
# These crash on an anonymous enrollment context ('AnonymousUser' object
|
||||
# has no attribute 'group_attributes', etc). A prompt stage needs none here.
|
||||
# Eindeutigkeit case-insensitiv pruefen (#0043). Authentiks eigene Pruefung
|
||||
# ist case-sensitiv, "Boje" und "boje" gelten ihr also als verschieden --
|
||||
# in Matrix kollidieren sie. Seit ADR-0011 faengt MAS das mit
|
||||
# on_conflict:fail ab, aber erst BEIM LOGIN: der Nutzer registriert sich
|
||||
# erfolgreich und scheitert spaeter ohne Erklaerung. Diese Policy gibt die
|
||||
# Rueckmeldung dort, wo der Fehler entsteht.
|
||||
#
|
||||
# ⚠️ Bewusst ohne Zugriff auf request.user: die Stage laeuft im ANONYMEN
|
||||
# Enrollment-Kontext. Genau daran sind die frueher hier haengenden
|
||||
# System-Policies gescheitert ('AnonymousUser' hat kein group_attributes).
|
||||
# Gelesen wird ausschliesslich prompt_data.
|
||||
- model: authentik_policies_expression.expressionpolicy
|
||||
state: present
|
||||
identifiers:
|
||||
name: matrix-username-eindeutig-ci
|
||||
id: username_unique_ci
|
||||
attrs:
|
||||
execution_logging: false
|
||||
expression: |
|
||||
from authentik.core.models import User
|
||||
|
||||
gewuenscht = (request.context.get("prompt_data") or {}).get("username") or ""
|
||||
gewuenscht = gewuenscht.strip()
|
||||
if not gewuenscht:
|
||||
return True # Pflichtfeld-Pruefung macht der Prompt selbst
|
||||
|
||||
if User.objects.filter(username__iexact=gewuenscht).exists():
|
||||
ak_message("Dieser Benutzername ist bereits vergeben - auch in anderer "
|
||||
"Gross-/Kleinschreibung. Bitte waehle einen anderen.")
|
||||
return False
|
||||
return True
|
||||
|
||||
- model: authentik_stages_prompt.promptstage
|
||||
state: present
|
||||
identifiers:
|
||||
name: matrix-invitation-prompt
|
||||
attrs:
|
||||
validation_policies: []
|
||||
validation_policies:
|
||||
- !KeyOf username_unique_ci
|
||||
|
||||
# Correct stage chain, mirroring the working matrix-enrollment flow:
|
||||
# Invite -> Prompt (username/email/password) -> Write -> Password -> Login
|
||||
|
||||
Reference in New Issue
Block a user