137 lines
4.7 KiB
Python
137 lines
4.7 KiB
Python
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.users_repo import UsersRepository
|
|
from app.repositories.user_sites_repo import UserSitesRepository
|
|
from app.schemas.idp_admin import ProviderEnsureUserRequest, ProviderEnsureUserResponse
|
|
from app.schemas.internal import InternalUpsertUserBySubResponse, InternalUserRoleItem, InternalUserRoleResponse
|
|
from app.schemas.users import UserUpsertBySubRequest
|
|
from app.security.api_client_auth import require_api_client
|
|
from app.services.idp_admin_service import ProviderAdminService
|
|
from app.services.runtime_cache import runtime_cache
|
|
|
|
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,
|
|
status=payload.status,
|
|
)
|
|
return InternalUpsertUserBySubResponse(
|
|
id=user.id,
|
|
user_sub=user.user_sub,
|
|
provider_user_id=user.provider_user_id,
|
|
username=user.username,
|
|
email=user.email,
|
|
display_name=user.display_name,
|
|
is_active=user.is_active,
|
|
status=user.status,
|
|
)
|
|
|
|
|
|
def _build_user_role_rows(db: Session, user_sub: str) -> list[tuple[str, str, str, str, str, str, str, str]]:
|
|
users_repo = UsersRepository(db)
|
|
user_sites_repo = UserSitesRepository(db)
|
|
|
|
user = users_repo.get_by_sub(user_sub)
|
|
if user is None:
|
|
return []
|
|
|
|
rows = user_sites_repo.get_user_role_rows(user.id)
|
|
return [
|
|
(
|
|
site.site_key,
|
|
site.display_name,
|
|
company.company_key,
|
|
company.name,
|
|
system.system_key,
|
|
system.name,
|
|
role.role_key,
|
|
role.name,
|
|
)
|
|
for site, company, role, system in rows
|
|
]
|
|
|
|
|
|
@router.get("/users/{user_sub}/roles", response_model=InternalUserRoleResponse)
|
|
def get_user_roles(user_sub: str, db: Session = Depends(get_db)) -> InternalUserRoleResponse:
|
|
cache_key = f"internal:user_roles:{user_sub}"
|
|
cached = runtime_cache.get(cache_key)
|
|
if isinstance(cached, InternalUserRoleResponse):
|
|
return cached
|
|
|
|
rows = _build_user_role_rows(db, user_sub)
|
|
result = InternalUserRoleResponse(
|
|
user_sub=user_sub,
|
|
roles=[
|
|
InternalUserRoleItem(
|
|
site_key=site_key,
|
|
site_display_name=site_display_name,
|
|
company_key=company_key,
|
|
company_display_name=company_display_name,
|
|
system_key=system_key,
|
|
system_name=system_name,
|
|
role_key=role_key,
|
|
role_name=role_name,
|
|
)
|
|
for (
|
|
site_key,
|
|
site_display_name,
|
|
company_key,
|
|
company_display_name,
|
|
system_key,
|
|
system_name,
|
|
role_key,
|
|
role_name,
|
|
) in rows
|
|
],
|
|
)
|
|
runtime_cache.set(cache_key, result, ttl_seconds=30)
|
|
return result
|
|
|
|
|
|
@router.post("/provider/users/ensure", response_model=ProviderEnsureUserResponse)
|
|
@router.post("/idp/users/ensure", response_model=ProviderEnsureUserResponse, include_in_schema=False)
|
|
@router.post("/keycloak/users/ensure", response_model=ProviderEnsureUserResponse, include_in_schema=False)
|
|
def ensure_idp_user(
|
|
payload: ProviderEnsureUserRequest,
|
|
db: Session = Depends(get_db),
|
|
) -> ProviderEnsureUserResponse:
|
|
settings = get_settings()
|
|
idp_service = ProviderAdminService(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 sync_result.user_sub or ""
|
|
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,
|
|
status="active",
|
|
provider_user_id=sync_result.user_id,
|
|
)
|
|
return ProviderEnsureUserResponse(provider_user_id=sync_result.user_id, action=sync_result.action)
|