142 lines
5.8 KiB
Python
142 lines
5.8 KiB
Python
from sqlalchemy.orm import Session
|
|
from fastapi import HTTPException, status, BackgroundTasks
|
|
from typing import List, Optional
|
|
import uuid
|
|
from app.models.auth.user_model import User
|
|
from app.schemas.auth.user_schema import UserCreate, UserUpdate
|
|
from app.services.auth.user_service import UserService
|
|
from app.middleware.tenant_middleware import is_superadmin
|
|
|
|
class UserController:
|
|
@staticmethod
|
|
def _resolve_tenant_id(current_user: User, requested_tenant_id: Optional[uuid.UUID]) -> Optional[uuid.UUID]:
|
|
if is_superadmin(current_user):
|
|
return requested_tenant_id
|
|
|
|
if requested_tenant_id and requested_tenant_id != current_user.tenant_id:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Not authorized to access this tenant"
|
|
)
|
|
|
|
return current_user.tenant_id
|
|
|
|
@staticmethod
|
|
def create_user(db: Session, user_data: UserCreate, current_user: User, background_tasks: BackgroundTasks) -> User:
|
|
tenant_id = UserController._resolve_tenant_id(current_user, user_data.tenant_id)
|
|
return UserService.create_user(db, user_data, tenant_id, background_tasks)
|
|
|
|
@staticmethod
|
|
def _scoped_tenant_id(current_user: User) -> Optional[uuid.UUID]:
|
|
"""The tenant filter to apply for this actor.
|
|
|
|
None means "no filter", which is only ever correct for a superadmin.
|
|
A tenant-less non-superadmin is an artefact of the signup defect and
|
|
is refused rather than being handed an unfiltered query.
|
|
"""
|
|
if is_superadmin(current_user):
|
|
return None
|
|
if current_user.tenant_id is None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Account is not associated with a workspace",
|
|
)
|
|
return current_user.tenant_id
|
|
|
|
@staticmethod
|
|
def _visible_ids(db: Session, current_user: User):
|
|
"""Whose accounts this person may administer, or None for everyone.
|
|
|
|
Computed here rather than inside `UserService` so the rule lives in one
|
|
place: every path an administrator reaches a user through goes past this
|
|
method, and a query that forgot it would be a scoped administrator
|
|
quietly seeing the whole workspace.
|
|
"""
|
|
from app.services.auth import org_unit_service
|
|
|
|
if is_superadmin(current_user):
|
|
return None
|
|
return org_unit_service.visible_user_ids(db, current_user)
|
|
|
|
@staticmethod
|
|
def get_all_users(db: Session, current_user: User) -> List[User]:
|
|
tenant_id = UserController._scoped_tenant_id(current_user)
|
|
return UserService.get_all_users(
|
|
db, tenant_id, visible_ids=UserController._visible_ids(db, current_user)
|
|
)
|
|
|
|
@staticmethod
|
|
def get_user_by_id(db: Session, user_id: uuid.UUID, current_user: User) -> User:
|
|
tenant_id = UserController._scoped_tenant_id(current_user)
|
|
return UserService.get_user_by_id(
|
|
db, user_id, tenant_id,
|
|
visible_ids=UserController._visible_ids(db, current_user),
|
|
)
|
|
|
|
@staticmethod
|
|
def update_user(db: Session, user_id: uuid.UUID, user_data: UserUpdate, current_user: User, background_tasks: BackgroundTasks) -> User:
|
|
if not is_superadmin(current_user) and user_data.tenant_id is not None:
|
|
UserController._resolve_tenant_id(current_user, user_data.tenant_id)
|
|
|
|
tenant_id = UserController._scoped_tenant_id(current_user)
|
|
UserService.get_user_by_id(
|
|
db, user_id, tenant_id,
|
|
visible_ids=UserController._visible_ids(db, current_user),
|
|
)
|
|
return UserService.update_user(db, user_id, user_data, tenant_id, background_tasks)
|
|
|
|
@staticmethod
|
|
def delete_user(db: Session, user_id: uuid.UUID, current_user: User):
|
|
tenant_id = UserController._scoped_tenant_id(current_user)
|
|
UserService.get_user_by_id(
|
|
db, user_id, tenant_id,
|
|
visible_ids=UserController._visible_ids(db, current_user),
|
|
)
|
|
return UserService.delete_user(db, user_id, tenant_id,
|
|
actor_id=current_user.id)
|
|
|
|
@staticmethod
|
|
def deleted_users(db: Session, current_user: User):
|
|
tenant_id = UserController._scoped_tenant_id(current_user)
|
|
return UserService.deleted_users(db, tenant_id)
|
|
|
|
@staticmethod
|
|
def restore_user(db: Session, user_id: uuid.UUID, current_user: User):
|
|
tenant_id = UserController._scoped_tenant_id(current_user)
|
|
user = UserService.get_user_by_id(db, user_id, tenant_id,
|
|
include_deleted=True)
|
|
return UserService.restore(db, user)
|
|
|
|
@staticmethod
|
|
def get_users_paginated(
|
|
db: Session,
|
|
current_user: User,
|
|
page: int = 1,
|
|
page_size: int = 10,
|
|
search: Optional[str] = None,
|
|
filter_names: Optional[List[str]] = None,
|
|
filter_emails: Optional[List[str]] = None,
|
|
statuses: Optional[List[str]] = None,
|
|
filter_tenant_ids: Optional[List[uuid.UUID]] = None,
|
|
filter_role_ids: Optional[List[uuid.UUID]] = None,
|
|
sort_by: Optional[str] = None,
|
|
sort_order: Optional[str] = None,
|
|
):
|
|
tenant_id = current_user.tenant_id
|
|
visible = UserController._visible_ids(db, current_user)
|
|
return UserService.get_users_paginated(
|
|
db=db,
|
|
tenant_id=tenant_id,
|
|
page=page,
|
|
page_size=page_size,
|
|
search=search,
|
|
filter_names=filter_names,
|
|
filter_emails=filter_emails,
|
|
statuses=statuses,
|
|
filter_tenant_ids=filter_tenant_ids,
|
|
filter_role_ids=filter_role_ids,
|
|
sort_by=sort_by,
|
|
sort_order=sort_order,
|
|
visible_ids=visible,
|
|
)
|