Priradenia v rozsahu a s časovým oknom
Od verzie 2.1 môže rola alebo priame oprávnenie platiť len v rámci rozsahu — ľubovoľného Eloquent modelu: projektu, tímu, tenanta — a len medzi dvoma okamihmi. Funkciu zapína permissions.assignments.enabled; pri vypnutej posiela balík presne tie isté dopyty ako 2.0. Rozsah aj okno určíte na tom istom handli Permissions::for():
use RoundlyConsulting\Permissions\Facades\Permissions;
// Writes — exactly one scope, an optional window
Permissions::for($user)->in($project)->assignRole('editor', 'reviewer');
Permissions::for($user)->in($tenant)->until(now()->addWeek())->assignRole('admin');
Permissions::for($user)->between($from, $until)->givePermissionTo('reports.export'); // unscoped, time-boxed
// Checks — one scope or several (any of them)
Permissions::for($user)->in($project)->hasPermissionTo('posts.edit'); // bool
Permissions::for($user)->in([$team, $parentTeam])->hasRole('editor'); // bool
$user->can('posts.edit', Permissions::scope($project)); // through the GateZapnutie
Publikujte a spustite tri migrácie z verzie 2.1 a potom zapnite prepínač — postup prechodu nájdete v Inštalácii, scope_key_type a global_applies_in_scope v Konfigurácii:
php artisan vendor:publish --tag="permissions-migrations"
php artisan migrate# Only when your scope models key differently from your holders — before migrating
PERMISSIONS_SCOPE_KEY_TYPE=ulid
# After the 2.1 migrations ran
PERMISSIONS_ASSIGNMENTS=truephp artisan about potom zobrazí Assignments … ON s typom kľúča rozsahov a s tým, či globálne priradenia platia v rozsahoch. Kým je prepínač vypnutý, každé volanie in(), from(), until(), between(), at(), scope(), owner(), forgetScope() a pruneExpired() vyhodí PermissionException.
Pravidlá
- Rozsah — priradenie v rozsahu platí len pre kontrolu v tom istom rozsahu, porovnáva sa morph trieda aj kľúč: Team #1 a Project #1 sú rôzne rozsahy. Nikdy nesplní kontrolu bez rozsahu, ako je $user->hasPermissionTo() či $user->can('x').
- Globálne v rozsahu — priradenie bez rozsahu, či už z 2.0, alebo časovo obmedzené bez rozsahu, platí v každom rozsahu, kým je global_applies_in_scope zapnuté.
- Okno — valid_from platí vrátane, valid_until bez neho, obe v UTC na sekundy. Teraz znamená Date::now()->utc(), takže travelTo() a Date::setTestNow() ho v testoch posunú.
- Viac okien — tá istá rola v tom istom rozsahu s iným oknom je samostatný riadok a držiteľ rolu má, kedykoľvek je otvorené ktorékoľvek z nich. To isté priradenie dvakrát je jeden riadok.
- Kontroly prijmú viac rozsahov a stačí ktorýkoľvek z nich — napríklad tím a jeho nadradené tímy. Zápisy prijmú práve jeden. Prázdny zoznam vyhodí výnimku, nikdy sa nečíta ako „bez rozsahu“; na to slúži in(null).
- Držitelia — priradenia majú len modely s HasRoles; Permissions::for($role)->in() vyhodí výnimku, pretože Role drží len oprávnenia.
Čítanie v rozsahu
Čítacie metódy handlu odpovedajú v jeho rozsahoch k jeho času kontroly — at() tento okamih posunie. Bez in() čítajú bez rozsahu a fungujú aj pri vypnutom prepínači, vtedy len nad priradeniami z 2.0:
$grants = Permissions::for($user)->in($project);
$grants->hasRole('editor'); // any of the given roles
$grants->hasAnyRole('editor', 'reviewer'); // the same, variadic
$grants->hasPermissionTo('posts.edit'); // directly or through a role
$grants->roleNames(); // Collection<int, string>
$grants->permissionNames(); // Collection<int, string>
$grants->assignments(); // Collection<int, Assignment>
$grants->at(now()->addWeek())->hasRole('editor'); // as of next weekassignments() vypíše všetko ako objekty Assignment — na handli bez rozsahu priradenia z 2.0 (ako trvalé, bez rozsahu) aj všetky rozsahy, s in() len tieto rozsahy. Je to živý dopyt s načítaným rozsahom pri každom riadku a jeho serializácia nenesie žiadny názov triedy:
foreach (Permissions::for($user)->assignments() as $assignment) {
$assignment->kind; // AssignmentKind::Role or AssignmentKind::Permission
$assignment->name; // 'editor'
$assignment->scope; // ?Model — null when unscoped, or when the scope no longer exists
$assignment->validFrom; // ?CarbonImmutable, UTC, inclusive
$assignment->validUntil; // ?CarbonImmutable, UTC, exclusive
$assignment->isActive; // bool, at the handle's check time
}Zápis v rozsahu
Slovesá z 2.0 sa riadia tým, čo handle nesie. Bez rozsahu a okna idú cestou 2.0. S oknom a bez rozsahu zapíšu riadky priradení bez rozsahu. S rozsahom zapisujú — či odoberajú — len riadky tohto rozsahu:
// The complete role set of this project — other scopes are untouched
Permissions::for($user)->in($project)->syncRoles(['viewer']);
// Every window of the role in this project
Permissions::for($user)->in($project)->removeRole('editor');
// Everything the holder has in this project
Permissions::for($user)->in($project)->forgetAllAuthorization();- syncRoles() a syncPermissions() v rozsahu spravia zo zadanej množiny úplnú množinu tohto rozsahu: v jednej transakcii so zámkom na riadku držiteľa zmiznú všetky riadky v presne tomto rozsahu, ktoré nie sú cieľom — tá istá rola a okno — a potom sa ciele zapíšu. Súbežné synchronizácie jedného držiteľa idú za sebou a prežije množina práve jedného volajúceho.
- Synchronizácia s oknom bez rozsahu je úplná množina bez rozsahu, preto odpojí aj roly či oprávnenia držiteľa z 2.0.
- removeRole() v rozsahu odstráni všetky okná roly v ňom; názov vyčistí každú rolu, ktorú v tom rozsahu môže znamenať — vlastnú rolu rozsahu aj globálnu. Odoberanie sa riadi len rozsahom: odobratie bez rozsahu vyčistí všetky okná.
- Aditívne zápisy sú upserty podľa odtlačku držiteľa, roly či oprávnenia, rozsahu a okna, takže rovnaké súbežné zápisy nechajú jeden riadok a iné okná pridajú riadky.
- Okno, ktorého koniec nie je po začiatku, alebo ktoré už skončilo, vyhodí PermissionException — úplne minulé okno by neudelilo nič.
Slovesá z 2.0 pri zapnutých priradeniach
Bez rozsahu siahajú známe slovesá aj na nové riadky, takže držiteľovi nikdy nezostane priradenie, ktoré ste mu chceli odobrať:
- hasRole(), hasPermissionTo() aj Gate započítajú aj časovo obmedzené priradenia bez rozsahu, kým je ich okno otvorené.
- syncRoles() a syncPermissions() zmažú aj riadky priradení držiteľa bez rozsahu; riadky v rozsahu patria synchronizácii svojho rozsahu.
- removeRole() a revokePermissionTo() zmažú aj riadky priradení držiteľa bez rozsahu s týmito názvami, všetky okná.
- forgetAllAuthorization() zmaže aj všetky priradenia držiteľa vo všetkých rozsahoch — je to sloveso na úplné odstránenie. Pre jeden rozsah ho zavolajte na Permissions::for($holder)->in($scope).
- Relácie roles a permissions, getRoleNames(), getDirectPermissions() aj getAllPermissions() naďalej vypisujú len priradenia z 2.0 — úplný obraz dajú assignments(), roleNames() a permissionNames() na handli.
Na modeli
HasRoles pridáva skratky pre rozsahy. roleIn() započíta aj riadky bez rozsahu a roly z 2.0, kým je global_applies_in_scope zapnuté:
$user->hasRoleIn($project, 'editor'); // = Permissions::for($user)->in($project)->hasRole('editor')
$user->hasPermissionIn([$team, $parent], 'posts.edit'); // any of the scopes
User::query()->roleIn($project, 'editor')->get(); // query scope, window checked at the current UTC timeKeď sa rozsah zmaže
Rozsahy sú polymorfné, preto sa ich riadky nemôžu kaskádovo zmazať. Volajte Permissions::forgetScope() z hooku deleting každého modelu rozsahu — zmaže všetky priradenia v rozsahu aj všetky roly, ktoré vlastní, a vráti ich počet:
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Permissions\Facades\Permissions;
class Project extends Model
{
protected static function booted(): void
{
static::deleting(fn (self $project) => Permissions::forgetScope($project));
}
}Prečistenie skončených okien
Skončené okno neudeľuje nič, takže prečistenie je len hygiena tabuliek, nikdy otázka správnosti. Permissions::pruneExpired() zmaže riadky, ktorých okno skončilo do zadaného okamihu — predvolene teraz:
use RoundlyConsulting\Permissions\Facades\Permissions;
Permissions::pruneExpired(); // int — rows whose window ended by now
Permissions::pruneExpired(now()->subDays(30)); // keep 30 days of historyphp artisan permissions:prune-expired # every row whose window has ended
php artisan permissions:prune-expired --after="30 days" # keep rows that ended less than 30 days agoHodnota --after, ktorá nie je kladná dĺžka trvania, skončí kódom 2. Udalosti o blížiacej sa expirácii balík nemá — potrebovali by vlastné pravidelné prehľadávanie.
Cache
Riadky priradení si drží memo každého procesu podľa držiteľa, a to aj s oknami — nikdy ako áno či nie —, takže každá kontrola filtruje podľa hodín a dlho bežiaci worker nikdy nepoužije expirované priradenie. Memo sa vyprázdni pre držiteľa pri každom zápise a celé pri každom requeste, úlohe a ticku v Octane, pri každom jobe vo fronte, pri Permissions::cache()->flushMemo() a pri každom rollbacku transakcie. Medzi procesmi sa nič nezdieľa: priradenie zapísané iným procesom sa prejaví od nasledujúceho requestu či jobu. Zdieľaná cache katalógu sa nemení a roly — s vlastníkom či bez — do nej nikdy nevstupujú.
Stavanie nad touto funkciou
Pre balík či modul, ktorý overuje priradenia pre tím alebo projekt, je stabilné toto API: Permissions::for($member)->in($scopes)->hasPermissionTo() a hasRole() s viacerými rozsahmi, resolveScopeUsing() pre implicitný rozsah, in($scope)->forgetAllAuthorization() pri skončení členstva, forgetScope() pri zmazaní rozsahu, owner() pre roly definované rozsahom a Permissions::scope() pre volania Gate. Najprv overte PermissionRegistrar::assignmentsEnabled() — pri vypnutom prepínači tieto volania vyhodia výnimku. Presne takto je postavený most k permissions v teams-for-laravel.
Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.