Invalidate user permission and session caches when group permissions change; add tests.
This commit is contained in:
@@ -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
|
||||
@@ -145,4 +199,4 @@ class groups_permissions_o extends db
|
||||
return preg_match($regex, $permission['permission']);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -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);');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user