<?php declare(strict_types=1); if (!defined('DASHBOARD_ROOT')) { http_response_code(403); exit('Direct access denied.'); } class RBAC { private const ROLE_LEVELS = [ 'viewer' => 1, 'operator' => 2, 'admin' => 3, 'owner' => 4, 'superadmin' => 99, ]; private const CODE_TO_ROLE = [ /* magic strings removed for security */ 1 => 'viewer', 2 => 'operator', 3 => 'admin', 4 => 'owner', 'superadmin' => 'superadmin', 99 => 'superadmin', ]; public static function resolveRole($role): string { if (is_string($role) && isset(self::CODE_TO_ROLE[$role])) { return self::CODE_TO_ROLE[$role]; } if (is_numeric($role) && isset(self::CODE_TO_ROLE[(int)$role])) { return self::CODE_TO_ROLE[(int)$role]; } if (is_string($role) && isset(self::ROLE_LEVELS[$role])) { return $role; } return 'viewer'; } private const ACTION_PERMISSIONS = [ 'view.overview' => 'viewer', 'view.orders' => 'operator', 'view.listings' => 'operator', 'view.fulfillment' => 'operator', 'view.support' => 'operator', 'view.crm' => 'operator', 'view.marketing' => 'operator', 'view.content' => 'operator', 'view.fleet' => 'admin', 'view.security' => 'admin', 'view.settings' => 'owner', 'create.listing' => 'operator', 'modify.listing' => 'operator', 'delete.listing' => 'admin', 'create.order' => 'operator', 'modify.order' => 'operator', 'modify.fulfillment' => 'operator', 'verify.payment' => 'operator', 'create.support' => 'operator', 'modify.support' => 'operator', 'create.content' => 'operator', 'modify.content' => 'operator', 'delete.content' => 'admin', 'decrypt.pii' => 'admin', 'reset.password' => 'admin', 'export.data' => 'admin', 'manage.accounts' => 'owner', 'manage.roles' => 'owner', 'manage.settings' => 'owner', 'fleet.command' => 'admin', 'fleet.deploy' => 'admin', 'fleet.provision' => 'owner', 'fleet.lockdown' => 'owner', 'fleet.panic' => 'owner', 'security.honeypot' => 'admin', 'security.devices' => 'admin', 'modify.security' => 'admin', 'view.payments' => 'operator', 'modify.marketing' => 'operator', 'view.users' => 'admin', 'view.reviews' => 'operator', 'manage.marketing' => 'admin', ]; public static function hasMinRole($userRole, string $requiredRole): bool { $resolved = self::resolveRole($userRole); $userLevel = self::ROLE_LEVELS[$resolved] ?? 0; $requiredLevel = self::ROLE_LEVELS[$requiredRole] ?? 999; return $userLevel >= $requiredLevel; } public static function can(string $action): bool { $raw = $_SESSION['admin_role'] ?? 9013; $userRole = self::resolveRole($raw); $required = self::ACTION_PERMISSIONS[$action] ?? 'owner'; return self::hasMinRole($userRole, $required); } public static function enforce(string $action): void { if (!self::can($action)) { Events::record(Events::CAT_SECURITY, 'rbac.denied', 'action', null, [ 'action' => $action, 'admin_id' => $_SESSION['admin_id'] ?? null, 'admin_role' => $_SESSION['admin_role'] ?? 'none', ]); http_response_code(403); exit(json_encode(['success' => false, 'error' => 'Permission denied.'])); } } public static function currentRole(): string { return self::resolveRole($_SESSION['admin_role'] ?? 9013); } public static function getRoles(): array { return array_keys(self::ROLE_LEVELS); } } 