Die belgische „SoftwareGang“ Spatie haut ein coole Laravel Package nach dem anderen raus. Super nützlich und sehr angenehm zu nutzen. Heute gibt es einen Einstieg in das Package Spatie Permission Package.
I. Einrichten
1. Schritt:
composer require spatie/laravel-permission
2. Schritt: Permission Service Provider anmelden
Füge dafür den „PermissionServiceProvider“ in der Datei config/app.php ein.
'providers' => [
// ...
Spatie\Permission\PermissionServiceProvider::class,
];
3. Schritt: Kopiere die migration aus dem Vendor in das Projektverzeichnis
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
4. Nun noch den Config cache bereinigen
php artisan config:clear
5. Migration starten
php artisan migrate
6. Füge den Trait zum User Model hinzu.
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, HasRoles;
Dadurch haben wir nun Zugriff auf die Spatie Permissions Methoden um dem User Rolen zuzuweisen.
Das war das einrichten und nun gehen wir über zum nutzen.
II. Anwenden
Dafür gehen wir aus, dass wir einen Blog bauen. Hier haben wir unterschiedliche Rollen wie zum Beispiel: Admin, Writer, Reviewer. Der Weiter hat folgende Berechtigung: „edit post“.
Um die Rollen und Berechtigungen zu erstellen, nutzen wir einen Seeder. Den erstellt man in Laravel mit:
php artisan make:seeder RolesSeeder
In deren run Methode definieren wir unser Model.
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
class RolesSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$roles = ['admin', 'provider', 'customer'];
foreach($roles as $role) {
Role::create(['name' => $role]);
}
}
}
Das Gleiche nun auch für die Berechtigungen:
php artisan make:seeder PermissionsSeeder
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
class PermissionsSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$permissions = ['edit post', 'delete post'];
foreach($permissions as $permission) {
Permission::create(['name' => $permission]);
}
}
}
Jetzt deklarieren wir der DatabaseSeeder Klasse, welche Seeds er „runen“ soll.
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(RolesSeeder::class);
$this->call(PermissionsSeeder::class);
// create User
$user = \App\Models\User::create([
'name' => 'Martin',
'email' => 'martin@peoplehelper.org',
'password' => bcrypt('password')
]);
// asaign user a role as writer
$user->assignRole('writer');
// assign permission to an role (1 = edit post)
$role = $user->roles[0];
$role->givePermissionTo(\Spatie\Permission\Models\Permission::find(1));
}
}
Sobald wir das haben können wir mal die Migration durchlaufen lassen:
php artisan db:seed
// bzw.
php artisan migrate:refresh –seed
Ein Blick in unsere Datenbank zeigt, dass wir erfolgreich die Role Writer dem User Martin zugewiesen haben.
Für das Wiederrufen einer Berechtigung ($user->revokePermissionTo(‘delete post‘) ) oder das entfernen einer Role vom User ($user->removeRole(‘writer‘)) könnt das ganz gut in der Spatie Doku nachlesen. Das Gleiche gilt für die Konditional Abfragen wie zum Beispiel $user→can(‘delete post‘).
Die „Blade Directives“
Hier kann man mit:
@can(‘edit post‘)
...
@endcan
@role(‘writer‘)
…
@endrole
@hasrole(‘writer‘)
...
@endhasrole
@hasanyrole(Collection || Array )
@endhasanyrol
@hasanyrole(‘writer|admin‘)
...
@endhasanyrole
@unlessrole('admin')
...
@end unlessrole
Logiksecurity statt nur Viewhidding
Ich musste mal mit einem Indischen PHP Entwickler zusammen arbeiten, der die Berechtigungslogiken nur in den Blades abbildete. Im Controller und bzw. oder Serviceklassen hat er es weitestgehend vermieden. Was zu einem Sicherheitsproblem wurde.
Deshalb sein es hier wichtig zu erwähnen, dass nicht zu vergessen. Ein eleganter und einfacher Ansatz wäre hier mit einer Middleware zu arbeiten. Somit kann man die Routes ohne viel Umwege gut vor unberechtigten URL aufrufen schützen.
Alles was wir machen müssen ist:
1: Schritt: Das PermissionRole Package der Route Middleware hinzufügen.
Das passiert in der Kernel.php Datei in der Membervariablen $routeMiddleware. Hier fügen wir dem Array folgende Middlewarepakete hinzu.
protected $routeMiddleware = [
// ...
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];
Ab diesem Moment können wir in den Routes über Rollen und Berechtigungen schützen.
Route::group(['middleware' => ['role:admin']], function () {
//
});
// bzw.
Route::get(‘posts/create‘, [\App\Controllers\PostController,])→ ‘create‘])->middleware(['middleware' => ['role:admin']);
Route::group(['middleware' => ['permission:publish articles']], function () {
//
});
Route::group(['middleware' => ['role:super-admin','permission:publish articles']], function () {
//
});
Route::group(['middleware' => ['role_or_permission:super-admin|edit articles']], function () {
//
});
Route::group(['middleware' => ['role_or_permission:publish articles']], function () {
//
});
Wer die routes aus einem Grund nicht schützen kann oder will, kann das im Controller oder in der ServiceKlasse Deiner Anwendung auch vornehmen. Dort lädt man im Konstruktor der jeweiligen Klasse die Middleware und überprüft dann die Userberechtigung bzw. Userrolle.
public function __construct()
{
$this->middleware(['role:admin','permission:publish articles|edit articles']);
// oder
$this->middleware(['role_or_permission:super-admin|edit articles']);
}
Das war es auch schon für den kurzen Einstieg. Viel Spaß beim ausprobieren und nutzen!