diff --git a/app/Models/User.php b/app/Models/User.php index 6b2055596..ade71262c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -1647,9 +1647,15 @@ final class User extends Authenticatable implements CanResetPasswordContract, Ha { $user = static::findOrFail($userId); - $userAllowed = $user->getDirectPermissions()->pluck('name')->toArray(); - $roleAllowed = $user->getAllPermissions()->pluck('name')->toArray(); - $allowed = array_intersect($roleAllowed, $userAllowed); + // getAllPermissions() already merges permissions granted directly to the + // user with permissions granted via their role(s). Intersecting it with + // getDirectPermissions() (as this used to do) meant that any user whose + // permissions come only from their role -- which is how every seeded + // role in RolesAndPermissionsSeeder grants them, including Admin -- ended + // up with an empty $allowed array, and therefore every category root + // excluded. That silently zeroed out browse/API results for all users + // on a fresh install. + $allowed = $user->getAllPermissions()->pluck('name')->toArray(); $categoryPermissions = [ 'view console' => 1000, diff --git a/tests/Feature/UserExcludedCategoryTest.php b/tests/Feature/UserExcludedCategoryTest.php index dd00ce143..57a865843 100644 --- a/tests/Feature/UserExcludedCategoryTest.php +++ b/tests/Feature/UserExcludedCategoryTest.php @@ -266,6 +266,29 @@ final class UserExcludedCategoryTest extends TestCase $this->assertContains(2070, $exclusions); } + public function test_role_only_permissions_are_not_excluded(): void + { + // Regression test: RolesAndPermissionsSeeder grants every view permission + // via the role only (Role::givePermissionTo), never directly to the user + // (User::givePermissionTo). getCategoryExclusionById() must honor + // role-granted permissions, not just permissions assigned directly to + // the user, or every category ends up excluded on a fresh install. + $roleOnlyUser = $this->createTestUser($this->userRole->id); + $roleOnlyUser->assignRole($this->userRole); + // Deliberately do NOT call $roleOnlyUser->givePermissionTo(...) here. + + $exclusions = User::getCategoryExclusionById($roleOnlyUser->id); + + // The role has every 'view *' permission except 'view other', so only + // the 'Other' root category (id 1) should be excluded -- nothing from + // the seeded Movies (2000) or TV (5000) root categories. + $this->assertNotContains(2030, $exclusions); + $this->assertNotContains(2040, $exclusions); + $this->assertNotContains(2070, $exclusions); + $this->assertNotContains(5030, $exclusions); + $this->assertNotContains(5040, $exclusions); + } + public function test_clearing_exclusions_works(): void { // First add exclusions