Invalidate user permission and session caches when group permissions change; add tests.

This commit is contained in:
Jeppe Bundgaard
2026-03-19 17:13:45 +01:00
parent b547a8b029
commit 219f739b54
2 changed files with 70 additions and 3 deletions
@@ -6,6 +6,7 @@ use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
use Throwable;
class groups_permissions_o extends db
{
@@ -48,10 +49,11 @@ class groups_permissions_o extends db
]);
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
if (!$this->id) {
throw new Exception('The permission was not created successfully.');
}
$this->invalidateGroupSessionCaches($group_id);
self::objectChanged();
}
public function getObjectProperties(): void
@@ -62,7 +64,7 @@ class groups_permissions_o extends db
public function objectChanged(): void
{
//TODO: Add cache invalidation
// Cache invalidation is handled in add/remove where group context is guaranteed.
}
/**
@@ -96,6 +98,58 @@ class groups_permissions_o extends db
$tmp_id->select((int)$id);
$tmp_id->requireSelected();
$tmp_id->delete();
$this->invalidateGroupSessionCaches($group_id);
}
/**
* Invalidate cached permissions and session payloads for users associated with a group.
*/
private function invalidateGroupSessionCaches(int $group_id): void
{
if ($group_id <= 0 || !defined('redis')) {
return;
}
try {
$userRows = (new users_o())->getFieldsWhere([
'group_id' => $group_id,
], ['id']);
if (count($userRows) === 0) {
return;
}
$userIds = [];
foreach ($userRows as $userRow) {
$id = (int)($userRow['id'] ?? 0);
if ($id > 0) {
$userIds[] = $id;
}
}
$userIds = array_values(array_unique($userIds));
if (count($userIds) === 0) {
return;
}
foreach ($userIds as $userId) {
redis->clear_keys('perm:user:' . $userId . ':*');
}
$tokenRows = (new tokens_o())->getFieldsWhere([
'user_id' => $userIds,
], ['token']);
foreach ($tokenRows as $tokenRow) {
$token = (string)($tokenRow['token'] ?? '');
if ($token === '') {
continue;
}
redis->clear_auth_session($token);
}
} catch (Throwable) {
// Cache invalidation must not block permission updates.
}
}
public function asArray(): array
@@ -0,0 +1,13 @@
<?php
it('invalidates related user permission and auth-session caches when group permissions change', function (): void {
$content = file_get_contents(app_path('objects/groups_permissions_o.php'));
expect($content)->not->toBeFalse();
expect($content)->toContain('private function invalidateGroupSessionCaches(int $group_id): void');
expect($content)->toContain('$this->invalidateGroupSessionCaches($group_id);');
expect(substr_count((string)$content, '$this->invalidateGroupSessionCaches($group_id);'))->toBeGreaterThanOrEqual(2);
expect($content)->toContain("redis->clear_keys('perm:user:' . \$userId . ':*');");
expect($content)->toContain('redis->clear_auth_session($token);');
});