mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
core: Share account selection user serialization
Centralize the browser-local account display shape so the API and account selection stage do not duplicate it. Agent-thread: https://sdko.org/internal/threads/019e9417-19b5-7e60-9be4-3f2c423366dd A7k-product: product A7k-product-repo: 3 Co-authored-by: Agent <agent@svc.sdko.net>
This commit is contained in:
@@ -26,6 +26,7 @@ from authentik.flows.planner import (
|
||||
FlowPlanner,
|
||||
)
|
||||
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER
|
||||
from authentik.lib.avatars import get_avatar
|
||||
from authentik.lib.utils.urls import is_url_absolute
|
||||
from authentik.policies.engine import PolicyEngine
|
||||
from authentik.root.middleware import SessionMiddleware
|
||||
@@ -60,6 +61,35 @@ def _coerce_known_account(raw_account: object) -> KnownAccount | None:
|
||||
return KnownAccount(uid=uid, session_key=session_key)
|
||||
|
||||
|
||||
def user_matches_hint(user: User, hint: str) -> bool:
|
||||
"""Check whether an account matches the supplied login hint."""
|
||||
return hint in {user.uuid.hex, user.email, user.username}
|
||||
|
||||
|
||||
def serialize_account_selection_user(
|
||||
request: HttpRequest,
|
||||
user: User,
|
||||
hint: str | None = None,
|
||||
) -> dict[str, object]:
|
||||
"""Serialize a browser-local account for account selection surfaces."""
|
||||
is_current = (
|
||||
request.user.is_authenticated
|
||||
and not isinstance(request.user, AnonymousUser)
|
||||
and user.pk == request.user.pk
|
||||
)
|
||||
data = {
|
||||
"uid": user.uuid.hex,
|
||||
"username": user.username,
|
||||
"name": user.name,
|
||||
"email": user.email,
|
||||
"avatar": get_avatar(user, request),
|
||||
"is_current": is_current,
|
||||
}
|
||||
if hint is not None:
|
||||
data["is_hint"] = bool(hint and user_matches_hint(user, hint))
|
||||
return data
|
||||
|
||||
|
||||
def get_known_accounts(request: HttpRequest) -> list[KnownAccount]:
|
||||
"""Return remembered accounts from the signed browser cookie."""
|
||||
raw_accounts = request.COOKIES.get(COOKIE_NAME_KNOWN_ACCOUNTS)
|
||||
|
||||
@@ -65,7 +65,10 @@ from authentik.api.search.fields import (
|
||||
from authentik.api.validation import validate
|
||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
||||
from authentik.brands.models import Brand
|
||||
from authentik.core.account_selection import get_known_account_users
|
||||
from authentik.core.account_selection import (
|
||||
get_known_account_users,
|
||||
serialize_account_selection_user,
|
||||
)
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import (
|
||||
JSONDictField,
|
||||
@@ -811,14 +814,7 @@ class UserViewSet(
|
||||
|
||||
def _serialize_account_selection_user(self, user: User) -> dict[str, Any]:
|
||||
"""Serialize an account remembered by this browser."""
|
||||
return {
|
||||
"uid": user.uuid.hex,
|
||||
"username": user.username,
|
||||
"name": user.name,
|
||||
"email": user.email,
|
||||
"avatar": get_avatar(user, self.request),
|
||||
"is_current": user.pk == self.request.user.pk,
|
||||
}
|
||||
return serialize_account_selection_user(self.request, user)
|
||||
|
||||
def _get_account_selection_users(self) -> list[User]:
|
||||
"""Return active known accounts, including the current session user first."""
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
@@ -19,9 +18,11 @@ from authentik.core.account_selection import (
|
||||
get_known_account_session,
|
||||
get_known_account_users,
|
||||
get_live_account_session,
|
||||
serialize_account_selection_user,
|
||||
set_account_selection_context,
|
||||
set_session_cookie,
|
||||
start_fresh_session,
|
||||
user_matches_hint,
|
||||
)
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.core.models import Application, AuthenticatedSession, User
|
||||
@@ -29,7 +30,6 @@ from authentik.flows.challenge import Challenge, ChallengeResponse
|
||||
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, PLAN_CONTEXT_REDIRECT
|
||||
from authentik.flows.stage import ChallengeStageView, StageView
|
||||
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET
|
||||
from authentik.lib.avatars import get_avatar
|
||||
|
||||
LOGGER = get_logger()
|
||||
COMPONENT = "ak-stage-account-selection"
|
||||
@@ -47,11 +47,6 @@ class AccountSelectionChallengeUser(PassiveSerializer):
|
||||
is_hint = BooleanField()
|
||||
|
||||
|
||||
def user_matches_hint(user: User, hint: str) -> bool:
|
||||
"""Check whether an account matches the supplied login hint."""
|
||||
return hint in {user.uuid.hex, user.email, user.username}
|
||||
|
||||
|
||||
class AccountSelectionChallenge(Challenge):
|
||||
"""Challenge for selecting a browser-local account."""
|
||||
|
||||
@@ -101,20 +96,7 @@ class AccountSelectionStageView(ChallengeStageView):
|
||||
|
||||
def serialize_account(self, user: User, hint: str = "") -> dict[str, object]:
|
||||
"""Serialize a selectable account."""
|
||||
is_current = (
|
||||
self.request.user.is_authenticated
|
||||
and not isinstance(self.request.user, AnonymousUser)
|
||||
and user.pk == self.request.user.pk
|
||||
)
|
||||
return {
|
||||
"uid": user.uuid.hex,
|
||||
"username": user.username,
|
||||
"name": user.name,
|
||||
"email": user.email,
|
||||
"avatar": get_avatar(user, self.request),
|
||||
"is_current": is_current,
|
||||
"is_hint": bool(hint and user_matches_hint(user, hint)),
|
||||
}
|
||||
return serialize_account_selection_user(self.request, user, hint)
|
||||
|
||||
def get_challenge(self) -> AccountSelectionChallenge:
|
||||
"""Show the current account and live remembered accounts for this browser."""
|
||||
|
||||
Reference in New Issue
Block a user