refactor(identity): rename authentik_sub to user_sub and authentik_user_id to idp_user_id
This commit is contained in:
@@ -71,7 +71,7 @@ def grant_permission(
|
||||
perms_repo = PermissionsRepository(db)
|
||||
|
||||
user = users_repo.upsert_by_sub(
|
||||
authentik_sub=payload.authentik_sub,
|
||||
user_sub=payload.user_sub,
|
||||
username=None,
|
||||
email=payload.email,
|
||||
display_name=payload.display_name,
|
||||
@@ -99,7 +99,7 @@ def revoke_permission(
|
||||
users_repo = UsersRepository(db)
|
||||
perms_repo = PermissionsRepository(db)
|
||||
|
||||
user = users_repo.get_by_sub(payload.authentik_sub)
|
||||
user = users_repo.get_by_sub(payload.user_sub)
|
||||
if user is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
|
||||
|
||||
|
||||
@@ -135,8 +135,8 @@ def _generate_api_key() -> str:
|
||||
|
||||
def _sync_member_to_authentik(
|
||||
*,
|
||||
authentik_sub: str | None,
|
||||
authentik_user_id: int | None,
|
||||
user_sub: str | None,
|
||||
idp_user_id: int | None,
|
||||
username: str | None,
|
||||
email: str | None,
|
||||
display_name: str | None,
|
||||
@@ -147,17 +147,17 @@ def _sync_member_to_authentik(
|
||||
settings = get_settings()
|
||||
service = AuthentikAdminService(settings=settings)
|
||||
result = service.ensure_user(
|
||||
sub=authentik_sub,
|
||||
sub=user_sub,
|
||||
email=email,
|
||||
username=username,
|
||||
display_name=display_name,
|
||||
is_active=is_active,
|
||||
authentik_user_id=authentik_user_id,
|
||||
idp_user_id=idp_user_id,
|
||||
)
|
||||
return {
|
||||
"authentik_user_id": result.user_id,
|
||||
"idp_user_id": result.user_id,
|
||||
"sync_action": result.action,
|
||||
"authentik_sub": result.authentik_sub or "",
|
||||
"user_sub": result.user_sub or "",
|
||||
}
|
||||
|
||||
|
||||
@@ -316,7 +316,7 @@ def list_system_members(
|
||||
return {
|
||||
"items": [
|
||||
MemberRelationItem(
|
||||
authentik_sub=m.authentik_sub,
|
||||
user_sub=m.user_sub,
|
||||
email=m.email,
|
||||
display_name=m.display_name,
|
||||
is_active=m.is_active,
|
||||
@@ -359,7 +359,7 @@ def list_module_members(
|
||||
return {
|
||||
"items": [
|
||||
MemberRelationItem(
|
||||
authentik_sub=m.authentik_sub,
|
||||
user_sub=m.user_sub,
|
||||
email=m.email,
|
||||
display_name=m.display_name,
|
||||
is_active=m.is_active,
|
||||
@@ -567,7 +567,7 @@ def list_members(
|
||||
"items": [
|
||||
MemberItem(
|
||||
id=i.id,
|
||||
authentik_sub=i.authentik_sub,
|
||||
user_sub=i.user_sub,
|
||||
username=i.username,
|
||||
email=i.email,
|
||||
display_name=i.display_name,
|
||||
@@ -587,37 +587,37 @@ def upsert_member(
|
||||
db: Session = Depends(get_db),
|
||||
) -> MemberItem:
|
||||
users_repo = UsersRepository(db)
|
||||
resolved_sub = payload.authentik_sub
|
||||
resolved_sub = payload.user_sub
|
||||
resolved_username = payload.username
|
||||
authentik_user_id = None
|
||||
idp_user_id = None
|
||||
if payload.sync_to_authentik:
|
||||
seed_sub = payload.authentik_sub or payload.username
|
||||
seed_sub = payload.user_sub or payload.username
|
||||
if not seed_sub:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="authentik_sub_or_username_required")
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="user_sub_or_username_required")
|
||||
sync = _sync_member_to_authentik(
|
||||
authentik_sub=seed_sub,
|
||||
authentik_user_id=authentik_user_id,
|
||||
user_sub=seed_sub,
|
||||
idp_user_id=idp_user_id,
|
||||
username=payload.username,
|
||||
email=payload.email,
|
||||
display_name=payload.display_name,
|
||||
is_active=payload.is_active,
|
||||
)
|
||||
authentik_user_id = int(sync["authentik_user_id"])
|
||||
if sync.get("authentik_sub"):
|
||||
resolved_sub = str(sync["authentik_sub"])
|
||||
idp_user_id = int(sync["idp_user_id"])
|
||||
if sync.get("user_sub"):
|
||||
resolved_sub = str(sync["user_sub"])
|
||||
if not resolved_sub:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="authentik_sub_required")
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="user_sub_required")
|
||||
row = users_repo.upsert_by_sub(
|
||||
authentik_sub=resolved_sub,
|
||||
user_sub=resolved_sub,
|
||||
username=resolved_username,
|
||||
email=payload.email,
|
||||
display_name=payload.display_name,
|
||||
is_active=payload.is_active,
|
||||
authentik_user_id=authentik_user_id,
|
||||
idp_user_id=idp_user_id,
|
||||
)
|
||||
return MemberItem(
|
||||
id=row.id,
|
||||
authentik_sub=row.authentik_sub,
|
||||
user_sub=row.user_sub,
|
||||
username=row.username,
|
||||
email=row.email,
|
||||
display_name=row.display_name,
|
||||
@@ -625,14 +625,14 @@ def upsert_member(
|
||||
)
|
||||
|
||||
|
||||
@router.patch("/members/{authentik_sub}", response_model=MemberItem)
|
||||
@router.patch("/members/{user_sub}", response_model=MemberItem)
|
||||
def update_member(
|
||||
authentik_sub: str,
|
||||
user_sub: str,
|
||||
payload: MemberUpdateRequest,
|
||||
db: Session = Depends(get_db),
|
||||
) -> MemberItem:
|
||||
users_repo = UsersRepository(db)
|
||||
row = users_repo.get_by_sub(authentik_sub)
|
||||
row = users_repo.get_by_sub(user_sub)
|
||||
if not row:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
|
||||
|
||||
@@ -641,29 +641,29 @@ def update_member(
|
||||
next_display_name = payload.display_name if payload.display_name is not None else row.display_name
|
||||
next_is_active = payload.is_active if payload.is_active is not None else row.is_active
|
||||
|
||||
authentik_user_id = row.authentik_user_id
|
||||
idp_user_id = row.idp_user_id
|
||||
if payload.sync_to_authentik:
|
||||
sync = _sync_member_to_authentik(
|
||||
authentik_sub=row.authentik_sub,
|
||||
authentik_user_id=row.authentik_user_id,
|
||||
user_sub=row.user_sub,
|
||||
idp_user_id=row.idp_user_id,
|
||||
username=next_username,
|
||||
email=next_email,
|
||||
display_name=next_display_name,
|
||||
is_active=next_is_active,
|
||||
)
|
||||
authentik_user_id = int(sync["authentik_user_id"])
|
||||
idp_user_id = int(sync["idp_user_id"])
|
||||
|
||||
row = users_repo.upsert_by_sub(
|
||||
authentik_sub=row.authentik_sub,
|
||||
user_sub=row.user_sub,
|
||||
username=next_username,
|
||||
email=next_email,
|
||||
display_name=next_display_name,
|
||||
is_active=next_is_active,
|
||||
authentik_user_id=authentik_user_id,
|
||||
idp_user_id=idp_user_id,
|
||||
)
|
||||
return MemberItem(
|
||||
id=row.id,
|
||||
authentik_sub=row.authentik_sub,
|
||||
user_sub=row.user_sub,
|
||||
username=row.username,
|
||||
email=row.email,
|
||||
display_name=row.display_name,
|
||||
@@ -671,78 +671,78 @@ def update_member(
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/members/{authentik_sub}")
|
||||
@router.delete("/members/{user_sub}")
|
||||
def delete_member(
|
||||
authentik_sub: str,
|
||||
user_sub: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, int | str]:
|
||||
users_repo = UsersRepository(db)
|
||||
row = users_repo.get_by_sub(authentik_sub)
|
||||
row = users_repo.get_by_sub(user_sub)
|
||||
if not row:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
|
||||
settings = get_settings()
|
||||
service = AuthentikAdminService(settings=settings)
|
||||
service.delete_user(
|
||||
authentik_user_id=row.authentik_user_id,
|
||||
idp_user_id=row.idp_user_id,
|
||||
email=row.email,
|
||||
username=row.username,
|
||||
)
|
||||
db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.authentik_sub == authentik_sub))
|
||||
db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.user_sub == user_sub))
|
||||
db.delete(row)
|
||||
db.commit()
|
||||
return {"deleted": 1, "result": "deleted"}
|
||||
|
||||
|
||||
@router.post("/members/{authentik_sub}/password/reset", response_model=MemberPasswordResetResponse)
|
||||
@router.post("/members/{user_sub}/password/reset", response_model=MemberPasswordResetResponse)
|
||||
def reset_member_password(
|
||||
authentik_sub: str,
|
||||
user_sub: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> MemberPasswordResetResponse:
|
||||
users_repo = UsersRepository(db)
|
||||
user = users_repo.get_by_sub(authentik_sub)
|
||||
user = users_repo.get_by_sub(user_sub)
|
||||
if not user:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
|
||||
settings = get_settings()
|
||||
service = AuthentikAdminService(settings=settings)
|
||||
result = service.reset_password(
|
||||
authentik_user_id=user.authentik_user_id,
|
||||
idp_user_id=user.idp_user_id,
|
||||
email=user.email,
|
||||
username=user.username,
|
||||
)
|
||||
user = users_repo.upsert_by_sub(
|
||||
authentik_sub=user.authentik_sub,
|
||||
user_sub=user.user_sub,
|
||||
username=user.username,
|
||||
email=user.email,
|
||||
display_name=user.display_name,
|
||||
is_active=user.is_active,
|
||||
authentik_user_id=result.user_id,
|
||||
idp_user_id=result.user_id,
|
||||
)
|
||||
return MemberPasswordResetResponse(authentik_sub=user.authentik_sub, temporary_password=result.temporary_password)
|
||||
return MemberPasswordResetResponse(user_sub=user.user_sub, temporary_password=result.temporary_password)
|
||||
|
||||
|
||||
@router.get("/members/{authentik_sub}/permission-groups", response_model=MemberPermissionGroupsResponse)
|
||||
@router.get("/members/{user_sub}/permission-groups", response_model=MemberPermissionGroupsResponse)
|
||||
def get_member_permission_groups(
|
||||
authentik_sub: str,
|
||||
user_sub: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> MemberPermissionGroupsResponse:
|
||||
users_repo = UsersRepository(db)
|
||||
groups_repo = PermissionGroupsRepository(db)
|
||||
user = users_repo.get_by_sub(authentik_sub)
|
||||
user = users_repo.get_by_sub(user_sub)
|
||||
if not user:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
|
||||
group_keys = groups_repo.list_group_keys_by_member_sub(authentik_sub)
|
||||
return MemberPermissionGroupsResponse(authentik_sub=authentik_sub, group_keys=group_keys)
|
||||
group_keys = groups_repo.list_group_keys_by_member_sub(user_sub)
|
||||
return MemberPermissionGroupsResponse(user_sub=user_sub, group_keys=group_keys)
|
||||
|
||||
|
||||
@router.put("/members/{authentik_sub}/permission-groups", response_model=MemberPermissionGroupsResponse)
|
||||
@router.put("/members/{user_sub}/permission-groups", response_model=MemberPermissionGroupsResponse)
|
||||
def set_member_permission_groups(
|
||||
authentik_sub: str,
|
||||
user_sub: str,
|
||||
payload: MemberPermissionGroupsUpdateRequest,
|
||||
db: Session = Depends(get_db),
|
||||
) -> MemberPermissionGroupsResponse:
|
||||
users_repo = UsersRepository(db)
|
||||
groups_repo = PermissionGroupsRepository(db)
|
||||
user = users_repo.get_by_sub(authentik_sub)
|
||||
user = users_repo.get_by_sub(user_sub)
|
||||
if not user:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
|
||||
|
||||
@@ -753,8 +753,8 @@ def set_member_permission_groups(
|
||||
if missing:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"group_not_found:{','.join(missing)}")
|
||||
|
||||
groups_repo.replace_member_groups(authentik_sub, [g.id for g in groups])
|
||||
return MemberPermissionGroupsResponse(authentik_sub=authentik_sub, group_keys=unique_group_keys)
|
||||
groups_repo.replace_member_groups(user_sub, [g.id for g in groups])
|
||||
return MemberPermissionGroupsResponse(user_sub=user_sub, group_keys=unique_group_keys)
|
||||
|
||||
|
||||
@router.get("/api-clients")
|
||||
@@ -1023,31 +1023,31 @@ def delete_permission_group(
|
||||
return {"deleted": 1, "result": "deleted"}
|
||||
|
||||
|
||||
@router.post("/permission-groups/{group_key}/members/{authentik_sub}")
|
||||
@router.post("/permission-groups/{group_key}/members/{user_sub}")
|
||||
def add_group_member(
|
||||
group_key: str,
|
||||
authentik_sub: str,
|
||||
user_sub: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, str]:
|
||||
groups_repo = PermissionGroupsRepository(db)
|
||||
group = groups_repo.get_by_key(group_key)
|
||||
if not group:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="group_not_found")
|
||||
row = groups_repo.add_member_if_not_exists(group.id, authentik_sub)
|
||||
row = groups_repo.add_member_if_not_exists(group.id, user_sub)
|
||||
return {"membership_id": row.id, "result": "added"}
|
||||
|
||||
|
||||
@router.delete("/permission-groups/{group_key}/members/{authentik_sub}")
|
||||
@router.delete("/permission-groups/{group_key}/members/{user_sub}")
|
||||
def remove_group_member(
|
||||
group_key: str,
|
||||
authentik_sub: str,
|
||||
user_sub: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, int | str]:
|
||||
groups_repo = PermissionGroupsRepository(db)
|
||||
group = groups_repo.get_by_key(group_key)
|
||||
if not group:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="group_not_found")
|
||||
deleted = groups_repo.remove_member(group.id, authentik_sub)
|
||||
deleted = groups_repo.remove_member(group.id, user_sub)
|
||||
return {"deleted": deleted, "result": "removed"}
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ def upsert_user_by_sub(
|
||||
) -> InternalUpsertUserBySubResponse:
|
||||
repo = UsersRepository(db)
|
||||
user = repo.upsert_by_sub(
|
||||
authentik_sub=payload.sub,
|
||||
user_sub=payload.user_sub,
|
||||
username=payload.username,
|
||||
email=payload.email,
|
||||
display_name=payload.display_name,
|
||||
@@ -31,8 +31,8 @@ def upsert_user_by_sub(
|
||||
)
|
||||
return {
|
||||
"id": user.id,
|
||||
"sub": user.authentik_sub,
|
||||
"authentik_user_id": user.authentik_user_id,
|
||||
"user_sub": user.user_sub,
|
||||
"idp_user_id": user.idp_user_id,
|
||||
"username": user.username,
|
||||
"email": user.email,
|
||||
"display_name": user.display_name,
|
||||
@@ -40,20 +40,20 @@ def upsert_user_by_sub(
|
||||
}
|
||||
|
||||
|
||||
@router.get("/permissions/{authentik_sub}/snapshot", response_model=PermissionSnapshotResponse)
|
||||
@router.get("/permissions/{user_sub}/snapshot", response_model=PermissionSnapshotResponse)
|
||||
def get_permission_snapshot(
|
||||
authentik_sub: str,
|
||||
user_sub: str,
|
||||
db: Session = Depends(get_db),
|
||||
) -> PermissionSnapshotResponse:
|
||||
users_repo = UsersRepository(db)
|
||||
perms_repo = PermissionsRepository(db)
|
||||
|
||||
user = users_repo.get_by_sub(authentik_sub)
|
||||
user = users_repo.get_by_sub(user_sub)
|
||||
if user is None:
|
||||
return PermissionSnapshotResponse(authentik_sub=authentik_sub, permissions=[])
|
||||
return PermissionSnapshotResponse(user_sub=user_sub, permissions=[])
|
||||
|
||||
permissions = perms_repo.list_by_user(user.id, user.authentik_sub)
|
||||
return PermissionService.build_snapshot(authentik_sub=authentik_sub, permissions=permissions)
|
||||
permissions = perms_repo.list_by_user(user.id, user.user_sub)
|
||||
return PermissionService.build_snapshot(user_sub=user_sub, permissions=permissions)
|
||||
|
||||
|
||||
@router.post("/authentik/users/ensure", response_model=AuthentikEnsureUserResponse)
|
||||
@@ -64,7 +64,7 @@ def ensure_authentik_user(
|
||||
settings = get_settings()
|
||||
authentik_service = AuthentikAdminService(settings=settings)
|
||||
sync_result = authentik_service.ensure_user(
|
||||
sub=payload.sub,
|
||||
sub=payload.user_sub,
|
||||
email=payload.email,
|
||||
username=payload.username,
|
||||
display_name=payload.display_name,
|
||||
@@ -72,17 +72,17 @@ def ensure_authentik_user(
|
||||
)
|
||||
|
||||
users_repo = UsersRepository(db)
|
||||
resolved_sub = payload.sub or ""
|
||||
if sync_result.authentik_sub:
|
||||
resolved_sub = sync_result.authentik_sub
|
||||
resolved_sub = payload.user_sub or ""
|
||||
if sync_result.user_sub:
|
||||
resolved_sub = sync_result.user_sub
|
||||
if not resolved_sub:
|
||||
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail="authentik_missing_sub")
|
||||
users_repo.upsert_by_sub(
|
||||
authentik_sub=resolved_sub,
|
||||
user_sub=resolved_sub,
|
||||
username=payload.username,
|
||||
email=payload.email,
|
||||
display_name=payload.display_name,
|
||||
is_active=payload.is_active,
|
||||
authentik_user_id=sync_result.user_id,
|
||||
idp_user_id=sync_result.user_id,
|
||||
)
|
||||
return AuthentikEnsureUserResponse(authentik_user_id=sync_result.user_id, action=sync_result.action)
|
||||
return AuthentikEnsureUserResponse(idp_user_id=sync_result.user_id, action=sync_result.action)
|
||||
|
||||
@@ -100,7 +100,7 @@ def internal_list_members(
|
||||
"items": [
|
||||
{
|
||||
"id": i.id,
|
||||
"authentik_sub": i.authentik_sub,
|
||||
"user_sub": i.user_sub,
|
||||
"username": i.username,
|
||||
"email": i.email,
|
||||
"display_name": i.display_name,
|
||||
|
||||
@@ -21,13 +21,13 @@ def get_me(
|
||||
try:
|
||||
users_repo = UsersRepository(db)
|
||||
user = users_repo.upsert_by_sub(
|
||||
authentik_sub=principal.sub,
|
||||
user_sub=principal.sub,
|
||||
username=principal.preferred_username,
|
||||
email=principal.email,
|
||||
display_name=principal.name or principal.preferred_username,
|
||||
is_active=True,
|
||||
)
|
||||
return MeSummaryResponse(sub=user.authentik_sub, email=user.email, display_name=user.display_name)
|
||||
return MeSummaryResponse(sub=user.user_sub, email=user.email, display_name=user.display_name)
|
||||
except SQLAlchemyError:
|
||||
# DB schema compatibility fallback for local bring-up.
|
||||
return MeSummaryResponse(
|
||||
@@ -47,13 +47,13 @@ def get_my_permission_snapshot(
|
||||
perms_repo = PermissionsRepository(db)
|
||||
|
||||
user = users_repo.upsert_by_sub(
|
||||
authentik_sub=principal.sub,
|
||||
user_sub=principal.sub,
|
||||
username=principal.preferred_username,
|
||||
email=principal.email,
|
||||
display_name=principal.name or principal.preferred_username,
|
||||
is_active=True,
|
||||
)
|
||||
permissions = perms_repo.list_by_user(user.id, user.authentik_sub)
|
||||
return PermissionService.build_snapshot(authentik_sub=principal.sub, permissions=permissions)
|
||||
permissions = perms_repo.list_by_user(user.id, user.user_sub)
|
||||
return PermissionService.build_snapshot(user_sub=principal.sub, permissions=permissions)
|
||||
except SQLAlchemyError:
|
||||
return PermissionSnapshotResponse(authentik_sub=principal.sub, permissions=[])
|
||||
return PermissionSnapshotResponse(user_sub=principal.sub, permissions=[])
|
||||
|
||||
@@ -10,11 +10,11 @@ from app.db.base import Base
|
||||
|
||||
class PermissionGroupMember(Base):
|
||||
__tablename__ = "permission_group_members"
|
||||
__table_args__ = (UniqueConstraint("group_id", "authentik_sub", name="uq_permission_group_members_group_sub"),)
|
||||
__table_args__ = (UniqueConstraint("group_id", "user_sub", name="uq_permission_group_members_group_sub"),)
|
||||
|
||||
id: Mapped[str] = mapped_column(UUID(as_uuid=False), primary_key=True, default=lambda: str(uuid4()))
|
||||
group_id: Mapped[str] = mapped_column(
|
||||
UUID(as_uuid=False), ForeignKey("permission_groups.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
authentik_sub: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
user_sub: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
|
||||
@@ -12,8 +12,8 @@ class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: Mapped[str] = mapped_column(UUID(as_uuid=False), primary_key=True, default=lambda: str(uuid4()))
|
||||
authentik_sub: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True)
|
||||
authentik_user_id: Mapped[int | None] = mapped_column(Integer)
|
||||
user_sub: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True)
|
||||
idp_user_id: Mapped[int | None] = mapped_column(Integer)
|
||||
username: Mapped[str | None] = mapped_column(String(255), unique=True)
|
||||
email: Mapped[str | None] = mapped_column(String(320))
|
||||
display_name: Mapped[str | None] = mapped_column(String(255))
|
||||
|
||||
@@ -46,43 +46,43 @@ class PermissionGroupsRepository:
|
||||
self.db.refresh(item)
|
||||
return item
|
||||
|
||||
def add_member_if_not_exists(self, group_id: str, authentik_sub: str) -> PermissionGroupMember:
|
||||
def add_member_if_not_exists(self, group_id: str, user_sub: str) -> PermissionGroupMember:
|
||||
existing = self.db.scalar(
|
||||
select(PermissionGroupMember).where(
|
||||
PermissionGroupMember.group_id == group_id, PermissionGroupMember.authentik_sub == authentik_sub
|
||||
PermissionGroupMember.group_id == group_id, PermissionGroupMember.user_sub == user_sub
|
||||
)
|
||||
)
|
||||
if existing:
|
||||
return existing
|
||||
row = PermissionGroupMember(group_id=group_id, authentik_sub=authentik_sub)
|
||||
row = PermissionGroupMember(group_id=group_id, user_sub=user_sub)
|
||||
self.db.add(row)
|
||||
self.db.commit()
|
||||
self.db.refresh(row)
|
||||
return row
|
||||
|
||||
def remove_member(self, group_id: str, authentik_sub: str) -> int:
|
||||
def remove_member(self, group_id: str, user_sub: str) -> int:
|
||||
result = self.db.execute(
|
||||
delete(PermissionGroupMember).where(
|
||||
PermissionGroupMember.group_id == group_id, PermissionGroupMember.authentik_sub == authentik_sub
|
||||
PermissionGroupMember.group_id == group_id, PermissionGroupMember.user_sub == user_sub
|
||||
)
|
||||
)
|
||||
self.db.commit()
|
||||
return int(result.rowcount or 0)
|
||||
|
||||
def list_group_keys_by_member_sub(self, authentik_sub: str) -> list[str]:
|
||||
def list_group_keys_by_member_sub(self, user_sub: str) -> list[str]:
|
||||
stmt = (
|
||||
select(PermissionGroup.group_key)
|
||||
.select_from(PermissionGroupMember)
|
||||
.join(PermissionGroup, PermissionGroup.id == PermissionGroupMember.group_id)
|
||||
.where(PermissionGroupMember.authentik_sub == authentik_sub)
|
||||
.where(PermissionGroupMember.user_sub == user_sub)
|
||||
.order_by(PermissionGroup.group_key.asc())
|
||||
)
|
||||
return [row[0] for row in self.db.execute(stmt).all()]
|
||||
|
||||
def replace_member_groups(self, authentik_sub: str, group_ids: list[str]) -> None:
|
||||
self.db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.authentik_sub == authentik_sub))
|
||||
def replace_member_groups(self, user_sub: str, group_ids: list[str]) -> None:
|
||||
self.db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.user_sub == user_sub))
|
||||
for group_id in group_ids:
|
||||
self.db.add(PermissionGroupMember(group_id=group_id, authentik_sub=authentik_sub))
|
||||
self.db.add(PermissionGroupMember(group_id=group_id, user_sub=user_sub))
|
||||
self.db.commit()
|
||||
|
||||
def grant_group_permission(
|
||||
@@ -155,7 +155,7 @@ class PermissionGroupsRepository:
|
||||
self.db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.group_id == group_id))
|
||||
|
||||
for sub in normalized_member_subs:
|
||||
self.db.add(PermissionGroupMember(group_id=group_id, authentik_sub=sub))
|
||||
self.db.add(PermissionGroupMember(group_id=group_id, user_sub=sub))
|
||||
|
||||
for site_key in normalized_sites:
|
||||
for action in normalized_actions:
|
||||
@@ -199,9 +199,9 @@ class PermissionGroupsRepository:
|
||||
|
||||
def list_group_member_subs(self, group_id: str) -> list[str]:
|
||||
stmt = (
|
||||
select(PermissionGroupMember.authentik_sub)
|
||||
select(PermissionGroupMember.user_sub)
|
||||
.where(PermissionGroupMember.group_id == group_id)
|
||||
.order_by(PermissionGroupMember.authentik_sub.asc())
|
||||
.order_by(PermissionGroupMember.user_sub.asc())
|
||||
)
|
||||
return [row[0] for row in self.db.execute(stmt).all()]
|
||||
|
||||
@@ -218,10 +218,10 @@ class PermissionGroupsRepository:
|
||||
def list_system_members(self, system_key: str) -> list[User]:
|
||||
stmt = (
|
||||
select(User)
|
||||
.join(PermissionGroupMember, PermissionGroupMember.authentik_sub == User.authentik_sub)
|
||||
.join(PermissionGroupMember, PermissionGroupMember.user_sub == User.user_sub)
|
||||
.join(PermissionGroupPermission, PermissionGroupPermission.group_id == PermissionGroupMember.group_id)
|
||||
.where(PermissionGroupPermission.system == system_key)
|
||||
.order_by(User.email.asc(), User.authentik_sub.asc())
|
||||
.order_by(User.email.asc(), User.user_sub.asc())
|
||||
.distinct()
|
||||
)
|
||||
return list(self.db.scalars(stmt).all())
|
||||
@@ -239,10 +239,10 @@ class PermissionGroupsRepository:
|
||||
def list_module_members(self, system_key: str, module_name: str) -> list[User]:
|
||||
stmt = (
|
||||
select(User)
|
||||
.join(PermissionGroupMember, PermissionGroupMember.authentik_sub == User.authentik_sub)
|
||||
.join(PermissionGroupMember, PermissionGroupMember.user_sub == User.user_sub)
|
||||
.join(PermissionGroupPermission, PermissionGroupPermission.group_id == PermissionGroupMember.group_id)
|
||||
.where(PermissionGroupPermission.system == system_key, PermissionGroupPermission.module == module_name)
|
||||
.order_by(User.email.asc(), User.authentik_sub.asc())
|
||||
.order_by(User.email.asc(), User.user_sub.asc())
|
||||
.distinct()
|
||||
)
|
||||
return list(self.db.scalars(stmt).all())
|
||||
|
||||
@@ -14,7 +14,7 @@ class PermissionsRepository:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
|
||||
def list_by_user(self, user_id: str, authentik_sub: str) -> list[tuple[str, str, str | None, str, str]]:
|
||||
def list_by_user(self, user_id: str, user_sub: str) -> list[tuple[str, str, str | None, str, str]]:
|
||||
direct_stmt = (
|
||||
select(
|
||||
literal("direct"),
|
||||
@@ -44,7 +44,7 @@ class PermissionsRepository:
|
||||
)
|
||||
.select_from(PermissionGroupPermission)
|
||||
.join(PermissionGroupMember, PermissionGroupMember.group_id == PermissionGroupPermission.group_id)
|
||||
.where(PermissionGroupMember.authentik_sub == authentik_sub)
|
||||
.where(PermissionGroupMember.user_sub == user_sub)
|
||||
.where(PermissionGroupPermission.action.in_(["view", "edit"]))
|
||||
.where(PermissionGroupPermission.scope_type == "site")
|
||||
)
|
||||
@@ -138,7 +138,7 @@ class PermissionsRepository:
|
||||
stmt = (
|
||||
select(
|
||||
UserScopePermission.id,
|
||||
User.authentik_sub,
|
||||
User.user_sub,
|
||||
User.email,
|
||||
User.display_name,
|
||||
UserScopePermission.scope_type,
|
||||
@@ -175,7 +175,7 @@ class PermissionsRepository:
|
||||
if keyword:
|
||||
pattern = f"%{keyword}%"
|
||||
cond = or_(
|
||||
User.authentik_sub.ilike(pattern),
|
||||
User.user_sub.ilike(pattern),
|
||||
User.email.ilike(pattern),
|
||||
User.display_name.ilike(pattern),
|
||||
Module.module_key.ilike(pattern),
|
||||
@@ -193,7 +193,7 @@ class PermissionsRepository:
|
||||
for row in rows:
|
||||
(
|
||||
permission_id,
|
||||
authentik_sub,
|
||||
user_sub,
|
||||
email,
|
||||
display_name,
|
||||
row_scope_type,
|
||||
@@ -211,7 +211,7 @@ class PermissionsRepository:
|
||||
items.append(
|
||||
{
|
||||
"permission_id": permission_id,
|
||||
"authentik_sub": authentik_sub,
|
||||
"user_sub": user_sub,
|
||||
"email": email,
|
||||
"display_name": display_name,
|
||||
"scope_type": row_scope_type,
|
||||
|
||||
@@ -8,8 +8,8 @@ class UsersRepository:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
|
||||
def get_by_sub(self, authentik_sub: str) -> User | None:
|
||||
stmt = select(User).where(User.authentik_sub == authentik_sub)
|
||||
def get_by_sub(self, user_sub: str) -> User | None:
|
||||
stmt = select(User).where(User.user_sub == user_sub)
|
||||
return self.db.scalar(stmt)
|
||||
|
||||
def get_by_id(self, user_id: str) -> User | None:
|
||||
@@ -29,7 +29,7 @@ class UsersRepository:
|
||||
if keyword:
|
||||
pattern = f"%{keyword}%"
|
||||
cond = or_(
|
||||
User.authentik_sub.ilike(pattern),
|
||||
User.user_sub.ilike(pattern),
|
||||
User.username.ilike(pattern),
|
||||
User.email.ilike(pattern),
|
||||
User.display_name.ilike(pattern),
|
||||
@@ -48,18 +48,18 @@ class UsersRepository:
|
||||
|
||||
def upsert_by_sub(
|
||||
self,
|
||||
authentik_sub: str,
|
||||
user_sub: str,
|
||||
username: str | None,
|
||||
email: str | None,
|
||||
display_name: str | None,
|
||||
is_active: bool,
|
||||
authentik_user_id: int | None = None,
|
||||
idp_user_id: int | None = None,
|
||||
) -> User:
|
||||
user = self.get_by_sub(authentik_sub)
|
||||
user = self.get_by_sub(user_sub)
|
||||
if user is None:
|
||||
user = User(
|
||||
authentik_sub=authentik_sub,
|
||||
authentik_user_id=authentik_user_id,
|
||||
user_sub=user_sub,
|
||||
idp_user_id=idp_user_id,
|
||||
username=username,
|
||||
email=email,
|
||||
display_name=display_name,
|
||||
@@ -67,8 +67,8 @@ class UsersRepository:
|
||||
)
|
||||
self.db.add(user)
|
||||
else:
|
||||
if authentik_user_id is not None:
|
||||
user.authentik_user_id = authentik_user_id
|
||||
if idp_user_id is not None:
|
||||
user.idp_user_id = idp_user_id
|
||||
user.username = username
|
||||
user.email = email
|
||||
user.display_name = display_name
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from pydantic import BaseModel
|
||||
from pydantic import AliasChoices, BaseModel, Field
|
||||
|
||||
|
||||
class AuthentikEnsureUserRequest(BaseModel):
|
||||
sub: str | None = None
|
||||
user_sub: str | None = Field(default=None, validation_alias=AliasChoices("user_sub", "sub"))
|
||||
username: str | None = None
|
||||
email: str
|
||||
display_name: str | None = None
|
||||
@@ -10,5 +10,5 @@ class AuthentikEnsureUserRequest(BaseModel):
|
||||
|
||||
|
||||
class AuthentikEnsureUserResponse(BaseModel):
|
||||
authentik_user_id: int
|
||||
idp_user_id: int
|
||||
action: str
|
||||
|
||||
@@ -77,7 +77,7 @@ class SiteItem(BaseModel):
|
||||
|
||||
class MemberItem(BaseModel):
|
||||
id: str
|
||||
authentik_sub: str
|
||||
user_sub: str
|
||||
username: str | None = None
|
||||
email: str | None = None
|
||||
display_name: str | None = None
|
||||
@@ -85,7 +85,7 @@ class MemberItem(BaseModel):
|
||||
|
||||
|
||||
class MemberUpsertRequest(BaseModel):
|
||||
authentik_sub: str | None = None
|
||||
user_sub: str | None = None
|
||||
username: str | None = None
|
||||
email: str | None = None
|
||||
display_name: str | None = None
|
||||
@@ -102,7 +102,7 @@ class MemberUpdateRequest(BaseModel):
|
||||
|
||||
|
||||
class MemberPasswordResetResponse(BaseModel):
|
||||
authentik_sub: str
|
||||
user_sub: str
|
||||
temporary_password: str
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ class PermissionGroupPermissionItem(BaseModel):
|
||||
|
||||
|
||||
class MemberPermissionGroupsResponse(BaseModel):
|
||||
authentik_sub: str
|
||||
user_sub: str
|
||||
group_keys: list[str]
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ class GroupRelationItem(BaseModel):
|
||||
|
||||
|
||||
class MemberRelationItem(BaseModel):
|
||||
authentik_sub: str
|
||||
user_sub: str
|
||||
email: str | None = None
|
||||
display_name: str | None = None
|
||||
is_active: bool
|
||||
|
||||
@@ -61,7 +61,7 @@ class InternalSiteListResponse(BaseModel):
|
||||
|
||||
class InternalMemberItem(BaseModel):
|
||||
id: str
|
||||
authentik_sub: str
|
||||
user_sub: str
|
||||
username: str | None = None
|
||||
email: str | None = None
|
||||
display_name: str | None = None
|
||||
@@ -77,8 +77,8 @@ class InternalMemberListResponse(BaseModel):
|
||||
|
||||
class InternalUpsertUserBySubResponse(BaseModel):
|
||||
id: str
|
||||
sub: str
|
||||
authentik_user_id: int | None = None
|
||||
user_sub: str
|
||||
idp_user_id: int | None = None
|
||||
username: str | None = None
|
||||
email: str | None = None
|
||||
display_name: str | None = None
|
||||
|
||||
@@ -8,7 +8,7 @@ ScopeType = Literal["site"]
|
||||
|
||||
|
||||
class PermissionGrantRequest(BaseModel):
|
||||
authentik_sub: str
|
||||
user_sub: str
|
||||
email: str | None = None
|
||||
display_name: str | None = None
|
||||
scope_type: ScopeType
|
||||
@@ -19,7 +19,7 @@ class PermissionGrantRequest(BaseModel):
|
||||
|
||||
|
||||
class PermissionRevokeRequest(BaseModel):
|
||||
authentik_sub: str
|
||||
user_sub: str
|
||||
scope_type: ScopeType
|
||||
scope_id: str
|
||||
system: str
|
||||
@@ -36,13 +36,13 @@ class PermissionItem(BaseModel):
|
||||
|
||||
|
||||
class PermissionSnapshotResponse(BaseModel):
|
||||
authentik_sub: str
|
||||
user_sub: str
|
||||
permissions: list[PermissionItem]
|
||||
|
||||
|
||||
class DirectPermissionRow(BaseModel):
|
||||
permission_id: str
|
||||
authentik_sub: str
|
||||
user_sub: str
|
||||
email: str | None = None
|
||||
display_name: str | None = None
|
||||
scope_type: ScopeType
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from pydantic import BaseModel
|
||||
from pydantic import AliasChoices, BaseModel, Field
|
||||
|
||||
|
||||
class UserUpsertBySubRequest(BaseModel):
|
||||
sub: str
|
||||
user_sub: str = Field(validation_alias=AliasChoices("user_sub", "sub"))
|
||||
username: str | None = None
|
||||
email: str | None = None
|
||||
display_name: str | None = None
|
||||
|
||||
@@ -14,7 +14,7 @@ from app.core.config import Settings
|
||||
class AuthentikSyncResult:
|
||||
user_id: int
|
||||
action: str
|
||||
authentik_sub: str | None = None
|
||||
user_sub: str | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -108,7 +108,7 @@ class AuthentikAdminService:
|
||||
username: str | None,
|
||||
display_name: str | None,
|
||||
is_active: bool = True,
|
||||
authentik_user_id: int | None = None,
|
||||
idp_user_id: int | None = None,
|
||||
) -> AuthentikSyncResult:
|
||||
resolved_username = username or self._safe_username(sub=sub, email=email)
|
||||
payload = {
|
||||
@@ -120,8 +120,8 @@ class AuthentikAdminService:
|
||||
|
||||
with self._client() as client:
|
||||
existing = None
|
||||
if authentik_user_id is not None:
|
||||
existing = self._lookup_user_by_id(client, authentik_user_id)
|
||||
if idp_user_id is not None:
|
||||
existing = self._lookup_user_by_id(client, idp_user_id)
|
||||
if existing is None:
|
||||
existing = self._lookup_user_by_email_or_username(client, email=email, username=resolved_username)
|
||||
|
||||
@@ -130,7 +130,7 @@ class AuthentikAdminService:
|
||||
patch_resp = client.patch(f"/api/v3/core/users/{user_pk}/", json=payload)
|
||||
if patch_resp.status_code >= 400:
|
||||
raise HTTPException(status_code=502, detail="authentik_update_failed")
|
||||
return AuthentikSyncResult(user_id=user_pk, action="updated", authentik_sub=existing.get("uid"))
|
||||
return AuthentikSyncResult(user_id=user_pk, action="updated", user_sub=existing.get("uid"))
|
||||
|
||||
create_resp = client.post("/api/v3/core/users/", json=payload)
|
||||
if create_resp.status_code >= 400:
|
||||
@@ -139,20 +139,20 @@ class AuthentikAdminService:
|
||||
return AuthentikSyncResult(
|
||||
user_id=int(created["pk"]),
|
||||
action="created",
|
||||
authentik_sub=created.get("uid"),
|
||||
user_sub=created.get("uid"),
|
||||
)
|
||||
|
||||
def reset_password(
|
||||
self,
|
||||
*,
|
||||
authentik_user_id: int | None,
|
||||
idp_user_id: int | None,
|
||||
email: str | None,
|
||||
username: str | None,
|
||||
) -> AuthentikPasswordResetResult:
|
||||
with self._client() as client:
|
||||
existing = None
|
||||
if authentik_user_id is not None:
|
||||
existing = self._lookup_user_by_id(client, authentik_user_id)
|
||||
if idp_user_id is not None:
|
||||
existing = self._lookup_user_by_id(client, idp_user_id)
|
||||
if existing is None:
|
||||
existing = self._lookup_user_by_email_or_username(client, email=email, username=username)
|
||||
if not existing or existing.get("pk") is None:
|
||||
@@ -169,14 +169,14 @@ class AuthentikAdminService:
|
||||
def delete_user(
|
||||
self,
|
||||
*,
|
||||
authentik_user_id: int | None,
|
||||
idp_user_id: int | None,
|
||||
email: str | None,
|
||||
username: str | None,
|
||||
) -> AuthentikDeleteResult:
|
||||
with self._client() as client:
|
||||
existing = None
|
||||
if authentik_user_id is not None:
|
||||
existing = self._lookup_user_by_id(client, authentik_user_id)
|
||||
if idp_user_id is not None:
|
||||
existing = self._lookup_user_by_id(client, idp_user_id)
|
||||
if existing is None:
|
||||
existing = self._lookup_user_by_email_or_username(client, email=email, username=username)
|
||||
if not existing or existing.get("pk") is None:
|
||||
|
||||
@@ -3,9 +3,9 @@ from app.schemas.permissions import PermissionItem, PermissionSnapshotResponse
|
||||
|
||||
class PermissionService:
|
||||
@staticmethod
|
||||
def build_snapshot(authentik_sub: str, permissions: list[tuple[str, str, str | None, str, str]]) -> PermissionSnapshotResponse:
|
||||
def build_snapshot(user_sub: str, permissions: list[tuple[str, str, str | None, str, str]]) -> PermissionSnapshotResponse:
|
||||
return PermissionSnapshotResponse(
|
||||
authentik_sub=authentik_sub,
|
||||
user_sub=user_sub,
|
||||
permissions=[
|
||||
PermissionItem(scope_type=s_type, scope_id=s_id, system=system, module=module, action=action)
|
||||
for s_type, s_id, system, module, action in permissions
|
||||
|
||||
Reference in New Issue
Block a user