Karbantartás menedzsment rendszer - 6. rész: Jogosultsági rendszer alapjai

Attila | 2023. 10. 09. 21:33 | Olvasási idő: 4 perc

Címkék: #Adatbetöltő (Seeder) #Digitalizáció (Digitalization) #Filament #Jogosultság (Permission) #Telepítés (Installation) #Többnyelvűsítés (Localization)

Elkezdjük felépíteni a jogosultsági rendszert, amelyhez egy külső, harmadik féltől származó csomagot is használunk. Ez a Spatie Laravel Permission csomagja lesz, amely a szerepkörökkel (csoportokkal), felhasználókkal és a konkrét jogosultságokkal dolgozik azért, hogy bizonyos funkcionalitásokhoz, erőforrásokhoz csak megszorításokkal lehessen hozzáférni a webalkalmazásban. Ezt az elgondolást fogjuk integrálni a Karbantartás menedzsment rendszerünkbe.
filament-permissions

Jogosultsági rendszer alapjai

A jogosultsági rendszerek kialakítása egy nagyon jól átlátható, emiatt egyszerű és hatékony módszer arra, hogy bizonyos funkcionalitások végrehajtásához engedélyeket kössünk. Ilyenkor általában regisztrált felhasználók bizonyos csoportjaival dolgozunk, amely csoportokat szerepköröknek hívunk. Mivel rengeteg rendszernek képezi alapját egy jól felépített és könnyen használható jogosultsági rendszer, emiatt léteznek erre harmadik féltől származó megoldások. Egy ilyet fogunk mi is használni, a spatie permission csomagját: https://spatie.be/docs/laravel-permission/v5/introduction

A kiinduló weboldala elég jó kezdésnek, de szeretném felhívni a figyelmet a "Best practice" menü szekcióra az oldalon, amely ajánlásokat, "legjobb gyakorlatokat" fogalmaz meg a fejlesztők számára, amikor építik a csomag segítségével a saját jogosultsági rendszerüket. Néhányat én is kiemelek közülük:

  • A felhasználóknak szerepkörük (csoportjuk) van, vagy mondhatnánk úgy is, hogy szerepkörhöz tartoznak a felhasználók, például Kiss István az Adminisztrátorok szerepkörrel rendelkezik, vagy ahhoz a csoporthoz tartozik.
  • A szerepkörök jogosultságokkal rendelkeznek, például az Adminisztrátorok tudnak valamit (képesek valamire), létrehozni egy berendezést a rendszerben.
  • Az alkalmazás mindig a jogosultságokat ellenőrzi (amennyire lehetséges), és nem a szerepköröket, tehát az alkalmazás azt nézi, hogy adott felhasználó (a szerepkörén keresztül) képes-e, jogosult-e megcsinálni valamit az oldalon.

Kezdetnek, ha ennyit megértünk, az már elég, aztán a gyakorlat során úgyis megismerjük a részletes működését.

Telepítés

A composer segítségével telepítsük a csomagot a terminal-ban:

composer require spatie/laravel-permission

Utána publikáljuk a PermissionServiceProvider-t:

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

Az utasítás hatására létrejött a config / permission.php és egy új migrációs fájl, ami a jogosultsági rendszerhez tartozó adattáblák szerkezetét definiálja a CreatePermissionTables migrációs osztályban.

Végül következhet a migrálás:

php artisan migrate

Az app / Models / User.php -ban lévő User osztályon belül adjuk hozzá a HasRoles trait-et és az osztály előtt importáljuk is ezt be:

// osztály előtt:
use Spatie\Permission\Traits\HasRoles;

// User osztályon belül:
use HasApiTokens, HasFactory, Notifiable, HasRoles;

Ezzel települt a csomag és elvégeztük az alapbeállításait, készen áll most már arra, hogy használatba vegyük.


Seeder osztályok

A Seeder osztályokkal tudjuk adatokkal feltölteni az adattábláinkat, így akár újra és újra felépíthetjük az adatszerkezeteinket, ha utána megint futtatjuk a seedelést, akkor mindig meglesznek a kezdeti adataink az adatbázisunkban.

Adminisztrációs Seeder osztály

Hozzuk létre a terminal-ban a Seeder osztályt:

php artisan make:seeder AdministrationSeeder

A run() metódus magjának, tartalmának struktúrája:

  1. Mielőtt a tényleges nekiállnánk a seedelésnek a jogosultsági rendszer kapcsán, rögtön az első sorban érdemes kiüríteni a cache-t (gyorsítótárat), hogy ne ragadhasson be semmilyen egyéb adat ide, amit esetleg újra szeretnénk szinkronizálni az adatbázisba (további információ erről itt elérhető).
  2. Hozzuk létre a meglévő modellekhez tartozó jogosultságokat (így a permissions adattábla fog majd feltöltődni). Adjunk a jogosultságoknak beszédes nevet, például: "create users" jogosultsággal rendelkező szerepkör felhasználói létre fognak majd tudni hozni felhasználókat.
  3. Hozzuk létre a szerepköröket (így a roles adattábla fog majd feltöltődni), amelyekhez utána rögtön hozzá is rendeljük azokat a jogosultságokat, amelyek hozzá tartoznak. Például az admin szerepkör mindent fog majd tudni, de a gépkezelő és / vagy karbantartó szerepkörű felhasználóknak ennél már sokkal kevesebb joguk lesz az alkalmazásban.
  4. Hozzunk még létre három felhasználót, mindhárom szerepkörhöz 1-1 darabot. Arra figyeljünk, hogy az e-mail címük "@admin.hu"-ra végződjön, hiszen korábban ezt a szűrési feltételt állítottuk be a User Model osztály canAccessPanel() metódusában, így csak azok a felhasználók férhetnek majd hozzá az admin felülethez, akiknek az e-mail címe erre végződik.
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();

// USER MODEL
$userPermission1 = Permission::create(['name' => 'create users']);
$userPermission2 = Permission::create(['name' => 'read users']);
$userPermission3 = Permission::create(['name' => 'update users']);
$userPermission4 = Permission::create(['name' => 'delete users']);

// ROLE MODEL
$rolePermission1 = Permission::create(['name' => 'create roles']);
$rolePermission2 = Permission::create(['name' => 'read roles']);
$rolePermission3 = Permission::create(['name' => 'update roles']);
$rolePermission4 = Permission::create(['name' => 'delete roles']);

// PERMISSION MODEL
$permission1 = Permission::create(['name' => 'create permissions']);
$permission2 = Permission::create(['name' => 'read permissions']);
$permission3 = Permission::create(['name' => 'update permissions']);
$permission4 = Permission::create(['name' => 'delete permissions']);

// DEVICETYPE MODEL
$deviceType1 = Permission::create(['name' => 'create devicetypes']);
$deviceType2 = Permission::create(['name' => 'read devicetypes']);
$deviceType3 = Permission::create(['name' => 'update devicetypes']);
$deviceType4 = Permission::create(['name' => 'delete devicetypes']);

// DEVICE MODEL
$device1 = Permission::create(['name' => 'create devices']);
$device2 = Permission::create(['name' => 'read devices']);
$device3 = Permission::create(['name' => 'update devices']);
$device4 = Permission::create(['name' => 'delete devices']);

// DOCUMENT MODEL
$document1 = Permission::create(['name' => 'create documents']);
$document2 = Permission::create(['name' => 'read documents']);
$document3 = Permission::create(['name' => 'update documents']);
$document4 = Permission::create(['name' => 'delete documents']);

$adminRole = Role::create(['name' => 'admin'])->syncPermissions([
  $userPermission1,
  $userPermission2,
  $userPermission3,
  $userPermission4,
  $rolePermission1,
  $rolePermission2,
  $rolePermission3,
  $rolePermission4,
  $permission1,
  $permission2,
  $permission3,
  $permission4,
  $deviceType1,
  $deviceType2,
  $deviceType3,
  $deviceType4,
  $device1,
  $device2,
  $device3,
  $device4,
  $document1,
  $document2,
  $document3,
  $document4,
]);
$repairerRole = Role::create(['name' => 'karbantartó'])->syncPermissions([
  $device2,
  $document2,
]);
$operatorRole = Role::create(['name' => 'gépkezelő'])->syncPermissions([
  $device2,
  $document2,
]);

// CREATE ADMINS & USERS
User::create([
  'name' => 'admin',
  'email' => 'admin@admin.hu',
  'email_verified_at' => now(),
  'password' => Hash::make('password'),
  'remember_token' => Str::random(10),
])->assignRole($adminRole);

User::create([
  'name' => 'repairer',
  'email' => 'repairer@admin.hu',
  'email_verified_at' => now(),
  'password' => Hash::make('password'),
  'remember_token' => Str::random(10),
])->assignRole($repairerRole);

User::create([
  'name' => 'operator',
  'email' => 'operator@admin.hu',
  'email_verified_at' => now(),
  'password' => Hash::make('password'),
  'remember_token' => Str::random(10),
])->assignRole($operatorRole);

Itt fontos az importálás is, úgyhogy külön kiemelem, hogy mely osztályokat kell importálni a fájl elején:

use App\Models\User;
use Illuminate\Support\Str;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
use Illuminate\Support\Facades\Hash;
use Spatie\Permission\Models\Permission;

Ezekre az osztályokra van szükség, hogy a Seeder fájlban lévő run() metódus magja majd hibátlanul le tudjon futni.

Karbantartási Seeder osztály

Ez korábban elmaradt, de hogy ne kelljen manuálisan feltölteni adatokkal a berendezés típusokat és a berendezéseket, hozzuk létre ezt a Seeder osztályt neki, hogy legalább néhány adatsor szerepeljen az említett adattáblákban.

Hozzuk létre a terminal-ban a Seeder osztályt:

php artisan make:seeder MaintenanceSeeder

Az osztályban lévő run() metódus magjához adjuk hozzá ezeket:

// DeviceType MODEL
DeviceType::create(['name' => 'Gépek']);
DeviceType::create(['name' => 'Elszívók']);

// Device MODEL
Device::create([
  'name' => 'Siemens',
  'erp_code' => 'D001',
  'type_id' => 1,
  'plant' => 'K',
  'active' => true
]);
Device::create([
  'name' => 'KUKA',
  'erp_code' => 'D002',
  'type_id' => 1,
  'plant' => 'L',
  'active' => true
]);
Device::create([
  'name' => 'Atlas',
  'erp_code' => 'D003',
  'type_id' => 2,
  'plant' => 'L',
  'active' => true
]);
Device::create([
  'name' => 'Bernardi',
  'erp_code' => 'D004',
  'type_id' => 2,
  'plant' => 'K',
  'active' => true
]);

Így most már lesz majd két berendezés típusunk és darabonként 2-2 berendezésünk.

DatabaseSeeder osztály

Indítsuk be közösen a két korábbi Seeder osztályt ennek a segítségével. Az osztályban lévő run() metódus magjához adjuk hozzá ezeket:

$this->call([
  AdministrationSeeder::class,
  MaintenanceSeeder::class,
]);

Ne feledkezzünk meg a két osztály importálásáról sem a fájl elején!

Majd adjuk ki az utasítást:

php artisan migrate:fresh --seed

Így először kitörlődnek a meglévő adattábláink, majd újra felépül a szerkezetük és feltöltődnek tartalommal. A meglévő berendezés típusunk, berendezésünk, dokumentumunk és a felhasználónk is megsemmisült, helyette most a Seeder fájloknak köszönhetően jöttek létre újak (leszámítva a dokumentumokat). Így most már az említett adattábláink feltöltődtök tartalommal, amelyet a Filament Admin panelen keresztül is tudunk ellenőrizni, ha végigkattintjuk az erőforrások listázásait, miután bejelentkeztünk az új admin felhasználónkkal: felhasználónév: admin@admin.hu jelszó: password


Permission Filament erőforrás

Egyszerű erőforrást hozzunk neki létre, mert úgyis csak neve van.

php artisan make:filament-resource Permission --simple

A létrejövő PermissionResource.php fájlban az App\Models\Permission osztályt szeretné alapból importálni a fájl elején, de ilyen nem létezik, úgyhogy ehelyett importáljuk ezt: Spatie\Permission\Models\Permission

Alapbeállítások

Állítsuk be az ikonját, a csoport nevét, a menü listában a sorrendjét, egyes és többesszámát (amelyekhez természetesen szükség van a szótárbejegyzésekre is, ezt tegyük meg a korábbiak után önállóan):

protected static ?string $navigationIcon = 'heroicon-o-key';

public static function getNavigationGroup(): string
{
  return __('module_names.navigation_groups.administration');
}

protected static ?int $navigationSort = 2;

public static function getModelLabel(): string
{
  return __('module_names.permissions.label');
}

public static function getPluralModelLabel(): string
{
  return __('module_names.permissions.plural_label');
}

Ezzel az alapbeállítások elkészültek a Permission erőforráshoz.

Űrlap

A form() metódusban lévő $form->schema([]) tömbjéhez adjuk hozzá az alábbit (akár korábbi erőforrásokból is kimásolhatjuk):

Forms\Components\Section::make()
  ->schema([
    Forms\Components\TextInput::make('name')->label(__('fields.name'))
    ->required()
    ->unique(ignoreRecord: true)
    ->maxLength(255),
])

Ezzel a létrehozási / szerkesztési űrlap elkészült a Permission erőforráshoz.

Táblázat

A table() metódusban lévő $table->columns([]) tömbjéhez adjuk hozzá az alábbit (akár a korábbi erőforrásokból is kimásolhatjuk):

Tables\Columns\TextColumn::make('id')
  ->sortable(),
Tables\Columns\TextColumn::make('name')->label(__('fields.name'))
  ->sortable()
  ->searchable(),
Tables\Columns\TextColumn::make('created_at')->label(__('fields.created_at'))
  ->dateTime('Y-m-d H:i')
  ->sortable()
  ->searchable(),

Ezzel a táblázatos listanézet oszlopai elkészültek a Permission erőforráshoz.


Kitérő: menüstruktúra átszervezése

Az "Adminisztráció" menücsoportot használjuk inkább a most kialakítandó jogosultsági rendszer menüpontjaihoz. Míg a berendezés típusok, berendezések, dokumentumok menüpontokat helyezzük át egy új "Karbantartás" menücsoportba. Ehhez szükség van a module_names szótárakban, a navigation_groups-hoz új elem hozzáadására: a karbantartás bejegyzések létrehozására. Majd ezt az új csoportnevet adjuk hozzá a korábbi erőforrásaink getNavigationGroup() metódus visszatérési értékének ('module_names.navigation_groups.maintenance')

Az új menüstruktúra nézzen ki így:

Úgy érhetjük el, ha a DeviceTypeResource navigationSort beállítása 4 lesz, a DeviceResource-nál 5 lesz, a DocumentResource-nál pedig 6. A PermissionResource-nál maradjon 2, aztán a későbbiekben a szerepkörök lesznek majd az 1., a felhasználók pedig a 3.


Összegzés, továbblépés

A bejegyzés során elkezdtük felépíteni a jogosultsági rendszert, amihez a Spatie Laravel-Permission csomagját használjuk. A telepítés és a kezdeti beállítások elvégzése után egy-egy seeder fájlt hoztunk létre ahhoz, hogy mindig újraépíthető legyen könnyedén az alapvető adatstruktúránk a példa adatokkal. Utána pedig felépítésre került a Permission erőforrásunk a szükséges elemeivel. Ezek a változások ebben a GitHub commit-ben érhetők el.

A közvetlen folytatásban következik a jogosultsági rendszer bővítése a szerepkörökkel, felhasználókkal. Majd utána visszatérünk és a hibabejelentő űrlapot fogjuk felépíteni a Filament segítségével.


Dolgozzunk együtt: egyéni oktatás, mentorálás, fejlesztés, tanácsadás

Ha egyéni oktatás, mentorálás, vagy fejlesztési projekt kapcsán szeretnél segítséget kérni tőlem, esetleg együttműködni velem, akkor keress meg a weboldalamon található elérhetőségeken keresztül!