from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.core.config import get_settings from app.db.session import get_db from app.repositories.permissions_repo import PermissionsRepository from app.schemas.internal import InternalUpsertUserBySubResponse from app.repositories.users_repo import UsersRepository from app.schemas.idp_admin import KeycloakEnsureUserRequest, KeycloakEnsureUserResponse from app.schemas.permissions import PermissionSnapshotResponse from app.schemas.users import UserUpsertBySubRequest from app.security.api_client_auth import require_api_client from app.services.idp_admin_service import KeycloakAdminService from app.services.permission_service import PermissionService router = APIRouter(prefix="/internal", tags=["internal"], dependencies=[Depends(require_api_client)]) @router.post("/users/upsert-by-sub", response_model=InternalUpsertUserBySubResponse) def upsert_user_by_sub( payload: UserUpsertBySubRequest, db: Session = Depends(get_db), ) -> InternalUpsertUserBySubResponse: repo = UsersRepository(db) user = repo.upsert_by_sub( user_sub=payload.user_sub, username=payload.username, email=payload.email, display_name=payload.display_name, is_active=payload.is_active, ) return { "id": 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, "is_active": user.is_active, } @router.get("/permissions/{user_sub}/snapshot", response_model=PermissionSnapshotResponse) def get_permission_snapshot( user_sub: str, db: Session = Depends(get_db), ) -> PermissionSnapshotResponse: users_repo = UsersRepository(db) perms_repo = PermissionsRepository(db) user = users_repo.get_by_sub(user_sub) if user is None: return PermissionSnapshotResponse(user_sub=user_sub, permissions=[]) permissions = perms_repo.list_by_user(user.id, user.user_sub) return PermissionService.build_snapshot(user_sub=user_sub, permissions=permissions) @router.post("/idp/users/ensure", response_model=KeycloakEnsureUserResponse) @router.post("/keycloak/users/ensure", response_model=KeycloakEnsureUserResponse) def ensure_idp_user( payload: KeycloakEnsureUserRequest, db: Session = Depends(get_db), ) -> KeycloakEnsureUserResponse: settings = get_settings() idp_service = KeycloakAdminService(settings=settings) sync_result = idp_service.ensure_user( sub=payload.user_sub, email=payload.email, username=payload.username, display_name=payload.display_name, is_active=payload.is_active, ) users_repo = UsersRepository(db) 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="idp_missing_sub") users_repo.upsert_by_sub( user_sub=resolved_sub, username=payload.username, email=payload.email, display_name=payload.display_name, is_active=payload.is_active, idp_user_id=sync_result.user_id, ) return KeycloakEnsureUserResponse(idp_user_id=sync_result.user_id, action=sync_result.action)