195 lines
6.0 KiB
Python
195 lines
6.0 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.db.deps import get_common_db
|
|
from app.core.security.session_auth import require_login
|
|
from app.modules.core.audit.service import write_audit_log
|
|
from app.modules.core.iam.models import User
|
|
from app.modules.core.iam.scope import build_scope, scope_to_http, assert_can_manage_role_object
|
|
from app.modules.core.rbac.models import Permission, Role, RolePermission
|
|
|
|
router = APIRouter(prefix="/rbac", tags=["rbac"])
|
|
|
|
|
|
class RoleCreateRequest(BaseModel):
|
|
name: str
|
|
is_active: bool = True
|
|
|
|
|
|
class PermissionCreateRequest(BaseModel):
|
|
code: str
|
|
name: str
|
|
is_active: bool = True
|
|
|
|
|
|
class RolePermissionUpdateRequest(BaseModel):
|
|
permission_ids: list[int] = []
|
|
|
|
|
|
def _require_system_admin_scope(db: Session, current_user: User):
|
|
scope = build_scope(db, current_user)
|
|
if not scope.is_system_admin:
|
|
raise HTTPException(status_code=403, detail="Only System Admin can access RBAC.")
|
|
return scope
|
|
|
|
|
|
@router.get("/roles")
|
|
def list_roles(
|
|
current_user: User = Depends(require_login),
|
|
db: Session = Depends(get_common_db),
|
|
):
|
|
_require_system_admin_scope(db, current_user)
|
|
roles = db.execute(select(Role).order_by(Role.name)).scalars().all()
|
|
return [{"id": r.id, "name": r.name, "is_active": r.is_active} for r in roles]
|
|
|
|
|
|
@router.post("/roles")
|
|
def create_role(
|
|
payload: RoleCreateRequest,
|
|
current_user: User = Depends(require_login),
|
|
db: Session = Depends(get_common_db),
|
|
):
|
|
_require_system_admin_scope(db, current_user)
|
|
|
|
name = payload.name.strip()
|
|
if not name:
|
|
raise HTTPException(status_code=400, detail="Role name is required")
|
|
|
|
exists = db.execute(select(Role).where(Role.name == name)).scalar_one_or_none()
|
|
if exists:
|
|
raise HTTPException(status_code=400, detail="Role already exists")
|
|
|
|
role = Role(name=name, is_active=payload.is_active)
|
|
db.add(role)
|
|
db.commit()
|
|
db.refresh(role)
|
|
|
|
write_audit_log(
|
|
db,
|
|
action="role.create.api",
|
|
entity_type="role",
|
|
actor=current_user,
|
|
entity_id=role.id,
|
|
entity_name=role.name,
|
|
target_tenant_id=current_user.tenant_id,
|
|
target_branch_id=current_user.branch_id,
|
|
details={"name": role.name, "is_active": role.is_active},
|
|
)
|
|
return {"status": "ok", "id": role.id}
|
|
|
|
|
|
@router.get("/permissions")
|
|
def list_permissions(
|
|
current_user: User = Depends(require_login),
|
|
db: Session = Depends(get_common_db),
|
|
):
|
|
_require_system_admin_scope(db, current_user)
|
|
permissions = db.execute(select(Permission).order_by(Permission.code)).scalars().all()
|
|
return [{"id": p.id, "code": p.code, "name": p.name, "is_active": p.is_active} for p in permissions]
|
|
|
|
|
|
@router.post("/permissions")
|
|
def create_permission(
|
|
payload: PermissionCreateRequest,
|
|
current_user: User = Depends(require_login),
|
|
db: Session = Depends(get_common_db),
|
|
):
|
|
_require_system_admin_scope(db, current_user)
|
|
|
|
code = payload.code.strip()
|
|
if not code:
|
|
raise HTTPException(status_code=400, detail="Permission code is required")
|
|
|
|
exists = db.execute(select(Permission).where(Permission.code == code)).scalar_one_or_none()
|
|
if exists:
|
|
raise HTTPException(status_code=400, detail="Permission already exists")
|
|
|
|
permission = Permission(code=code, name=payload.name.strip(), is_active=payload.is_active)
|
|
db.add(permission)
|
|
db.commit()
|
|
db.refresh(permission)
|
|
|
|
write_audit_log(
|
|
db,
|
|
action="permission.create.api",
|
|
entity_type="permission",
|
|
actor=current_user,
|
|
entity_id=permission.id,
|
|
entity_name=permission.code,
|
|
target_tenant_id=current_user.tenant_id,
|
|
target_branch_id=current_user.branch_id,
|
|
details={"code": permission.code, "name": permission.name, "is_active": permission.is_active},
|
|
)
|
|
return {"status": "ok", "id": permission.id}
|
|
|
|
|
|
@router.get("/roles/{role_id}")
|
|
def role_detail(
|
|
role_id: int,
|
|
current_user: User = Depends(require_login),
|
|
db: Session = Depends(get_common_db),
|
|
):
|
|
scope = _require_system_admin_scope(db, current_user)
|
|
|
|
role = db.execute(select(Role).where(Role.id == role_id)).scalar_one_or_none()
|
|
if not role:
|
|
raise HTTPException(status_code=404, detail="Role not found")
|
|
|
|
try:
|
|
assert_can_manage_role_object(scope, role)
|
|
except Exception as exc:
|
|
raise scope_to_http(exc)
|
|
|
|
permission_ids = db.execute(
|
|
select(RolePermission.permission_id).where(RolePermission.role_id == role.id)
|
|
).scalars().all()
|
|
|
|
return {
|
|
"id": role.id,
|
|
"name": role.name,
|
|
"is_active": role.is_active,
|
|
"permission_ids": list(permission_ids),
|
|
}
|
|
|
|
|
|
@router.put("/roles/{role_id}/permissions")
|
|
def update_role_permissions(
|
|
role_id: int,
|
|
payload: RolePermissionUpdateRequest,
|
|
current_user: User = Depends(require_login),
|
|
db: Session = Depends(get_common_db),
|
|
):
|
|
_require_system_admin_scope(db, current_user)
|
|
|
|
role = db.execute(select(Role).where(Role.id == role_id)).scalar_one_or_none()
|
|
if not role:
|
|
raise HTTPException(status_code=404, detail="Role not found")
|
|
|
|
old_permission_ids = db.execute(
|
|
select(RolePermission.permission_id).where(RolePermission.role_id == role.id)
|
|
).scalars().all()
|
|
|
|
db.execute(RolePermission.__table__.delete().where(RolePermission.role_id == role.id))
|
|
for permission_id in payload.permission_ids:
|
|
db.add(RolePermission(role_id=role.id, permission_id=permission_id))
|
|
db.commit()
|
|
|
|
write_audit_log(
|
|
db,
|
|
action="role.permissions.update.api",
|
|
entity_type="role",
|
|
actor=current_user,
|
|
entity_id=role.id,
|
|
entity_name=role.name,
|
|
target_tenant_id=current_user.tenant_id,
|
|
target_branch_id=current_user.branch_id,
|
|
details={
|
|
"old_permission_ids": list(old_permission_ids),
|
|
"new_permission_ids": list(payload.permission_ids),
|
|
},
|
|
)
|
|
return {"status": "ok"} |