jogosultsági szintek RBAC beállítva tesztelve
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user