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

@@ -0,0 +1,308 @@
# P0: Fully Database-Driven RBAC - Audit & Architectural Proposal
**Datum:** 2026-06-25
**Status:** DRAFT - Waiting for Architect Approval
**Prioritás:** P0 (Critical)
---
## 1. Executive Summary
A jelenlegi Service Finder RBAC rendszer **két rétegből** áll, amelyek közül az egyik részben adatbázis-vezérelt, a másik viszont **teljesen hardcoded**:
| Réteg | Státusz | Részletek |
|-------|---------|-----------|
| **Org-level (fleet.org_roles)** | Részben DB-driven | `OrgRole` modell JSONB `permissions` oszloppal, 5 szerepkör seedelve |
| **System-level (SYSTEM_CAPABILITIES_MATRIX)** | Teljesen hardcoded | 200+ soros Python dict, szerepkörönként 25+ capability bool-lal |
| **Endpoint authorization** | Hardcoded | `RequireRole([UserRole.SUPERADMIN, UserRole.ADMIN])` - enum-függő |
| **Frontend permission UI** | Hardcoded | Statikus JavaScript array-ek superadmin/garage perms-ekkel |
**Következmény:** Minden új szerepkör vagy permission hozzáadása kódmódosítást igényel. Nincs admin UI a permission-ök kezelésére.
---
## 2. Audit Findings - 8 Hardcoded Problématerület
### F1: SYSTEM_CAPABILITIES_MATRIX - 200+ soros hardcoded dict
**Fájl:** `backend/app/core/capabilities.py`
A `SYSTEM_CAPABILITIES_MATRIX` egy hatalmas, teljesen hardcoded Python dict, amely minden UserRole-hoz hozzárendeli a capability-ket.
A `Capability` osztály 25+ string konstanst definiál. Helper függvények: `get_capabilities_for_role()`, `role_has_capability()`.
### F2: UserRole - Hardcoded Python Enum
**Fájl:** `backend/app/models/identity/identity.py:24`
6 érték: SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER. PG_ENUM-ként tárolva - új szerepkör ALTER TYPE-t igényel.
### F3: get_current_admin() - Hardcoded Role Set
**Fájl:** `backend/app/api/deps.py:160`
```python
allowed_roles = {UserRole.SUPERADMIN, UserRole.ADMIN, UserRole.MODERATOR}
```
30+ endpoint használja ezt a dependency-t.
### F4: RequireRole() - Enum-függő Dependency
**Fájl:** `backend/app/api/deps.py:213`
7+ endpoint fájl használja. Példák endpoint-szintű hardcoded check-ekre:
- `analytics.py:40`: `if current_user.role == "superadmin":`
- `finance_admin.py:27`: `if current_user.role not in [UserRole.SUPERADMIN, UserRole.ADMIN]:`
- `security.py:39`: `if current_user.role not in [UserRole.ADMIN, UserRole.SUPERADMIN]:`
- `system_parameters.py:125`: `if current_user.role not in (UserRole.SUPERADMIN, UserRole.ADMIN):`
### F5: RequireSystemCapability() - Matrix Dependency
**Fájl:** `backend/app/api/deps.py:278`
A `role_has_capability()` a `SYSTEM_CAPABILITIES_MATRIX` hardcoded dict-be indexel.
### F6: rbac_service.py - ROLE_ACTIONS Hardcoded Set-ek
**Fájl:** `backend/app/services/rbac_service.py:132`
Minden UserRole-hoz külön hardcoded Set[str] action-ök. A `check_admin_access()` explicit SUPERADMIN bypass-t tartalmaz.
### F7: 45 darab endpoint-szintű if role == check
Összesen 45 helyen található hardcoded role ellenőrzés a kódban (search_files eredmény).
### F8: Frontend Static Permission Array-ek
**Fájl:** `frontend_admin/pages/permissions/index.vue:272-286`
Statikus `superadminPerms` és `garagePerms` JavaScript array-ek. Ha a backend új permission-t vezet be, a frontend UI nem frissül.
---
## 3. Proposed Architecture
### 3.1 New Database Tables (system schema)
#### system.roles
| Mező | Típus | Megjegyzés |
|------|-------|-----------|
| id | int PK | |
| name | varchar(50) UK | pl. SUPERADMIN, ADMIN |
| description | text | Opcionális leírás |
| rank | int | Hierarchikus rang (100=SUPERADMIN, 0=USER) |
| is_system | bool | Rendszer által védett (nem törölhető) |
| is_active | bool | Aktív-e |
| created_at | datetime | |
| updated_at | datetime | |
#### system.permissions
| Mező | Típus | Megjegyzés |
|------|-------|-----------|
| id | int PK | |
| code | varchar(100) UK | Format: domain:action (pl. fleet:view) |
| domain | varchar(50) | fleet, user, finance, system, org, reports, audit, settings |
| action | varchar(50) | view, create, edit, delete, approve, manage, export |
| description | text | Opcionális leírás |
| is_system | bool | Rendszer által védett |
| created_at | datetime | |
#### system.role_permissions
| Mező | Típus | Megjegyzés |
|------|-------|-----------|
| id | int PK | |
| role_id | int FK -> system.roles.id | |
| permission_id | int FK -> system.permissions.id | |
| granted | bool | Explicit grant (true) vagy deny (false) |
| created_at | datetime | |
Unique constraint: (role_id, permission_id)
### 3.2 Seed Data - 28 Permission Codes
| Code | SUPERADMIN | ADMIN | MODERATOR | SALES_REP | SERVICE_MGR | USER |
|------|------------|-------|-----------|-----------|-------------|------|
| fleet:view | Y | Y | Y | Y | Y | Y |
| fleet:create | Y | Y | N | Y | N | N |
| fleet:edit | Y | Y | N | N | N | N |
| fleet:delete | Y | N | N | N | N | N |
| fleet:approve | Y | Y | N | N | N | N |
| user:view | Y | Y | Y | N | N | N |
| user:create | Y | Y | N | N | N | N |
| user:edit | Y | Y | N | N | N | N |
| user:delete | Y | N | N | N | N | N |
| user:manage-roles | Y | N | N | N | N | N |
| finance:view | Y | Y | Y | N | N | N |
| finance:edit | Y | Y | N | N | N | N |
| finance:approve | Y | N | N | N | N | N |
| finance:refund | Y | N | N | N | N | N |
| system:view | Y | Y | Y | N | N | N |
| system:edit | Y | Y | N | N | N | N |
| system:manage | Y | N | N | N | N | N |
| org:view | Y | Y | Y | Y | Y | N |
| org:edit | Y | Y | N | N | N | N |
| org:delete | Y | N | N | N | N | N |
| org:manage-members | Y | Y | N | N | N | N |
| reports:view | Y | Y | Y | Y | Y | N |
| reports:export | Y | Y | Y | N | N | N |
| reports:manage | Y | N | N | N | N | N |
| audit:view | Y | N | N | N | N | N |
| audit:export | Y | N | N | N | N | N |
| settings:view | Y | Y | N | N | N | N |
| settings:edit | Y | Y | N | N | N | N |
### 3.3 FastAPI Dependency: RequirePermission()
```python
from functools import lru_cache
from redis.asyncio import Redis
class RBACService:
"""Database-driven RBAC service with Redis caching."""
def __init__(self, redis: Redis):
self.redis = redis
self.cache_ttl = 300 # 5 minutes
async def get_role_permissions(self, db, role_id):
"""Get all permission codes for a role, with Redis caching."""
cache_key = f"rbac:role:{role_id}:perms"
# Try cache first
cached = await self.redis.smembers(cache_key)
if cached:
return set(cached)
# Query database
result = await db.execute(
select(SystemPermission.code)
.join(SystemRolePermission)
.where(
SystemRolePermission.role_id == role_id,
SystemRolePermission.granted == True,
SystemRole.is_active == True,
)
.join(SystemRole)
)
codes = {row[0] for row in result.fetchall()}
# Cache the result
if codes:
await self.redis.sadd(cache_key, *codes)
await self.redis.expire(cache_key, self.cache_ttl)
return codes
async def invalidate_cache(self, role_id):
await self.redis.delete(f"rbac:role:{role_id}:perms")
def RequirePermission(permission_code: str):
"""
Database-driven permission checker dependency.
Usage:
@router.get("/fleet")
async def list_fleet(
user = Depends(RequirePermission("fleet:view"))
):
...
"""
async def permission_checker(
current_user = Depends(get_current_active_user),
db = Depends(get_db),
rbac = Depends(get_rbac_service),
):
# SUPERADMIN bypass (rank threshold)
if current_user.role_id == SUPERADMIN_ROLE_ID:
return current_user
role_perms = await rbac.get_role_permissions(db, current_user.role_id)
if permission_code not in role_perms:
raise HTTPException(status_code=403,
detail=f"Missing permission: {permission_code}")
return current_user
return permission_checker
```
### 3.4 Refactoring Dependency Map
| Jelenlegi dependency | Új permission code | Érintett endpointok |
|---------------------|--------------------|--------------------|
| get_current_admin() | fleet:view (min) | 30+ admin endpoint |
| RequireRole([SUPERADMIN, ADMIN]) | user:view, finance:view | analytics, finance_admin |
| RequireSystemCapability(can_manage_users) | user:manage-roles | admin endpoints |
| check_admin_access(action) | fleet:edit, fleet:view | rbac_service használat |
| if role == superadmin | system:manage | security.py, system_parameters.py |
| Frontend superadminPerms | GET /admin/permissions (API) | permissions/index.vue |
### 3.5 fleet.org_roles Normalizálás
A jelenlegi JSONB permissions-t normalizálni kell, hogy ugyanazokat a permission code namespace-eket használja:
```
# Jelenleg (JSONB):
{"can_add_expense": true, "can_approve_expense": false}
# Új (normalizált):
{"fleet:view": true, "finance:view": true, "finance:approve": false}
```
---
## 4. Migration Plan - 5 Fázis
### Phase 1: Foundation (Backend DB Schema)
1. Create system.roles, system.permissions, system.role_permissions SQLAlchemy models
2. Run sync_engine.py to create tables
3. Create seed script: insert current 6 roles + 28 permissions + mappings
4. Add role_id FK column to identity.users (nullable initially, migrate, then NOT NULL)
### Phase 2: Backend Service Layer
1. Implement RBACService with Redis caching
2. Implement RequirePermission() FastAPI dependency
3. Implement admin API endpoints for CRUD on roles/permissions
4. Implement cache invalidation on role/permission changes
### Phase 3: Endpoint Migration
1. Replace get_current_admin() with RequirePermission(fleet:view) in 30+ endpoints
2. Replace RequireRole([...]) with RequirePermission(domain:action) in 7+ endpoint files
3. Replace RequireSystemCapability(...) with RequirePermission(domain:action)
4. Remove hardcoded if role == checks from 5+ endpoint files
5. Remove or deprecate rbac_service.py ROLE_ACTIONS
### Phase 4: Frontend Integration
1. Add GET /admin/permissions endpoint that returns available permissions from DB
2. Update frontend_admin to fetch permissions dynamically
3. Remove hardcoded superadminPerms and garagePerms arrays
### Phase 5: Cleanup
1. Remove SYSTEM_CAPABILITIES_MATRIX from capabilities.py
2. Remove ROLE_ACTIONS from rbac_service.py
3. Deprecate old RequireRole(), RequireSystemCapability(), get_current_admin()
4. Update tests
5. Update documentation
---
## 5. Risk Assessment
| Kockázat | Valószínűség | Hatás | Mitigáció |
|----------|-------------|-------|-----------|
| Permission cache stale | Low | Medium | 5 perces TTL + manual invalidation endpoint |
| Migration data loss | Low | Critical | Add role_id as nullable first, migrate, then NOT NULL |
| Frontend backward compat | Medium | Medium | Keep old endpoints during transition period |
| Redis dependency | Low | Low | Fallback to DB query if Redis unavailable |
| Performance overhead | Low | Low | Redis cache + lazy loading |
---
## 6. Recommendation
**APPROVE** with P0 (Critical) priority.
A javasolt rendszer:
- Teljesen adatbázis-vezérelt (nincs hardcoded permission logika)
- Redis cache-elés (5 perces TTL) a teljesítményért
- Explicit grant/deny mechanizmus (granted boolean)
- Admin UI-kompatibilis (CRUD endpointok a role-ok és permission-ök kezelésére)
- Normál fleet.org_roles JSONB permissions (ugyanaz a namespace)
- Fokozatos migráció (5 fázis, minden fázis külön deploy-olható)
**DO NOT IMPLEMENT YET.** Wait for explicit approval before starting Phase 1.

View File

@@ -0,0 +1,226 @@
# P0 DISCOVERY — RBAC Permission Matrix Alignment
**Date:** 2026-06-25
**Scope:** Full Gap Analysis between Code-required vs Database-stored permissions
**Methodology:** `RequirePermission()` decorator extraction from endpoints → PostgreSQL `system.permissions` table query → Diff Analysis
---
## 1. Codebase Scan — Permissions REQUIRED by the API
Extracted from all `backend/app/api/v1/endpoints/` files using `RequirePermission("...")` decorators.
| # | Permission Code | Endpoint File | Line(s) |
|---|----------------|---------------|---------|
| 1 | `org:view` | `admin_organizations.py` | 176, 324 |
| 2 | `org:view` | `admin.py` | 763 |
| 3 | `org:edit` | `admin_organizations.py` | 517 |
| 4 | `org:manage-members` | `admin_organizations.py` | 630, 743, 830 |
| 5 | `system:manage` | `system_parameters.py` | 118 |
| 6 | `dual-control:request` | `security.py` | 28 |
| 7 | `dual-control:approve` | `security.py` | 82, 111 |
| 8 | `dual-control:view` | `security.py` | 142 |
| 9 | `permissions:view` | `admin_permissions.py` | 137, 271, 317 |
| 10 | `permissions:edit` | `admin_permissions.py` | 177, 355 |
| 11 | `fleet:view` | `admin_permissions.py` | 456 |
| 12 | `finance:view` | `finance_admin.py` | 24 |
| 13 | `finance:edit` | `finance_admin.py` | 41 |
| 14 | `services:manage` | `admin_services.py` | 31, 58, 92 |
| 15 | `services:manage` | `admin.py` | 309, 370 |
| 16 | `subscription:manage` | `admin_packages.py` | 115, 188, 269, 393 |
| 17 | `settings:view` | `admin.py` | 37, 68, 92, 134, 163 |
| 18 | `settings:edit` | `admin.py` | 79, 101, 153 |
| 19 | `user:manage` | `admin.py` | 179, 664 |
| 20 | `moderation:manage` | `admin.py` | 240 |
| 21 | `gamification:manage` | `admin.py` | 418 |
| 22 | `user:view` | `admin.py` | 502 |
### Unique permission codes required by code: **20**
```
org:view, org:edit, org:manage-members,
system:manage,
dual-control:request, dual-control:approve, dual-control:view,
permissions:view, permissions:edit,
fleet:view,
finance:view, finance:edit,
services:manage,
subscription:manage,
settings:view, settings:edit,
user:manage, user:view,
moderation:manage,
gamification:manage
```
---
## 2. Database Scan — Permissions CURRENTLY in `system.permissions`
Queried from `system.permissions` table (PostgreSQL).
| ID | Code | Domain | Action | Description |
|----|------|--------|--------|-------------|
| 1 | `fleet:view` | fleet | view | View fleet/vehicle data |
| 2 | `fleet:create` | fleet | create | Create new fleet/vehicle entries |
| 3 | `fleet:edit` | fleet | edit | Edit existing fleet/vehicle data |
| 4 | `fleet:delete` | fleet | delete | Delete fleet/vehicle entries |
| 5 | `fleet:approve` | fleet | approve | Approve fleet/vehicle submissions |
| 6 | `user:view` | user | view | View user profiles and data |
| 7 | `user:create` | user | create | Create new user accounts |
| 8 | `user:edit` | user | edit | Edit user profiles and data |
| 9 | `user:delete` | user | delete | Delete user accounts |
| 10 | `user:manage-roles` | user | manage-roles | Manage user role assignments |
| 11 | `finance:view` | finance | view | View financial data and transactions |
| 12 | `finance:edit` | finance | edit | Edit financial records |
| 13 | `finance:approve` | finance | approve | Approve financial transactions |
| 14 | `finance:refund` | finance | refund | Process refunds |
| 15 | `system:view` | system | view | View system configuration |
| 16 | `system:edit` | system | edit | Edit system configuration |
| 17 | `system:manage` | system | manage | Full system management access |
| 18 | `org:view` | org | view | View organization data |
| 19 | `org:edit` | org | edit | Edit organization data |
| 20 | `org:delete` | org | delete | Delete organizations |
| 21 | `org:manage-members` | org | manage-members | Manage organization members |
| 22 | `reports:view` | reports | view | View reports |
| 23 | `reports:export` | reports | export | Export reports |
| 24 | `reports:manage` | reports | manage | Manage report configurations |
| 25 | `audit:view` | audit | view | View audit logs |
| 26 | `audit:export` | audit | export | Export audit logs |
| 27 | `settings:view` | settings | view | View system settings |
| 28 | `settings:edit` | settings | edit | Edit system settings |
| 29 | `permissions:view` | permissions | view | View permission settings and matrix |
| 30 | `permissions:edit` | permissions | edit | Edit permission settings and role-permission mappings |
### Unique permission codes in database: **30**
---
## 3. GAP ANALYSIS
### 3A. ORPHANED CODES — In Code, MISSING from Database
**These 8 permissions are the ROOT CAUSE of the 403 Forbidden errors for non-Superadmin users.**
The API checks for them via `RequirePermission()` in `deps.py:283`, but `system.permissions` has no such records → `rbac_service.get_role_permissions()` returns empty → `403 Forbidden`.
| Permission Code | Endpoint File(s) | Routes Affected |
|----------------|-----------------|-----------------|
| **`dual-control:request`** | `security.py:28` | POST `/security/dual-control/request` |
| **`dual-control:approve`** | `security.py:82,111` | POST `/security/dual-control/approve`, POST `/security/dual-control/reject` |
| **`dual-control:view`** | `security.py:142` | GET `/security/dual-control/pending` |
| **`services:manage`** | `admin_services.py:31,58,92`; `admin.py:309,370` | CRUD `/admin/services/*` |
| **`subscription:manage`** | `admin_packages.py:115,188,269,393` | CRUD `/admin/packages/*` |
| **`user:manage`** | `admin.py:179,664` | POST `/admin/users`, PUT `/admin/users/{id}` |
| **`moderation:manage`** | `admin.py:240` | GET `/admin/moderation` |
| **`gamification:manage`** | `admin.py:418` | GET `/admin/gamification` |
---
### 3B. UNUSED CODES — In Database, NEVER referenced in any Endpoint
**These 18 permissions exist in the database but no `RequirePermission("...")` references them anywhere.**
| Permission Code | Domain | Notes |
|----------------|--------|-------|
| `fleet:create` | fleet | No endpoint checks this |
| `fleet:edit` | fleet | No endpoint checks this |
| `fleet:delete` | fleet | No endpoint checks this |
| `fleet:approve` | fleet | No endpoint checks this |
| `user:create` | user | No endpoint checks this |
| `user:edit` | user | No endpoint checks this |
| `user:delete` | user | No endpoint checks this |
| `user:manage-roles` | user | No endpoint checks this |
| `finance:approve` | finance | No endpoint checks this |
| `finance:refund` | finance | No endpoint checks this |
| `system:view` | system | No endpoint checks this |
| `system:edit` | system | No endpoint checks this |
| `org:delete` | org | No endpoint checks this |
| `reports:view` | reports | No endpoint checks this |
| `reports:export` | reports | No endpoint checks this |
| `reports:manage` | reports | No endpoint checks this |
| `audit:view` | audit | No endpoint checks this |
| `audit:export` | audit | No endpoint checks this |
---
### 3C. Intersection — Permissions that EXIST in BOTH (Correct)
These 12 permissions are properly aligned — they exist in the code and the database.
| Permission Code | In Code | In DB |
|----------------|---------|-------|
| `org:view` | 3 endpoints | ID 18 |
| `org:edit` | 1 endpoint | ID 19 |
| `org:manage-members` | 3 endpoints | ID 21 |
| `system:manage` | 1 endpoint | ID 17 |
| `permissions:view` | 3 endpoints | ID 29 |
| `permissions:edit` | 2 endpoints | ID 30 |
| `fleet:view` | 1 endpoint | ID 1 |
| `finance:view` | 1 endpoint | ID 11 |
| `finance:edit` | 1 endpoint | ID 12 |
| `settings:view` | 4 endpoints | ID 27 |
| `settings:edit` | 3 endpoints | ID 28 |
| `user:view` | 1 endpoint | ID 6 |
---
## 4. ROOT CAUSE SUMMARY
### The 403 Forbidden Problem
The `ADMIN` role receives 403 errors because **8 critical permission codes are missing from the database**:
| Domain | Missing Codes |
|--------|--------------|
| **Dual Control** | `dual-control:request`, `dual-control:approve`, `dual-control:view` |
| **Services** | `services:manage` |
| **Subscriptions** | `subscription:manage` |
| **User Management** | `user:manage` |
| **Moderation** | `moderation:manage` |
| **Gamification** | `gamification:manage` |
When a non-Superadmin user calls any endpoint protected by these permissions, `RequirePermission()` in `deps.py:333` calls `rbac_service.get_role_permissions()` in `rbac_service.py:263` which queries `system.role_permissions` joined with `system.permissions`. Since the code doesn't exist in `system.permissions`, no `system.role_permissions` record can reference it → the permission check fails → `403 Forbidden`.
### How the code resolves this (from `deps.py:311-336`):
```python
# SUPERADMIN bypass — rank 100 bypasses all checks
if current_user.role == UserRole.SUPERADMIN:
return current_user
# Non-Superadmin: query DB
user_permissions = await rbac_service.get_role_permissions(db, role_id)
if permission_code not in user_permissions:
raise HTTPException(403, f"Missing permission: {permission_code}")
```
This is why `SUPERADMIN` works but `ADMIN` (or any other role) gets 403 on all 8 phantom permissions.
---
## 5. CRITICAL FINDINGS
| Metric | Value |
|--------|-------|
| Total permissions required by code | **20** |
| Total permissions in database | **30** |
| Properly aligned (in both) | **12 (60% of code requirements are satisfied)** |
| **ORPHANED (code-only, breaking 403s)** | **8 (40% of code requirements)** |
| **UNUSED (DB-only, no endpoint uses)** | **18 (60% of database entries)** |
**The database is 60% polluted with unused permissions, while simultaneously missing 40% of the permissions the code actually needs.**
---
## 6. RECOMMENDED NEXT STEPS (Not Implemented — report only)
1. **INSERT** the 8 orphaned permission codes into `system.permissions`:
- `dual-control:request`, `dual-control:approve`, `dual-control:view`
- `services:manage`, `subscription:manage`
- `user:manage`, `moderation:manage`, `gamification:manage`
2. **GRANT** these permissions to the `admin` role in `system.role_permissions`.
3. **(Optional) Cleanup:** Review the 18 unused codes — keep those planned for future use, archive the rest.
---
*Report generated by Service Finder Rendszer-Architect. No code or database changes were made — pure discovery audit.*