refactor(identity): rename authentik_sub to user_sub and authentik_user_id to idp_user_id

This commit is contained in:
Chris
2026-03-31 22:32:48 +08:00
parent ed5679948b
commit 4060ebff70
22 changed files with 208 additions and 165 deletions

View File

@@ -48,7 +48,7 @@ python scripts/generate_api_key_hash.py 'YOUR_PLAIN_KEY'
- `GET /me` (Bearer token required) - `GET /me` (Bearer token required)
- `GET /me/permissions/snapshot` (Bearer token required) - `GET /me/permissions/snapshot` (Bearer token required)
- `POST /internal/users/upsert-by-sub` - `POST /internal/users/upsert-by-sub`
- `GET /internal/permissions/{authentik_sub}/snapshot` - `GET /internal/permissions/{user_sub}/snapshot`
- `POST /internal/authentik/users/ensure` - `POST /internal/authentik/users/ensure`
- `POST /admin/permissions/grant` - `POST /admin/permissions/grant`
- `POST /admin/permissions/revoke` - `POST /admin/permissions/revoke`
@@ -58,7 +58,7 @@ python scripts/generate_api_key_hash.py 'YOUR_PLAIN_KEY'
- `GET|POST /admin/sites` - `GET|POST /admin/sites`
- `GET /admin/members` - `GET /admin/members`
- `GET|POST /admin/permission-groups` - `GET|POST /admin/permission-groups`
- `POST|DELETE /admin/permission-groups/{group_key}/members/{authentik_sub}` - `POST|DELETE /admin/permission-groups/{group_key}/members/{user_sub}`
- `POST /admin/permission-groups/{group_key}/permissions/grant|revoke` - `POST /admin/permission-groups/{group_key}/permissions/grant|revoke`
- `GET /internal/systems` - `GET /internal/systems`
- `GET /internal/modules` - `GET /internal/modules`

View File

@@ -71,7 +71,7 @@ def grant_permission(
perms_repo = PermissionsRepository(db) perms_repo = PermissionsRepository(db)
user = users_repo.upsert_by_sub( user = users_repo.upsert_by_sub(
authentik_sub=payload.authentik_sub, user_sub=payload.user_sub,
username=None, username=None,
email=payload.email, email=payload.email,
display_name=payload.display_name, display_name=payload.display_name,
@@ -99,7 +99,7 @@ def revoke_permission(
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
perms_repo = PermissionsRepository(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: if user is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")

View File

@@ -135,8 +135,8 @@ def _generate_api_key() -> str:
def _sync_member_to_authentik( def _sync_member_to_authentik(
*, *,
authentik_sub: str | None, user_sub: str | None,
authentik_user_id: int | None, idp_user_id: int | None,
username: str | None, username: str | None,
email: str | None, email: str | None,
display_name: str | None, display_name: str | None,
@@ -147,17 +147,17 @@ def _sync_member_to_authentik(
settings = get_settings() settings = get_settings()
service = AuthentikAdminService(settings=settings) service = AuthentikAdminService(settings=settings)
result = service.ensure_user( result = service.ensure_user(
sub=authentik_sub, sub=user_sub,
email=email, email=email,
username=username, username=username,
display_name=display_name, display_name=display_name,
is_active=is_active, is_active=is_active,
authentik_user_id=authentik_user_id, idp_user_id=idp_user_id,
) )
return { return {
"authentik_user_id": result.user_id, "idp_user_id": result.user_id,
"sync_action": result.action, "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 { return {
"items": [ "items": [
MemberRelationItem( MemberRelationItem(
authentik_sub=m.authentik_sub, user_sub=m.user_sub,
email=m.email, email=m.email,
display_name=m.display_name, display_name=m.display_name,
is_active=m.is_active, is_active=m.is_active,
@@ -359,7 +359,7 @@ def list_module_members(
return { return {
"items": [ "items": [
MemberRelationItem( MemberRelationItem(
authentik_sub=m.authentik_sub, user_sub=m.user_sub,
email=m.email, email=m.email,
display_name=m.display_name, display_name=m.display_name,
is_active=m.is_active, is_active=m.is_active,
@@ -567,7 +567,7 @@ def list_members(
"items": [ "items": [
MemberItem( MemberItem(
id=i.id, id=i.id,
authentik_sub=i.authentik_sub, user_sub=i.user_sub,
username=i.username, username=i.username,
email=i.email, email=i.email,
display_name=i.display_name, display_name=i.display_name,
@@ -587,37 +587,37 @@ 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 resolved_sub = payload.user_sub
resolved_username = payload.username resolved_username = payload.username
authentik_user_id = None idp_user_id = None
if payload.sync_to_authentik: 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: 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( sync = _sync_member_to_authentik(
authentik_sub=seed_sub, user_sub=seed_sub,
authentik_user_id=authentik_user_id, idp_user_id=idp_user_id,
username=payload.username, username=payload.username,
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"]) idp_user_id = int(sync["idp_user_id"])
if sync.get("authentik_sub"): if sync.get("user_sub"):
resolved_sub = str(sync["authentik_sub"]) resolved_sub = str(sync["user_sub"])
if not resolved_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( row = users_repo.upsert_by_sub(
authentik_sub=resolved_sub, user_sub=resolved_sub,
username=resolved_username, username=resolved_username,
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=authentik_user_id, idp_user_id=idp_user_id,
) )
return MemberItem( return MemberItem(
id=row.id, id=row.id,
authentik_sub=row.authentik_sub, user_sub=row.user_sub,
username=row.username, username=row.username,
email=row.email, email=row.email,
display_name=row.display_name, 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( def update_member(
authentik_sub: str, user_sub: str,
payload: MemberUpdateRequest, payload: MemberUpdateRequest,
db: Session = Depends(get_db), db: Session = Depends(get_db),
) -> MemberItem: ) -> MemberItem:
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
row = users_repo.get_by_sub(authentik_sub) row = users_repo.get_by_sub(user_sub)
if not row: if not row:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found") 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_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 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: if payload.sync_to_authentik:
sync = _sync_member_to_authentik( sync = _sync_member_to_authentik(
authentik_sub=row.authentik_sub, user_sub=row.user_sub,
authentik_user_id=row.authentik_user_id, idp_user_id=row.idp_user_id,
username=next_username, username=next_username,
email=next_email, email=next_email,
display_name=next_display_name, display_name=next_display_name,
is_active=next_is_active, 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( row = users_repo.upsert_by_sub(
authentik_sub=row.authentik_sub, user_sub=row.user_sub,
username=next_username, username=next_username,
email=next_email, email=next_email,
display_name=next_display_name, display_name=next_display_name,
is_active=next_is_active, is_active=next_is_active,
authentik_user_id=authentik_user_id, idp_user_id=idp_user_id,
) )
return MemberItem( return MemberItem(
id=row.id, id=row.id,
authentik_sub=row.authentik_sub, user_sub=row.user_sub,
username=row.username, username=row.username,
email=row.email, email=row.email,
display_name=row.display_name, 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( def delete_member(
authentik_sub: str, user_sub: str,
db: Session = Depends(get_db), db: Session = Depends(get_db),
) -> dict[str, int | str]: ) -> dict[str, int | str]:
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
row = users_repo.get_by_sub(authentik_sub) row = users_repo.get_by_sub(user_sub)
if not row: if not row:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
settings = get_settings() settings = get_settings()
service = AuthentikAdminService(settings=settings) service = AuthentikAdminService(settings=settings)
service.delete_user( service.delete_user(
authentik_user_id=row.authentik_user_id, idp_user_id=row.idp_user_id,
email=row.email, email=row.email,
username=row.username, 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.delete(row)
db.commit() db.commit()
return {"deleted": 1, "result": "deleted"} 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( def reset_member_password(
authentik_sub: str, user_sub: str,
db: Session = Depends(get_db), db: Session = Depends(get_db),
) -> MemberPasswordResetResponse: ) -> MemberPasswordResetResponse:
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
user = users_repo.get_by_sub(authentik_sub) user = users_repo.get_by_sub(user_sub)
if not user: if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
settings = get_settings() settings = get_settings()
service = AuthentikAdminService(settings=settings) service = AuthentikAdminService(settings=settings)
result = service.reset_password( result = service.reset_password(
authentik_user_id=user.authentik_user_id, idp_user_id=user.idp_user_id,
email=user.email, email=user.email,
username=user.username, username=user.username,
) )
user = users_repo.upsert_by_sub( user = users_repo.upsert_by_sub(
authentik_sub=user.authentik_sub, user_sub=user.user_sub,
username=user.username, username=user.username,
email=user.email, email=user.email,
display_name=user.display_name, display_name=user.display_name,
is_active=user.is_active, 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( def get_member_permission_groups(
authentik_sub: str, user_sub: str,
db: Session = Depends(get_db), db: Session = Depends(get_db),
) -> MemberPermissionGroupsResponse: ) -> MemberPermissionGroupsResponse:
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
groups_repo = PermissionGroupsRepository(db) groups_repo = PermissionGroupsRepository(db)
user = users_repo.get_by_sub(authentik_sub) user = users_repo.get_by_sub(user_sub)
if not user: if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found") 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) group_keys = groups_repo.list_group_keys_by_member_sub(user_sub)
return MemberPermissionGroupsResponse(authentik_sub=authentik_sub, group_keys=group_keys) 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( def set_member_permission_groups(
authentik_sub: str, user_sub: str,
payload: MemberPermissionGroupsUpdateRequest, payload: MemberPermissionGroupsUpdateRequest,
db: Session = Depends(get_db), db: Session = Depends(get_db),
) -> MemberPermissionGroupsResponse: ) -> MemberPermissionGroupsResponse:
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
groups_repo = PermissionGroupsRepository(db) groups_repo = PermissionGroupsRepository(db)
user = users_repo.get_by_sub(authentik_sub) user = users_repo.get_by_sub(user_sub)
if not user: if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found") raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
@@ -753,8 +753,8 @@ def set_member_permission_groups(
if missing: if missing:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"group_not_found:{','.join(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]) groups_repo.replace_member_groups(user_sub, [g.id for g in groups])
return MemberPermissionGroupsResponse(authentik_sub=authentik_sub, group_keys=unique_group_keys) return MemberPermissionGroupsResponse(user_sub=user_sub, group_keys=unique_group_keys)
@router.get("/api-clients") @router.get("/api-clients")
@@ -1023,31 +1023,31 @@ def delete_permission_group(
return {"deleted": 1, "result": "deleted"} 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( def add_group_member(
group_key: str, group_key: str,
authentik_sub: str, user_sub: str,
db: Session = Depends(get_db), db: Session = Depends(get_db),
) -> dict[str, str]: ) -> dict[str, str]:
groups_repo = PermissionGroupsRepository(db) groups_repo = PermissionGroupsRepository(db)
group = groups_repo.get_by_key(group_key) group = groups_repo.get_by_key(group_key)
if not group: if not group:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="group_not_found") 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"} 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( def remove_group_member(
group_key: str, group_key: str,
authentik_sub: str, user_sub: str,
db: Session = Depends(get_db), db: Session = Depends(get_db),
) -> dict[str, int | str]: ) -> dict[str, int | str]:
groups_repo = PermissionGroupsRepository(db) groups_repo = PermissionGroupsRepository(db)
group = groups_repo.get_by_key(group_key) group = groups_repo.get_by_key(group_key)
if not group: if not group:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="group_not_found") 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"} return {"deleted": deleted, "result": "removed"}

View File

@@ -23,7 +23,7 @@ def upsert_user_by_sub(
) -> InternalUpsertUserBySubResponse: ) -> InternalUpsertUserBySubResponse:
repo = UsersRepository(db) repo = UsersRepository(db)
user = repo.upsert_by_sub( user = repo.upsert_by_sub(
authentik_sub=payload.sub, user_sub=payload.user_sub,
username=payload.username, username=payload.username,
email=payload.email, email=payload.email,
display_name=payload.display_name, display_name=payload.display_name,
@@ -31,8 +31,8 @@ def upsert_user_by_sub(
) )
return { return {
"id": user.id, "id": user.id,
"sub": user.authentik_sub, "user_sub": user.user_sub,
"authentik_user_id": user.authentik_user_id, "idp_user_id": user.idp_user_id,
"username": user.username, "username": user.username,
"email": user.email, "email": user.email,
"display_name": user.display_name, "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( def get_permission_snapshot(
authentik_sub: str, user_sub: str,
db: Session = Depends(get_db), db: Session = Depends(get_db),
) -> PermissionSnapshotResponse: ) -> PermissionSnapshotResponse:
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
perms_repo = PermissionsRepository(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: 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) permissions = perms_repo.list_by_user(user.id, user.user_sub)
return PermissionService.build_snapshot(authentik_sub=authentik_sub, permissions=permissions) return PermissionService.build_snapshot(user_sub=user_sub, permissions=permissions)
@router.post("/authentik/users/ensure", response_model=AuthentikEnsureUserResponse) @router.post("/authentik/users/ensure", response_model=AuthentikEnsureUserResponse)
@@ -64,7 +64,7 @@ def ensure_authentik_user(
settings = get_settings() settings = get_settings()
authentik_service = AuthentikAdminService(settings=settings) authentik_service = AuthentikAdminService(settings=settings)
sync_result = authentik_service.ensure_user( sync_result = authentik_service.ensure_user(
sub=payload.sub, sub=payload.user_sub,
email=payload.email, email=payload.email,
username=payload.username, username=payload.username,
display_name=payload.display_name, display_name=payload.display_name,
@@ -72,17 +72,17 @@ def ensure_authentik_user(
) )
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
resolved_sub = payload.sub or "" resolved_sub = payload.user_sub or ""
if sync_result.authentik_sub: if sync_result.user_sub:
resolved_sub = sync_result.authentik_sub resolved_sub = sync_result.user_sub
if not resolved_sub: if not resolved_sub:
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail="authentik_missing_sub") raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail="authentik_missing_sub")
users_repo.upsert_by_sub( users_repo.upsert_by_sub(
authentik_sub=resolved_sub, user_sub=resolved_sub,
username=payload.username, username=payload.username,
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=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)

View File

@@ -100,7 +100,7 @@ def internal_list_members(
"items": [ "items": [
{ {
"id": i.id, "id": i.id,
"authentik_sub": i.authentik_sub, "user_sub": i.user_sub,
"username": i.username, "username": i.username,
"email": i.email, "email": i.email,
"display_name": i.display_name, "display_name": i.display_name,

View File

@@ -21,13 +21,13 @@ def get_me(
try: try:
users_repo = UsersRepository(db) users_repo = UsersRepository(db)
user = users_repo.upsert_by_sub( user = users_repo.upsert_by_sub(
authentik_sub=principal.sub, user_sub=principal.sub,
username=principal.preferred_username, username=principal.preferred_username,
email=principal.email, email=principal.email,
display_name=principal.name or principal.preferred_username, display_name=principal.name or principal.preferred_username,
is_active=True, 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: except SQLAlchemyError:
# DB schema compatibility fallback for local bring-up. # DB schema compatibility fallback for local bring-up.
return MeSummaryResponse( return MeSummaryResponse(
@@ -47,13 +47,13 @@ def get_my_permission_snapshot(
perms_repo = PermissionsRepository(db) perms_repo = PermissionsRepository(db)
user = users_repo.upsert_by_sub( user = users_repo.upsert_by_sub(
authentik_sub=principal.sub, user_sub=principal.sub,
username=principal.preferred_username, username=principal.preferred_username,
email=principal.email, email=principal.email,
display_name=principal.name or principal.preferred_username, display_name=principal.name or principal.preferred_username,
is_active=True, is_active=True,
) )
permissions = perms_repo.list_by_user(user.id, user.authentik_sub) permissions = perms_repo.list_by_user(user.id, user.user_sub)
return PermissionService.build_snapshot(authentik_sub=principal.sub, permissions=permissions) return PermissionService.build_snapshot(user_sub=principal.sub, permissions=permissions)
except SQLAlchemyError: except SQLAlchemyError:
return PermissionSnapshotResponse(authentik_sub=principal.sub, permissions=[]) return PermissionSnapshotResponse(user_sub=principal.sub, permissions=[])

View File

@@ -10,11 +10,11 @@ from app.db.base import Base
class PermissionGroupMember(Base): class PermissionGroupMember(Base):
__tablename__ = "permission_group_members" __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())) id: Mapped[str] = mapped_column(UUID(as_uuid=False), primary_key=True, default=lambda: str(uuid4()))
group_id: Mapped[str] = mapped_column( group_id: Mapped[str] = mapped_column(
UUID(as_uuid=False), ForeignKey("permission_groups.id", ondelete="CASCADE"), nullable=False 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) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)

View File

@@ -12,8 +12,8 @@ class User(Base):
__tablename__ = "users" __tablename__ = "users"
id: Mapped[str] = mapped_column(UUID(as_uuid=False), primary_key=True, default=lambda: str(uuid4())) 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) user_sub: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True)
authentik_user_id: Mapped[int | None] = mapped_column(Integer) idp_user_id: Mapped[int | None] = mapped_column(Integer)
username: Mapped[str | None] = mapped_column(String(255), unique=True) username: Mapped[str | None] = mapped_column(String(255), unique=True)
email: Mapped[str | None] = mapped_column(String(320)) email: Mapped[str | None] = mapped_column(String(320))
display_name: Mapped[str | None] = mapped_column(String(255)) display_name: Mapped[str | None] = mapped_column(String(255))

View File

@@ -46,43 +46,43 @@ class PermissionGroupsRepository:
self.db.refresh(item) self.db.refresh(item)
return 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( existing = self.db.scalar(
select(PermissionGroupMember).where( 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: if existing:
return 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.add(row)
self.db.commit() self.db.commit()
self.db.refresh(row) self.db.refresh(row)
return 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( result = self.db.execute(
delete(PermissionGroupMember).where( 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() self.db.commit()
return int(result.rowcount or 0) 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 = ( stmt = (
select(PermissionGroup.group_key) select(PermissionGroup.group_key)
.select_from(PermissionGroupMember) .select_from(PermissionGroupMember)
.join(PermissionGroup, PermissionGroup.id == PermissionGroupMember.group_id) .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()) .order_by(PermissionGroup.group_key.asc())
) )
return [row[0] for row in self.db.execute(stmt).all()] return [row[0] for row in self.db.execute(stmt).all()]
def replace_member_groups(self, authentik_sub: str, group_ids: list[str]) -> None: def replace_member_groups(self, user_sub: str, group_ids: list[str]) -> None:
self.db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.authentik_sub == authentik_sub)) self.db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.user_sub == user_sub))
for group_id in group_ids: 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() self.db.commit()
def grant_group_permission( def grant_group_permission(
@@ -155,7 +155,7 @@ class PermissionGroupsRepository:
self.db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.group_id == group_id)) self.db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.group_id == group_id))
for sub in normalized_member_subs: 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 site_key in normalized_sites:
for action in normalized_actions: for action in normalized_actions:
@@ -199,9 +199,9 @@ class PermissionGroupsRepository:
def list_group_member_subs(self, group_id: str) -> list[str]: def list_group_member_subs(self, group_id: str) -> list[str]:
stmt = ( stmt = (
select(PermissionGroupMember.authentik_sub) select(PermissionGroupMember.user_sub)
.where(PermissionGroupMember.group_id == group_id) .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()] 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]: def list_system_members(self, system_key: str) -> list[User]:
stmt = ( stmt = (
select(User) 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) .join(PermissionGroupPermission, PermissionGroupPermission.group_id == PermissionGroupMember.group_id)
.where(PermissionGroupPermission.system == system_key) .where(PermissionGroupPermission.system == system_key)
.order_by(User.email.asc(), User.authentik_sub.asc()) .order_by(User.email.asc(), User.user_sub.asc())
.distinct() .distinct()
) )
return list(self.db.scalars(stmt).all()) 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]: def list_module_members(self, system_key: str, module_name: str) -> list[User]:
stmt = ( stmt = (
select(User) 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) .join(PermissionGroupPermission, PermissionGroupPermission.group_id == PermissionGroupMember.group_id)
.where(PermissionGroupPermission.system == system_key, PermissionGroupPermission.module == module_name) .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() .distinct()
) )
return list(self.db.scalars(stmt).all()) return list(self.db.scalars(stmt).all())

View File

@@ -14,7 +14,7 @@ class PermissionsRepository:
def __init__(self, db: Session) -> None: def __init__(self, db: Session) -> None:
self.db = db 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 = ( direct_stmt = (
select( select(
literal("direct"), literal("direct"),
@@ -44,7 +44,7 @@ class PermissionsRepository:
) )
.select_from(PermissionGroupPermission) .select_from(PermissionGroupPermission)
.join(PermissionGroupMember, PermissionGroupMember.group_id == PermissionGroupPermission.group_id) .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.action.in_(["view", "edit"]))
.where(PermissionGroupPermission.scope_type == "site") .where(PermissionGroupPermission.scope_type == "site")
) )
@@ -138,7 +138,7 @@ class PermissionsRepository:
stmt = ( stmt = (
select( select(
UserScopePermission.id, UserScopePermission.id,
User.authentik_sub, User.user_sub,
User.email, User.email,
User.display_name, User.display_name,
UserScopePermission.scope_type, UserScopePermission.scope_type,
@@ -175,7 +175,7 @@ class PermissionsRepository:
if keyword: if keyword:
pattern = f"%{keyword}%" pattern = f"%{keyword}%"
cond = or_( cond = or_(
User.authentik_sub.ilike(pattern), User.user_sub.ilike(pattern),
User.email.ilike(pattern), User.email.ilike(pattern),
User.display_name.ilike(pattern), User.display_name.ilike(pattern),
Module.module_key.ilike(pattern), Module.module_key.ilike(pattern),
@@ -193,7 +193,7 @@ class PermissionsRepository:
for row in rows: for row in rows:
( (
permission_id, permission_id,
authentik_sub, user_sub,
email, email,
display_name, display_name,
row_scope_type, row_scope_type,
@@ -211,7 +211,7 @@ class PermissionsRepository:
items.append( items.append(
{ {
"permission_id": permission_id, "permission_id": permission_id,
"authentik_sub": authentik_sub, "user_sub": user_sub,
"email": email, "email": email,
"display_name": display_name, "display_name": display_name,
"scope_type": row_scope_type, "scope_type": row_scope_type,

View File

@@ -8,8 +8,8 @@ class UsersRepository:
def __init__(self, db: Session) -> None: def __init__(self, db: Session) -> None:
self.db = db self.db = db
def get_by_sub(self, authentik_sub: str) -> User | None: def get_by_sub(self, user_sub: str) -> User | None:
stmt = select(User).where(User.authentik_sub == authentik_sub) stmt = select(User).where(User.user_sub == user_sub)
return self.db.scalar(stmt) return self.db.scalar(stmt)
def get_by_id(self, user_id: str) -> User | None: def get_by_id(self, user_id: str) -> User | None:
@@ -29,7 +29,7 @@ class UsersRepository:
if keyword: if keyword:
pattern = f"%{keyword}%" pattern = f"%{keyword}%"
cond = or_( cond = or_(
User.authentik_sub.ilike(pattern), User.user_sub.ilike(pattern),
User.username.ilike(pattern), User.username.ilike(pattern),
User.email.ilike(pattern), User.email.ilike(pattern),
User.display_name.ilike(pattern), User.display_name.ilike(pattern),
@@ -48,18 +48,18 @@ class UsersRepository:
def upsert_by_sub( def upsert_by_sub(
self, self,
authentik_sub: str, user_sub: str,
username: str | None, username: str | None,
email: str | None, email: str | None,
display_name: str | None, display_name: str | None,
is_active: bool, is_active: bool,
authentik_user_id: int | None = None, idp_user_id: int | None = None,
) -> User: ) -> User:
user = self.get_by_sub(authentik_sub) user = self.get_by_sub(user_sub)
if user is None: if user is None:
user = User( user = User(
authentik_sub=authentik_sub, user_sub=user_sub,
authentik_user_id=authentik_user_id, idp_user_id=idp_user_id,
username=username, username=username,
email=email, email=email,
display_name=display_name, display_name=display_name,
@@ -67,8 +67,8 @@ class UsersRepository:
) )
self.db.add(user) self.db.add(user)
else: else:
if authentik_user_id is not None: if idp_user_id is not None:
user.authentik_user_id = authentik_user_id user.idp_user_id = idp_user_id
user.username = username user.username = username
user.email = email user.email = email
user.display_name = display_name user.display_name = display_name

View File

@@ -1,8 +1,8 @@
from pydantic import BaseModel from pydantic import AliasChoices, BaseModel, Field
class AuthentikEnsureUserRequest(BaseModel): class AuthentikEnsureUserRequest(BaseModel):
sub: str | None = None user_sub: str | None = Field(default=None, validation_alias=AliasChoices("user_sub", "sub"))
username: str | None = None username: str | None = None
email: str email: str
display_name: str | None = None display_name: str | None = None
@@ -10,5 +10,5 @@ class AuthentikEnsureUserRequest(BaseModel):
class AuthentikEnsureUserResponse(BaseModel): class AuthentikEnsureUserResponse(BaseModel):
authentik_user_id: int idp_user_id: int
action: str action: str

View File

@@ -77,7 +77,7 @@ class SiteItem(BaseModel):
class MemberItem(BaseModel): class MemberItem(BaseModel):
id: str id: str
authentik_sub: str user_sub: str
username: str | None = None username: str | None = None
email: str | None = None email: str | None = None
display_name: str | None = None display_name: str | None = None
@@ -85,7 +85,7 @@ class MemberItem(BaseModel):
class MemberUpsertRequest(BaseModel): class MemberUpsertRequest(BaseModel):
authentik_sub: str | None = None user_sub: str | None = None
username: str | None = None username: str | None = None
email: str | None = None email: str | None = None
display_name: str | None = None display_name: str | None = None
@@ -102,7 +102,7 @@ class MemberUpdateRequest(BaseModel):
class MemberPasswordResetResponse(BaseModel): class MemberPasswordResetResponse(BaseModel):
authentik_sub: str user_sub: str
temporary_password: str temporary_password: str
@@ -144,7 +144,7 @@ class PermissionGroupPermissionItem(BaseModel):
class MemberPermissionGroupsResponse(BaseModel): class MemberPermissionGroupsResponse(BaseModel):
authentik_sub: str user_sub: str
group_keys: list[str] group_keys: list[str]
@@ -172,7 +172,7 @@ class GroupRelationItem(BaseModel):
class MemberRelationItem(BaseModel): class MemberRelationItem(BaseModel):
authentik_sub: str user_sub: str
email: str | None = None email: str | None = None
display_name: str | None = None display_name: str | None = None
is_active: bool is_active: bool

View File

@@ -61,7 +61,7 @@ class InternalSiteListResponse(BaseModel):
class InternalMemberItem(BaseModel): class InternalMemberItem(BaseModel):
id: str id: str
authentik_sub: str user_sub: str
username: str | None = None username: str | None = None
email: str | None = None email: str | None = None
display_name: str | None = None display_name: str | None = None
@@ -77,8 +77,8 @@ class InternalMemberListResponse(BaseModel):
class InternalUpsertUserBySubResponse(BaseModel): class InternalUpsertUserBySubResponse(BaseModel):
id: str id: str
sub: str user_sub: str
authentik_user_id: int | None = None idp_user_id: int | None = None
username: str | None = None username: str | None = None
email: str | None = None email: str | None = None
display_name: str | None = None display_name: str | None = None

View File

@@ -8,7 +8,7 @@ ScopeType = Literal["site"]
class PermissionGrantRequest(BaseModel): class PermissionGrantRequest(BaseModel):
authentik_sub: str user_sub: str
email: str | None = None email: str | None = None
display_name: str | None = None display_name: str | None = None
scope_type: ScopeType scope_type: ScopeType
@@ -19,7 +19,7 @@ class PermissionGrantRequest(BaseModel):
class PermissionRevokeRequest(BaseModel): class PermissionRevokeRequest(BaseModel):
authentik_sub: str user_sub: str
scope_type: ScopeType scope_type: ScopeType
scope_id: str scope_id: str
system: str system: str
@@ -36,13 +36,13 @@ class PermissionItem(BaseModel):
class PermissionSnapshotResponse(BaseModel): class PermissionSnapshotResponse(BaseModel):
authentik_sub: str user_sub: str
permissions: list[PermissionItem] permissions: list[PermissionItem]
class DirectPermissionRow(BaseModel): class DirectPermissionRow(BaseModel):
permission_id: str permission_id: str
authentik_sub: str user_sub: str
email: str | None = None email: str | None = None
display_name: str | None = None display_name: str | None = None
scope_type: ScopeType scope_type: ScopeType

View File

@@ -1,8 +1,8 @@
from pydantic import BaseModel from pydantic import AliasChoices, BaseModel, Field
class UserUpsertBySubRequest(BaseModel): class UserUpsertBySubRequest(BaseModel):
sub: str user_sub: str = Field(validation_alias=AliasChoices("user_sub", "sub"))
username: str | None = None username: str | None = None
email: str | None = None email: str | None = None
display_name: str | None = None display_name: str | None = None

View File

@@ -14,7 +14,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 user_sub: str | None = None
@dataclass @dataclass
@@ -108,7 +108,7 @@ class AuthentikAdminService:
username: str | None, username: str | None,
display_name: str | None, display_name: str | None,
is_active: bool = True, is_active: bool = True,
authentik_user_id: int | None = None, idp_user_id: int | None = None,
) -> AuthentikSyncResult: ) -> AuthentikSyncResult:
resolved_username = username or self._safe_username(sub=sub, email=email) resolved_username = username or self._safe_username(sub=sub, email=email)
payload = { payload = {
@@ -120,8 +120,8 @@ class AuthentikAdminService:
with self._client() as client: with self._client() as client:
existing = None existing = None
if authentik_user_id is not None: if idp_user_id is not None:
existing = self._lookup_user_by_id(client, authentik_user_id) existing = self._lookup_user_by_id(client, idp_user_id)
if existing is None: if existing is None:
existing = self._lookup_user_by_email_or_username(client, email=email, username=resolved_username) 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) 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", 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) create_resp = client.post("/api/v3/core/users/", json=payload)
if create_resp.status_code >= 400: if create_resp.status_code >= 400:
@@ -139,20 +139,20 @@ class AuthentikAdminService:
return AuthentikSyncResult( return AuthentikSyncResult(
user_id=int(created["pk"]), user_id=int(created["pk"]),
action="created", action="created",
authentik_sub=created.get("uid"), user_sub=created.get("uid"),
) )
def reset_password( def reset_password(
self, self,
*, *,
authentik_user_id: int | None, idp_user_id: int | None,
email: str | None, email: str | None,
username: str | None, username: str | None,
) -> AuthentikPasswordResetResult: ) -> AuthentikPasswordResetResult:
with self._client() as client: with self._client() as client:
existing = None existing = None
if authentik_user_id is not None: if idp_user_id is not None:
existing = self._lookup_user_by_id(client, authentik_user_id) existing = self._lookup_user_by_id(client, idp_user_id)
if existing is None: if existing is None:
existing = self._lookup_user_by_email_or_username(client, email=email, username=username) existing = self._lookup_user_by_email_or_username(client, email=email, username=username)
if not existing or existing.get("pk") is None: if not existing or existing.get("pk") is None:
@@ -169,14 +169,14 @@ class AuthentikAdminService:
def delete_user( def delete_user(
self, self,
*, *,
authentik_user_id: int | None, idp_user_id: int | None,
email: str | None, email: str | None,
username: str | None, username: str | None,
) -> AuthentikDeleteResult: ) -> AuthentikDeleteResult:
with self._client() as client: with self._client() as client:
existing = None existing = None
if authentik_user_id is not None: if idp_user_id is not None:
existing = self._lookup_user_by_id(client, authentik_user_id) existing = self._lookup_user_by_id(client, idp_user_id)
if existing is None: if existing is None:
existing = self._lookup_user_by_email_or_username(client, email=email, username=username) existing = self._lookup_user_by_email_or_username(client, email=email, username=username)
if not existing or existing.get("pk") is None: if not existing or existing.get("pk") is None:

View File

@@ -3,9 +3,9 @@ from app.schemas.permissions import PermissionItem, PermissionSnapshotResponse
class PermissionService: class PermissionService:
@staticmethod @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( return PermissionSnapshotResponse(
authentik_sub=authentik_sub, user_sub=user_sub,
permissions=[ permissions=[
PermissionItem(scope_type=s_type, scope_id=s_id, system=system, module=module, action=action) 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 for s_type, s_id, system, module, action in permissions

View File

@@ -19,8 +19,8 @@ DROP TABLE IF EXISTS permissions CASCADE;
CREATE TABLE users ( CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
authentik_sub TEXT NOT NULL UNIQUE, user_sub TEXT NOT NULL UNIQUE,
authentik_user_id INTEGER, idp_user_id INTEGER,
username TEXT UNIQUE, username TEXT UNIQUE,
email TEXT UNIQUE, email TEXT UNIQUE,
display_name TEXT, display_name TEXT,
@@ -105,9 +105,9 @@ CREATE TABLE permission_groups (
CREATE TABLE permission_group_members ( CREATE TABLE permission_group_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
group_id UUID NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE, group_id UUID NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE,
authentik_sub TEXT NOT NULL, user_sub TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_permission_group_members_group_sub UNIQUE (group_id, authentik_sub) CONSTRAINT uq_permission_group_members_group_sub UNIQUE (group_id, user_sub)
); );
CREATE TABLE permission_group_permissions ( CREATE TABLE permission_group_permissions (
@@ -144,7 +144,7 @@ INSERT INTO systems (system_key, name, status)
VALUES ('member', 'Member Center', 'active') VALUES ('member', 'Member Center', 'active')
ON CONFLICT (system_key) DO NOTHING; ON CONFLICT (system_key) DO NOTHING;
CREATE INDEX idx_users_authentik_sub ON users(authentik_sub); CREATE INDEX idx_users_user_sub ON users(user_sub);
CREATE INDEX idx_users_username ON users(username); CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_sites_company_id ON sites(company_id); CREATE INDEX idx_sites_company_id ON sites(company_id);
CREATE INDEX idx_usp_user_id ON user_scope_permissions(user_id); CREATE INDEX idx_usp_user_id ON user_scope_permissions(user_id);
@@ -153,7 +153,7 @@ CREATE INDEX idx_usp_site_id ON user_scope_permissions(site_id);
CREATE UNIQUE INDEX uq_usp_site CREATE UNIQUE INDEX uq_usp_site
ON user_scope_permissions(user_id, module_id, action, scope_type, site_id); ON user_scope_permissions(user_id, module_id, action, scope_type, site_id);
CREATE INDEX idx_pgm_group_id ON permission_group_members(group_id); CREATE INDEX idx_pgm_group_id ON permission_group_members(group_id);
CREATE INDEX idx_pgm_authentik_sub ON permission_group_members(authentik_sub); CREATE INDEX idx_pgm_user_sub ON permission_group_members(user_sub);
CREATE INDEX idx_pgp_group_id ON permission_group_permissions(group_id); CREATE INDEX idx_pgp_group_id ON permission_group_permissions(group_id);
CREATE INDEX idx_pgp_scope_site ON permission_group_permissions(scope_id); CREATE INDEX idx_pgp_scope_site ON permission_group_permissions(scope_id);
CREATE INDEX idx_api_clients_status ON api_clients(status); CREATE INDEX idx_api_clients_status ON api_clients(status);

View File

@@ -1,2 +1,2 @@
ALTER TABLE users ALTER TABLE users
ADD COLUMN IF NOT EXISTS authentik_user_id INTEGER; ADD COLUMN IF NOT EXISTS idp_user_id INTEGER;

View File

@@ -46,9 +46,9 @@ CREATE TABLE IF NOT EXISTS permission_groups (
CREATE TABLE IF NOT EXISTS permission_group_members ( CREATE TABLE IF NOT EXISTS permission_group_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
group_id UUID NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE, group_id UUID NOT NULL REFERENCES permission_groups(id) ON DELETE CASCADE,
authentik_sub TEXT NOT NULL, user_sub TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_permission_group_members_group_sub UNIQUE (group_id, authentik_sub) CONSTRAINT uq_permission_group_members_group_sub UNIQUE (group_id, user_sub)
); );
CREATE TABLE IF NOT EXISTS permission_group_permissions ( CREATE TABLE IF NOT EXISTS permission_group_permissions (
@@ -64,7 +64,7 @@ CREATE TABLE IF NOT EXISTS permission_group_permissions (
CREATE INDEX IF NOT EXISTS idx_systems_system_key ON systems(system_key); CREATE INDEX IF NOT EXISTS idx_systems_system_key ON systems(system_key);
CREATE INDEX IF NOT EXISTS idx_pgm_group_id ON permission_group_members(group_id); CREATE INDEX IF NOT EXISTS idx_pgm_group_id ON permission_group_members(group_id);
CREATE INDEX IF NOT EXISTS idx_pgm_authentik_sub ON permission_group_members(authentik_sub); CREATE INDEX IF NOT EXISTS idx_pgm_user_sub ON permission_group_members(user_sub);
CREATE INDEX IF NOT EXISTS idx_pgp_group_id ON permission_group_permissions(group_id); CREATE INDEX IF NOT EXISTS idx_pgp_group_id ON permission_group_permissions(group_id);
CREATE UNIQUE INDEX IF NOT EXISTS uq_pgp_group_rule CREATE UNIQUE INDEX IF NOT EXISTS uq_pgp_group_rule

View File

@@ -0,0 +1,43 @@
BEGIN;
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'authentik_sub'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'user_sub'
) THEN
ALTER TABLE users RENAME COLUMN authentik_sub TO user_sub;
END IF;
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'authentik_user_id'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'idp_user_id'
) THEN
ALTER TABLE users RENAME COLUMN authentik_user_id TO idp_user_id;
END IF;
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'permission_group_members' AND column_name = 'authentik_sub'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'permission_group_members' AND column_name = 'user_sub'
) THEN
ALTER TABLE permission_group_members RENAME COLUMN authentik_sub TO user_sub;
END IF;
END
$$;
ALTER INDEX IF EXISTS idx_users_authentik_sub RENAME TO idx_users_user_sub;
ALTER INDEX IF EXISTS idx_pgm_authentik_sub RENAME TO idx_pgm_user_sub;
CREATE INDEX IF NOT EXISTS idx_users_user_sub ON users(user_sub);
CREATE INDEX IF NOT EXISTS idx_pgm_user_sub ON permission_group_members(user_sub);
COMMIT;