Add Redis-based permission caching for users and subusers

- Introduced Redis-backed caching for user and subuser permission evaluations in the `route_t` trait, reducing database queries.
- Enhanced `Redis` class with methods for permission caching: `cache_permission`, `get_permission`, and `clear_permission`.
- Added test coverage for the new caching logic in `PermissionRedisCacheTest.php`.
- Implemented Redis caching for authentication sessions with `cache_auth_session`, `get_auth_session`, and `clear_auth_session`.
- Improved CORS handling for preflight requests in `index.php`.
This commit is contained in:
Jeppe Bundgaard
2026-02-24 15:00:22 +01:00
parent 2534ffb1b9
commit 5cc311ae32
7 changed files with 367 additions and 12 deletions
+35 -2
View File
@@ -346,7 +346,25 @@ trait route_t
}
// Expose resolved target customer in response meta for subuser requests
$response->add_meta('target_customer_number', (int)$resolvedCustomer);
$subuser_has_permission = $subuser->hasPermission($permission->subusers_node_key);
$cacheKey = "subuser:{$subuser->id}:{$resolvedCustomer}:{$permission->subusers_node_key->name}";
if (!isset(self::$__perm_subuser_grant_cache[$cacheKey])) {
$cached = null;
if (defined('redis')) {
$cached = redis->get_permission($cacheKey);
}
if ($cached !== null) {
self::$__perm_subuser_grant_cache[$cacheKey] = $cached;
} else {
$subuser_has_permission = $subuser->hasPermission($permission->subusers_node_key);
if (defined('redis')) {
redis->cache_permission($cacheKey, $subuser_has_permission);
}
self::$__perm_subuser_grant_cache[$cacheKey] = $subuser_has_permission;
}
}
$subuser_has_permission = self::$__perm_subuser_grant_cache[$cacheKey];
if (!$subuser_has_permission && $throwOnDeny) {
(new logs_o())->add('global', 'global', 1, $subuser->id ?? 0, 'PERMISSION_DENIED', 'Permission denied via subuser node: ' . $permission->permission . ' (Node: ' . $permission->subusers_node_key->name . ', Customer: ' . $resolvedCustomer . ')');
$response->error('Permission denied for subuser. Missing permission: ' . $permission->permission . ' (Customer context: ' . $resolvedCustomer . ')', 403);
@@ -365,10 +383,25 @@ trait route_t
}
$perm_string = $permission instanceof permission_node ? $permission->permission : $permission;
$userCacheKey = (string)$user->id . ':' . $perm_string;
$redisKey = "user:{$user->id}:{$perm_string}";
if (!isset(self::$__perm_user_cache[$userCacheKey])) {
self::$__perm_user_cache[$userCacheKey] = (bool)$user->hasPermission($perm_string);
$cached = null;
if (defined('redis')) {
$cached = redis->get_permission($redisKey);
}
if ($cached !== null) {
self::$__perm_user_cache[$userCacheKey] = $cached;
} else {
$allowed = (bool)$user->hasPermission($perm_string);
if (defined('redis')) {
redis->cache_permission($redisKey, $allowed);
}
self::$__perm_user_cache[$userCacheKey] = $allowed;
}
}
$allowed = (bool)self::$__perm_user_cache[$userCacheKey];
if (!$allowed && $throwOnDeny) {
(new logs_o())->add('global', 'global', 1, $user->id, 'PERMISSION_DENIED', 'Permission denied. Missing permission: ' . $perm_string);
$response->error('Permission denied. Missing permission: ' . $perm_string . ' for user: ' . $user->id . ' In group: ' . $user->group_id->value(), 403);