first commit

This commit is contained in:
Sampanna Rimal
2024-08-27 17:48:06 +05:45
commit 53c0140f58
10839 changed files with 1125847 additions and 0 deletions

View File

@ -0,0 +1,22 @@
<?php
namespace Spatie\Permission\Commands;
use Illuminate\Console\Command;
use Spatie\Permission\PermissionRegistrar;
class CacheReset extends Command
{
protected $signature = 'permission:cache-reset';
protected $description = 'Reset the permission cache';
public function handle()
{
if (app(PermissionRegistrar::class)->forgetCachedPermissions()) {
$this->info('Permission cache flushed.');
} else {
$this->error('Unable to flush cache.');
}
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace Spatie\Permission\Commands;
use Illuminate\Console\Command;
use Spatie\Permission\Contracts\Permission as PermissionContract;
class CreatePermission extends Command
{
protected $signature = 'permission:create-permission
{name : The name of the permission}
{guard? : The name of the guard}';
protected $description = 'Create a permission';
public function handle()
{
$permissionClass = app(PermissionContract::class);
$permission = $permissionClass::findOrCreate($this->argument('name'), $this->argument('guard'));
$this->info("Permission `{$permission->name}` ".($permission->wasRecentlyCreated ? 'created' : 'already exists'));
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace Spatie\Permission\Commands;
use Illuminate\Console\Command;
use Spatie\Permission\Contracts\Permission as PermissionContract;
use Spatie\Permission\Contracts\Role as RoleContract;
use Spatie\Permission\PermissionRegistrar;
class CreateRole extends Command
{
protected $signature = 'permission:create-role
{name : The name of the role}
{guard? : The name of the guard}
{permissions? : A list of permissions to assign to the role, separated by | }
{--team-id=}';
protected $description = 'Create a role';
public function handle(PermissionRegistrar $permissionRegistrar)
{
$roleClass = app(RoleContract::class);
$teamIdAux = getPermissionsTeamId();
setPermissionsTeamId($this->option('team-id') ?: null);
if (! $permissionRegistrar->teams && $this->option('team-id')) {
$this->warn('Teams feature disabled, argument --team-id has no effect. Either enable it in permissions config file or remove --team-id parameter');
return;
}
$role = $roleClass::findOrCreate($this->argument('name'), $this->argument('guard'));
setPermissionsTeamId($teamIdAux);
$teams_key = $permissionRegistrar->teamsKey;
if ($permissionRegistrar->teams && $this->option('team-id') && is_null($role->$teams_key)) {
$this->warn("Role `{$role->name}` already exists on the global team; argument --team-id has no effect");
}
$role->givePermissionTo($this->makePermissions($this->argument('permissions')));
$this->info("Role `{$role->name}` ".($role->wasRecentlyCreated ? 'created' : 'updated'));
}
/**
* @param array|null|string $string
*/
protected function makePermissions($string = null)
{
if (empty($string)) {
return;
}
$permissionClass = app(PermissionContract::class);
$permissions = explode('|', $string);
$models = [];
foreach ($permissions as $permission) {
$models[] = $permissionClass::findOrCreate(trim($permission), $this->argument('guard'));
}
return collect($models);
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace Spatie\Permission\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Spatie\Permission\Contracts\Permission as PermissionContract;
use Spatie\Permission\Contracts\Role as RoleContract;
use Symfony\Component\Console\Helper\TableCell;
class Show extends Command
{
protected $signature = 'permission:show
{guard? : The name of the guard}
{style? : The display style (default|borderless|compact|box)}';
protected $description = 'Show a table of roles and permissions per guard';
public function handle()
{
$permissionClass = app(PermissionContract::class);
$roleClass = app(RoleContract::class);
$teamsEnabled = config('permission.teams');
$team_key = config('permission.column_names.team_foreign_key');
$style = $this->argument('style') ?? 'default';
$guard = $this->argument('guard');
if ($guard) {
$guards = Collection::make([$guard]);
} else {
$guards = $permissionClass::pluck('guard_name')->merge($roleClass::pluck('guard_name'))->unique();
}
foreach ($guards as $guard) {
$this->info("Guard: $guard");
$roles = $roleClass::whereGuardName($guard)
->with('permissions')
->when($teamsEnabled, fn ($q) => $q->orderBy($team_key))
->orderBy('name')->get()->mapWithKeys(fn ($role) => [
$role->name.'_'.($teamsEnabled ? ($role->$team_key ?: '') : '') => [
'permissions' => $role->permissions->pluck($permissionClass->getKeyName()),
$team_key => $teamsEnabled ? $role->$team_key : null,
],
]);
$permissions = $permissionClass::whereGuardName($guard)->orderBy('name')->pluck('name', $permissionClass->getKeyName());
$body = $permissions->map(fn ($permission, $id) => $roles->map(
fn (array $role_data) => $role_data['permissions']->contains($id) ? ' ✔' : ' ·'
)->prepend($permission)
);
if ($teamsEnabled) {
$teams = $roles->groupBy($team_key)->values()->map(
fn ($group, $id) => new TableCell('Team ID: '.($id ?: 'NULL'), ['colspan' => $group->count()])
);
}
$this->table(
array_merge(
isset($teams) ? $teams->prepend(new TableCell(''))->toArray() : [],
$roles->keys()->map(function ($val) {
$name = explode('_', $val);
array_pop($name);
return implode('_', $name);
})
->prepend(new TableCell(''))->toArray(),
),
$body->toArray(),
$style
);
}
}
}

View File

@ -0,0 +1,122 @@
<?php
namespace Spatie\Permission\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;
class UpgradeForTeams extends Command
{
protected $signature = 'permission:setup-teams';
protected $description = 'Setup the teams feature by generating the associated migration.';
protected $migrationSuffix = 'add_teams_fields.php';
public function handle()
{
if (! Config::get('permission.teams')) {
$this->error('Teams feature is disabled in your permission.php file.');
$this->warn('Please enable the teams setting in your configuration.');
return;
}
$this->line('');
$this->info('The teams feature setup is going to add a migration and a model');
$existingMigrations = $this->alreadyExistingMigrations();
if ($existingMigrations) {
$this->line('');
$this->warn($this->getExistingMigrationsWarning($existingMigrations));
}
$this->line('');
if (! $this->confirm('Proceed with the migration creation?', true)) {
return;
}
$this->line('');
$this->line('Creating migration');
if ($this->createMigration()) {
$this->info('Migration created successfully.');
} else {
$this->error(
"Couldn't create migration.\n".
'Check the write permissions within the database/migrations directory.'
);
}
$this->line('');
}
/**
* Create the migration.
*
* @return bool
*/
protected function createMigration()
{
try {
$migrationStub = __DIR__."/../../database/migrations/{$this->migrationSuffix}.stub";
copy($migrationStub, $this->getMigrationPath());
return true;
} catch (\Throwable $e) {
$this->error($e->getMessage());
return false;
}
}
/**
* Build a warning regarding possible duplication
* due to already existing migrations.
*
* @return string
*/
protected function getExistingMigrationsWarning(array $existingMigrations)
{
if (count($existingMigrations) > 1) {
$base = "Setup teams migrations already exist.\nFollowing files were found: ";
} else {
$base = "Setup teams migration already exists.\nFollowing file was found: ";
}
return $base.array_reduce($existingMigrations, fn ($carry, $fileName) => $carry."\n - ".$fileName);
}
/**
* Check if there is another migration
* with the same suffix.
*
* @return array
*/
protected function alreadyExistingMigrations()
{
$matchingFiles = glob($this->getMigrationPath('*'));
return array_map(fn ($path) => basename($path), $matchingFiles);
}
/**
* Get the migration path.
*
* The date parameter is optional for ability
* to provide a custom value or a wildcard.
*
* @param string|null $date
* @return string
*/
protected function getMigrationPath($date = null)
{
$date = $date ?: date('Y_m_d_His');
return database_path("migrations/{$date}_{$this->migrationSuffix}");
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Spatie\Permission\Contracts;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/**
* @property int|string $id
* @property string $name
* @property string|null $guard_name
*
* @mixin \Spatie\Permission\Models\Permission
*/
interface Permission
{
/**
* A permission can be applied to roles.
*/
public function roles(): BelongsToMany;
/**
* Find a permission by its name.
*
*
* @throws \Spatie\Permission\Exceptions\PermissionDoesNotExist
*/
public static function findByName(string $name, ?string $guardName): self;
/**
* Find a permission by its id.
*
*
* @throws \Spatie\Permission\Exceptions\PermissionDoesNotExist
*/
public static function findById(int|string $id, ?string $guardName): self;
/**
* Find or Create a permission by its name and guard name.
*/
public static function findOrCreate(string $name, ?string $guardName): self;
}

View File

@ -0,0 +1,48 @@
<?php
namespace Spatie\Permission\Contracts;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/**
* @property int|string $id
* @property string $name
* @property string|null $guard_name
*
* @mixin \Spatie\Permission\Models\Role
*/
interface Role
{
/**
* A role may be given various permissions.
*/
public function permissions(): BelongsToMany;
/**
* Find a role by its name and guard name.
*
*
* @throws \Spatie\Permission\Exceptions\RoleDoesNotExist
*/
public static function findByName(string $name, ?string $guardName): self;
/**
* Find a role by its id and guard name.
*
*
* @throws \Spatie\Permission\Exceptions\RoleDoesNotExist
*/
public static function findById(int|string $id, ?string $guardName): self;
/**
* Find or create a role by its name and guard name.
*/
public static function findOrCreate(string $name, ?string $guardName): self;
/**
* Determine if the user may perform the given permission.
*
* @param string|\Spatie\Permission\Contracts\Permission $permission
*/
public function hasPermissionTo($permission, ?string $guardName): bool;
}

View File

@ -0,0 +1,14 @@
<?php
namespace Spatie\Permission\Contracts;
use Illuminate\Database\Eloquent\Model;
interface Wildcard
{
public function __construct(Model $record);
public function getIndex(): array;
public function implies(string $permission, string $guardName, array $index): bool;
}

View File

@ -0,0 +1,14 @@
<?php
namespace Spatie\Permission\Exceptions;
use Illuminate\Support\Collection;
use InvalidArgumentException;
class GuardDoesNotMatch extends InvalidArgumentException
{
public static function create(string $givenGuard, Collection $expectedGuards)
{
return new static("The given role or permission should use guard `{$expectedGuards->implode(', ')}` instead of `{$givenGuard}`.");
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Spatie\Permission\Exceptions;
use InvalidArgumentException;
class PermissionAlreadyExists extends InvalidArgumentException
{
public static function create(string $permissionName, string $guardName)
{
return new static("A `{$permissionName}` permission already exists for guard `{$guardName}`.");
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace Spatie\Permission\Exceptions;
use InvalidArgumentException;
class PermissionDoesNotExist extends InvalidArgumentException
{
public static function create(string $permissionName, ?string $guardName)
{
return new static("There is no permission named `{$permissionName}` for guard `{$guardName}`.");
}
/**
* @param int|string $permissionId
* @return static
*/
public static function withId($permissionId, ?string $guardName)
{
return new static("There is no [permission] with ID `{$permissionId}` for guard `{$guardName}`.");
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Spatie\Permission\Exceptions;
use InvalidArgumentException;
class RoleAlreadyExists extends InvalidArgumentException
{
public static function create(string $roleName, string $guardName)
{
return new static("A role `{$roleName}` already exists for guard `{$guardName}`.");
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace Spatie\Permission\Exceptions;
use InvalidArgumentException;
class RoleDoesNotExist extends InvalidArgumentException
{
public static function named(string $roleName, ?string $guardName)
{
return new static("There is no role named `{$roleName}` for guard `{$guardName}`.");
}
/**
* @param int|string $roleId
* @return static
*/
public static function withId($roleId, ?string $guardName)
{
return new static("There is no role with ID `{$roleId}` for guard `{$guardName}`.");
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace Spatie\Permission\Exceptions;
use Illuminate\Contracts\Auth\Access\Authorizable;
use Symfony\Component\HttpKernel\Exception\HttpException;
class UnauthorizedException extends HttpException
{
private $requiredRoles = [];
private $requiredPermissions = [];
public static function forRoles(array $roles): self
{
$message = 'User does not have the right roles.';
if (config('permission.display_role_in_exception')) {
$message .= ' Necessary roles are '.implode(', ', $roles);
}
$exception = new static(403, $message, null, []);
$exception->requiredRoles = $roles;
return $exception;
}
public static function forPermissions(array $permissions): self
{
$message = 'User does not have the right permissions.';
if (config('permission.display_permission_in_exception')) {
$message .= ' Necessary permissions are '.implode(', ', $permissions);
}
$exception = new static(403, $message, null, []);
$exception->requiredPermissions = $permissions;
return $exception;
}
public static function forRolesOrPermissions(array $rolesOrPermissions): self
{
$message = 'User does not have any of the necessary access rights.';
if (config('permission.display_permission_in_exception') && config('permission.display_role_in_exception')) {
$message .= ' Necessary roles or permissions are '.implode(', ', $rolesOrPermissions);
}
$exception = new static(403, $message, null, []);
$exception->requiredPermissions = $rolesOrPermissions;
return $exception;
}
public static function missingTraitHasRoles(Authorizable $user): self
{
$class = get_class($user);
return new static(403, "Authorizable class `{$class}` must use Spatie\Permission\Traits\HasRoles trait.", null, []);
}
public static function notLoggedIn(): self
{
return new static(403, 'User is not logged in.', null, []);
}
public function getRequiredRoles(): array
{
return $this->requiredRoles;
}
public function getRequiredPermissions(): array
{
return $this->requiredPermissions;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Spatie\Permission\Exceptions;
use InvalidArgumentException;
class WildcardPermissionInvalidArgument extends InvalidArgumentException
{
public static function create()
{
return new static('Wildcard permission must be string, permission id or permission instance');
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Spatie\Permission\Exceptions;
use InvalidArgumentException;
class WildcardPermissionNotImplementsContract extends InvalidArgumentException
{
public static function create()
{
return new static('Wildcard permission class must implements Spatie\Permission\Contracts\Wildcard contract');
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Spatie\Permission\Exceptions;
use InvalidArgumentException;
class WildcardPermissionNotProperlyFormatted extends InvalidArgumentException
{
public static function create(string $permission)
{
return new static("Wildcard permission `{$permission}` is not properly formatted.");
}
}

View File

@ -0,0 +1,107 @@
<?php
namespace Spatie\Permission;
use Illuminate\Contracts\Auth\Access\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
class Guard
{
/**
* Return a collection of guard names suitable for the $model,
* as indicated by the presence of a $guard_name property or a guardName() method on the model.
*
* @param string|Model $model model class object or name
*/
public static function getNames($model): Collection
{
$class = is_object($model) ? get_class($model) : $model;
if (is_object($model)) {
if (\method_exists($model, 'guardName')) {
$guardName = $model->guardName();
} else {
$guardName = $model->getAttributeValue('guard_name');
}
}
if (! isset($guardName)) {
$guardName = (new \ReflectionClass($class))->getDefaultProperties()['guard_name'] ?? null;
}
if ($guardName) {
return collect($guardName);
}
return self::getConfigAuthGuards($class);
}
/**
* Get list of relevant guards for the $class model based on config(auth) settings.
*
* Lookup flow:
* - get names of models for guards defined in auth.guards where a provider is set
* - filter for provider models matching the model $class being checked (important for Lumen)
* - keys() gives just the names of the matched guards
* - return collection of guard names
*/
protected static function getConfigAuthGuards(string $class): Collection
{
return collect(config('auth.guards'))
->map(fn ($guard) => isset($guard['provider']) ? config("auth.providers.{$guard['provider']}.model") : null)
->filter(fn ($model) => $class === $model)
->keys();
}
/**
* Lookup a guard name relevant for the $class model and the current user.
*
* @param string|Model $class model class object or name
* @return string guard name
*/
public static function getDefaultName($class): string
{
$default = config('auth.defaults.guard');
$possible_guards = static::getNames($class);
// return current-detected auth.defaults.guard if it matches one of those that have been checked
if ($possible_guards->contains($default)) {
return $default;
}
return $possible_guards->first() ?: $default;
}
/**
* Lookup a passport guard
*/
public static function getPassportClient($guard): ?Authorizable
{
$guards = collect(config('auth.guards'))->where('driver', 'passport');
if (! $guards->count()) {
return null;
}
$authGuard = Auth::guard($guards->keys()[0]);
if (! \method_exists($authGuard, 'client')) {
return null;
}
$client = $authGuard->client();
if (! $guard || ! $client) {
return $client;
}
if (self::getNames($client)->contains($guard)) {
return $client;
}
return null;
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Spatie\Permission\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Spatie\Permission\Exceptions\UnauthorizedException;
use Spatie\Permission\Guard;
class PermissionMiddleware
{
public function handle($request, Closure $next, $permission, $guard = null)
{
$authGuard = Auth::guard($guard);
$user = $authGuard->user();
// For machine-to-machine Passport clients
if (! $user && $request->bearerToken() && config('permission.use_passport_client_credentials')) {
$user = Guard::getPassportClient($guard);
}
if (! $user) {
throw UnauthorizedException::notLoggedIn();
}
if (! method_exists($user, 'hasAnyPermission')) {
throw UnauthorizedException::missingTraitHasRoles($user);
}
$permissions = is_array($permission)
? $permission
: explode('|', $permission);
if (! $user->canAny($permissions)) {
throw UnauthorizedException::forPermissions($permissions);
}
return $next($request);
}
/**
* Specify the permission and guard for the middleware.
*
* @param array|string $permission
* @param string|null $guard
* @return string
*/
public static function using($permission, $guard = null)
{
$permissionString = is_string($permission) ? $permission : implode('|', $permission);
$args = is_null($guard) ? $permissionString : "$permissionString,$guard";
return static::class.':'.$args;
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Spatie\Permission\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Spatie\Permission\Exceptions\UnauthorizedException;
use Spatie\Permission\Guard;
class RoleMiddleware
{
public function handle($request, Closure $next, $role, $guard = null)
{
$authGuard = Auth::guard($guard);
$user = $authGuard->user();
// For machine-to-machine Passport clients
if (! $user && $request->bearerToken() && config('permission.use_passport_client_credentials')) {
$user = Guard::getPassportClient($guard);
}
if (! $user) {
throw UnauthorizedException::notLoggedIn();
}
if (! method_exists($user, 'hasAnyRole')) {
throw UnauthorizedException::missingTraitHasRoles($user);
}
$roles = is_array($role)
? $role
: explode('|', $role);
if (! $user->hasAnyRole($roles)) {
throw UnauthorizedException::forRoles($roles);
}
return $next($request);
}
/**
* Specify the role and guard for the middleware.
*
* @param array|string $role
* @param string|null $guard
* @return string
*/
public static function using($role, $guard = null)
{
$roleString = is_string($role) ? $role : implode('|', $role);
$args = is_null($guard) ? $roleString : "$roleString,$guard";
return static::class.':'.$args;
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Spatie\Permission\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Spatie\Permission\Exceptions\UnauthorizedException;
use Spatie\Permission\Guard;
class RoleOrPermissionMiddleware
{
public function handle($request, Closure $next, $roleOrPermission, $guard = null)
{
$authGuard = Auth::guard($guard);
$user = $authGuard->user();
// For machine-to-machine Passport clients
if (! $user && $request->bearerToken() && config('permission.use_passport_client_credentials')) {
$user = Guard::getPassportClient($guard);
}
if (! $user) {
throw UnauthorizedException::notLoggedIn();
}
if (! method_exists($user, 'hasAnyRole') || ! method_exists($user, 'hasAnyPermission')) {
throw UnauthorizedException::missingTraitHasRoles($user);
}
$rolesOrPermissions = is_array($roleOrPermission)
? $roleOrPermission
: explode('|', $roleOrPermission);
if (! $user->canAny($rolesOrPermissions) && ! $user->hasAnyRole($rolesOrPermissions)) {
throw UnauthorizedException::forRolesOrPermissions($rolesOrPermissions);
}
return $next($request);
}
/**
* Specify the role or permission and guard for the middleware.
*
* @param array|string $roleOrPermission
* @param string|null $guard
* @return string
*/
public static function using($roleOrPermission, $guard = null)
{
$roleOrPermissionString = is_string($roleOrPermission) ? $roleOrPermission : implode('|', $roleOrPermission);
$args = is_null($guard) ? $roleOrPermissionString : "$roleOrPermissionString,$guard";
return static::class.':'.$args;
}
}

View File

@ -0,0 +1,156 @@
<?php
namespace Spatie\Permission\Models;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Spatie\Permission\Contracts\Permission as PermissionContract;
use Spatie\Permission\Exceptions\PermissionAlreadyExists;
use Spatie\Permission\Exceptions\PermissionDoesNotExist;
use Spatie\Permission\Guard;
use Spatie\Permission\PermissionRegistrar;
use Spatie\Permission\Traits\HasRoles;
use Spatie\Permission\Traits\RefreshesPermissionCache;
/**
* @property ?\Illuminate\Support\Carbon $created_at
* @property ?\Illuminate\Support\Carbon $updated_at
*/
class Permission extends Model implements PermissionContract
{
use HasRoles;
use RefreshesPermissionCache;
protected $guarded = [];
public function __construct(array $attributes = [])
{
$attributes['guard_name'] = $attributes['guard_name'] ?? config('auth.defaults.guard');
parent::__construct($attributes);
$this->guarded[] = $this->primaryKey;
$this->table = config('permission.table_names.permissions') ?: parent::getTable();
}
/**
* @return PermissionContract|Permission
*
* @throws PermissionAlreadyExists
*/
public static function create(array $attributes = [])
{
$attributes['guard_name'] = $attributes['guard_name'] ?? Guard::getDefaultName(static::class);
$permission = static::getPermission(['name' => $attributes['name'], 'guard_name' => $attributes['guard_name']]);
if ($permission) {
throw PermissionAlreadyExists::create($attributes['name'], $attributes['guard_name']);
}
return static::query()->create($attributes);
}
/**
* A permission can be applied to roles.
*/
public function roles(): BelongsToMany
{
return $this->belongsToMany(
config('permission.models.role'),
config('permission.table_names.role_has_permissions'),
app(PermissionRegistrar::class)->pivotPermission,
app(PermissionRegistrar::class)->pivotRole
);
}
/**
* A permission belongs to some users of the model associated with its guard.
*/
public function users(): BelongsToMany
{
return $this->morphedByMany(
getModelForGuard($this->attributes['guard_name'] ?? config('auth.defaults.guard')),
'model',
config('permission.table_names.model_has_permissions'),
app(PermissionRegistrar::class)->pivotPermission,
config('permission.column_names.model_morph_key')
);
}
/**
* Find a permission by its name (and optionally guardName).
*
* @return PermissionContract|Permission
*
* @throws PermissionDoesNotExist
*/
public static function findByName(string $name, ?string $guardName = null): PermissionContract
{
$guardName = $guardName ?? Guard::getDefaultName(static::class);
$permission = static::getPermission(['name' => $name, 'guard_name' => $guardName]);
if (! $permission) {
throw PermissionDoesNotExist::create($name, $guardName);
}
return $permission;
}
/**
* Find a permission by its id (and optionally guardName).
*
* @return PermissionContract|Permission
*
* @throws PermissionDoesNotExist
*/
public static function findById(int|string $id, ?string $guardName = null): PermissionContract
{
$guardName = $guardName ?? Guard::getDefaultName(static::class);
$permission = static::getPermission([(new static())->getKeyName() => $id, 'guard_name' => $guardName]);
if (! $permission) {
throw PermissionDoesNotExist::withId($id, $guardName);
}
return $permission;
}
/**
* Find or create permission by its name (and optionally guardName).
*
* @return PermissionContract|Permission
*/
public static function findOrCreate(string $name, ?string $guardName = null): PermissionContract
{
$guardName = $guardName ?? Guard::getDefaultName(static::class);
$permission = static::getPermission(['name' => $name, 'guard_name' => $guardName]);
if (! $permission) {
return static::query()->create(['name' => $name, 'guard_name' => $guardName]);
}
return $permission;
}
/**
* Get the current cached permissions.
*/
protected static function getPermissions(array $params = [], bool $onlyOne = false): Collection
{
return app(PermissionRegistrar::class)
->setPermissionClass(static::class)
->getPermissions($params, $onlyOne);
}
/**
* Get the current cached first permission.
*
* @return PermissionContract|Permission|null
*/
protected static function getPermission(array $params = []): ?PermissionContract
{
/** @var PermissionContract|null */
return static::getPermissions($params, true)->first();
}
}

View File

@ -0,0 +1,193 @@
<?php
namespace Spatie\Permission\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Spatie\Permission\Contracts\Role as RoleContract;
use Spatie\Permission\Exceptions\GuardDoesNotMatch;
use Spatie\Permission\Exceptions\PermissionDoesNotExist;
use Spatie\Permission\Exceptions\RoleAlreadyExists;
use Spatie\Permission\Exceptions\RoleDoesNotExist;
use Spatie\Permission\Guard;
use Spatie\Permission\PermissionRegistrar;
use Spatie\Permission\Traits\HasPermissions;
use Spatie\Permission\Traits\RefreshesPermissionCache;
/**
* @property ?\Illuminate\Support\Carbon $created_at
* @property ?\Illuminate\Support\Carbon $updated_at
*/
class Role extends Model implements RoleContract
{
use HasPermissions;
use RefreshesPermissionCache;
protected $guarded = [];
public function __construct(array $attributes = [])
{
$attributes['guard_name'] = $attributes['guard_name'] ?? config('auth.defaults.guard');
parent::__construct($attributes);
$this->guarded[] = $this->primaryKey;
$this->table = config('permission.table_names.roles') ?: parent::getTable();
}
/**
* @return RoleContract|Role
*
* @throws RoleAlreadyExists
*/
public static function create(array $attributes = [])
{
$attributes['guard_name'] = $attributes['guard_name'] ?? Guard::getDefaultName(static::class);
$params = ['name' => $attributes['name'], 'guard_name' => $attributes['guard_name']];
if (app(PermissionRegistrar::class)->teams) {
$teamsKey = app(PermissionRegistrar::class)->teamsKey;
if (array_key_exists($teamsKey, $attributes)) {
$params[$teamsKey] = $attributes[$teamsKey];
} else {
$attributes[$teamsKey] = getPermissionsTeamId();
}
}
if (static::findByParam($params)) {
throw RoleAlreadyExists::create($attributes['name'], $attributes['guard_name']);
}
return static::query()->create($attributes);
}
/**
* A role may be given various permissions.
*/
public function permissions(): BelongsToMany
{
return $this->belongsToMany(
config('permission.models.permission'),
config('permission.table_names.role_has_permissions'),
app(PermissionRegistrar::class)->pivotRole,
app(PermissionRegistrar::class)->pivotPermission
);
}
/**
* A role belongs to some users of the model associated with its guard.
*/
public function users(): BelongsToMany
{
return $this->morphedByMany(
getModelForGuard($this->attributes['guard_name'] ?? config('auth.defaults.guard')),
'model',
config('permission.table_names.model_has_roles'),
app(PermissionRegistrar::class)->pivotRole,
config('permission.column_names.model_morph_key')
);
}
/**
* Find a role by its name and guard name.
*
* @return RoleContract|Role
*
* @throws RoleDoesNotExist
*/
public static function findByName(string $name, ?string $guardName = null): RoleContract
{
$guardName = $guardName ?? Guard::getDefaultName(static::class);
$role = static::findByParam(['name' => $name, 'guard_name' => $guardName]);
if (! $role) {
throw RoleDoesNotExist::named($name, $guardName);
}
return $role;
}
/**
* Find a role by its id (and optionally guardName).
*
* @return RoleContract|Role
*/
public static function findById(int|string $id, ?string $guardName = null): RoleContract
{
$guardName = $guardName ?? Guard::getDefaultName(static::class);
$role = static::findByParam([(new static())->getKeyName() => $id, 'guard_name' => $guardName]);
if (! $role) {
throw RoleDoesNotExist::withId($id, $guardName);
}
return $role;
}
/**
* Find or create role by its name (and optionally guardName).
*
* @return RoleContract|Role
*/
public static function findOrCreate(string $name, ?string $guardName = null): RoleContract
{
$guardName = $guardName ?? Guard::getDefaultName(static::class);
$role = static::findByParam(['name' => $name, 'guard_name' => $guardName]);
if (! $role) {
return static::query()->create(['name' => $name, 'guard_name' => $guardName] + (app(PermissionRegistrar::class)->teams ? [app(PermissionRegistrar::class)->teamsKey => getPermissionsTeamId()] : []));
}
return $role;
}
/**
* Finds a role based on an array of parameters.
*
* @return RoleContract|Role|null
*/
protected static function findByParam(array $params = []): ?RoleContract
{
$query = static::query();
if (app(PermissionRegistrar::class)->teams) {
$teamsKey = app(PermissionRegistrar::class)->teamsKey;
$query->where(fn ($q) => $q->whereNull($teamsKey)
->orWhere($teamsKey, $params[$teamsKey] ?? getPermissionsTeamId())
);
unset($params[$teamsKey]);
}
foreach ($params as $key => $value) {
$query->where($key, $value);
}
return $query->first();
}
/**
* Determine if the role may perform the given permission.
*
* @param string|int|Permission|\BackedEnum $permission
*
* @throws PermissionDoesNotExist|GuardDoesNotMatch
*/
public function hasPermissionTo($permission, ?string $guardName = null): bool
{
if ($this->getWildcardClass()) {
return $this->hasWildcardPermission($permission, $guardName);
}
$permission = $this->filterPermission($permission, $guardName);
if (! $this->getGuardNames()->contains($permission->guard_name)) {
throw GuardDoesNotMatch::create($permission->guard_name, $guardName ?? $this->getGuardNames());
}
return $this->permissions->contains($permission->getKeyName(), $permission->getKey());
}
}

View File

@ -0,0 +1,411 @@
<?php
namespace Spatie\Permission;
use Illuminate\Cache\CacheManager;
use Illuminate\Contracts\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Contracts\Permission;
use Spatie\Permission\Contracts\Role;
class PermissionRegistrar
{
protected Repository $cache;
protected CacheManager $cacheManager;
protected string $permissionClass;
protected string $roleClass;
/** @var Collection|array|null */
protected $permissions;
public string $pivotRole;
public string $pivotPermission;
/** @var \DateInterval|int */
public $cacheExpirationTime;
public bool $teams;
public string $teamsKey;
/** @var int|string */
protected $teamId = null;
public string $cacheKey;
private array $cachedRoles = [];
private array $alias = [];
private array $except = [];
private array $wildcardPermissionsIndex = [];
/**
* PermissionRegistrar constructor.
*/
public function __construct(CacheManager $cacheManager)
{
$this->permissionClass = config('permission.models.permission');
$this->roleClass = config('permission.models.role');
$this->cacheManager = $cacheManager;
$this->initializeCache();
}
public function initializeCache(): void
{
$this->cacheExpirationTime = config('permission.cache.expiration_time') ?: \DateInterval::createFromDateString('24 hours');
$this->teams = config('permission.teams', false);
$this->teamsKey = config('permission.column_names.team_foreign_key', 'team_id');
$this->cacheKey = config('permission.cache.key');
$this->pivotRole = config('permission.column_names.role_pivot_key') ?: 'role_id';
$this->pivotPermission = config('permission.column_names.permission_pivot_key') ?: 'permission_id';
$this->cache = $this->getCacheStoreFromConfig();
}
protected function getCacheStoreFromConfig(): Repository
{
// the 'default' fallback here is from the permission.php config file,
// where 'default' means to use config(cache.default)
$cacheDriver = config('permission.cache.store', 'default');
// when 'default' is specified, no action is required since we already have the default instance
if ($cacheDriver === 'default') {
return $this->cacheManager->store();
}
// if an undefined cache store is specified, fallback to 'array' which is Laravel's closest equiv to 'none'
if (! \array_key_exists($cacheDriver, config('cache.stores'))) {
$cacheDriver = 'array';
}
return $this->cacheManager->store($cacheDriver);
}
/**
* Set the team id for teams/groups support, this id is used when querying permissions/roles
*
* @param int|string|\Illuminate\Database\Eloquent\Model|null $id
*/
public function setPermissionsTeamId($id): void
{
if ($id instanceof \Illuminate\Database\Eloquent\Model) {
$id = $id->getKey();
}
$this->teamId = $id;
}
/**
* @return int|string|null
*/
public function getPermissionsTeamId()
{
return $this->teamId;
}
/**
* Register the permission check method on the gate.
* We resolve the Gate fresh here, for benefit of long-running instances.
*/
public function registerPermissions(Gate $gate): bool
{
$gate->before(function (Authorizable $user, string $ability, array &$args = []) {
if (is_string($args[0] ?? null) && ! class_exists($args[0])) {
$guard = array_shift($args);
}
if (method_exists($user, 'checkPermissionTo')) {
return $user->checkPermissionTo($ability, $guard ?? null) ?: null;
}
});
return true;
}
/**
* Flush the cache.
*/
public function forgetCachedPermissions()
{
$this->permissions = null;
$this->forgetWildcardPermissionIndex();
return $this->cache->forget($this->cacheKey);
}
public function forgetWildcardPermissionIndex(?Model $record = null): void
{
if ($record) {
unset($this->wildcardPermissionsIndex[get_class($record)][$record->getKey()]);
return;
}
$this->wildcardPermissionsIndex = [];
}
public function getWildcardPermissionIndex(Model $record): array
{
if (isset($this->wildcardPermissionsIndex[get_class($record)][$record->getKey()])) {
return $this->wildcardPermissionsIndex[get_class($record)][$record->getKey()];
}
return $this->wildcardPermissionsIndex[get_class($record)][$record->getKey()] = app($record->getWildcardClass(), ['record' => $record])->getIndex();
}
/**
* Clear already-loaded permissions collection.
* This is only intended to be called by the PermissionServiceProvider on boot,
* so that long-running instances like Octane or Swoole don't keep old data in memory.
*/
public function clearPermissionsCollection(): void
{
$this->permissions = null;
$this->wildcardPermissionsIndex = [];
}
/**
* @deprecated
*
* @alias of clearPermissionsCollection()
*/
public function clearClassPermissions()
{
$this->clearPermissionsCollection();
}
/**
* Load permissions from cache
* And turns permissions array into a \Illuminate\Database\Eloquent\Collection
*/
private function loadPermissions(): void
{
if ($this->permissions) {
return;
}
$this->permissions = $this->cache->remember(
$this->cacheKey, $this->cacheExpirationTime, fn () => $this->getSerializedPermissionsForCache()
);
// fallback for old cache method, must be removed on next mayor version
if (! isset($this->permissions['alias'])) {
$this->forgetCachedPermissions();
$this->loadPermissions();
return;
}
$this->alias = $this->permissions['alias'];
$this->hydrateRolesCache();
$this->permissions = $this->getHydratedPermissionCollection();
$this->cachedRoles = $this->alias = $this->except = [];
}
/**
* Get the permissions based on the passed params.
*/
public function getPermissions(array $params = [], bool $onlyOne = false): Collection
{
$this->loadPermissions();
$method = $onlyOne ? 'first' : 'filter';
$permissions = $this->permissions->$method(static function ($permission) use ($params) {
foreach ($params as $attr => $value) {
if ($permission->getAttribute($attr) != $value) {
return false;
}
}
return true;
});
if ($onlyOne) {
$permissions = new Collection($permissions ? [$permissions] : []);
}
return $permissions;
}
public function getPermissionClass(): string
{
return $this->permissionClass;
}
public function setPermissionClass($permissionClass)
{
$this->permissionClass = $permissionClass;
config()->set('permission.models.permission', $permissionClass);
app()->bind(Permission::class, $permissionClass);
return $this;
}
public function getRoleClass(): string
{
return $this->roleClass;
}
public function setRoleClass($roleClass)
{
$this->roleClass = $roleClass;
config()->set('permission.models.role', $roleClass);
app()->bind(Role::class, $roleClass);
return $this;
}
public function getCacheRepository(): Repository
{
return $this->cache;
}
public function getCacheStore(): Store
{
return $this->cache->getStore();
}
protected function getPermissionsWithRoles(): Collection
{
return $this->permissionClass::select()->with('roles')->get();
}
/**
* Changes array keys with alias
*/
private function aliasedArray($model): array
{
return collect(is_array($model) ? $model : $model->getAttributes())->except($this->except)
->keyBy(fn ($value, $key) => $this->alias[$key] ?? $key)
->all();
}
/**
* Array for cache alias
*/
private function aliasModelFields($newKeys = []): void
{
$i = 0;
$alphas = ! count($this->alias) ? range('a', 'h') : range('j', 'p');
foreach (array_keys($newKeys->getAttributes()) as $value) {
if (! isset($this->alias[$value])) {
$this->alias[$value] = $alphas[$i++] ?? $value;
}
}
$this->alias = array_diff_key($this->alias, array_flip($this->except));
}
/*
* Make the cache smaller using an array with only required fields
*/
private function getSerializedPermissionsForCache(): array
{
$this->except = config('permission.cache.column_names_except', ['created_at', 'updated_at', 'deleted_at']);
$permissions = $this->getPermissionsWithRoles()
->map(function ($permission) {
if (! $this->alias) {
$this->aliasModelFields($permission);
}
return $this->aliasedArray($permission) + $this->getSerializedRoleRelation($permission);
})->all();
$roles = array_values($this->cachedRoles);
$this->cachedRoles = [];
return ['alias' => array_flip($this->alias)] + compact('permissions', 'roles');
}
private function getSerializedRoleRelation($permission): array
{
if (! $permission->roles->count()) {
return [];
}
if (! isset($this->alias['roles'])) {
$this->alias['roles'] = 'r';
$this->aliasModelFields($permission->roles[0]);
}
return [
'r' => $permission->roles->map(function ($role) {
if (! isset($this->cachedRoles[$role->getKey()])) {
$this->cachedRoles[$role->getKey()] = $this->aliasedArray($role);
}
return $role->getKey();
})->all(),
];
}
private function getHydratedPermissionCollection(): Collection
{
$permissionInstance = new ($this->getPermissionClass())();
return Collection::make(array_map(
fn ($item) => $permissionInstance->newInstance([], true)
->setRawAttributes($this->aliasedArray(array_diff_key($item, ['r' => 0])), true)
->setRelation('roles', $this->getHydratedRoleCollection($item['r'] ?? [])),
$this->permissions['permissions']
));
}
private function getHydratedRoleCollection(array $roles): Collection
{
return Collection::make(array_values(
array_intersect_key($this->cachedRoles, array_flip($roles))
));
}
private function hydrateRolesCache(): void
{
$roleInstance = new ($this->getRoleClass())();
array_map(function ($item) use ($roleInstance) {
$role = $roleInstance->newInstance([], true)
->setRawAttributes($this->aliasedArray($item), true);
$this->cachedRoles[$role->getKey()] = $role;
}, $this->permissions['roles']);
$this->permissions['roles'] = [];
}
public static function isUid($value): bool
{
if (! is_string($value) || empty(trim($value))) {
return false;
}
// check if is UUID/GUID
$uid = preg_match('/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value) > 0;
if ($uid) {
return true;
}
// check if is ULID
$ulid = strlen($value) == 26 && strspn($value, '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz') == 26 && $value[0] <= '7';
if ($ulid) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,202 @@
<?php
namespace Spatie\Permission;
use Composer\InstalledVersions;
use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Console\AboutCommand;
use Illuminate\Routing\Route;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;
use Illuminate\View\Compilers\BladeCompiler;
use Spatie\Permission\Contracts\Permission as PermissionContract;
use Spatie\Permission\Contracts\Role as RoleContract;
class PermissionServiceProvider extends ServiceProvider
{
public function boot()
{
$this->offerPublishing();
$this->registerMacroHelpers();
$this->registerCommands();
$this->registerModelBindings();
$this->registerOctaneListener();
$this->callAfterResolving(Gate::class, function (Gate $gate, Application $app) {
if ($this->app['config']->get('permission.register_permission_check_method')) {
/** @var PermissionRegistrar $permissionLoader */
$permissionLoader = $app->get(PermissionRegistrar::class);
$permissionLoader->clearPermissionsCollection();
$permissionLoader->registerPermissions($gate);
}
});
$this->app->singleton(PermissionRegistrar::class);
$this->registerAbout();
}
public function register()
{
$this->mergeConfigFrom(
__DIR__.'/../config/permission.php',
'permission'
);
$this->callAfterResolving('blade.compiler', fn (BladeCompiler $bladeCompiler) => $this->registerBladeExtensions($bladeCompiler));
}
protected function offerPublishing(): void
{
if (! $this->app->runningInConsole()) {
return;
}
if (! function_exists('config_path')) {
// function not available and 'publish' not relevant in Lumen
return;
}
$this->publishes([
__DIR__.'/../config/permission.php' => config_path('permission.php'),
], 'permission-config');
$this->publishes([
__DIR__.'/../database/migrations/create_permission_tables.php.stub' => $this->getMigrationFileName('create_permission_tables.php'),
], 'permission-migrations');
}
protected function registerCommands(): void
{
$this->commands([
Commands\CacheReset::class,
]);
if (! $this->app->runningInConsole()) {
return;
}
$this->commands([
Commands\CreateRole::class,
Commands\CreatePermission::class,
Commands\Show::class,
Commands\UpgradeForTeams::class,
]);
}
protected function registerOctaneListener(): void
{
if ($this->app->runningInConsole() || ! $this->app['config']->get('octane.listeners')) {
return;
}
$dispatcher = $this->app[Dispatcher::class];
// @phpstan-ignore-next-line
$dispatcher->listen(function (\Laravel\Octane\Contracts\OperationTerminated $event) {
// @phpstan-ignore-next-line
$event->sandbox->make(PermissionRegistrar::class)->setPermissionsTeamId(null);
});
if (! $this->app['config']->get('permission.register_octane_reset_listener')) {
return;
}
// @phpstan-ignore-next-line
$dispatcher->listen(function (\Laravel\Octane\Contracts\OperationTerminated $event) {
// @phpstan-ignore-next-line
$event->sandbox->make(PermissionRegistrar::class)->clearPermissionsCollection();
});
}
protected function registerModelBindings(): void
{
$this->app->bind(PermissionContract::class, fn ($app) => $app->make($app->config['permission.models.permission']));
$this->app->bind(RoleContract::class, fn ($app) => $app->make($app->config['permission.models.role']));
}
public static function bladeMethodWrapper($method, $role, $guard = null): bool
{
return auth($guard)->check() && auth($guard)->user()->{$method}($role);
}
protected function registerBladeExtensions(BladeCompiler $bladeCompiler): void
{
$bladeMethodWrapper = '\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper';
// permission checks
$bladeCompiler->if('haspermission', fn () => $bladeMethodWrapper('checkPermissionTo', ...func_get_args()));
// role checks
$bladeCompiler->if('role', fn () => $bladeMethodWrapper('hasRole', ...func_get_args()));
$bladeCompiler->if('hasrole', fn () => $bladeMethodWrapper('hasRole', ...func_get_args()));
$bladeCompiler->if('hasanyrole', fn () => $bladeMethodWrapper('hasAnyRole', ...func_get_args()));
$bladeCompiler->if('hasallroles', fn () => $bladeMethodWrapper('hasAllRoles', ...func_get_args()));
$bladeCompiler->if('hasexactroles', fn () => $bladeMethodWrapper('hasExactRoles', ...func_get_args()));
$bladeCompiler->directive('endunlessrole', fn () => '<?php endif; ?>');
}
protected function registerMacroHelpers(): void
{
if (! method_exists(Route::class, 'macro')) { // Lumen
return;
}
Route::macro('role', function ($roles = []) {
/** @var Route $this */
return $this->middleware('role:'.implode('|', Arr::wrap($roles)));
});
Route::macro('permission', function ($permissions = []) {
/** @var Route $this */
return $this->middleware('permission:'.implode('|', Arr::wrap($permissions)));
});
}
/**
* Returns existing migration file if found, else uses the current timestamp.
*/
protected function getMigrationFileName(string $migrationFileName): string
{
$timestamp = date('Y_m_d_His');
$filesystem = $this->app->make(Filesystem::class);
return Collection::make([$this->app->databasePath().DIRECTORY_SEPARATOR.'migrations'.DIRECTORY_SEPARATOR])
->flatMap(fn ($path) => $filesystem->glob($path.'*_'.$migrationFileName))
->push($this->app->databasePath()."/migrations/{$timestamp}_{$migrationFileName}")
->first();
}
protected function registerAbout(): void
{
if (! class_exists(InstalledVersions::class) || ! class_exists(AboutCommand::class)) {
return;
}
// array format: 'Display Text' => 'boolean-config-key name'
$features = [
'Teams' => 'teams',
'Wildcard-Permissions' => 'enable_wildcard_permission',
'Octane-Listener' => 'register_octane_reset_listener',
'Passport' => 'use_passport_client_credentials',
];
$config = $this->app['config'];
AboutCommand::add('Spatie Permissions', static fn () => [
'Features Enabled' => collect($features)
->filter(fn (string $feature, string $name): bool => $config->get("permission.{$feature}"))
->keys()
->whenEmpty(fn (Collection $collection) => $collection->push('Default'))
->join(', '),
'Version' => InstalledVersions::getPrettyVersion('spatie/laravel-permission'),
]);
}
}

View File

@ -0,0 +1,575 @@
<?php
namespace Spatie\Permission\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Spatie\Permission\Contracts\Permission;
use Spatie\Permission\Contracts\Role;
use Spatie\Permission\Contracts\Wildcard;
use Spatie\Permission\Exceptions\GuardDoesNotMatch;
use Spatie\Permission\Exceptions\PermissionDoesNotExist;
use Spatie\Permission\Exceptions\WildcardPermissionInvalidArgument;
use Spatie\Permission\Exceptions\WildcardPermissionNotImplementsContract;
use Spatie\Permission\Guard;
use Spatie\Permission\PermissionRegistrar;
use Spatie\Permission\WildcardPermission;
trait HasPermissions
{
private ?string $permissionClass = null;
private ?string $wildcardClass = null;
private array $wildcardPermissionsIndex;
public static function bootHasPermissions()
{
static::deleting(function ($model) {
if (method_exists($model, 'isForceDeleting') && ! $model->isForceDeleting()) {
return;
}
$teams = app(PermissionRegistrar::class)->teams;
app(PermissionRegistrar::class)->teams = false;
if (! is_a($model, Permission::class)) {
$model->permissions()->detach();
}
if (is_a($model, Role::class)) {
$model->users()->detach();
}
app(PermissionRegistrar::class)->teams = $teams;
});
}
public function getPermissionClass(): string
{
if (! $this->permissionClass) {
$this->permissionClass = app(PermissionRegistrar::class)->getPermissionClass();
}
return $this->permissionClass;
}
public function getWildcardClass()
{
if (! is_null($this->wildcardClass)) {
return $this->wildcardClass;
}
$this->wildcardClass = '';
if (config('permission.enable_wildcard_permission')) {
$this->wildcardClass = config('permission.wildcard_permission', WildcardPermission::class);
if (! is_subclass_of($this->wildcardClass, Wildcard::class)) {
throw WildcardPermissionNotImplementsContract::create();
}
}
return $this->wildcardClass;
}
/**
* A model may have multiple direct permissions.
*/
public function permissions(): BelongsToMany
{
$relation = $this->morphToMany(
config('permission.models.permission'),
'model',
config('permission.table_names.model_has_permissions'),
config('permission.column_names.model_morph_key'),
app(PermissionRegistrar::class)->pivotPermission
);
if (! app(PermissionRegistrar::class)->teams) {
return $relation;
}
return $relation->wherePivot(app(PermissionRegistrar::class)->teamsKey, getPermissionsTeamId());
}
/**
* Scope the model query to certain permissions only.
*
* @param string|int|array|Permission|Collection|\BackedEnum $permissions
* @param bool $without
*/
public function scopePermission(Builder $query, $permissions, $without = false): Builder
{
$permissions = $this->convertToPermissionModels($permissions);
$permissionKey = (new ($this->getPermissionClass())())->getKeyName();
$roleKey = (new (is_a($this, Role::class) ? static::class : $this->getRoleClass())())->getKeyName();
$rolesWithPermissions = is_a($this, Role::class) ? [] : array_unique(
array_reduce($permissions, fn ($result, $permission) => array_merge($result, $permission->roles->all()), [])
);
return $query->where(fn (Builder $query) => $query
->{! $without ? 'whereHas' : 'whereDoesntHave'}('permissions', fn (Builder $subQuery) => $subQuery
->whereIn(config('permission.table_names.permissions').".$permissionKey", \array_column($permissions, $permissionKey))
)
->when(count($rolesWithPermissions), fn ($whenQuery) => $whenQuery
->{! $without ? 'orWhereHas' : 'whereDoesntHave'}('roles', fn (Builder $subQuery) => $subQuery
->whereIn(config('permission.table_names.roles').".$roleKey", \array_column($rolesWithPermissions, $roleKey))
)
)
);
}
/**
* Scope the model query to only those without certain permissions,
* whether indirectly by role or by direct permission.
*
* @param string|int|array|Permission|Collection|\BackedEnum $permissions
*/
public function scopeWithoutPermission(Builder $query, $permissions): Builder
{
return $this->scopePermission($query, $permissions, true);
}
/**
* @param string|int|array|Permission|Collection|\BackedEnum $permissions
*
* @throws PermissionDoesNotExist
*/
protected function convertToPermissionModels($permissions): array
{
if ($permissions instanceof Collection) {
$permissions = $permissions->all();
}
return array_map(function ($permission) {
if ($permission instanceof Permission) {
return $permission;
}
if ($permission instanceof \BackedEnum) {
$permission = $permission->value;
}
$method = is_int($permission) || PermissionRegistrar::isUid($permission) ? 'findById' : 'findByName';
return $this->getPermissionClass()::{$method}($permission, $this->getDefaultGuardName());
}, Arr::wrap($permissions));
}
/**
* Find a permission.
*
* @param string|int|Permission|\BackedEnum $permission
* @return Permission
*
* @throws PermissionDoesNotExist
*/
public function filterPermission($permission, $guardName = null)
{
if ($permission instanceof \BackedEnum) {
$permission = $permission->value;
}
if (is_int($permission) || PermissionRegistrar::isUid($permission)) {
$permission = $this->getPermissionClass()::findById(
$permission,
$guardName ?? $this->getDefaultGuardName()
);
}
if (is_string($permission)) {
$permission = $this->getPermissionClass()::findByName(
$permission,
$guardName ?? $this->getDefaultGuardName()
);
}
if (! $permission instanceof Permission) {
throw new PermissionDoesNotExist();
}
return $permission;
}
/**
* Determine if the model may perform the given permission.
*
* @param string|int|Permission|\BackedEnum $permission
* @param string|null $guardName
*
* @throws PermissionDoesNotExist
*/
public function hasPermissionTo($permission, $guardName = null): bool
{
if ($this->getWildcardClass()) {
return $this->hasWildcardPermission($permission, $guardName);
}
$permission = $this->filterPermission($permission, $guardName);
return $this->hasDirectPermission($permission) || $this->hasPermissionViaRole($permission);
}
/**
* Validates a wildcard permission against all permissions of a user.
*
* @param string|int|Permission|\BackedEnum $permission
* @param string|null $guardName
*/
protected function hasWildcardPermission($permission, $guardName = null): bool
{
$guardName = $guardName ?? $this->getDefaultGuardName();
if ($permission instanceof \BackedEnum) {
$permission = $permission->value;
}
if (is_int($permission) || PermissionRegistrar::isUid($permission)) {
$permission = $this->getPermissionClass()::findById($permission, $guardName);
}
if ($permission instanceof Permission) {
$guardName = $permission->guard_name ?? $guardName;
$permission = $permission->name;
}
if (! is_string($permission)) {
throw WildcardPermissionInvalidArgument::create();
}
return app($this->getWildcardClass(), ['record' => $this])->implies(
$permission,
$guardName,
app(PermissionRegistrar::class)->getWildcardPermissionIndex($this),
);
}
/**
* An alias to hasPermissionTo(), but avoids throwing an exception.
*
* @param string|int|Permission|\BackedEnum $permission
* @param string|null $guardName
*/
public function checkPermissionTo($permission, $guardName = null): bool
{
try {
return $this->hasPermissionTo($permission, $guardName);
} catch (PermissionDoesNotExist $e) {
return false;
}
}
/**
* Determine if the model has any of the given permissions.
*
* @param string|int|array|Permission|Collection|\BackedEnum ...$permissions
*/
public function hasAnyPermission(...$permissions): bool
{
$permissions = collect($permissions)->flatten();
foreach ($permissions as $permission) {
if ($this->checkPermissionTo($permission)) {
return true;
}
}
return false;
}
/**
* Determine if the model has all of the given permissions.
*
* @param string|int|array|Permission|Collection|\BackedEnum ...$permissions
*/
public function hasAllPermissions(...$permissions): bool
{
$permissions = collect($permissions)->flatten();
foreach ($permissions as $permission) {
if (! $this->checkPermissionTo($permission)) {
return false;
}
}
return true;
}
/**
* Determine if the model has, via roles, the given permission.
*/
protected function hasPermissionViaRole(Permission $permission): bool
{
if (is_a($this, Role::class)) {
return false;
}
return $this->hasRole($permission->roles);
}
/**
* Determine if the model has the given permission.
*
* @param string|int|Permission|\BackedEnum $permission
*
* @throws PermissionDoesNotExist
*/
public function hasDirectPermission($permission): bool
{
$permission = $this->filterPermission($permission);
return $this->permissions->contains($permission->getKeyName(), $permission->getKey());
}
/**
* Return all the permissions the model has via roles.
*/
public function getPermissionsViaRoles(): Collection
{
if (is_a($this, Role::class) || is_a($this, Permission::class)) {
return collect();
}
return $this->loadMissing('roles', 'roles.permissions')
->roles->flatMap(fn ($role) => $role->permissions)
->sort()->values();
}
/**
* Return all the permissions the model has, both directly and via roles.
*/
public function getAllPermissions(): Collection
{
/** @var Collection $permissions */
$permissions = $this->permissions;
if (method_exists($this, 'roles')) {
$permissions = $permissions->merge($this->getPermissionsViaRoles());
}
return $permissions->sort()->values();
}
/**
* Returns array of permissions ids
*
* @param string|int|array|Permission|Collection|\BackedEnum $permissions
*/
private function collectPermissions(...$permissions): array
{
return collect($permissions)
->flatten()
->reduce(function ($array, $permission) {
if (empty($permission)) {
return $array;
}
$permission = $this->getStoredPermission($permission);
if (! $permission instanceof Permission) {
return $array;
}
if (! in_array($permission->getKey(), $array)) {
$this->ensureModelSharesGuard($permission);
$array[] = $permission->getKey();
}
return $array;
}, []);
}
/**
* Grant the given permission(s) to a role.
*
* @param string|int|array|Permission|Collection|\BackedEnum $permissions
* @return $this
*/
public function givePermissionTo(...$permissions)
{
$permissions = $this->collectPermissions($permissions);
$model = $this->getModel();
$teamPivot = app(PermissionRegistrar::class)->teams && ! is_a($this, Role::class) ?
[app(PermissionRegistrar::class)->teamsKey => getPermissionsTeamId()] : [];
if ($model->exists) {
$currentPermissions = $this->permissions->map(fn ($permission) => $permission->getKey())->toArray();
$this->permissions()->attach(array_diff($permissions, $currentPermissions), $teamPivot);
$model->unsetRelation('permissions');
} else {
$class = \get_class($model);
$class::saved(
function ($object) use ($permissions, $model, $teamPivot) {
if ($model->getKey() != $object->getKey()) {
return;
}
$model->permissions()->attach($permissions, $teamPivot);
$model->unsetRelation('permissions');
}
);
}
if (is_a($this, Role::class)) {
$this->forgetCachedPermissions();
}
$this->forgetWildcardPermissionIndex();
return $this;
}
public function forgetWildcardPermissionIndex(): void
{
app(PermissionRegistrar::class)->forgetWildcardPermissionIndex(
is_a($this, Role::class) ? null : $this,
);
}
/**
* Remove all current permissions and set the given ones.
*
* @param string|int|array|Permission|Collection|\BackedEnum $permissions
* @return $this
*/
public function syncPermissions(...$permissions)
{
if ($this->getModel()->exists) {
$this->collectPermissions($permissions);
$this->permissions()->detach();
$this->setRelation('permissions', collect());
}
return $this->givePermissionTo($permissions);
}
/**
* Revoke the given permission(s).
*
* @param Permission|Permission[]|string|string[]|\BackedEnum $permission
* @return $this
*/
public function revokePermissionTo($permission)
{
$this->permissions()->detach($this->getStoredPermission($permission));
if (is_a($this, Role::class)) {
$this->forgetCachedPermissions();
}
$this->forgetWildcardPermissionIndex();
$this->unsetRelation('permissions');
return $this;
}
public function getPermissionNames(): Collection
{
return $this->permissions->pluck('name');
}
/**
* @param string|int|array|Permission|Collection|\BackedEnum $permissions
* @return Permission|Permission[]|Collection
*/
protected function getStoredPermission($permissions)
{
if ($permissions instanceof \BackedEnum) {
$permissions = $permissions->value;
}
if (is_int($permissions) || PermissionRegistrar::isUid($permissions)) {
return $this->getPermissionClass()::findById($permissions, $this->getDefaultGuardName());
}
if (is_string($permissions)) {
return $this->getPermissionClass()::findByName($permissions, $this->getDefaultGuardName());
}
if (is_array($permissions)) {
$permissions = array_map(function ($permission) {
if ($permission instanceof \BackedEnum) {
return $permission->value;
}
return is_a($permission, Permission::class) ? $permission->name : $permission;
}, $permissions);
return $this->getPermissionClass()::whereIn('name', $permissions)
->whereIn('guard_name', $this->getGuardNames())
->get();
}
return $permissions;
}
/**
* @param Permission|Role $roleOrPermission
*
* @throws GuardDoesNotMatch
*/
protected function ensureModelSharesGuard($roleOrPermission)
{
if (! $this->getGuardNames()->contains($roleOrPermission->guard_name)) {
throw GuardDoesNotMatch::create($roleOrPermission->guard_name, $this->getGuardNames());
}
}
protected function getGuardNames(): Collection
{
return Guard::getNames($this);
}
protected function getDefaultGuardName(): string
{
return Guard::getDefaultName($this);
}
/**
* Forget the cached permissions.
*/
public function forgetCachedPermissions()
{
app(PermissionRegistrar::class)->forgetCachedPermissions();
}
/**
* Check if the model has All of the requested Direct permissions.
*
* @param string|int|array|Permission|Collection|\BackedEnum ...$permissions
*/
public function hasAllDirectPermissions(...$permissions): bool
{
$permissions = collect($permissions)->flatten();
foreach ($permissions as $permission) {
if (! $this->hasDirectPermission($permission)) {
return false;
}
}
return true;
}
/**
* Check if the model has Any of the requested Direct permissions.
*
* @param string|int|array|Permission|Collection|\BackedEnum ...$permissions
*/
public function hasAnyDirectPermission(...$permissions): bool
{
$permissions = collect($permissions)->flatten();
foreach ($permissions as $permission) {
if ($this->hasDirectPermission($permission)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,418 @@
<?php
namespace Spatie\Permission\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Spatie\Permission\Contracts\Permission;
use Spatie\Permission\Contracts\Role;
use Spatie\Permission\PermissionRegistrar;
trait HasRoles
{
use HasPermissions;
private ?string $roleClass = null;
public static function bootHasRoles()
{
static::deleting(function ($model) {
if (method_exists($model, 'isForceDeleting') && ! $model->isForceDeleting()) {
return;
}
$teams = app(PermissionRegistrar::class)->teams;
app(PermissionRegistrar::class)->teams = false;
$model->roles()->detach();
if (is_a($model, Permission::class)) {
$model->users()->detach();
}
app(PermissionRegistrar::class)->teams = $teams;
});
}
public function getRoleClass(): string
{
if (! $this->roleClass) {
$this->roleClass = app(PermissionRegistrar::class)->getRoleClass();
}
return $this->roleClass;
}
/**
* A model may have multiple roles.
*/
public function roles(): BelongsToMany
{
$relation = $this->morphToMany(
config('permission.models.role'),
'model',
config('permission.table_names.model_has_roles'),
config('permission.column_names.model_morph_key'),
app(PermissionRegistrar::class)->pivotRole
);
if (! app(PermissionRegistrar::class)->teams) {
return $relation;
}
$teamField = config('permission.table_names.roles').'.'.app(PermissionRegistrar::class)->teamsKey;
return $relation->wherePivot(app(PermissionRegistrar::class)->teamsKey, getPermissionsTeamId())
->where(fn ($q) => $q->whereNull($teamField)->orWhere($teamField, getPermissionsTeamId()));
}
/**
* Scope the model query to certain roles only.
*
* @param string|int|array|Role|Collection|\BackedEnum $roles
* @param string $guard
* @param bool $without
*/
public function scopeRole(Builder $query, $roles, $guard = null, $without = false): Builder
{
if ($roles instanceof Collection) {
$roles = $roles->all();
}
$roles = array_map(function ($role) use ($guard) {
if ($role instanceof Role) {
return $role;
}
if ($role instanceof \BackedEnum) {
$role = $role->value;
}
$method = is_int($role) || PermissionRegistrar::isUid($role) ? 'findById' : 'findByName';
return $this->getRoleClass()::{$method}($role, $guard ?: $this->getDefaultGuardName());
}, Arr::wrap($roles));
$key = (new ($this->getRoleClass())())->getKeyName();
return $query->{! $without ? 'whereHas' : 'whereDoesntHave'}('roles', fn (Builder $subQuery) => $subQuery
->whereIn(config('permission.table_names.roles').".$key", \array_column($roles, $key))
);
}
/**
* Scope the model query to only those without certain roles.
*
* @param string|int|array|Role|Collection|\BackedEnum $roles
* @param string $guard
*/
public function scopeWithoutRole(Builder $query, $roles, $guard = null): Builder
{
return $this->scopeRole($query, $roles, $guard, true);
}
/**
* Returns array of role ids
*
* @param string|int|array|Role|Collection|\BackedEnum $roles
*/
private function collectRoles(...$roles): array
{
return collect($roles)
->flatten()
->reduce(function ($array, $role) {
if (empty($role)) {
return $array;
}
$role = $this->getStoredRole($role);
if (! $role instanceof Role) {
return $array;
}
if (! in_array($role->getKey(), $array)) {
$this->ensureModelSharesGuard($role);
$array[] = $role->getKey();
}
return $array;
}, []);
}
/**
* Assign the given role to the model.
*
* @param string|int|array|Role|Collection|\BackedEnum ...$roles
* @return $this
*/
public function assignRole(...$roles)
{
$roles = $this->collectRoles($roles);
$model = $this->getModel();
$teamPivot = app(PermissionRegistrar::class)->teams && ! is_a($this, Permission::class) ?
[app(PermissionRegistrar::class)->teamsKey => getPermissionsTeamId()] : [];
if ($model->exists) {
$currentRoles = $this->roles->map(fn ($role) => $role->getKey())->toArray();
$this->roles()->attach(array_diff($roles, $currentRoles), $teamPivot);
$model->unsetRelation('roles');
} else {
$class = \get_class($model);
$class::saved(
function ($object) use ($roles, $model, $teamPivot) {
if ($model->getKey() != $object->getKey()) {
return;
}
$model->roles()->attach($roles, $teamPivot);
$model->unsetRelation('roles');
}
);
}
if (is_a($this, Permission::class)) {
$this->forgetCachedPermissions();
}
return $this;
}
/**
* Revoke the given role from the model.
*
* @param string|int|Role|\BackedEnum $role
*/
public function removeRole($role)
{
$this->roles()->detach($this->getStoredRole($role));
$this->unsetRelation('roles');
if (is_a($this, Permission::class)) {
$this->forgetCachedPermissions();
}
return $this;
}
/**
* Remove all current roles and set the given ones.
*
* @param string|int|array|Role|Collection|\BackedEnum ...$roles
* @return $this
*/
public function syncRoles(...$roles)
{
if ($this->getModel()->exists) {
$this->collectRoles($roles);
$this->roles()->detach();
$this->setRelation('roles', collect());
}
return $this->assignRole($roles);
}
/**
* Determine if the model has (one of) the given role(s).
*
* @param string|int|array|Role|Collection|\BackedEnum $roles
*/
public function hasRole($roles, ?string $guard = null): bool
{
$this->loadMissing('roles');
if (is_string($roles) && strpos($roles, '|') !== false) {
$roles = $this->convertPipeToArray($roles);
}
if ($roles instanceof \BackedEnum) {
$roles = $roles->value;
return $this->roles
->when($guard, fn ($q) => $q->where('guard_name', $guard))
->contains(function ($role) use ($roles) {
if ($role->name instanceof \BackedEnum) {
return $role->name->value == $roles;
}
return $role->name == $roles;
});
}
if (is_int($roles) || PermissionRegistrar::isUid($roles)) {
$key = (new ($this->getRoleClass())())->getKeyName();
return $guard
? $this->roles->where('guard_name', $guard)->contains($key, $roles)
: $this->roles->contains($key, $roles);
}
if (is_string($roles)) {
return $guard
? $this->roles->where('guard_name', $guard)->contains('name', $roles)
: $this->roles->contains('name', $roles);
}
if ($roles instanceof Role) {
return $this->roles->contains($roles->getKeyName(), $roles->getKey());
}
if (is_array($roles)) {
foreach ($roles as $role) {
if ($this->hasRole($role, $guard)) {
return true;
}
}
return false;
}
if ($roles instanceof Collection) {
return $roles->intersect($guard ? $this->roles->where('guard_name', $guard) : $this->roles)->isNotEmpty();
}
throw new \TypeError('Unsupported type for $roles parameter to hasRole().');
}
/**
* Determine if the model has any of the given role(s).
*
* Alias to hasRole() but without Guard controls
*
* @param string|int|array|Role|Collection|\BackedEnum $roles
*/
public function hasAnyRole(...$roles): bool
{
return $this->hasRole($roles);
}
/**
* Determine if the model has all of the given role(s).
*
* @param string|array|Role|Collection|\BackedEnum $roles
*/
public function hasAllRoles($roles, ?string $guard = null): bool
{
$this->loadMissing('roles');
if ($roles instanceof \BackedEnum) {
$roles = $roles->value;
}
if (is_string($roles) && strpos($roles, '|') !== false) {
$roles = $this->convertPipeToArray($roles);
}
if (is_string($roles)) {
return $this->hasRole($roles, $guard);
}
if ($roles instanceof Role) {
return $this->roles->contains($roles->getKeyName(), $roles->getKey());
}
$roles = collect()->make($roles)->map(function ($role) {
if ($role instanceof \BackedEnum) {
return $role->value;
}
return $role instanceof Role ? $role->name : $role;
});
$roleNames = $guard
? $this->roles->where('guard_name', $guard)->pluck('name')
: $this->getRoleNames();
$roleNames = $roleNames->transform(function ($roleName) {
if ($roleName instanceof \BackedEnum) {
return $roleName->value;
}
return $roleName;
});
return $roles->intersect($roleNames) == $roles;
}
/**
* Determine if the model has exactly all of the given role(s).
*
* @param string|array|Role|Collection|\BackedEnum $roles
*/
public function hasExactRoles($roles, ?string $guard = null): bool
{
$this->loadMissing('roles');
if (is_string($roles) && strpos($roles, '|') !== false) {
$roles = $this->convertPipeToArray($roles);
}
if (is_string($roles)) {
$roles = [$roles];
}
if ($roles instanceof Role) {
$roles = [$roles->name];
}
$roles = collect()->make($roles)->map(fn ($role) => $role instanceof Role ? $role->name : $role
);
return $this->roles->count() == $roles->count() && $this->hasAllRoles($roles, $guard);
}
/**
* Return all permissions directly coupled to the model.
*/
public function getDirectPermissions(): Collection
{
return $this->permissions;
}
public function getRoleNames(): Collection
{
$this->loadMissing('roles');
return $this->roles->pluck('name');
}
protected function getStoredRole($role): Role
{
if ($role instanceof \BackedEnum) {
$role = $role->value;
}
if (is_int($role) || PermissionRegistrar::isUid($role)) {
return $this->getRoleClass()::findById($role, $this->getDefaultGuardName());
}
if (is_string($role)) {
return $this->getRoleClass()::findByName($role, $this->getDefaultGuardName());
}
return $role;
}
protected function convertPipeToArray(string $pipeString)
{
$pipeString = trim($pipeString);
if (strlen($pipeString) <= 2) {
return [str_replace('|', '', $pipeString)];
}
$quoteCharacter = substr($pipeString, 0, 1);
$endCharacter = substr($quoteCharacter, -1, 1);
if ($quoteCharacter !== $endCharacter) {
return explode('|', $pipeString);
}
if (! in_array($quoteCharacter, ["'", '"'])) {
return explode('|', $pipeString);
}
return explode('|', trim($pipeString, $quoteCharacter));
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Spatie\Permission\Traits;
use Spatie\Permission\PermissionRegistrar;
trait RefreshesPermissionCache
{
public static function bootRefreshesPermissionCache()
{
static::saved(function () {
app(PermissionRegistrar::class)->forgetCachedPermissions();
});
static::deleted(function () {
app(PermissionRegistrar::class)->forgetCachedPermissions();
});
}
}

View File

@ -0,0 +1,118 @@
<?php
namespace Spatie\Permission;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Spatie\Permission\Contracts\Wildcard;
use Spatie\Permission\Exceptions\WildcardPermissionNotProperlyFormatted;
class WildcardPermission implements Wildcard
{
/** @var string */
public const WILDCARD_TOKEN = '*';
/** @var non-empty-string */
public const PART_DELIMITER = '.';
/** @var non-empty-string */
public const SUBPART_DELIMITER = ',';
protected Model $record;
public function __construct(Model $record)
{
$this->record = $record;
}
public function getIndex(): array
{
$index = [];
foreach ($this->record->getAllPermissions() as $permission) {
$index[$permission->guard_name] = $this->buildIndex(
$index[$permission->guard_name] ?? [],
explode(static::PART_DELIMITER, $permission->name),
$permission->name,
);
}
return $index;
}
protected function buildIndex(array $index, array $parts, string $permission): array
{
if (empty($parts)) {
$index[null] = true;
return $index;
}
$part = array_shift($parts);
if (blank($part)) {
throw WildcardPermissionNotProperlyFormatted::create($permission);
}
if (! Str::contains($part, static::SUBPART_DELIMITER)) {
$index[$part] = $this->buildIndex(
$index[$part] ?? [],
$parts,
$permission,
);
}
$subParts = explode(static::SUBPART_DELIMITER, $part);
foreach ($subParts as $subPart) {
if (blank($subPart)) {
throw WildcardPermissionNotProperlyFormatted::create($permission);
}
$index[$subPart] = $this->buildIndex(
$index[$subPart] ?? [],
$parts,
$permission,
);
}
return $index;
}
public function implies(string $permission, string $guardName, array $index): bool
{
if (! array_key_exists($guardName, $index)) {
return false;
}
$permission = explode(static::PART_DELIMITER, $permission);
return $this->checkIndex($permission, $index[$guardName]);
}
protected function checkIndex(array $permission, array $index): bool
{
if (array_key_exists(strval(null), $index)) {
return true;
}
if (empty($permission)) {
return false;
}
$firstPermission = array_shift($permission);
if (
array_key_exists($firstPermission, $index) &&
$this->checkIndex($permission, $index[$firstPermission])
) {
return true;
}
if (array_key_exists(static::WILDCARD_TOKEN, $index)) {
return $this->checkIndex($permission, $index[static::WILDCARD_TOKEN]);
}
return false;
}
}

View File

@ -0,0 +1,33 @@
<?php
if (! function_exists('getModelForGuard')) {
/**
* @return string|null
*/
function getModelForGuard(string $guard)
{
return collect(config('auth.guards'))
->map(fn ($guard) => isset($guard['provider']) ? config("auth.providers.{$guard['provider']}.model") : null)
->get($guard);
}
}
if (! function_exists('setPermissionsTeamId')) {
/**
* @param int|string|null|\Illuminate\Database\Eloquent\Model $id
*/
function setPermissionsTeamId($id)
{
app(\Spatie\Permission\PermissionRegistrar::class)->setPermissionsTeamId($id);
}
}
if (! function_exists('getPermissionsTeamId')) {
/**
* @return int|string|null
*/
function getPermissionsTeamId()
{
return app(\Spatie\Permission\PermissionRegistrar::class)->getPermissionsTeamId();
}
}