Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ProcessMaker/Http/Middleware/GenerateMenus.php
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ public static function userHasPermission($permission)
return $user && $user->can($permission) && $user->hasPermission($permission);
}

$userPermissions = $user->permissions()->pluck('group')->unique()->toArray();
$userPermissions = $user->cachedPermissionGroups();
$defaultPermissions = Permission::DEFAULT_PERMISSIONS;
$userWithDefaultPermissions = empty(array_diff($userPermissions, $defaultPermissions));

Expand Down
14 changes: 10 additions & 4 deletions ProcessMaker/Models/Permission.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ public static function byResource($resource)
return $filtered;
}

public static function cachedNames(): array
{
return Cache::remember('permissions', 86400, function () {
return self::pluck('name')->toArray();
});
}

public static function byName($name)
{
try {
Expand Down Expand Up @@ -139,7 +146,7 @@ public static function getUsersByGroup(array $groups)
->on('assignables.assignable_id', '=', 'users.id');
})
->select('users.*')
->union(\ProcessMaker\Models\User::where('is_administrator', '=', true))
->union(User::where('is_administrator', '=', true))
->groupBy('users.id')
->get();

Expand All @@ -148,8 +155,7 @@ public static function getUsersByGroup(array $groups)

private static function clearAndRebuildCache()
{
// Rebuild and update the permissions cache
$permissions = self::pluck('name')->toArray();
Cache::put('permissions', $permissions, 86400);
Cache::forget('permissions');
self::cachedNames();
}
}
2 changes: 1 addition & 1 deletion ProcessMaker/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ public function getFullName()
public function hasPermissionsFor(...$resources)
{
if ($this->is_administrator) {
$perms = Permission::all(['name'])->pluck('name');
$perms = collect(Permission::cachedNames());
} else {
$perms = collect(session('permissions'));
}
Expand Down
4 changes: 1 addition & 3 deletions ProcessMaker/Providers/AuthServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,7 @@ public function defineGates()

try {
// Cache the permissions for a day to improve performance
$permissions = Cache::remember('permissions', 86400, function () {
return Permission::pluck('name')->toArray();
});
$permissions = Permission::cachedNames();
foreach ($permissions as $permission) {
Gate::define($permission, function ($user) use ($permission) {
return $user->hasPermission($permission);
Expand Down
38 changes: 38 additions & 0 deletions ProcessMaker/Services/PermissionCacheService.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class PermissionCacheService implements PermissionCacheInterface

private const LEGACY_USER_PERMISSIONS_KEY = 'user';

private const USER_PERMISSION_GROUPS_KEY = 'user_permission_groups';

private const TRACKED_PERMISSION_KEYS = 'permission_cache_keys';

private const TRACKED_PERMISSION_KEYS_LOCK = 'permission_cache_keys_lock';
Expand Down Expand Up @@ -91,11 +93,42 @@ public function cacheGroupPermissions(int $groupId, array $permissions): void
/**
* Invalidate user permissions cache
*/
public function rememberUserPermissionGroups(int $userId, int $ttl, callable $callback): array
{
$key = $this->getUserPermissionGroupsKey($userId);

try {
$groups = Cache::remember($key, $ttl, $callback);
$this->trackPermissionKey($key);

return is_array($groups) ? $groups : [];
} catch (\Exception $e) {
Log::warning("Failed to remember user permission groups for user {$userId}: " . $e->getMessage());

$groups = $callback();

return is_array($groups) ? $groups : [];
}
}

public function forgetUserPermissionGroups(int $userId): void
{
$key = $this->getUserPermissionGroupsKey($userId);

try {
Cache::forget($key);
$this->untrackPermissionKey($key);
} catch (\Exception $e) {
Log::warning("Failed to forget user permission groups for user {$userId}: " . $e->getMessage());
}
}

public function invalidateUserPermissions(int $userId): void
{
$keys = [
$this->getUserPermissionsKey($userId),
$this->getLegacyUserPermissionsKey($userId),
$this->getUserPermissionGroupsKey($userId),
];

try {
Expand Down Expand Up @@ -216,6 +249,11 @@ private function getLegacyUserPermissionsKey(int $userId): string
return self::LEGACY_USER_PERMISSIONS_KEY . "_{$userId}_permissions";
}

private function getUserPermissionGroupsKey(int $userId): string
{
return self::USER_PERMISSION_GROUPS_KEY . ":{$userId}";
}

/**
* Warm up cache for a user
*/
Expand Down
9 changes: 9 additions & 0 deletions ProcessMaker/Traits/HasAuthorization.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ function () use ($user) {
return $this->addCategoryViewPermissions($permissions);
}

public function cachedPermissionGroups(): array
{
return app(PermissionCacheService::class)->rememberUserPermissionGroups(
$this->id,
86400,
fn () => $this->permissions()->pluck('group')->unique()->values()->toArray()
);
}

public function loadGroupPermissions()
{
$processedGroups = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,20 @@ public function test_invalidate_user_permissions_clears_cache_correctly()
// Cache user permissions
$this->cacheService->cacheUserPermissions($this->userId, $this->userPermissions);
$this->cacheService->putLegacyUserPermissions($this->userId, $this->userPermissions, 3600);
$this->cacheService->rememberUserPermissionGroups($this->userId, 3600, fn () => ['Projects', 'Process Catalog']);

// Verify cache exists
$this->assertNotNull(Cache::get("user_permissions:{$this->userId}"));
$this->assertNotNull(Cache::get("user_{$this->userId}_permissions"));
$this->assertNotNull(Cache::get("user_permission_groups:{$this->userId}"));

// Invalidate cache
$this->cacheService->invalidateUserPermissions($this->userId);

// Verify cache was cleared
$this->assertNull(Cache::get("user_permissions:{$this->userId}"));
$this->assertNull(Cache::get("user_{$this->userId}_permissions"));
$this->assertNull(Cache::get("user_permission_groups:{$this->userId}"));
}

/**
Expand Down
Loading