10 KiB
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):
# 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)
-
INSERT the 8 orphaned permission codes into
system.permissions:dual-control:request,dual-control:approve,dual-control:viewservices:manage,subscription:manageuser:manage,moderation:manage,gamification:manage
-
GRANT these permissions to the
adminrole insystem.role_permissions. -
(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.