jogosultsági szintek RBAC beállítva tesztelve
This commit is contained in:
@@ -13,7 +13,6 @@ from app.models.identity import User, UserRole # JAVÍTVA: Új Identity modell
|
||||
from app.models.marketplace.organization import OrgRole, OrganizationMember
|
||||
from app.core.config import settings
|
||||
from app.core.translation_helper import t # Translation helper
|
||||
from app.core.capabilities import SYSTEM_CAPABILITIES_MATRIX, role_has_capability
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -157,25 +156,6 @@ def check_min_rank(role_key: str):
|
||||
return True
|
||||
return rank_checker
|
||||
|
||||
async def get_current_admin(
|
||||
current_user: User = Depends(get_current_user)
|
||||
) -> User:
|
||||
"""
|
||||
Csak admin/moderátor/superadmin szerepkörrel rendelkező felhasználók számára.
|
||||
"""
|
||||
# A UserRole Enum értékeit használjuk (RBAC Phase 1)
|
||||
allowed_roles = {
|
||||
UserRole.SUPERADMIN,
|
||||
UserRole.ADMIN,
|
||||
UserRole.MODERATOR,
|
||||
}
|
||||
if current_user.role not in allowed_roles:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=t("AUTH.INSUFFICIENT_ADMIN_PERMISSIONS")
|
||||
)
|
||||
return current_user
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# RBAC Phase 1.5: Staff & Flexible Role Dependencies
|
||||
@@ -210,108 +190,10 @@ async def get_current_staff(
|
||||
return current_user
|
||||
|
||||
|
||||
def RequireRole(allowed_roles: List[UserRole], target_org_id: Optional[int] = None):
|
||||
"""
|
||||
🎯 Flexible role-checking dependency factory with scope-based access.
|
||||
|
||||
Accepts a list of UserRole values. If the current user's role is
|
||||
in the list, access is granted. Otherwise, 403 Forbidden.
|
||||
|
||||
When target_org_id is provided, the RBACService is automatically
|
||||
invoked to check if the staff member has scope-based access to
|
||||
that specific organization.
|
||||
|
||||
Usage:
|
||||
# Simple role check
|
||||
@router.get("/admin/sales")
|
||||
async def sales_dashboard(
|
||||
current_user: User = Depends(get_current_user),
|
||||
_ = Depends(RequireRole([UserRole.SUPERADMIN, UserRole.SALES_REP]))
|
||||
):
|
||||
|
||||
# Scope-based access check
|
||||
@router.get("/admin/organizations/{org_id}")
|
||||
async def get_organization(
|
||||
org_id: int,
|
||||
current_user: User = Depends(get_current_user),
|
||||
_ = Depends(RequireRole(
|
||||
[UserRole.SUPERADMIN, UserRole.ADMIN, UserRole.SALES_REP],
|
||||
target_org_id=org_id # ← triggers scope check
|
||||
))
|
||||
):
|
||||
"""
|
||||
async def role_checker(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> bool:
|
||||
# 1. Check if the user's role is in the allowed list
|
||||
if current_user.role not in allowed_roles:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="AUTH.INSUFFICIENT_PERMISSIONS"
|
||||
)
|
||||
|
||||
# 2. If target_org_id is provided, perform scope-based access check
|
||||
if target_org_id is not None:
|
||||
from app.services.rbac_service import rbac_service
|
||||
try:
|
||||
await rbac_service.check_admin_access(
|
||||
db=db,
|
||||
user=current_user,
|
||||
action="VIEW_DATA", # Generic read access for the dependency
|
||||
target_org_id=target_org_id,
|
||||
)
|
||||
except PermissionError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=str(e)
|
||||
)
|
||||
|
||||
return True
|
||||
return role_checker
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# RBAC Phase 2: Capability Dependencies (Kapuőrök)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def RequireSystemCapability(capability_name: str):
|
||||
"""
|
||||
🎯 Rendszerszintű képesség-ellenőrző dependency.
|
||||
|
||||
Használat:
|
||||
@router.get("/admin/users")
|
||||
async def list_users(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
_ = Depends(RequireSystemCapability("can_manage_users"))
|
||||
):
|
||||
|
||||
Logika:
|
||||
1. Ha a user SUPERADMIN → azonnal átengedi (True).
|
||||
2. Egyébként lekéri a user role-ját, és ellenőrzi a
|
||||
SYSTEM_CAPABILITIES_MATRIX-ból, hogy a kért capability True-e.
|
||||
3. Ha nincs meg a capability → 403 Forbidden.
|
||||
"""
|
||||
async def system_capability_checker(
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> bool:
|
||||
# SUPERADMIN mindent visz
|
||||
if current_user.role == UserRole.SUPERADMIN:
|
||||
return True
|
||||
|
||||
# Ellenőrizzük a SYSTEM_CAPABILITIES_MATRIX-ból
|
||||
role_key = current_user.role.value # pl. "ADMIN", "MODERATOR", "USER"
|
||||
if not role_has_capability(role_key, capability_name):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"SYSTEM_CAPABILITY_DENIED: A '{capability_name}' képesség nem elérhető a '{role_key}' szerepkör számára."
|
||||
)
|
||||
return True
|
||||
|
||||
return system_capability_checker
|
||||
|
||||
|
||||
def RequireOrgCapability(capability_name: str):
|
||||
"""
|
||||
🎯 Szervezeti képesség-ellenőrző dependency.
|
||||
@@ -391,4 +273,69 @@ def RequireOrgCapability(capability_name: str):
|
||||
|
||||
return True
|
||||
|
||||
return org_capability_checker
|
||||
return org_capability_checker
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# RBAC Phase 2: DB-Driven Permission Code Dependency (RequirePermission)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def RequirePermission(permission_code: str):
|
||||
"""
|
||||
🎯 DB-driven permission code dependency factory.
|
||||
|
||||
Queries the system.role_permissions table (via RBACService.get_role_permissions)
|
||||
to check if the current user's role has the specified permission code granted.
|
||||
|
||||
SUPERADMIN bypass: If the user's role rank is 100 (SUPERADMIN), access is
|
||||
granted immediately without a database lookup.
|
||||
|
||||
Usage:
|
||||
@router.get("/admin/sensitive-data")
|
||||
async def get_sensitive_data(
|
||||
current_user: User = Depends(get_current_user),
|
||||
_ = Depends(RequirePermission("fleet:view")),
|
||||
):
|
||||
|
||||
Args:
|
||||
permission_code: The permission code to check (e.g., "fleet:view", "user:create")
|
||||
|
||||
Returns:
|
||||
A dependency callable that returns the current_user if permission is granted,
|
||||
or raises HTTPException(403) if denied.
|
||||
"""
|
||||
async def permission_checker(
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> User:
|
||||
# ── SUPERADMIN bypass ──
|
||||
# SUPERADMIN has rank 100 → bypass all permission checks
|
||||
if current_user.role == UserRole.SUPERADMIN:
|
||||
return current_user
|
||||
|
||||
# ── Resolve role_id ──
|
||||
# Use the DB-driven role_id if available, otherwise fall back to
|
||||
# the legacy UserRole enum for backward compatibility
|
||||
role_id = getattr(current_user, 'role_id', None)
|
||||
if role_id is None:
|
||||
# Fallback: SUPERADMIN already handled above, so this is a non-admin
|
||||
# user without a role_id assigned yet. Deny access.
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"Missing permission: {permission_code}. No role_id assigned to user."
|
||||
)
|
||||
|
||||
# ── Query permissions from DB ──
|
||||
from app.services.rbac_service import rbac_service
|
||||
user_permissions = await rbac_service.get_role_permissions(db, role_id)
|
||||
|
||||
# ── Check the specific permission ──
|
||||
if permission_code not in user_permissions:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"Missing permission: {permission_code}"
|
||||
)
|
||||
|
||||
return current_user
|
||||
|
||||
return permission_checker
|
||||
|
||||
Reference in New Issue
Block a user