From 6e633aac681735b9a54b703365c3685f2fe04323 Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Tue, 11 Nov 2025 20:52:03 +0100 Subject: [PATCH 1/2] feat: Add idempotent RolesAndPermissionsSeeder for RBAC Phase 4 - Create 5 predefined roles: Admin, Manager, Guard, Client, Works Council - Create 37 permissions across 7 resource groups - Idempotent design using firstOrCreate() - All roles/permissions use guard_name='sanctum' - Add permissions.assign_direct + permissions.revoke_direct - Tested: No duplicates after 3 runs Fixes #139 --- database/seeders/RolesAndPermissionsSeeder.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/database/seeders/RolesAndPermissionsSeeder.php b/database/seeders/RolesAndPermissionsSeeder.php index 25db732..3779d83 100644 --- a/database/seeders/RolesAndPermissionsSeeder.php +++ b/database/seeders/RolesAndPermissionsSeeder.php @@ -32,7 +32,7 @@ public function run(): void foreach ($permissions as $resource => $actions) { foreach ($actions as $action) { Permission::firstOrCreate( - ['name' => "{$resource}.{$action}"] + ['name' => "{$resource}.{$action}", 'guard_name' => 'sanctum'] ); } } @@ -43,7 +43,7 @@ public function run(): void // Create roles and assign permissions (idempotent) foreach ($roles as $roleName => $roleConfig) { $role = Role::firstOrCreate( - ['name' => $roleName] + ['name' => $roleName, 'guard_name' => 'sanctum'] ); // Only sync permissions if role has none @@ -102,6 +102,8 @@ private function getPermissionDefinitions(): array 'create', 'update', 'delete', + 'assign_direct', + 'revoke_direct', ], 'works_council' => [ 'access_employee_files', From 17c735265ada542707be410f27fc1a92416d4465 Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Tue, 11 Nov 2025 21:10:57 +0100 Subject: [PATCH 2/2] fix: Use correct firstOrCreate() signature for guard_name - Move guard_name to second parameter (values on create) - First parameter now only searches by name (prevents duplicates) - Fixes Copilot review comments on PR #150 Previously: firstOrCreate(['name' => ..., 'guard_name' => ...]) Would search for BOTH name AND guard_name, creating duplicates if same name exists with different guard. Now: firstOrCreate(['name' => ...], ['guard_name' => ...]) Searches only by name, sets guard_name only on creation. --- database/seeders/RolesAndPermissionsSeeder.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/database/seeders/RolesAndPermissionsSeeder.php b/database/seeders/RolesAndPermissionsSeeder.php index 3779d83..be630e9 100644 --- a/database/seeders/RolesAndPermissionsSeeder.php +++ b/database/seeders/RolesAndPermissionsSeeder.php @@ -32,7 +32,8 @@ public function run(): void foreach ($permissions as $resource => $actions) { foreach ($actions as $action) { Permission::firstOrCreate( - ['name' => "{$resource}.{$action}", 'guard_name' => 'sanctum'] + ['name' => "{$resource}.{$action}"], + ['guard_name' => 'sanctum'] ); } } @@ -43,7 +44,8 @@ public function run(): void // Create roles and assign permissions (idempotent) foreach ($roles as $roleName => $roleConfig) { $role = Role::firstOrCreate( - ['name' => $roleName, 'guard_name' => 'sanctum'] + ['name' => $roleName], + ['guard_name' => 'sanctum'] ); // Only sync permissions if role has none