Subiendo proyecto completo sin restricciones de git ignore

This commit is contained in:
Jose Sanchez
2023-08-17 11:44:02 -04:00
parent a0d4f5ba3b
commit 20f1c60600
19921 changed files with 2509159 additions and 45 deletions

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Spatie bvba <info@spatie.be>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,101 @@
<p align="center"><img src="/art/socialcard.png" alt="Social Card of Laravel Permission"></p>
# Associate users with permissions and roles
### Sponsor
<table>
<tr>
<td><img src="https://user-images.githubusercontent.com/6287961/92889815-d64c3d80-f416-11ea-894a-b4de7e8f7eef.png"></td>
<td>If you want to quickly add authentication and authorization to Laravel projects, feel free to check Auth0's Laravel SDK and free plan at <a href="https://auth0.com/developers?utm_source=GHsponsor&utm_medium=GHsponsor&utm_campaign=laravel-permission&utm_content=auth">https://auth0.com/developers</a>.</td>
</tr>
</table>
[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/laravel-permission.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-permission)
[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/spatie/laravel-permission/run-tests-L8.yml?branch=main&label=Tests)](https://github.com/spatie/laravel-permission/actions?query=workflow%3ATests+branch%3Amain)
[![Total Downloads](https://img.shields.io/packagist/dt/spatie/laravel-permission.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-permission)
## Documentation, Installation, and Usage Instructions
See the [documentation](https://spatie.be/docs/laravel-permission/) for detailed installation and usage instructions.
## What It Does
This package allows you to manage user permissions and roles in a database.
Once installed you can do stuff like this:
```php
// Adding permissions to a user
$user->givePermissionTo('edit articles');
// Adding permissions via a role
$user->assignRole('writer');
$role->givePermissionTo('edit articles');
```
Because all permissions will be registered on [Laravel's gate](https://laravel.com/docs/authorization), you can check if a user has a permission with Laravel's default `can` function:
```php
$user->can('edit articles');
```
## Support us
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/laravel-permission.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/laravel-permission)
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
### Testing
``` bash
composer test
```
### Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
## Contributing
Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
### Security
If you discover any security-related issues, please email [security@spatie.be](mailto:security@spatie.be) instead of using the issue tracker.
## Postcardware
You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.
Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.
We publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards).
## Credits
- [Chris Brown](https://github.com/drbyte)
- [Freek Van der Herten](https://github.com/freekmurze)
- [All Contributors](../../contributors)
This package is heavily based on [Jeffrey Way](https://twitter.com/jeffrey_way)'s awesome [Laracasts](https://laracasts.com) lessons
on [permissions and roles](https://laracasts.com/series/whats-new-in-laravel-5-1/episodes/16). His original code
can be found [in this repo on GitHub](https://github.com/laracasts/laravel-5-roles-and-permissions-demo).
Special thanks to [Alex Vanderbist](https://github.com/AlexVanderbist) who greatly helped with `v2`, and to [Chris Brown](https://github.com/drbyte) for his longtime support helping us maintain the package.
And a special thanks to [Caneco](https://twitter.com/caneco) for the logo ✨
## Alternatives
- [Povilas Korop](https://twitter.com/@povilaskorop) did an excellent job listing the alternatives [in an article on Laravel News](https://laravel-news.com/two-best-roles-permissions-packages). In that same article, he compares laravel-permission to [Joseph Silber](https://github.com/JosephSilber)'s [Bouncer]((https://github.com/JosephSilber/bouncer)), which in our book is also an excellent package.
- [ultraware/roles](https://github.com/ultraware/roles) takes a slightly different approach to its features.
- [santigarcor/laratrust](https://github.com/santigarcor/laratrust) implements team support
- [zizaco/entrust](https://github.com/zizaco/entrust) offers some wildcard pattern matching
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

View File

@@ -0,0 +1,68 @@
{
"name": "spatie/laravel-permission",
"description": "Permission handling for Laravel 6.0 and up",
"license": "MIT",
"keywords": [
"spatie",
"laravel",
"permission",
"permissions",
"roles",
"acl",
"rbac",
"security"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"homepage": "https://github.com/spatie/laravel-permission",
"require": {
"php": "^7.3|^8.0",
"illuminate/auth": "^7.0|^8.0|^9.0|^10.0",
"illuminate/container": "^7.0|^8.0|^9.0|^10.0",
"illuminate/contracts": "^7.0|^8.0|^9.0|^10.0",
"illuminate/database": "^7.0|^8.0|^9.0|^10.0"
},
"require-dev": {
"orchestra/testbench": "^5.0|^6.0|^7.0|^8.0",
"phpunit/phpunit": "^9.4",
"predis/predis": "^1.1"
},
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Spatie\\Permission\\": "src"
},
"files": [
"src/helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"Spatie\\Permission\\Test\\": "tests"
}
},
"config": {
"sort-packages": true
},
"extra": {
"branch-alias": {
"dev-main": "5.x-dev",
"dev-master": "5.x-dev"
},
"laravel": {
"providers": [
"Spatie\\Permission\\PermissionServiceProvider"
]
}
},
"scripts": {
"test": "phpunit"
}
}

View File

@@ -0,0 +1,161 @@
<?php
return [
'models' => [
/*
* When using the "HasPermissions" trait from this package, we need to know which
* Eloquent model should be used to retrieve your permissions. Of course, it
* is often just the "Permission" model but you may use whatever you like.
*
* The model you want to use as a Permission model needs to implement the
* `Spatie\Permission\Contracts\Permission` contract.
*/
'permission' => Spatie\Permission\Models\Permission::class,
/*
* When using the "HasRoles" trait from this package, we need to know which
* Eloquent model should be used to retrieve your roles. Of course, it
* is often just the "Role" model but you may use whatever you like.
*
* The model you want to use as a Role model needs to implement the
* `Spatie\Permission\Contracts\Role` contract.
*/
'role' => Spatie\Permission\Models\Role::class,
],
'table_names' => [
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your roles. We have chosen a basic
* default value but you may easily change it to any table you like.
*/
'roles' => 'roles',
/*
* When using the "HasPermissions" trait from this package, we need to know which
* table should be used to retrieve your permissions. We have chosen a basic
* default value but you may easily change it to any table you like.
*/
'permissions' => 'permissions',
/*
* When using the "HasPermissions" trait from this package, we need to know which
* table should be used to retrieve your models permissions. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'model_has_permissions' => 'model_has_permissions',
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your models roles. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'model_has_roles' => 'model_has_roles',
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your roles permissions. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'role_has_permissions' => 'role_has_permissions',
],
'column_names' => [
/*
* Change this if you want to name the related pivots other than defaults
*/
'role_pivot_key' => null, //default 'role_id',
'permission_pivot_key' => null, //default 'permission_id',
/*
* Change this if you want to name the related model primary key other than
* `model_id`.
*
* For example, this would be nice if your primary keys are all UUIDs. In
* that case, name this `model_uuid`.
*/
'model_morph_key' => 'model_id',
/*
* Change this if you want to use the teams feature and your related model's
* foreign key is other than `team_id`.
*/
'team_foreign_key' => 'team_id',
],
/*
* When set to true, the method for checking permissions will be registered on the gate.
* Set this to false, if you want to implement custom logic for checking permissions.
*/
'register_permission_check_method' => true,
/*
* When set to true the package implements teams using the 'team_foreign_key'. If you want
* the migrations to register the 'team_foreign_key', you must set this to true
* before doing the migration. If you already did the migration then you must make a new
* migration to also add 'team_foreign_key' to 'roles', 'model_has_roles', and
* 'model_has_permissions'(view the latest version of package's migration file)
*/
'teams' => false,
/*
* When set to true, the required permission names are added to the exception
* message. This could be considered an information leak in some contexts, so
* the default setting is false here for optimum safety.
*/
'display_permission_in_exception' => false,
/*
* When set to true, the required role names are added to the exception
* message. This could be considered an information leak in some contexts, so
* the default setting is false here for optimum safety.
*/
'display_role_in_exception' => false,
/*
* By default wildcard permission lookups are disabled.
*/
'enable_wildcard_permission' => false,
'cache' => [
/*
* By default all permissions are cached for 24 hours to speed up performance.
* When permissions or roles are updated the cache is flushed automatically.
*/
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
/*
* The cache key used to store all permissions.
*/
'key' => 'spatie.permission.cache',
/*
* You may optionally indicate a specific cache driver to use for permission and
* role caching using any of the `store` drivers listed in the cache.php config
* file. Using 'default' here means to use the `default` set in cache.php.
*/
'store' => 'default',
],
];

View File

@@ -0,0 +1,94 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Spatie\Permission\PermissionRegistrar;
class AddTeamsFields extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$teams = config('permission.teams');
$tableNames = config('permission.table_names');
$columnNames = config('permission.column_names');
if (! $teams) {
return;
}
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
if (empty($columnNames['team_foreign_key'] ?? null)) {
throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
if (! Schema::hasColumn($tableNames['roles'], $columnNames['team_foreign_key'])) {
Schema::table($tableNames['roles'], function (Blueprint $table) use ($columnNames) {
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable()->after('id');
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
$table->dropUnique('roles_name_guard_name_unique');
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
});
}
if (! Schema::hasColumn($tableNames['model_has_permissions'], $columnNames['team_foreign_key'])) {
Schema::table($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames) {
$table->unsignedBigInteger($columnNames['team_foreign_key'])->default('1');
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
if (DB::getDriverName() !== 'sqlite') {
$table->dropForeign([PermissionRegistrar::$pivotPermission]);
}
$table->dropPrimary();
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
if (DB::getDriverName() !== 'sqlite') {
$table->foreign(PermissionRegistrar::$pivotPermission)
->references('id')->on($tableNames['permissions'])->onDelete('cascade');
}
});
}
if (! Schema::hasColumn($tableNames['model_has_roles'], $columnNames['team_foreign_key'])) {
Schema::table($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames) {
$table->unsignedBigInteger($columnNames['team_foreign_key'])->default('1');;
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
if (DB::getDriverName() !== 'sqlite') {
$table->dropForeign([PermissionRegistrar::$pivotRole]);
}
$table->dropPrimary();
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
if (DB::getDriverName() !== 'sqlite') {
$table->foreign(PermissionRegistrar::$pivotRole)
->references('id')->on($tableNames['roles'])->onDelete('cascade');
}
});
}
app('cache')
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
->forget(config('permission.cache.key'));
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@@ -0,0 +1,141 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Spatie\Permission\PermissionRegistrar;
class CreatePermissionTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$tableNames = config('permission.table_names');
$columnNames = config('permission.column_names');
$teams = config('permission.teams');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
Schema::create($tableNames['permissions'], function (Blueprint $table) {
$table->bigIncrements('id'); // permission id
$table->string('name'); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
$table->unique(['name', 'guard_name']);
});
Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {
$table->bigIncrements('id'); // role id
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
}
$table->string('name'); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
if ($teams || config('permission.testing')) {
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
} else {
$table->unique(['name', 'guard_name']);
}
});
Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
$table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
$table->foreign(PermissionRegistrar::$pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
} else {
$table->primary([PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
}
});
Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
$table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
$table->foreign(PermissionRegistrar::$pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
} else {
$table->primary([PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
}
});
Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
$table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
$table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
$table->foreign(PermissionRegistrar::$pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
$table->foreign(PermissionRegistrar::$pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
$table->primary([PermissionRegistrar::$pivotPermission, PermissionRegistrar::$pivotRole], 'role_has_permissions_permission_id_role_id_primary');
});
app('cache')
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
->forget(config('permission.cache.key'));
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$tableNames = config('permission.table_names');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
}
Schema::drop($tableNames['role_has_permissions']);
Schema::drop($tableNames['model_has_roles']);
Schema::drop($tableNames['model_has_permissions']);
Schema::drop($tableNames['roles']);
Schema::drop($tableNames['permissions']);
}
}

View File

@@ -0,0 +1,72 @@
{
"$schema": "https://laravel-ide.com/schema/laravel-ide-v2.json",
"blade": {
"directives": [
{
"name": "role",
"prefix": "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {",
"suffix": "})): ?>"
},
{
"name": "elserole",
"prefix": "<?php elseif(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {",
"suffix": "})): ?>"
},
{
"name": "endrole",
"prefix": "",
"suffix": ""
},
{
"name": "hasrole",
"prefix": "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {",
"suffix": "})): ?>"
},
{
"name": "endhasrole",
"prefix": "",
"suffix": ""
},
{
"name": "hasanyrole",
"prefix": "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasAnyRole', {",
"suffix": "})): ?>"
},
{
"name": "endhasanyrole",
"prefix": "",
"suffix": ""
},
{
"name": "hasallroles",
"prefix": "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasAllRoles', {",
"suffix": "})): ?>"
},
{
"name": "endhasallroles",
"prefix": "",
"suffix": ""
},
{
"name": "unlessrole",
"prefix": "<?php if(! \\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {",
"suffix": "})): ?>"
},
{
"name": "endunlessrole",
"prefix": "",
"suffix": ""
},
{
"name": "hasexactroles",
"prefix": "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasExactRoles', {",
"suffix": "})): ?>"
},
{
"name": "endhasexactroles",
"prefix": "",
"suffix": ""
}
]
}
}

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()
{
$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,80 @@
<?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, function ($q) use ($team_key) {
$q->orderBy($team_key);
})
->orderBy('name')->get()->mapWithKeys(function ($role) use ($teamsEnabled, $team_key) {
return [$role->name.'_'.($teamsEnabled ? ($role->$team_key ?: '') : '') => [
'permissions' => $role->permissions->pluck('id'),
$team_key => $teamsEnabled ? $role->$team_key : null,
]];
});
$permissions = $permissionClass::whereGuardName($guard)->orderBy('name')->pluck('name', 'id');
$body = $permissions->map(function ($permission, $id) use ($roles) {
return $roles->map(function (array $role_data) use ($id) {
return $role_data['permissions']->contains($id) ? ' ✔' : ' ·';
})->prepend($permission);
});
if ($teamsEnabled) {
$teams = $roles->groupBy($team_key)->values()->map(function ($group, $id) {
return 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('')->toArray(),
]),
$body->toArray(),
$style
);
}
}
}

View File

@@ -0,0 +1,126 @@
<?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?', 'yes')) {
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, function ($carry, $fileName) {
return $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(function ($path) {
return 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,38 @@
<?php
namespace Spatie\Permission\Contracts;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
interface Permission
{
/**
* A permission can be applied to roles.
*/
public function roles(): BelongsToMany;
/**
* Find a permission by its name.
*
* @param string|null $guardName
*
* @throws \Spatie\Permission\Exceptions\PermissionDoesNotExist
*/
public static function findByName(string $name, $guardName): self;
/**
* Find a permission by its id.
*
* @param string|null $guardName
*
* @throws \Spatie\Permission\Exceptions\PermissionDoesNotExist
*/
public static function findById(int $id, $guardName): self;
/**
* Find or Create a permission by its name and guard name.
*
* @param string|null $guardName
*/
public static function findOrCreate(string $name, $guardName): self;
}

View File

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

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\Permission\Contracts;
interface Wildcard
{
/**
* @param string|Wildcard $permission
*/
public function implies($permission): 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,18 @@
<?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}`.");
}
public static function withId(int $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,18 @@
<?php
namespace Spatie\Permission\Exceptions;
use InvalidArgumentException;
class RoleDoesNotExist extends InvalidArgumentException
{
public static function named(string $roleName)
{
return new static("There is no role named `{$roleName}`.");
}
public static function withId(int $roleId)
{
return new static("There is no role with id `{$roleId}`.");
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Spatie\Permission\Exceptions;
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 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,83 @@
<?php
namespace Spatie\Permission;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
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(function ($guard) {
if (! isset($guard['provider'])) {
return null;
}
return config("auth.providers.{$guard['provider']}.model");
})
->filter(function ($model) use ($class) {
return $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;
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Spatie\Permission\Middlewares;
use Closure;
use Spatie\Permission\Exceptions\UnauthorizedException;
class PermissionMiddleware
{
public function handle($request, Closure $next, $permission, $guard = null)
{
$authGuard = app('auth')->guard($guard);
if ($authGuard->guest()) {
throw UnauthorizedException::notLoggedIn();
}
$permissions = is_array($permission)
? $permission
: explode('|', $permission);
foreach ($permissions as $permission) {
if ($authGuard->user()->can($permission)) {
return $next($request);
}
}
throw UnauthorizedException::forPermissions($permissions);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Spatie\Permission\Middlewares;
use Closure;
use Illuminate\Support\Facades\Auth;
use Spatie\Permission\Exceptions\UnauthorizedException;
class RoleMiddleware
{
public function handle($request, Closure $next, $role, $guard = null)
{
$authGuard = Auth::guard($guard);
if ($authGuard->guest()) {
throw UnauthorizedException::notLoggedIn();
}
$roles = is_array($role)
? $role
: explode('|', $role);
if (! $authGuard->user()->hasAnyRole($roles)) {
throw UnauthorizedException::forRoles($roles);
}
return $next($request);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Spatie\Permission\Middlewares;
use Closure;
use Illuminate\Support\Facades\Auth;
use Spatie\Permission\Exceptions\UnauthorizedException;
class RoleOrPermissionMiddleware
{
public function handle($request, Closure $next, $roleOrPermission, $guard = null)
{
$authGuard = Auth::guard($guard);
if ($authGuard->guest()) {
throw UnauthorizedException::notLoggedIn();
}
$rolesOrPermissions = is_array($roleOrPermission)
? $roleOrPermission
: explode('|', $roleOrPermission);
if (! $authGuard->user()->hasAnyRole($rolesOrPermissions) && ! $authGuard->user()->hasAnyPermission($rolesOrPermissions)) {
throw UnauthorizedException::forRolesOrPermissions($rolesOrPermissions);
}
return $next($request);
}
}

View File

@@ -0,0 +1,157 @@
<?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 int $id
* @property string $name
* @property string $guard_name
* @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;
}
public function getTable()
{
return config('permission.table_names.permissions', parent::getTable());
}
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'),
PermissionRegistrar::$pivotPermission,
PermissionRegistrar::$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'),
PermissionRegistrar::$pivotPermission,
config('permission.column_names.model_morph_key')
);
}
/**
* Find a permission by its name (and optionally guardName).
*
* @param string|null $guardName
*
* @throws \Spatie\Permission\Exceptions\PermissionDoesNotExist
*/
public static function findByName(string $name, $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).
*
* @param string|null $guardName
*
* @throws \Spatie\Permission\Exceptions\PermissionDoesNotExist
*/
public static function findById(int $id, $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).
*
* @param string|null $guardName
*/
public static function findOrCreate(string $name, $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 \Spatie\Permission\Contracts\Permission
*/
protected static function getPermission(array $params = []): ?PermissionContract
{
return static::getPermissions($params, true)->first();
}
}

View File

@@ -0,0 +1,197 @@
<?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\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 int $id
* @property string $name
* @property string $guard_name
* @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;
}
public function getTable()
{
return config('permission.table_names.roles', parent::getTable());
}
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 (PermissionRegistrar::$teams) {
if (array_key_exists(PermissionRegistrar::$teamsKey, $attributes)) {
$params[PermissionRegistrar::$teamsKey] = $attributes[PermissionRegistrar::$teamsKey];
} else {
$attributes[PermissionRegistrar::$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'),
PermissionRegistrar::$pivotRole,
PermissionRegistrar::$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'),
PermissionRegistrar::$pivotRole,
config('permission.column_names.model_morph_key')
);
}
/**
* Find a role by its name and guard name.
*
* @param string|null $guardName
* @return \Spatie\Permission\Contracts\Role|\Spatie\Permission\Models\Role
*
* @throws \Spatie\Permission\Exceptions\RoleDoesNotExist
*/
public static function findByName(string $name, $guardName = null): RoleContract
{
$guardName = $guardName ?? Guard::getDefaultName(static::class);
$role = static::findByParam(['name' => $name, 'guard_name' => $guardName]);
if (! $role) {
throw RoleDoesNotExist::named($name);
}
return $role;
}
/**
* Find a role by its id (and optionally guardName).
*
* @param string|null $guardName
* @return \Spatie\Permission\Contracts\Role|\Spatie\Permission\Models\Role
*/
public static function findById(int $id, $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);
}
return $role;
}
/**
* Find or create role by its name (and optionally guardName).
*
* @param string|null $guardName
* @return \Spatie\Permission\Contracts\Role|\Spatie\Permission\Models\Role
*/
public static function findOrCreate(string $name, $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] + (PermissionRegistrar::$teams ? [PermissionRegistrar::$teamsKey => getPermissionsTeamId()] : []));
}
return $role;
}
protected static function findByParam(array $params = [])
{
$query = static::query();
if (PermissionRegistrar::$teams) {
$query->where(function ($q) use ($params) {
$q->whereNull(PermissionRegistrar::$teamsKey)
->orWhere(PermissionRegistrar::$teamsKey, $params[PermissionRegistrar::$teamsKey] ?? getPermissionsTeamId());
});
unset($params[PermissionRegistrar::$teamsKey]);
}
foreach ($params as $key => $value) {
$query->where($key, $value);
}
return $query->first();
}
/**
* Determine if the user may perform the given permission.
*
* @param string|Permission $permission
*
* @throws \Spatie\Permission\Exceptions\GuardDoesNotMatch
*/
public function hasPermissionTo($permission): bool
{
if (config('permission.enable_wildcard_permission', false)) {
return $this->hasWildcardPermission($permission, $this->getDefaultGuardName());
}
$permissionClass = $this->getPermissionClass();
if (is_string($permission)) {
$permission = $permissionClass->findByName($permission, $this->getDefaultGuardName());
}
if (is_int($permission)) {
$permission = $permissionClass->findById($permission, $this->getDefaultGuardName());
}
if (! $this->getGuardNames()->contains($permission->guard_name)) {
throw GuardDoesNotMatch::create($permission->guard_name, $this->getGuardNames());
}
return $this->permissions->contains($permission->getKeyName(), $permission->getKey());
}
}

View File

@@ -0,0 +1,373 @@
<?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 Spatie\Permission\Contracts\Permission;
use Spatie\Permission\Contracts\Role;
class PermissionRegistrar
{
/** @var \Illuminate\Contracts\Cache\Repository */
protected $cache;
/** @var \Illuminate\Cache\CacheManager */
protected $cacheManager;
/** @var string */
protected $permissionClass;
/** @var string */
protected $roleClass;
/** @var \Illuminate\Database\Eloquent\Collection */
protected $permissions;
/** @var string */
public static $pivotRole;
/** @var string */
public static $pivotPermission;
/** @var \DateInterval|int */
public static $cacheExpirationTime;
/** @var bool */
public static $teams;
/** @var string */
public static $teamsKey;
/** @var int|string */
protected $teamId = null;
/** @var string */
public static $cacheKey;
/** @var array */
private $cachedRoles = [];
/** @var array */
private $alias = [];
/** @var array */
private $except = [];
/**
* 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()
{
self::$cacheExpirationTime = config('permission.cache.expiration_time') ?: \DateInterval::createFromDateString('24 hours');
self::$teams = config('permission.teams', false);
self::$teamsKey = config('permission.column_names.team_foreign_key');
self::$cacheKey = config('permission.cache.key');
self::$pivotRole = config('permission.column_names.role_pivot_key') ?: 'role_id';
self::$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 $id
*/
public function setPermissionsTeamId($id)
{
if ($id instanceof \Illuminate\Database\Eloquent\Model) {
$id = $id->getKey();
}
$this->teamId = $id;
}
/**
* @return int|string
*/
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(): bool
{
app(Gate::class)->before(function (Authorizable $user, string $ability) {
if (method_exists($user, 'checkPermissionTo')) {
return $user->checkPermissionTo($ability) ?: null;
}
});
return true;
}
/**
* Flush the cache.
*/
public function forgetCachedPermissions()
{
$this->permissions = null;
return $this->cache->forget(self::$cacheKey);
}
/**
* Clear class permissions.
* This is only intended to be called by the PermissionServiceProvider on boot,
* so that long-running instances like Swoole don't keep old data in memory.
*/
public function clearClassPermissions()
{
$this->permissions = null;
}
/**
* Load permissions from cache
* This get cache and turns array into \Illuminate\Database\Eloquent\Collection
*/
private function loadPermissions()
{
if ($this->permissions) {
return;
}
$this->permissions = $this->cache->remember(self::$cacheKey, self::$cacheExpirationTime, function () {
return $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;
}
/**
* Get an instance of the permission class.
*/
public function getPermissionClass(): Permission
{
return app($this->permissionClass);
}
public function setPermissionClass($permissionClass)
{
$this->permissionClass = $permissionClass;
config()->set('permission.models.permission', $permissionClass);
app()->bind(Permission::class, $permissionClass);
return $this;
}
/**
* Get an instance of the role class.
*/
public function getRoleClass(): Role
{
return app($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->getPermissionClass()->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(function ($value, $key) {
return $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()
{
$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)
{
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()
{
$permissionClass = $this->getPermissionClass();
$permissionInstance = new $permissionClass();
return Collection::make(
array_map(function ($item) use ($permissionInstance) {
return $permissionInstance
->newFromBuilder($this->aliasedArray(array_diff_key($item, ['r' => 0])))
->setRelation('roles', $this->getHydratedRoleCollection($item['r'] ?? []));
}, $this->permissions['permissions'])
);
}
private function getHydratedRoleCollection(array $roles)
{
return Collection::make(array_values(
array_intersect_key($this->cachedRoles, array_flip($roles))
));
}
private function hydrateRolesCache()
{
$roleClass = $this->getRoleClass();
$roleInstance = new $roleClass();
array_map(function ($item) use ($roleInstance) {
$role = $roleInstance->newFromBuilder($this->aliasedArray($item));
$this->cachedRoles[$role->getKey()] = $role;
}, $this->permissions['roles']);
$this->permissions['roles'] = [];
}
}

View File

@@ -0,0 +1,179 @@
<?php
namespace Spatie\Permission;
use Illuminate\Filesystem\Filesystem;
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(PermissionRegistrar $permissionLoader)
{
$this->offerPublishing();
$this->registerMacroHelpers();
$this->registerCommands();
$this->registerModelBindings();
if ($this->app->config['permission.register_permission_check_method']) {
$permissionLoader->clearClassPermissions();
$permissionLoader->registerPermissions();
}
$this->app->singleton(PermissionRegistrar::class, function ($app) use ($permissionLoader) {
return $permissionLoader;
});
}
public function register()
{
$this->mergeConfigFrom(
__DIR__.'/../config/permission.php',
'permission'
);
$this->callAfterResolving('blade.compiler', function (BladeCompiler $bladeCompiler) {
$this->registerBladeExtensions($bladeCompiler);
});
}
protected function offerPublishing()
{
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()
{
$this->commands([
Commands\CacheReset::class,
Commands\CreateRole::class,
Commands\CreatePermission::class,
Commands\Show::class,
Commands\UpgradeForTeams::class,
]);
}
protected function registerModelBindings()
{
$config = $this->app->config['permission.models'];
if (! $config) {
return;
}
$this->app->bind(PermissionContract::class, $config['permission']);
$this->app->bind(RoleContract::class, $config['role']);
}
public static function bladeMethodWrapper($method, $role, $guard = null)
{
return auth($guard)->check() && auth($guard)->user()->{$method}($role);
}
protected function registerBladeExtensions($bladeCompiler)
{
$bladeCompiler->directive('role', function ($arguments) {
return "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {$arguments})): ?>";
});
$bladeCompiler->directive('elserole', function ($arguments) {
return "<?php elseif(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {$arguments})): ?>";
});
$bladeCompiler->directive('endrole', function () {
return '<?php endif; ?>';
});
$bladeCompiler->directive('hasrole', function ($arguments) {
return "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {$arguments})): ?>";
});
$bladeCompiler->directive('endhasrole', function () {
return '<?php endif; ?>';
});
$bladeCompiler->directive('hasanyrole', function ($arguments) {
return "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasAnyRole', {$arguments})): ?>";
});
$bladeCompiler->directive('endhasanyrole', function () {
return '<?php endif; ?>';
});
$bladeCompiler->directive('hasallroles', function ($arguments) {
return "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasAllRoles', {$arguments})): ?>";
});
$bladeCompiler->directive('endhasallroles', function () {
return '<?php endif; ?>';
});
$bladeCompiler->directive('unlessrole', function ($arguments) {
return "<?php if(! \\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasRole', {$arguments})): ?>";
});
$bladeCompiler->directive('endunlessrole', function () {
return '<?php endif; ?>';
});
$bladeCompiler->directive('hasexactroles', function ($arguments) {
return "<?php if(\\Spatie\\Permission\\PermissionServiceProvider::bladeMethodWrapper('hasExactRoles', {$arguments})): ?>";
});
$bladeCompiler->directive('endhasexactroles', function () {
return '<?php endif; ?>';
});
}
protected function registerMacroHelpers()
{
if (! method_exists(Route::class, 'macro')) { // Lumen
return;
}
Route::macro('role', function ($roles = []) {
$roles = implode('|', Arr::wrap($roles));
$this->middleware("role:$roles");
return $this;
});
Route::macro('permission', function ($permissions = []) {
$permissions = implode('|', Arr::wrap($permissions));
$this->middleware("permission:$permissions");
return $this;
});
}
/**
* Returns existing migration file if found, else uses the current timestamp.
*/
protected function getMigrationFileName($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(function ($path) use ($filesystem, $migrationFileName) {
return $filesystem->glob($path.'*_'.$migrationFileName);
})
->push($this->app->databasePath()."/migrations/{$timestamp}_{$migrationFileName}")
->first();
}
}

View File

@@ -0,0 +1,531 @@
<?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
{
/** @var string */
private $permissionClass;
/** @var string */
private $wildcardClass;
public static function bootHasPermissions()
{
static::deleting(function ($model) {
if (method_exists($model, 'isForceDeleting') && ! $model->isForceDeleting()) {
return;
}
if (is_a($model, Permission::class)) {
return;
}
$teams = PermissionRegistrar::$teams;
PermissionRegistrar::$teams = false;
$model->permissions()->detach();
PermissionRegistrar::$teams = $teams;
});
}
public function getPermissionClass()
{
if (! isset($this->permissionClass)) {
$this->permissionClass = app(PermissionRegistrar::class)->getPermissionClass();
}
return $this->permissionClass;
}
protected function getWildcardClass()
{
if (! is_null($this->wildcardClass)) {
return $this->wildcardClass;
}
$this->wildcardClass = false;
if (config('permission.enable_wildcard_permission', false)) {
$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'),
PermissionRegistrar::$pivotPermission
);
if (! PermissionRegistrar::$teams) {
return $relation;
}
return $relation->wherePivot(PermissionRegistrar::$teamsKey, getPermissionsTeamId());
}
/**
* Scope the model query to certain permissions only.
*
* @param string|int|array|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection $permissions
*/
public function scopePermission(Builder $query, $permissions): Builder
{
$permissions = $this->convertToPermissionModels($permissions);
$rolesWithPermissions = array_unique(array_reduce($permissions, function ($result, $permission) {
return array_merge($result, $permission->roles->all());
}, []));
return $query->where(function (Builder $query) use ($permissions, $rolesWithPermissions) {
$query->whereHas('permissions', function (Builder $subQuery) use ($permissions) {
$permissionClass = $this->getPermissionClass();
$key = (new $permissionClass())->getKeyName();
$subQuery->whereIn(config('permission.table_names.permissions').".$key", \array_column($permissions, $key));
});
if (count($rolesWithPermissions) > 0) {
$query->orWhereHas('roles', function (Builder $subQuery) use ($rolesWithPermissions) {
$roleClass = $this->getRoleClass();
$key = (new $roleClass())->getKeyName();
$subQuery->whereIn(config('permission.table_names.roles').".$key", \array_column($rolesWithPermissions, $key));
});
}
});
}
/**
* @param string|int|array|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection $permissions
*
* @throws \Spatie\Permission\Exceptions\PermissionDoesNotExist
*/
protected function convertToPermissionModels($permissions): array
{
if ($permissions instanceof Collection) {
$permissions = $permissions->all();
}
return array_map(function ($permission) {
if ($permission instanceof Permission) {
return $permission;
}
$method = is_string($permission) ? 'findByName' : 'findById';
return $this->getPermissionClass()->{$method}($permission, $this->getDefaultGuardName());
}, Arr::wrap($permissions));
}
/**
* Find a permission.
*
* @param string|int|\Spatie\Permission\Contracts\Permission $permission
* @return \Spatie\Permission\Contracts\Permission
*
* @throws PermissionDoesNotExist
*/
public function filterPermission($permission, $guardName = null)
{
$permissionClass = $this->getPermissionClass();
if (is_string($permission)) {
$permission = $permissionClass->findByName(
$permission,
$guardName ?? $this->getDefaultGuardName()
);
}
if (is_int($permission)) {
$permission = $permissionClass->findById(
$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|\Spatie\Permission\Contracts\Permission $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|\Spatie\Permission\Contracts\Permission $permission
* @param string|null $guardName
*/
protected function hasWildcardPermission($permission, $guardName = null): bool
{
$guardName = $guardName ?? $this->getDefaultGuardName();
if (is_int($permission)) {
$permission = $this->getPermissionClass()->findById($permission, $guardName);
}
if ($permission instanceof Permission) {
$permission = $permission->name;
}
if (! is_string($permission)) {
throw WildcardPermissionInvalidArgument::create();
}
$WildcardPermissionClass = $this->getWildcardClass();
foreach ($this->getAllPermissions() as $userPermission) {
if ($guardName !== $userPermission->guard_name) {
continue;
}
$userPermission = new $WildcardPermissionClass($userPermission->name);
if ($userPermission->implies($permission)) {
return true;
}
}
return false;
}
/**
* An alias to hasPermissionTo(), but avoids throwing an exception.
*
* @param string|int|\Spatie\Permission\Contracts\Permission $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|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection ...$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|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection ...$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
{
return $this->hasRole($permission->roles);
}
/**
* Determine if the model has the given permission.
*
* @param string|int|\Spatie\Permission\Contracts\Permission $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
{
return $this->loadMissing('roles', 'roles.permissions')
->roles->flatMap(function ($role) {
return $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 permissions ids as array keys
*
* @param string|int|array|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection $permissions
* @return array
*/
public function collectPermissions(...$permissions)
{
return collect($permissions)
->flatten()
->reduce(function ($array, $permission) {
if (empty($permission)) {
return $array;
}
$permission = $this->getStoredPermission($permission);
if (! $permission instanceof Permission) {
return $array;
}
$this->ensureModelSharesGuard($permission);
$array[$permission->getKey()] = PermissionRegistrar::$teams && ! is_a($this, Role::class) ?
[PermissionRegistrar::$teamsKey => getPermissionsTeamId()] : [];
return $array;
}, []);
}
/**
* Grant the given permission(s) to a role.
*
* @param string|int|array|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection $permissions
* @return $this
*/
public function givePermissionTo(...$permissions)
{
$permissions = $this->collectPermissions(...$permissions);
$model = $this->getModel();
if ($model->exists) {
$this->permissions()->sync($permissions, false);
$model->load('permissions');
} else {
$class = \get_class($model);
$class::saved(
function ($object) use ($permissions, $model) {
if ($model->getKey() != $object->getKey()) {
return;
}
$model->permissions()->sync($permissions, false);
$model->load('permissions');
}
);
}
if (is_a($this, get_class(app(PermissionRegistrar::class)->getRoleClass()))) {
$this->forgetCachedPermissions();
}
return $this;
}
/**
* Remove all current permissions and set the given ones.
*
* @param string|int|array|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection $permissions
* @return $this
*/
public function syncPermissions(...$permissions)
{
$this->permissions()->detach();
return $this->givePermissionTo($permissions);
}
/**
* Revoke the given permission(s).
*
* @param \Spatie\Permission\Contracts\Permission|\Spatie\Permission\Contracts\Permission[]|string|string[] $permission
* @return $this
*/
public function revokePermissionTo($permission)
{
$this->permissions()->detach($this->getStoredPermission($permission));
if (is_a($this, get_class(app(PermissionRegistrar::class)->getRoleClass()))) {
$this->forgetCachedPermissions();
}
$this->load('permissions');
return $this;
}
public function getPermissionNames(): Collection
{
return $this->permissions->pluck('name');
}
/**
* @param string|int|array|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection $permissions
* @return \Spatie\Permission\Contracts\Permission|\Spatie\Permission\Contracts\Permission[]|\Illuminate\Support\Collection
*/
protected function getStoredPermission($permissions)
{
$permissionClass = $this->getPermissionClass();
if (is_numeric($permissions)) {
return $permissionClass->findById($permissions, $this->getDefaultGuardName());
}
if (is_string($permissions)) {
return $permissionClass->findByName($permissions, $this->getDefaultGuardName());
}
if (is_array($permissions)) {
$permissions = array_map(function ($permission) use ($permissionClass) {
return is_a($permission, get_class($permissionClass)) ? $permission->name : $permission;
}, $permissions);
return $permissionClass
->whereIn('name', $permissions)
->whereIn('guard_name', $this->getGuardNames())
->get();
}
return $permissions;
}
/**
* @param \Spatie\Permission\Contracts\Permission|\Spatie\Permission\Contracts\Role $roleOrPermission
*
* @throws \Spatie\Permission\Exceptions\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|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection ...$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|\Spatie\Permission\Contracts\Permission|\Illuminate\Support\Collection ...$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,351 @@
<?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;
/** @var string */
private $roleClass;
public static function bootHasRoles()
{
static::deleting(function ($model) {
if (method_exists($model, 'isForceDeleting') && ! $model->isForceDeleting()) {
return;
}
$teams = PermissionRegistrar::$teams;
PermissionRegistrar::$teams = false;
$model->roles()->detach();
PermissionRegistrar::$teams = $teams;
});
}
public function getRoleClass()
{
if (! isset($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'),
PermissionRegistrar::$pivotRole
);
if (! PermissionRegistrar::$teams) {
return $relation;
}
return $relation->wherePivot(PermissionRegistrar::$teamsKey, getPermissionsTeamId())
->where(function ($q) {
$teamField = config('permission.table_names.roles').'.'.PermissionRegistrar::$teamsKey;
$q->whereNull($teamField)->orWhere($teamField, getPermissionsTeamId());
});
}
/**
* Scope the model query to certain roles only.
*
* @param string|int|array|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection $roles
* @param string $guard
*/
public function scopeRole(Builder $query, $roles, $guard = null): Builder
{
if ($roles instanceof Collection) {
$roles = $roles->all();
}
$roles = array_map(function ($role) use ($guard) {
if ($role instanceof Role) {
return $role;
}
$method = is_numeric($role) ? 'findById' : 'findByName';
return $this->getRoleClass()->{$method}($role, $guard ?: $this->getDefaultGuardName());
}, Arr::wrap($roles));
return $query->whereHas('roles', function (Builder $subQuery) use ($roles) {
$roleClass = $this->getRoleClass();
$key = (new $roleClass())->getKeyName();
$subQuery->whereIn(config('permission.table_names.roles').".$key", \array_column($roles, $key));
});
}
/**
* Assign the given role to the model.
*
* @param array|string|int|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection ...$roles
* @return $this
*/
public function assignRole(...$roles)
{
$roles = collect($roles)
->flatten()
->reduce(function ($array, $role) {
if (empty($role)) {
return $array;
}
$role = $this->getStoredRole($role);
if (! $role instanceof Role) {
return $array;
}
$this->ensureModelSharesGuard($role);
$array[$role->getKey()] = PermissionRegistrar::$teams && ! is_a($this, Permission::class) ?
[PermissionRegistrar::$teamsKey => getPermissionsTeamId()] : [];
return $array;
}, []);
$model = $this->getModel();
if ($model->exists) {
$this->roles()->sync($roles, false);
$model->load('roles');
} else {
$class = \get_class($model);
$class::saved(
function ($object) use ($roles, $model) {
if ($model->getKey() != $object->getKey()) {
return;
}
$model->roles()->sync($roles, false);
$model->load('roles');
}
);
}
if (is_a($this, get_class($this->getPermissionClass()))) {
$this->forgetCachedPermissions();
}
return $this;
}
/**
* Revoke the given role from the model.
*
* @param string|int|\Spatie\Permission\Contracts\Role $role
*/
public function removeRole($role)
{
$this->roles()->detach($this->getStoredRole($role));
$this->load('roles');
if (is_a($this, get_class($this->getPermissionClass()))) {
$this->forgetCachedPermissions();
}
return $this;
}
/**
* Remove all current roles and set the given ones.
*
* @param array|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection|string|int ...$roles
* @return $this
*/
public function syncRoles(...$roles)
{
$this->roles()->detach();
return $this->assignRole($roles);
}
/**
* Determine if the model has (one of) the given role(s).
*
* @param string|int|array|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection $roles
*/
public function hasRole($roles, string $guard = null): bool
{
$this->loadMissing('roles');
if (is_string($roles) && false !== strpos($roles, '|')) {
$roles = $this->convertPipeToArray($roles);
}
if (is_string($roles)) {
return $guard
? $this->roles->where('guard_name', $guard)->contains('name', $roles)
: $this->roles->contains('name', $roles);
}
if (is_int($roles)) {
$roleClass = $this->getRoleClass();
$key = (new $roleClass())->getKeyName();
return $guard
? $this->roles->where('guard_name', $guard)->contains($key, $roles)
: $this->roles->contains($key, $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;
}
return $roles->intersect($guard ? $this->roles->where('guard_name', $guard) : $this->roles)->isNotEmpty();
}
/**
* Determine if the model has any of the given role(s).
*
* Alias to hasRole() but without Guard controls
*
* @param string|int|array|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection $roles
*/
public function hasAnyRole(...$roles): bool
{
return $this->hasRole($roles);
}
/**
* Determine if the model has all of the given role(s).
*
* @param string|array|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection $roles
*/
public function hasAllRoles($roles, string $guard = null): bool
{
$this->loadMissing('roles');
if (is_string($roles) && false !== strpos($roles, '|')) {
$roles = $this->convertPipeToArray($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());
}
$roles = collect()->make($roles)->map(function ($role) {
return $role instanceof Role ? $role->name : $role;
});
return $roles->intersect(
$guard
? $this->roles->where('guard_name', $guard)->pluck('name')
: $this->getRoleNames()
) == $roles;
}
/**
* Determine if the model has exactly all of the given role(s).
*
* @param string|array|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection $roles
*/
public function hasExactRoles($roles, string $guard = null): bool
{
$this->loadMissing('roles');
if (is_string($roles) && false !== strpos($roles, '|')) {
$roles = $this->convertPipeToArray($roles);
}
if (is_string($roles)) {
$roles = [$roles];
}
if ($roles instanceof Role) {
$roles = [$roles->name];
}
$roles = collect()->make($roles)->map(function ($role) {
return $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
{
$roleClass = $this->getRoleClass();
if (is_numeric($role)) {
return $roleClass->findById($role, $this->getDefaultGuardName());
}
if (is_string($role)) {
return $roleClass->findByName($role, $this->getDefaultGuardName());
}
return $role;
}
protected function convertPipeToArray(string $pipeString)
{
$pipeString = trim($pipeString);
if (strlen($pipeString) <= 2) {
return $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,110 @@
<?php
namespace Spatie\Permission;
use Illuminate\Support\Collection;
use Spatie\Permission\Contracts\Wildcard;
use Spatie\Permission\Exceptions\WildcardPermissionNotProperlyFormatted;
class WildcardPermission implements Wildcard
{
/** @var string */
public const WILDCARD_TOKEN = '*';
/** @var string */
public const PART_DELIMITER = '.';
/** @var string */
public const SUBPART_DELIMITER = ',';
/** @var string */
protected $permission;
/** @var Collection */
protected $parts;
public function __construct(string $permission)
{
$this->permission = $permission;
$this->parts = collect();
$this->setParts();
}
/**
* @param string|WildcardPermission $permission
*/
public function implies($permission): bool
{
if (is_string($permission)) {
$permission = new static($permission);
}
$otherParts = $permission->getParts();
$i = 0;
$partsCount = $this->getParts()->count();
foreach ($otherParts as $otherPart) {
if ($partsCount - 1 < $i) {
return true;
}
if (! $this->parts->get($i)->contains(static::WILDCARD_TOKEN)
&& ! $this->containsAll($this->parts->get($i), $otherPart)) {
return false;
}
$i++;
}
for ($i; $i < $partsCount; $i++) {
if (! $this->parts->get($i)->contains(static::WILDCARD_TOKEN)) {
return false;
}
}
return true;
}
protected function containsAll(Collection $part, Collection $otherPart): bool
{
foreach ($otherPart->toArray() as $item) {
if (! $part->contains($item)) {
return false;
}
}
return true;
}
public function getParts(): Collection
{
return $this->parts;
}
/**
* Sets the different parts and subparts from permission string.
*/
protected function setParts(): void
{
if (empty($this->permission) || $this->permission == null) {
throw WildcardPermissionNotProperlyFormatted::create($this->permission);
}
$parts = collect(explode(static::PART_DELIMITER, $this->permission));
$parts->each(function ($item, $key) {
$subParts = collect(explode(static::SUBPART_DELIMITER, $item));
if ($subParts->isEmpty() || $subParts->contains('')) {
throw WildcardPermissionNotProperlyFormatted::create($this->permission);
}
$this->parts->add($subParts);
});
if ($this->parts->isEmpty()) {
throw WildcardPermissionNotProperlyFormatted::create($this->permission);
}
}
}

View File

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