diff --git a/ProcessMaker/Traits/HasAuthorization.php b/ProcessMaker/Traits/HasAuthorization.php index f6eea268f1..d699b403ae 100644 --- a/ProcessMaker/Traits/HasAuthorization.php +++ b/ProcessMaker/Traits/HasAuthorization.php @@ -2,13 +2,10 @@ namespace ProcessMaker\Traits; -use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Log; use ProcessMaker\Models\Group; use ProcessMaker\Models\Permission; -use ProcessMaker\Models\Process; -use ProcessMaker\Models\ProcessPermission; -use ProcessMaker\Models\User; trait HasAuthorization { @@ -32,11 +29,12 @@ public function loadUserPermissions() public function loadGroupPermissions() { + $processedGroups = []; $permissions = []; foreach ($this->groupMembersFromMemberable as $gm) { $group = $gm->group; - $permissions = $this->loadPermissionOfGroups($group, $permissions); + $permissions = $this->loadPermissionOfGroups($group, $permissions, $processedGroups); $names = $group->permissions->pluck('name')->toArray(); $permissions = array_merge($permissions, $names); } @@ -44,15 +42,34 @@ public function loadGroupPermissions() return $this->addCategoryViewPermissions($permissions); } - public function loadPermissionOfGroups(Group $group, array $permissions = []) + public function loadPermissionOfGroups(Group $group, array $permissions = [], array $processedGroups = []) { - foreach ($group->groupMembersFromMemberable as $member) { - $group = $member->group; - $permissions = $this->loadPermissionOfGroups($group, $permissions); - $permissions = array_merge($permissions, $group->permissions->pluck('name')->toArray()); - } + try { + // Check if the group was proccessed + if (in_array($group->id, $processedGroups)) { + return $permissions; + } + // Add the group in the processedList + $processedGroups[] = $group->id; + // Load permissions + $groupPermissions = $group->permissions->pluck('name')->toArray(); + $permissions = array_merge($permissions, $groupPermissions); + // Review groups + foreach ($group->groupMembersFromMemberable as $member) { + $memberGroup = $member->group; + $permissions = $this->loadPermissionOfGroups( + $memberGroup, + $permissions, + $processedGroups + ); + } - return $permissions; + return array_unique($permissions); + } catch (\Exception $e) { + Log::error('Error loading group permissions: ' . $e->getMessage()); + + return $permissions; + } } public function hasPermission($permissionString) diff --git a/tests/Feature/PermissionsTest.php b/tests/Feature/PermissionsTest.php index b65402a227..9f1bdf83fb 100644 --- a/tests/Feature/PermissionsTest.php +++ b/tests/Feature/PermissionsTest.php @@ -123,4 +123,87 @@ public function testCreatePermission() $this->assertDatabaseHas('permissions', $attributes); } + + public function testLoadsGroupPermissions() + { + // Create group and assign permission + $group = Group::factory()->create(); + $permission = Permission::factory()->create(['name' => 'test-permission']); + $group->permissions()->attach($permission); + // Assign user in the group + $user = User::factory()->create(); + GroupMember::factory()->create([ + 'member_type' => get_class($user), + 'member_id' => $user->id, + 'group_id' => $group->id, + ]); + // Load permissions + $permissions = $user->loadGroupPermissions(); + // Assert + $this->assertContains('test-permission', $permissions); + } + + public function testLoadsNestedGroupPermissions() + { + // Create groups + $groupA = Group::factory()->create(); + $groupB = Group::factory()->create(); + // Create permissions + $permissionA = Permission::factory()->create(['name' => 'permission-a']); + $permissionB = Permission::factory()->create(['name' => 'permission-b']); + // Assign permissions to groups + $groupA->permissions()->attach($permissionA); + $groupB->permissions()->attach($permissionB); + // Make groupB member of groupA + GroupMember::factory()->create([ + 'member_type' => get_class($groupB), + 'member_id' => $groupB->id, + 'group_id' => $groupA->id, + ]); + // Assign user in the group + $user = User::factory()->create(); + GroupMember::factory()->create([ + 'member_type' => get_class($user), + 'member_id' => $user->id, + 'group_id' => $groupB->id, + ]); + // Load permissions + $permissions = $user->loadGroupPermissions(); + // Assert + $this->assertContains('permission-a', $permissions); + $this->assertContains('permission-b', $permissions); + } + + public function testItHandlesCircularGroupPermissionsReferences() + { + // Create groups + $groupA = Group::factory()->create(); + $groupB = Group::factory()->create(); + // Create circular reference + GroupMember::factory()->create([ + 'member_type' => get_class($groupB), + 'member_id' => $groupB->id, + 'group_id' => $groupA->id, + ]); + GroupMember::factory()->create([ + 'member_type' => get_class($groupA), + 'member_id' => $groupA->id, + 'group_id' => $groupB->id, + ]); + // Create permissions + $permissionA = Permission::factory()->create(['name' => 'permission-a']); + $groupA->permissions()->attach($permissionA); + // Assign user in the group + $user = User::factory()->create(); + GroupMember::factory()->create([ + 'member_type' => get_class($user), + 'member_id' => $user->id, + 'group_id' => $groupA->id, + ]); + // Load permissions + $permissions = $user->loadGroupPermissions(); + // Assert + $this->assertContains('permission-a', $permissions); + $this->assertNotNull($permissions); + } }