jogosultsági szintek RBAC beállítva tesztelve

This commit is contained in:
Roo
2026-06-25 20:41:49 +00:00
parent 52011606ff
commit 36109fc722
242 changed files with 8672 additions and 2237 deletions

View File

@@ -41,16 +41,17 @@ class NetworkResponse(BaseModel):
level3: List[NetworkMemberL2L3]
def _build_user_response(user: User, active_org_id: Optional[int] = None, db: Optional[AsyncSession] = None) -> dict:
async def _build_user_response(user: User, active_org_id: Optional[int] = None, db: Optional[AsyncSession] = None) -> dict:
"""
Segédfüggvény a UserResponse dict előállításához.
Beágyazza a Person és Address adatokat a 'person' mezőbe.
RBAC Phase 3: system_capabilities és org_capabilities is bekerül a válaszba.
P0: max_vehicles és max_garages a subscription_tier JSONB rules-ból.
P0 Phase 6: system_capabilities now populated via DB-driven RBAC lookup.
"""
from app.models.marketplace.organization import Organization, OrganizationMember
from app.models.marketplace.organization import OrgUserRole
from app.core.capabilities import get_capabilities_for_role
from app.services.rbac_service import rbac_service
# Determine active organization ID
if active_org_id is None:
@@ -102,9 +103,22 @@ def _build_user_response(user: User, active_org_id: Optional[int] = None, db: Op
first_name = person.first_name if person else ""
last_name = person.last_name if person else ""
# ── RBAC Phase 3: Resolve system capabilities ──
# ── RBAC Phase 3/6: Resolve system capabilities via DB-driven RBAC ──
role_key = user.role.value if hasattr(user.role, 'value') else str(user.role)
system_capabilities = get_capabilities_for_role(role_key)
system_capabilities: Dict[str, bool] = {}
if db is not None:
# Use the role_id from the user's SystemRole mapping
role_id = getattr(user, 'role_id', None)
if role_id is not None:
try:
# Fetch all granted permission codes for this role from the DB
perm_set = await rbac_service.get_role_permissions(db, role_id)
# Convert the set of codes into a dict mapping code -> True
system_capabilities = {code: True for code in perm_set}
except Exception as e:
logger.warning(f"Failed to resolve system_capabilities for user {user.id}: {e}")
system_capabilities = {}
# ── RBAC Phase 3: Resolve org capabilities ──
org_capabilities: Dict[str, Dict[str, bool]] = {}
@@ -135,7 +149,7 @@ def _build_user_response(user: User, active_org_id: Optional[int] = None, db: Op
"preferred_language": user.preferred_language or "hu",
"person": person_data,
"is_last_admin": False, # Default, will be overridden in read_users_me
# RBAC Phase 3: Rendszerszintű képességek
# RBAC Phase 3/6: Rendszerszintű képességek (DB-driven)
"system_capabilities": system_capabilities,
# RBAC Phase 3: Szervezeti képességek (feltöltve a read_users_me végpontban)
"org_capabilities": {},
@@ -150,8 +164,7 @@ async def read_users_me(
"""Visszaadja a bejelentkezett felhasználó profilját beágyazott Person és Address adatokkal.
RBAC Phase 3: system_capabilities és org_capabilities is bekerül a válaszba."""
from app.models.marketplace.organization import Organization, OrganizationMember
from app.models.marketplace.organization import OrgUserRole
from app.core.capabilities import get_capabilities_for_role
from app.models.marketplace.organization import OrgUserRole, OrgRole
# Determine active organization ID
active_org_id = None
@@ -252,7 +265,8 @@ async def read_users_me(
max_garages = int(allowances.get("max_garages", 1))
# ── RBAC Phase 3: Build base response ──
response_data = _build_user_response(current_user, active_org_id)
# P0 Phase 6: Pass db so system_capabilities gets populated from DB
response_data = await _build_user_response(current_user, active_org_id, db)
response_data["is_last_admin"] = is_last_admin
response_data["max_vehicles"] = max_vehicles
response_data["max_garages"] = max_garages
@@ -383,7 +397,7 @@ async def update_my_person(
raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
# Build and return the full response
response_data = _build_user_response(user)
response_data = await _build_user_response(user)
return UserResponse.model_validate(response_data)
@@ -487,7 +501,7 @@ async def update_user_preferences(
raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
# Return the Pydantic model instead of raw SQLAlchemy object
response_data = _build_user_response(current_user)
response_data = await _build_user_response(current_user)
return UserResponse.model_validate(response_data)
@@ -549,7 +563,7 @@ async def update_active_organization(
access_token, _ = create_tokens(data=token_payload)
# Return user data with new token
response_data = _build_user_response(current_user)
response_data = await _build_user_response(current_user)
return UserWithTokenResponse(
user=UserResponse.model_validate(response_data),
access_token=access_token,