feat(flow): auto-resolve authentik sub and improve admin dropdown UX

This commit is contained in:
Chris
2026-03-30 03:33:50 +08:00
parent 2f97f45795
commit cc9ad16311
3 changed files with 23 additions and 6 deletions

View File

@@ -83,7 +83,11 @@ def _sync_member_to_authentik(
display_name=display_name, display_name=display_name,
is_active=is_active, is_active=is_active,
) )
return {"authentik_user_id": result.user_id, "sync_action": result.action} return {
"authentik_user_id": result.user_id,
"sync_action": result.action,
"authentik_sub": result.authentik_sub or "",
}
@router.get("/systems") @router.get("/systems")
@@ -332,17 +336,25 @@ def upsert_member(
db: Session = Depends(get_db), db: Session = Depends(get_db),
) -> MemberItem: ) -> MemberItem:
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
resolved_sub = payload.authentik_sub
authentik_user_id = None authentik_user_id = None
if payload.sync_to_authentik: if payload.sync_to_authentik:
seed_sub = payload.authentik_sub or (payload.email or "")
if not seed_sub:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="authentik_sub_or_email_required")
sync = _sync_member_to_authentik( sync = _sync_member_to_authentik(
authentik_sub=payload.authentik_sub, authentik_sub=seed_sub,
email=payload.email, email=payload.email,
display_name=payload.display_name, display_name=payload.display_name,
is_active=payload.is_active, is_active=payload.is_active,
) )
authentik_user_id = int(sync["authentik_user_id"]) authentik_user_id = int(sync["authentik_user_id"])
if sync.get("authentik_sub"):
resolved_sub = str(sync["authentik_sub"])
if not resolved_sub:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="authentik_sub_required")
row = users_repo.upsert_by_sub( row = users_repo.upsert_by_sub(
authentik_sub=payload.authentik_sub, authentik_sub=resolved_sub,
email=payload.email, email=payload.email,
display_name=payload.display_name, display_name=payload.display_name,
is_active=payload.is_active, is_active=payload.is_active,

View File

@@ -87,7 +87,7 @@ class MemberItem(BaseModel):
class MemberUpsertRequest(BaseModel): class MemberUpsertRequest(BaseModel):
authentik_sub: str authentik_sub: str | None = None
email: str | None = None email: str | None = None
display_name: str | None = None display_name: str | None = None
is_active: bool = True is_active: bool = True

View File

@@ -12,6 +12,7 @@ from app.core.config import Settings
class AuthentikSyncResult: class AuthentikSyncResult:
user_id: int user_id: int
action: str action: str
authentik_sub: str | None = None
class AuthentikAdminService: class AuthentikAdminService:
@@ -66,10 +67,14 @@ class AuthentikAdminService:
patch_resp = client.patch(f"/api/v3/core/users/{user_pk}/", json=payload) patch_resp = client.patch(f"/api/v3/core/users/{user_pk}/", json=payload)
if patch_resp.status_code >= 400: if patch_resp.status_code >= 400:
raise HTTPException(status_code=502, detail="authentik_update_failed") raise HTTPException(status_code=502, detail="authentik_update_failed")
return AuthentikSyncResult(user_id=user_pk, action="updated") return AuthentikSyncResult(user_id=user_pk, action="updated", authentik_sub=existing.get("uid"))
create_resp = client.post("/api/v3/core/users/", json=payload) create_resp = client.post("/api/v3/core/users/", json=payload)
if create_resp.status_code >= 400: if create_resp.status_code >= 400:
raise HTTPException(status_code=502, detail="authentik_create_failed") raise HTTPException(status_code=502, detail="authentik_create_failed")
created = create_resp.json() created = create_resp.json()
return AuthentikSyncResult(user_id=int(created["pk"]), action="created") return AuthentikSyncResult(
user_id=int(created["pk"]),
action="created",
authentik_sub=created.get("uid"),
)