diff --git a/app/Livewire/RolePermissionManager.php b/app/Livewire/RolePermissionManager.php new file mode 100644 index 0000000..0671ce5 --- /dev/null +++ b/app/Livewire/RolePermissionManager.php @@ -0,0 +1,110 @@ +can(self::CORE_PERMISSION), 403); + } + + private function flushCache(): void + { + app(PermissionRegistrar::class)->forgetCachedPermissions(); + } + + public function togglePermission(int $roleId, string $permissionName): void + { + $role = Role::findOrFail($roleId); + + if ($role->hasPermissionTo($permissionName)) { + // Admin must always keep the core permission + if ($role->name === 'Admin' && $permissionName === self::CORE_PERMISSION) { + $this->dispatch('notify', "El rol Admin no puede perder '" . self::CORE_PERMISSION . "'."); + return; + } + $role->revokePermissionTo($permissionName); + } else { + $role->givePermissionTo($permissionName); + } + + $this->flushCache(); + $this->dispatch('notify', 'Permisos actualizados'); + } + + public function addRole(): void + { + $this->validate([ + 'newRole' => 'required|string|max:50|unique:roles,name', + ], [], ['newRole' => 'nombre de rol']); + + Role::create(['name' => trim($this->newRole)]); + $this->newRole = ''; + $this->flushCache(); + $this->dispatch('notify', 'Rol creado'); + } + + public function deleteRole(int $roleId): void + { + $role = Role::findOrFail($roleId); + + if (in_array($role->name, self::PROTECTED_ROLES, true)) { + $this->dispatch('notify', "El rol '{$role->name}' está protegido y no se puede borrar."); + return; + } + + $role->delete(); + $this->flushCache(); + $this->dispatch('notify', 'Rol eliminado'); + } + + public function addPermission(): void + { + $this->validate([ + 'newPermission' => 'required|string|max:50|unique:permissions,name', + ], [], ['newPermission' => 'nombre de permiso']); + + Permission::create(['name' => trim($this->newPermission)]); + $this->newPermission = ''; + $this->flushCache(); + $this->dispatch('notify', 'Permiso creado'); + } + + public function deletePermission(int $permissionId): void + { + $permission = Permission::findOrFail($permissionId); + + if ($permission->name === self::CORE_PERMISSION) { + $this->dispatch('notify', "El permiso '" . self::CORE_PERMISSION . "' está protegido y no se puede borrar."); + return; + } + + $permission->delete(); + $this->flushCache(); + $this->dispatch('notify', 'Permiso eliminado'); + } + + public function render() + { + return view('livewire.role-permission-manager', [ + 'roles' => Role::with('permissions')->orderBy('name')->get(), + 'permissions' => Permission::orderBy('name')->get(), + ]); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..26a92a5 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Support\ServiceProvider; +use Illuminate\Support\Facades\Gate; class AppServiceProvider extends ServiceProvider { @@ -19,6 +20,15 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { - // + // Super-admin bypass: anyone with the "manage all" permission + // (the Admin role has it) passes every authorization check. + // Return true to allow, or null to let normal checks run — never false. + Gate::before(function ($user, $ability) { + try { + return $user->hasPermissionTo('manage all') ? true : null; + } catch (\Throwable $e) { + return null; + } + }); } } diff --git a/resources/views/admin/users.blade.php b/resources/views/admin/users.blade.php index a8a3ea8..443d252 100644 --- a/resources/views/admin/users.blade.php +++ b/resources/views/admin/users.blade.php @@ -5,10 +5,15 @@ {{ __('Users') }} - - {{ __('New user') }} - - +
+ + {{ __('Permissions') }} + + + {{ __('New user') }} + +
+ diff --git a/resources/views/livewire/role-permission-manager.blade.php b/resources/views/livewire/role-permission-manager.blade.php new file mode 100644 index 0000000..4a7efa5 --- /dev/null +++ b/resources/views/livewire/role-permission-manager.blade.php @@ -0,0 +1,87 @@ +
+ +
+

{{ __('Permission management') }}

+

{{ __('Tick which permissions each role has. Changes are saved instantly.') }}

+
+ + {{-- Crear rol / permiso --}} +
+
+
+ + +
+ @error('newRole') {{ $message }} @enderror +
+ +
+
+ + +
+ @error('newPermission') {{ $message }} @enderror +
+
+ + {{-- Matriz Roles × Permisos --}} +
+ + + + + @foreach($roles as $role) + + @endforeach + + + + @forelse($permissions as $perm) + + + @foreach($roles as $role) + + @endforeach + + @empty + + @endforelse + +
{{ __('Permission') }} +
+ {{ $role->name }} + @unless(in_array($role->name, ['Admin'], true)) + + @endunless +
+
+
+ {{ $perm->name }} + @if($perm->name !== 'manage all') + + @endif +
+
+ permissions->contains('id', $perm->id)) + wire:click="togglePermission({{ $role->id }}, '{{ $perm->name }}')" /> +
{{ __('No permissions') }}
+
+ +

+ {{ __('The Admin role and the "manage all" permission are protected and cannot be removed.') }} +

+
diff --git a/routes/web.php b/routes/web.php index 05fafb9..3fcf6fe 100644 --- a/routes/web.php +++ b/routes/web.php @@ -136,6 +136,7 @@ Route::get('/reports/dashboard', ReportsDashboard::class)->name('reports.dashboa Route::get('/users/create', \App\Livewire\UserForm::class)->name('users.create'); Route::get('/users/{user}', \App\Livewire\UserView::class)->name('users.show'); Route::get('/users/{user}/edit', \App\Livewire\UserForm::class)->name('users.edit'); + Route::get('/permissions', \App\Livewire\RolePermissionManager::class)->name('permissions'); }); // Gestor de medios