feat(rbac): add roles and permissions
This commit is contained in:
parent
86a32a942c
commit
694b7bd09a
37 changed files with 2682 additions and 218 deletions
|
|
@ -1,12 +1,12 @@
|
|||
import logging
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.security import verify_access_token
|
||||
from app.core.rbac import get_user_permission_names, require_permission
|
||||
from app.db.database import get_db
|
||||
from app.models.user import User
|
||||
from app.repositories.rbac_repository import RbacRepository
|
||||
from app.repositories.user_repository import UserRepository
|
||||
from app.schemas.user import UserCreate, UserPasswordUpdate, UserResponse, UserUpdate
|
||||
|
||||
|
|
@ -17,45 +17,6 @@ router = APIRouter(
|
|||
tags=["Users"],
|
||||
)
|
||||
|
||||
bearer_scheme = HTTPBearer(auto_error=False)
|
||||
|
||||
|
||||
def get_current_user(
|
||||
credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme),
|
||||
db: Session = Depends(get_db),
|
||||
) -> User:
|
||||
if credentials is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Nicht authentifiziert",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
username = verify_access_token(credentials.credentials)
|
||||
user = UserRepository.get_by_username(db, username)
|
||||
|
||||
if user is None or not user.is_active:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Nicht authentifiziert",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
return user
|
||||
|
||||
|
||||
def require_role(*allowed_roles: str):
|
||||
def dependency(current_user: User = Depends(get_current_user)) -> User:
|
||||
if current_user.role not in allowed_roles:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Keine Berechtigung",
|
||||
)
|
||||
return current_user
|
||||
|
||||
return dependency
|
||||
|
||||
|
||||
def get_user_or_404(db: Session, user_id: int) -> User:
|
||||
db_user = UserRepository.get_by_id(db, user_id)
|
||||
|
||||
|
|
@ -80,11 +41,36 @@ def raise_conflict(field: str) -> None:
|
|||
)
|
||||
|
||||
|
||||
def require_actor_permission(current_user: User, permission: str) -> None:
|
||||
if permission not in get_user_permission_names(current_user):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Keine Berechtigung",
|
||||
)
|
||||
|
||||
|
||||
def apply_role(db: Session, user: UserCreate | UserUpdate) -> None:
|
||||
role = None
|
||||
if user.role_id is not None:
|
||||
role = RbacRepository.get_role_by_id(db, user.role_id)
|
||||
else:
|
||||
role = RbacRepository.get_role_by_name(db, user.role)
|
||||
|
||||
if role is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Rolle nicht gefunden",
|
||||
)
|
||||
|
||||
user.role_id = role.id
|
||||
user.role = role.name
|
||||
|
||||
|
||||
@router.get("", response_model=list[UserResponse])
|
||||
@router.get("/", response_model=list[UserResponse], include_in_schema=False)
|
||||
def get_users(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(require_permission("users.read")),
|
||||
):
|
||||
logger.info("users.list", extra={"actor_user_id": current_user.id})
|
||||
return UserRepository.get_all(db)
|
||||
|
|
@ -94,7 +80,7 @@ def get_users(
|
|||
def get_user(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(require_permission("users.read")),
|
||||
):
|
||||
logger.info(
|
||||
"users.detail",
|
||||
|
|
@ -113,8 +99,10 @@ def get_user(
|
|||
def create_user(
|
||||
user: UserCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(require_permission("users.create")),
|
||||
):
|
||||
require_actor_permission(current_user, "roles.assign")
|
||||
apply_role(db, user)
|
||||
conflict = UserRepository.find_conflict(
|
||||
db,
|
||||
username=user.username,
|
||||
|
|
@ -133,9 +121,12 @@ def update_user(
|
|||
user_id: int,
|
||||
user: UserUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(require_permission("users.update")),
|
||||
):
|
||||
db_user = get_user_or_404(db, user_id)
|
||||
apply_role(db, user)
|
||||
if user.role_id != db_user.role_id:
|
||||
require_actor_permission(current_user, "roles.assign")
|
||||
conflict = UserRepository.find_conflict(
|
||||
db,
|
||||
username=user.username,
|
||||
|
|
@ -158,7 +149,7 @@ def update_user_password(
|
|||
user_id: int,
|
||||
password_update: UserPasswordUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(require_permission("users.password.update")),
|
||||
):
|
||||
db_user = get_user_or_404(db, user_id)
|
||||
|
||||
|
|
@ -173,7 +164,7 @@ def update_user_password(
|
|||
def delete_user(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(require_permission("users.delete")),
|
||||
):
|
||||
if current_user.id == user_id:
|
||||
raise HTTPException(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue