├── .gitattributes ├── .github └── workflows │ └── run-tests.yml ├── .gitignore ├── .scrutinizer.yml ├── .styleci.yml ├── README.md ├── art ├── logo.png └── logo.svg ├── composer.json ├── database └── migrations │ └── 2014_10_12_200000_create_authorization_tables.php ├── license.md ├── phpunit.xml ├── src ├── Authorization.php ├── AuthorizationServiceProvider.php ├── Middleware │ ├── AuthorizationMiddleware.php │ ├── PermissionMiddleware.php │ └── RoleMiddleware.php ├── Permission.php ├── PermissionPivot.php ├── PermissionRegistrar.php ├── Pivot.php ├── Role.php ├── RolePivot.php ├── Traits │ ├── AssociatesPermissions.php │ ├── Authorizable.php │ ├── ClearsCachedPermissions.php │ ├── FlushesLoadedRelations.php │ ├── HasPermissions.php │ ├── HasRoles.php │ ├── HasUsers.php │ └── ManagesPermissions.php └── UserPivot.php └── tests ├── AuthorizationTest.php ├── Stubs └── User.php └── TestCase.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | jobs: 10 | run-tests: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: [ ubuntu-latest ] 16 | laravel: [ 12.*, 11.*, 10.*, 9.* ] 17 | dependency-version: [ prefer-stable ] 18 | include: 19 | - laravel: 12.* 20 | testbench: 10.* 21 | php: 8.4 22 | 23 | - laravel: 11.* 24 | testbench: 9.* 25 | php: 8.3 26 | 27 | - laravel: 10.* 28 | testbench: 8.* 29 | php: 8.3 30 | 31 | - laravel: 9.* 32 | testbench: 7.* 33 | php: 8.2 34 | 35 | name: ${{ matrix.os }} - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} 36 | 37 | steps: 38 | - name: Checkout code 39 | uses: actions/checkout@v4 40 | 41 | - name: Cache dependencies 42 | uses: actions/cache@v4 43 | with: 44 | path: ~/.composer/cache/files 45 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 46 | 47 | - name: Setup PHP 48 | uses: shivammathur/setup-php@v2 49 | with: 50 | php-version: ${{ matrix.php }} 51 | 52 | - name: Install dependencies 53 | run: | 54 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update --dev 55 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction 56 | 57 | - name: Execute tests 58 | run: vendor/bin/phpunit 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | Thumbs.db 5 | .idea 6 | .phpunit.result.cache 7 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: 3 | - tests/* 4 | build: 5 | nodes: 6 | analysis: 7 | tests: 8 | override: 9 | - command: php-scrutinizer-run 10 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | 5 |

6 | 7 |

8 | 9 | 10 | 11 | 12 |

13 | 14 |

15 | An easy, native role / permission management system for Laravel. 16 |

17 | 18 | ## Index 19 | 20 | - [Installation](#installation) 21 | - [Migration Customization](#migration-customization) 22 | - [Model Customization](#model-customization) 23 | - [Usage](#usage) 24 | - [Checking Permissions & Roles](#checking-permissions--roles) 25 | - [Caching](#caching) 26 | - [Gate Registration](#gate-registration) 27 | - [Middleware](#middleware) 28 | - [Testing](#running-tests) 29 | 30 | ## Installation 31 | 32 | To get started, install Authorization via the Composer package manager: 33 | 34 | composer require directorytree/authorization 35 | 36 | The Authorization service provider registers its own database migration directory 37 | with the framework, so you should migrate your database after installing the 38 | package. The Authorization migrations will create the tables your 39 | application needs to store roles and permissions: 40 | 41 | php artisan migrate 42 | 43 | Now insert the `DirectoryTree\Authorization\Traits\Authorizable` onto your `App\Models\User` model: 44 | 45 | ```php 46 | registerPolicies(); 103 | 104 | Authorization::useUserModel(\App\Models\User::class); 105 | Authorization::useRoleModel(\App\Models\Role::class); 106 | Authorization::usePermissionModel(\App\Models\Permission::class); 107 | } 108 | ``` 109 | 110 | Be sure to add the relevant traits for each of your custom models: 111 | 112 | **Role Model**: 113 | 114 | ```php 115 | namespace App\Models; 116 | 117 | use Illuminate\Database\Eloquent\Model; 118 | use DirectoryTree\Authorization\Traits\ManagesPermissions; 119 | 120 | class Role extends Model 121 | { 122 | use ManagesPermissions; 123 | } 124 | ``` 125 | 126 | **Permission Model**: 127 | 128 | ```php 129 | namespace App\Models; 130 | 131 | use Illuminate\Database\Eloquent\Model; 132 | use DirectoryTree\Authorization\Traits\HasUsers; 133 | use DirectoryTree\Authorization\Traits\HasRoles; 134 | use DirectoryTree\Authorization\Traits\ClearsCachedPermissions; 135 | 136 | class Permission extends Model 137 | { 138 | use HasUsers, HasRoles, ClearsCachedPermissions; 139 | } 140 | ``` 141 | 142 | ## Usage 143 | 144 | Authorization uses native Laravel relationships, so there's no need to learn a new API if you don't want to. 145 | 146 | > Due to Authorization's trait based implementation, all of Authorization's functionality can be overridden or extended with you own implementation. 147 | 148 | ### Managing Roles & Permissions 149 | 150 | Create a permission: 151 | 152 | ```php 153 | use DirectoryTree\Authorization\Permission; 154 | 155 | $createUsers = Permission::create([ 156 | 'name' => 'users.create', 157 | 'label' => 'Create Users', 158 | ]); 159 | ``` 160 | 161 | Create a role: 162 | 163 | ```php 164 | use DirectoryTree\Authorization\Role; 165 | 166 | $admin = Role::create([ 167 | 'name' => 'administrator', 168 | 'label' => 'Admin', 169 | ]); 170 | ``` 171 | 172 | Grant the permission to a role: 173 | 174 | ```php 175 | $admin->permissions()->save($createUsers); 176 | ``` 177 | 178 | Now assign the role to the user: 179 | 180 | ```php 181 | $user->roles()->save($admin); 182 | ``` 183 | 184 | You may also use the `grant()` or `revoke()` method on a `Role` model: 185 | 186 | ```php 187 | // Using the permission's name: 188 | $admin->grant('users.create'); 189 | 190 | // Using a permission model: 191 | $admin->grant($permission); 192 | 193 | // Granting multiple permissions: 194 | $admin->grant(['users.create', 'users.edit']); 195 | 196 | // Granting a collection of models: 197 | $admin->grant(Permission::all()); 198 | 199 | // Using a mix of models and permission name: 200 | $admin->grant([$createUsers, 'users.edit']); 201 | ``` 202 | 203 | You may also sync a role's permissions using the `grantOnly()` method: 204 | 205 | ```php 206 | // All permissions will be removed except, except for the given: 207 | $admin->grantOnly('users.create'); 208 | ``` 209 | 210 | ```php 211 | // Using the permission's name: 212 | $admin->revoke('users.create'); 213 | 214 | // Using a permission model: 215 | $admin->revoke($permission); 216 | 217 | // Revoking multiple permissions: 218 | $admin->revoke(['users.create', 'users.edit']); 219 | 220 | // Revoking a collection of models: 221 | $admin->revoke(Permission::all()); 222 | 223 | // Using a mix of models and permission name: 224 | $admin->revoke([$createUsers, 'users.edit']); 225 | ``` 226 | 227 | You may also detach all permissions from a role using `revokeAll()`: 228 | 229 | ```php 230 | $admin->revokeAll(); 231 | ``` 232 | 233 | ### Managing Users & Permissions 234 | 235 | You can also create user specific permissions: 236 | 237 | ```php 238 | $createUsers = Permission::create([ 239 | 'name' => 'users.create', 240 | 'label' => 'Create Users', 241 | ]); 242 | 243 | $user->permissions()->save($createUsers); 244 | ``` 245 | 246 | As with roles, may also use the `grant()` or `revoke()` method on an authorizable `User` model: 247 | 248 | ```php 249 | // Using the permission's name: 250 | $user->grant('users.create'); 251 | 252 | // Using a permission model: 253 | $user->grant($permission); 254 | 255 | // Granting multiple permissions: 256 | $user->grant(['users.create', 'users.edit']); 257 | 258 | // Granting a collection of models: 259 | $user->grant(Permission::all()); 260 | 261 | // Using a mix of models and permission name: 262 | $user->grant([$createUsers, 'users.edit']); 263 | ``` 264 | 265 | You may also sync a users' permissions using the `grantOnly()` method: 266 | 267 | ```php 268 | // All permissions will be removed except, except for the given: 269 | $user->grantOnly('users.create'); 270 | ``` 271 | 272 | ```php 273 | // Using the permission's name: 274 | $user->revoke('users.create'); 275 | 276 | // Using a permission model: 277 | $user->revoke($permission); 278 | 279 | // Granting multiple permissions: 280 | $user->revoke(['users.create', 'users.edit']); 281 | 282 | // Granting a collection of models: 283 | $user->revoke(Permission::all()); 284 | 285 | // Using a mix of models and permission name: 286 | $user->revoke([$createUsers, 'users.edit']); 287 | ``` 288 | 289 | ### Checking Permissions & Roles 290 | 291 | Using Laravel's native `can()` method: 292 | 293 | ```php 294 | if (Auth::user()->can('users.create')) { 295 | // This user can create other users. 296 | } 297 | ``` 298 | 299 | Using Laravel's native `authorize()` method in your controllers: 300 | 301 | ```php 302 | public function create() 303 | { 304 | $this->authorize('users.create'); 305 | 306 | User::create(['...']); 307 | } 308 | ``` 309 | 310 | Using Laravel's native `Gate` facade: 311 | 312 | ```php 313 | if (Gate::allows('users.create')) { 314 | // 315 | } 316 | ``` 317 | 318 | Using Laravel's native `@can` directive in your views: 319 | 320 | ```blade 321 | @can('users.create') 322 | 323 | @endcan 324 | ``` 325 | 326 | ### Checking Permissions & Roles (Using Authorization Package Methods) 327 | 328 | Checking for permission: 329 | 330 | ```php 331 | // Using the permissions name. 332 | if ($user->hasPermission('users.create')) { 333 | // 334 | } 335 | 336 | // Using the permissions model. 337 | if ($user->hasPermission($createUsers)) { 338 | // 339 | } 340 | ``` 341 | 342 | Checking for multiple permissions: 343 | 344 | ```php 345 | if ($user->hasPermissions(['users.create', 'users.edit'])) { 346 | // This user has both creation and edit rights. 347 | } else { 348 | // The user doesn't have one of the specified permissions. 349 | } 350 | ``` 351 | 352 | Checking if the user has any permissions: 353 | 354 | ```php 355 | if ($user->hasAnyPermissions(['users.create', 'users.edit', 'users.destroy'])) { 356 | // This user either has create, edit or destroy permissions. 357 | } else { 358 | // The user doesn't have any of the specified permissions. 359 | } 360 | ``` 361 | 362 | Checking if the user has a role: 363 | 364 | ```php 365 | if ($user->hasRole('administrator')) { 366 | // This user is an administrator. 367 | } else { 368 | // The user isn't an administrator. 369 | } 370 | ``` 371 | 372 | Checking if the user has specified roles: 373 | 374 | ```php 375 | if ($user->hasRoles(['administrator', 'member'])) { 376 | // This user is an administrator and a member. 377 | } else { 378 | // The user isn't an administrator or member. 379 | } 380 | ``` 381 | 382 | Checking if the user has any specified roles: 383 | 384 | ```php 385 | if ($user->hasAnyRoles(['administrator', 'member', 'guest'])) { 386 | // This user is either an administrator, member or guest. 387 | } else { 388 | // The user doesn't have any of these roles. 389 | } 390 | ``` 391 | 392 | ### Caching 393 | 394 | By default all permissions are cached to prevent them from being retrieved on every request. 395 | 396 | This cache is automatically flushed when permissions are created, updated, or deleted. 397 | 398 | If you would like to disable the cache, call `Authorization::disablePermissionCache` in your `AuthServiceProvider`: 399 | 400 | ```php 401 | use DirectoryTree\Authorization\Authorization; 402 | 403 | /** 404 | * Register any authentication / authorization services. 405 | * 406 | * @return void 407 | */ 408 | public function boot() 409 | { 410 | $this->registerPolicies(); 411 | 412 | Authorization::disablePermissionCache(); 413 | } 414 | ``` 415 | 416 | #### Cache Key 417 | 418 | By default, the permission cache key is `authorization.permissions`. 419 | 420 | To alter the cache key, call `Authorization::cacheKey` in your `AuthServiceProvider`: 421 | 422 | ```php 423 | use DirectoryTree\Authorization\Authorization; 424 | 425 | /** 426 | * Register any authentication / authorization services. 427 | * 428 | * @return void 429 | */ 430 | public function boot() 431 | { 432 | $this->registerPolicies(); 433 | 434 | Authorization::cacheKey('my-key'); 435 | } 436 | ``` 437 | 438 | #### Cache Expiry 439 | 440 | By default, the permission cache will expire daily. 441 | 442 | To alter this expiry date, call `Authorization::cacheExpiresIn` in your `AuthServiceProvider`: 443 | 444 | ```php 445 | use DirectoryTree\Authorization\Authorization; 446 | 447 | /** 448 | * Register any authentication / authorization services. 449 | * 450 | * @return void 451 | */ 452 | public function boot() 453 | { 454 | $this->registerPolicies(); 455 | 456 | Authorization::cacheExpiresIn(now()->addWeek()); 457 | } 458 | ``` 459 | 460 | ### Gate Registration 461 | 462 | By default all permissions you create are registered in Laravel's Gate. 463 | 464 | If you would like to disable this, call `Authorization::disableGateRegistration` in your `AuthServiceProvider`: 465 | 466 | ```php 467 | use DirectoryTree\Authorization\Authorization; 468 | 469 | /** 470 | * Register any authentication / authorization services. 471 | * 472 | * @return void 473 | */ 474 | public function boot() 475 | { 476 | $this->registerPolicies(); 477 | 478 | Authorization::disableGateRegistration(); 479 | } 480 | ``` 481 | 482 | ### Middleware 483 | 484 | Authorization includes two useful middleware classes you can utilize for your routes. 485 | 486 | Insert them into your `app/Http/Kernel.php`: 487 | 488 | ```php 489 | /** 490 | * The application's route middleware. 491 | * 492 | * These middleware may be assigned to groups or used individually. 493 | * 494 | * @var array 495 | */ 496 | protected $routeMiddleware = [ 497 | 'auth' => \App\Http\Middleware\Authenticate::class, 498 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 499 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 500 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 501 | 502 | // The role middleware: 503 | 'role' => \DirectoryTree\Authorization\Middleware\RoleMiddleware::class, 504 | 505 | // The permission middleware: 506 | 'permission' => \DirectoryTree\Authorization\Middleware\PermissionMiddleware::class, 507 | ]; 508 | ``` 509 | 510 | Once you've added them, you can start using them. 511 | 512 | > **Note**: When a user does not meet the requirements using the middleware, a 403 HTTP exception is thrown. 513 | 514 | To guard a route to only allow specific permissions: 515 | 516 | ```php 517 | Route::get('users', [ 518 | 'uses' => 'UsersController@index', 519 | 'middleware' => 'permission:users.index', 520 | ]); 521 | 522 | // Multiple permissions: 523 | Route::get('users', [ 524 | 'uses' => 'UsersController@index', 525 | // Users must have index **and** create rights to access this route. 526 | 'middleware' => 'permission:users.index,users.create', 527 | ]); 528 | ``` 529 | 530 | To guard a route to allow a specific role: 531 | 532 | ```php 533 | Route::get('users', [ 534 | 'uses' => 'UsersController@index', 535 | 'middleware' => 'role:administrator', 536 | ]); 537 | 538 | // Multiple roles: 539 | Route::get('users', [ 540 | 'uses' => 'UsersController@index', 541 | // Users must be an administrator **and** a member to access this route. 542 | 'middleware' => 'role:administrator,member', 543 | ]); 544 | ``` 545 | 546 | ### Running Tests 547 | 548 | To run your applications tests, **you must** instantiate the `PermissionRegistrar` 549 | inside your `TestCase::setUp()` method **before** running your 550 | tests for permissions to register properly: 551 | 552 | ```php 553 | use DirectoryTree\Authorization\PermissionRegistrar; 554 | ``` 555 | 556 | ```php 557 | protected function setUp() : void 558 | { 559 | parent::setUp(); 560 | 561 | app(PermissionRegistrar::class)->register(); 562 | } 563 | ``` 564 | -------------------------------------------------------------------------------- /art/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DirectoryTree/Authorization/803a7580a743e44f5195cd448eeec71d89e9d952/art/logo.png -------------------------------------------------------------------------------- /art/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "directorytree/authorization", 3 | "description": "Native Laravel Authorization.", 4 | "keywords": [ 5 | "authorization", 6 | "laravel", 7 | "permissions", 8 | "roles", 9 | "acl" 10 | ], 11 | "license": "MIT", 12 | "type": "project", 13 | "require": { 14 | "php": ">=7.4", 15 | "illuminate/database": "^5.5|^6.20|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0" 16 | }, 17 | "require-dev": { 18 | "orchestra/testbench": "^3.7|^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", 19 | "phpunit/phpunit": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0" 20 | }, 21 | "minimum-stability": "dev", 22 | "prefer-stable": true, 23 | "autoload": { 24 | "psr-4": { 25 | "DirectoryTree\\Authorization\\": "src/" 26 | } 27 | }, 28 | "autoload-dev": { 29 | "psr-4": { 30 | "DirectoryTree\\Authorization\\Tests\\": "tests/" 31 | } 32 | }, 33 | "extra": { 34 | "laravel": { 35 | "providers": [ 36 | "DirectoryTree\\Authorization\\AuthorizationServiceProvider" 37 | ] 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_200000_create_authorization_tables.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->timestamps(); 19 | $table->string('name')->unique()->index(); 20 | $table->string('label')->nullable(); 21 | }); 22 | 23 | Schema::create('permissions', function (Blueprint $table) { 24 | $table->increments('id'); 25 | $table->timestamps(); 26 | $table->string('name')->unique()->index(); 27 | $table->string('label')->nullable(); 28 | }); 29 | 30 | Schema::create('permission_role', function (Blueprint $table) { 31 | $table->unsignedInteger('permission_id'); 32 | $table->unsignedInteger('role_id'); 33 | 34 | $table->primary(['permission_id', 'role_id']); 35 | }); 36 | 37 | Schema::create('permission_user', function (Blueprint $table) { 38 | $table->unsignedInteger('permission_id'); 39 | $table->unsignedInteger('user_id'); 40 | 41 | $table->primary(['permission_id', 'user_id']); 42 | }); 43 | 44 | Schema::create('role_user', function (Blueprint $table) { 45 | $table->unsignedInteger('role_id'); 46 | $table->unsignedInteger('user_id'); 47 | 48 | $table->primary(['role_id', 'user_id']); 49 | }); 50 | } 51 | 52 | /** 53 | * Reverse the migrations. 54 | * 55 | * @return void 56 | */ 57 | public function down() 58 | { 59 | Schema::dropIfExists('role_user'); 60 | Schema::dropIfExists('permission_role'); 61 | Schema::dropIfExists('permission_user'); 62 | Schema::dropIfExists('permissions'); 63 | Schema::dropIfExists('roles'); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © Steve Bauman 3 | 4 | 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: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | 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. -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Authorization.php: -------------------------------------------------------------------------------- 1 | addDay(); 76 | } 77 | 78 | static::$cacheExpiresAt = $date; 79 | 80 | return new static; 81 | } 82 | 83 | /** 84 | * Get or set the cache key. 85 | * 86 | * @param string|null $cacheKey 87 | * 88 | * @return static|string 89 | */ 90 | public static function cacheKey($cacheKey = null) 91 | { 92 | if (is_null($cacheKey)) { 93 | return static::$cacheKey; 94 | } 95 | 96 | static::$cacheKey = $cacheKey; 97 | 98 | return new static; 99 | } 100 | 101 | /** 102 | * Set the user model class name. 103 | * 104 | * @param $userModel 105 | */ 106 | public static function useUserModel($userModel) 107 | { 108 | static::$userModel = $userModel; 109 | } 110 | 111 | /** 112 | * Get the user model class name. 113 | * 114 | * @return string 115 | */ 116 | public static function userModel() 117 | { 118 | return static::$userModel; 119 | } 120 | 121 | /** 122 | * Get a new user model instance. 123 | * 124 | * @return \Illuminate\Database\Eloquent\Model 125 | */ 126 | public static function user() 127 | { 128 | return new static::$userModel; 129 | } 130 | 131 | /** 132 | * Set the role model class name. 133 | * 134 | * @param $roleModel 135 | */ 136 | public static function useRoleModel($roleModel) 137 | { 138 | static::$roleModel = $roleModel; 139 | } 140 | 141 | /** 142 | * Get the role model class name. 143 | * 144 | * @return string 145 | */ 146 | public static function roleModel() 147 | { 148 | return static::$roleModel; 149 | } 150 | 151 | /** 152 | * Get a new role model instance. 153 | * 154 | * @return Role 155 | */ 156 | public static function role() 157 | { 158 | return new static::$roleModel; 159 | } 160 | 161 | /** 162 | * Set the permission model class name. 163 | * 164 | * @param $permissionModel 165 | */ 166 | public static function usePermissionModel($permissionModel) 167 | { 168 | static::$permissionModel = $permissionModel; 169 | } 170 | 171 | /** 172 | * Get the permission model class name. 173 | * 174 | * @return string 175 | */ 176 | public static function permissionModel() 177 | { 178 | return static::$permissionModel; 179 | } 180 | 181 | /** 182 | * Get a new permission model instance. 183 | * 184 | * @return Permission 185 | */ 186 | public static function permission() 187 | { 188 | return new static::$permissionModel; 189 | } 190 | 191 | /** 192 | * Configure Authorization to not register in the auth gate. 193 | * 194 | * @return static 195 | */ 196 | public function disableGateRegistration() 197 | { 198 | static::$registersInGate = false; 199 | 200 | return new static; 201 | } 202 | 203 | /** 204 | * Configure Authorization to not cache permissions. 205 | * 206 | * @return static 207 | */ 208 | public static function disablePermissionCache() 209 | { 210 | static::$cachesPermissions = false; 211 | 212 | return new static; 213 | } 214 | 215 | /** 216 | * Configure Authorization to not register its migrations. 217 | * 218 | * @return static 219 | */ 220 | public static function ignoreMigrations() 221 | { 222 | static::$runsMigrations = false; 223 | 224 | return new static; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/AuthorizationServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 17 | $this->registerMigrations(); 18 | 19 | $this->publishes([ 20 | __DIR__.'/../database/migrations' => database_path('migrations'), 21 | ], 'authorization-migrations'); 22 | } 23 | 24 | // Register the permissions. 25 | if (Authorization::$registersInGate) { 26 | app(PermissionRegistrar::class)->register(); 27 | } 28 | } 29 | 30 | /** 31 | * Register Authorization migration files. 32 | * 33 | * @return void 34 | */ 35 | protected function registerMigrations() 36 | { 37 | if (Authorization::$runsMigrations) { 38 | $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Middleware/AuthorizationMiddleware.php: -------------------------------------------------------------------------------- 1 | authorize($request->user(), $auth)) { 22 | return $this->unauthorized(); 23 | } 24 | 25 | return $next($request); 26 | } 27 | 28 | /** 29 | * Abort the application request. 30 | * 31 | * @return void 32 | */ 33 | protected function unauthorized() 34 | { 35 | abort(403, 'Unauthorized.'); 36 | } 37 | 38 | /** 39 | * Authorize whether the user has access to the route. 40 | * 41 | * @param \App\User $user 42 | * @param array|null $auth 43 | * 44 | * @return bool 45 | */ 46 | abstract protected function authorize($user, $auth = null); 47 | } 48 | -------------------------------------------------------------------------------- /src/Middleware/PermissionMiddleware.php: -------------------------------------------------------------------------------- 1 | hasPermissions($permissions); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Middleware/RoleMiddleware.php: -------------------------------------------------------------------------------- 1 | hasRoles($roles); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Permission.php: -------------------------------------------------------------------------------- 1 | gate = $gate; 34 | $this->cache = $manager; 35 | } 36 | 37 | /** 38 | * Registers permissions into the gate. 39 | * 40 | * @return void 41 | */ 42 | public function register() 43 | { 44 | // Dynamically register permissions with Laravel's Gate. 45 | foreach ($this->getPermissions() as $permission) { 46 | $this->gate->define($permission->name, function ($user) use ($permission) { 47 | return $user->hasPermission($permission); 48 | }); 49 | } 50 | } 51 | 52 | /** 53 | * Fetch the collection of permissions. 54 | * 55 | * @return \Illuminate\Database\Eloquent\Collection|array 56 | */ 57 | public function getPermissions() 58 | { 59 | try { 60 | if (Authorization::$cachesPermissions) { 61 | return $this->cache->remember(Authorization::cacheKey(), Authorization::cacheExpiresIn(), function () { 62 | return Authorization::permission()->get(); 63 | }); 64 | } 65 | 66 | return Authorization::permission()->get(); 67 | } catch (PDOException $e) { 68 | // Migrations haven't been ran yet. 69 | } 70 | 71 | return []; 72 | } 73 | 74 | /** 75 | * Flushes the permission cache. 76 | * 77 | * @return void 78 | */ 79 | public function flushCache() 80 | { 81 | $this->cache->forget(Authorization::cacheKey()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Pivot.php: -------------------------------------------------------------------------------- 1 | whereName($permissions)->first(); 24 | } 25 | 26 | if ($permissions instanceof Model) { 27 | return $this->hasPermission($permissions) 28 | ? $permissions 29 | : $this->permissions()->save($permissions); 30 | } 31 | 32 | return Collection::make($permissions)->map(function ($permission) { 33 | return $this->grant($permission) instanceof Model ? $permission : false; 34 | })->filter(); 35 | } 36 | 37 | /** 38 | * Grant only the given permission(s), detaching any previous permission(s). 39 | * 40 | * @param string|string[]|Model|Model[] $permissions 41 | * 42 | * @return Model|Model[]|Collection 43 | */ 44 | public function grantOnly($permissions) 45 | { 46 | $this->revokeAll(); 47 | 48 | return $this->grant($permissions); 49 | } 50 | 51 | /** 52 | * Revoke the given permission(s). 53 | * 54 | * Returns a collection of revoked permissions. 55 | * 56 | * @param string|string[]|Model|Model[] $permissions 57 | * 58 | * @return Model|Model[]|Collection 59 | */ 60 | public function revoke($permissions) 61 | { 62 | if (is_string($permissions)) { 63 | $permissions = Authorization::permission()->whereName($permissions)->first(); 64 | } 65 | 66 | if ($permissions instanceof Model) { 67 | if (! $this->hasPermission($permissions)) { 68 | return $permissions; 69 | } 70 | 71 | $this->permissions()->detach($permissions); 72 | 73 | return $permissions; 74 | } 75 | 76 | return Collection::make($permissions)->map(function ($permission) { 77 | return $this->revoke($permission) instanceof Model ? $permission : false; 78 | })->filter(); 79 | } 80 | 81 | /** 82 | * Revokes all permissions. 83 | * 84 | * Returns the number of revoked permissions. 85 | * 86 | * @return int 87 | */ 88 | public function revokeAll() 89 | { 90 | $detached = $this->permissions()->detach(); 91 | 92 | // When permissions are completely revoked, we must purge the 93 | // permissions relation so we do not get false positives on 94 | // any following permission checks during the same request. 95 | $this->unsetRelation('permissions'); 96 | 97 | return $detached; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Traits/Authorizable.php: -------------------------------------------------------------------------------- 1 | whereName($role)->firstOrFail(); 24 | } 25 | 26 | return $this->roles()->save($role); 27 | } 28 | 29 | /** 30 | * Removes the specified role from the user. 31 | * 32 | * @param string|Model $role 33 | * 34 | * @return int 35 | */ 36 | public function removeRole($role) 37 | { 38 | if (! $role instanceof Model) { 39 | $role = Authorization::role()->whereName($role)->firstOrFail(); 40 | } 41 | 42 | return $this->roles()->detach($role); 43 | } 44 | 45 | /** 46 | * Determine if the user has the given role. 47 | * 48 | * @param string|Model $role 49 | * 50 | * @return bool 51 | */ 52 | public function hasRole($role) 53 | { 54 | if (empty($role)) { 55 | return false; 56 | } 57 | 58 | $this->loadMissing('roles'); 59 | 60 | return $role instanceof Model 61 | ? $this->roles->contains($role) 62 | : $this->roles->contains('name', $role); 63 | } 64 | 65 | /** 66 | * Determine if the user has all of the given roles. 67 | * 68 | * @param array $roles 69 | * 70 | * @return bool 71 | */ 72 | public function hasRoles($roles) 73 | { 74 | $roles = Collection::make($roles); 75 | 76 | if ($roles->isEmpty()) { 77 | return false; 78 | } 79 | 80 | return $roles->filter(function ($role) { 81 | return $this->hasRole($role); 82 | })->count() === $roles->count(); 83 | } 84 | 85 | /** 86 | * Determine if the user has any of the given roles. 87 | * 88 | * @param array $roles 89 | * 90 | * @return bool 91 | */ 92 | public function hasAnyRoles($roles) 93 | { 94 | return Collection::make($roles)->filter(function ($role) { 95 | return $this->hasRole($role); 96 | })->isNotEmpty(); 97 | } 98 | 99 | /** 100 | * Determine if the user has the given permission. 101 | * 102 | * @param string|Model $permission 103 | * 104 | * @return bool 105 | */ 106 | public function hasPermission($permission) 107 | { 108 | if (is_string($permission)) { 109 | // If we've been given a string, then we can 110 | // assume its the permissions name. We will 111 | // attempt to fetch it from the database. 112 | $permission = Authorization::permission()->whereName($permission)->first(); 113 | } 114 | 115 | if (! $permission instanceof Model) { 116 | return false; 117 | } 118 | 119 | // Here we will check if the user has been granted 120 | // explicit this permission explicitly. If so, we 121 | // can return here. No further check is needed. 122 | if ($this->permissions()->find($permission->getKey())) { 123 | return true; 124 | } 125 | 126 | // Otherwise, we'll determine their permission by gathering 127 | // the roles that the permission belongs to and checking 128 | // if the user is a member of any of the fetched roles. 129 | $roles = $permission->roles()->get()->map(function ($role) { 130 | return $role->getKey(); 131 | }); 132 | 133 | return $this->roles() 134 | ->whereIn($this->roles()->getRelatedPivotKeyName(), $roles) 135 | ->count() > 0; 136 | } 137 | 138 | /** 139 | * Determine if the user has all of the given permissions. 140 | * 141 | * @param array|\Illuminate\Support\Collection $permissions 142 | * 143 | * @return bool 144 | */ 145 | public function hasPermissions($permissions) 146 | { 147 | $permissions = Collection::make($permissions); 148 | 149 | if ($permissions->isEmpty()) { 150 | return false; 151 | } 152 | 153 | return $permissions->filter(function ($permission) { 154 | return $this->hasPermission($permission); 155 | })->count() === $permissions->count(); 156 | } 157 | 158 | /** 159 | * Determine if the user has any of the permissions. 160 | * 161 | * @param array|\Illuminate\Support\Collection $permissions 162 | * 163 | * @return bool 164 | */ 165 | public function hasAnyPermissions($permissions) 166 | { 167 | return Collection::make($permissions)->filter(function ($permission) { 168 | return $this->hasPermission($permission); 169 | })->isNotEmpty(); 170 | } 171 | 172 | /** 173 | * Determine if the user does not have the given permission. 174 | * 175 | * @param string|Model $permission 176 | * 177 | * @return bool 178 | */ 179 | public function doesNotHavePermission($permission) 180 | { 181 | return ! $this->hasPermission($permission); 182 | } 183 | 184 | /** 185 | * Determine if the user does not have all of the given permissions. 186 | * 187 | * @param array|\Illuminate\Support\Collection $permissions 188 | * 189 | * @return bool 190 | */ 191 | public function doesNotHavePermissions($permissions) 192 | { 193 | return ! $this->hasPermissions($permissions); 194 | } 195 | 196 | /** 197 | * Determine if the user does not have any of the given permissions. 198 | * 199 | * @param array|\Illuminate\Support\Collection $permissions 200 | * 201 | * @return bool 202 | */ 203 | public function doesNotHaveAnyPermissions($permissions) 204 | { 205 | return ! $this->hasAnyPermissions($permissions); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/Traits/ClearsCachedPermissions.php: -------------------------------------------------------------------------------- 1 | flushCache(); 20 | }); 21 | 22 | static::deleted(function () { 23 | app(PermissionRegistrar::class)->flushCache(); 24 | }); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Traits/FlushesLoadedRelations.php: -------------------------------------------------------------------------------- 1 | pivotParent->unsetRelation(static::$flushingRelation); 25 | }); 26 | 27 | static::deleted(function (Pivot $pivot) { 28 | $pivot->pivotParent->unsetRelation(static::$flushingRelation); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Traits/HasPermissions.php: -------------------------------------------------------------------------------- 1 | belongsToMany(Authorization::permissionModel())->using(PermissionPivot::class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Traits/HasRoles.php: -------------------------------------------------------------------------------- 1 | belongsToMany(Authorization::roleModel())->using(RolePivot::class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Traits/HasUsers.php: -------------------------------------------------------------------------------- 1 | belongsToMany(Authorization::userModel())->using(UserPivot::class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Traits/ManagesPermissions.php: -------------------------------------------------------------------------------- 1 | loadMissing('permissions'); 27 | 28 | return $permission instanceof Model 29 | ? $this->permissions->contains($permission) 30 | : $this->permissions->contains('name', $permission); 31 | } 32 | 33 | /** 34 | * Determine if the current role has the given permissions. 35 | * 36 | * @param string|array $permissions 37 | * 38 | * @return bool 39 | */ 40 | public function hasPermissions($permissions) 41 | { 42 | return Collection::make(Arr::wrap($permissions))->filter(function ($permission) { 43 | return $this->hasPermission($permission); 44 | })->count() === count($permissions); 45 | } 46 | 47 | /** 48 | * Determine if the current role has any of the given permissions. 49 | * 50 | * @param array $permissions 51 | * 52 | * @return bool 53 | */ 54 | public function hasAnyPermissions($permissions) 55 | { 56 | return Collection::make(Arr::wrap($permissions))->filter(function ($permission) { 57 | return $this->hasPermission($permission); 58 | })->count() > 0; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/UserPivot.php: -------------------------------------------------------------------------------- 1 | createRole([ 14 | 'name' => 'administrator', 15 | 'label' => 'Admin', 16 | ]); 17 | 18 | $user = $this->createUser([ 19 | 'name' => 'John Doe', 20 | ]); 21 | 22 | $user->assignRole('administrator'); 23 | 24 | $this->assertCount(1, $user->roles); 25 | } 26 | 27 | public function test_assign_multiple_roles() 28 | { 29 | $this->createRole([ 30 | 'name' => 'administrator', 31 | 'label' => 'Admin', 32 | ]); 33 | 34 | $this->createRole([ 35 | 'name' => 'member', 36 | 'label' => 'Member', 37 | ]); 38 | 39 | $user = $this->createUser([ 40 | 'name' => 'John Doe', 41 | ]); 42 | 43 | $user->assignRole('administrator'); 44 | $user->assignRole('member'); 45 | 46 | $this->assertCount(2, $user->roles); 47 | } 48 | 49 | public function test_assign_roles_with_model() 50 | { 51 | $admin = $this->createRole([ 52 | 'name' => 'administrator', 53 | 'label' => 'Admin', 54 | ]); 55 | 56 | $member = $this->createRole([ 57 | 'name' => 'member', 58 | 'label' => 'Member', 59 | ]); 60 | 61 | $user = $this->createUser([ 62 | 'name' => 'John Doe', 63 | ]); 64 | 65 | $this->assertEmpty($user->roles); 66 | 67 | $user->assignRole($admin); 68 | $user->assignRole($member); 69 | 70 | $this->assertCount(2, $user->roles); 71 | } 72 | 73 | public function test_has_role() 74 | { 75 | $admin = $this->createRole([ 76 | 'name' => 'administrator', 77 | 'label' => 'Admin', 78 | ]); 79 | 80 | $user = $this->createUser([ 81 | 'name' => 'John Doe', 82 | ]); 83 | 84 | $this->assertFalse($user->hasRole($admin)); 85 | $this->assertFalse($user->hasRole('administrator')); 86 | 87 | $user->assignRole($admin); 88 | 89 | $this->assertTrue($user->hasRole($admin)); 90 | $this->assertTrue($user->hasRole('administrator')); 91 | $this->assertFalse($user->hasRole('non-existent')); 92 | } 93 | 94 | public function test_grant_permission() 95 | { 96 | $user = $this->createUser([ 97 | 'name' => 'John Doe', 98 | ]); 99 | 100 | $admin = $this->createRole([ 101 | 'name' => 'administrator', 102 | 'label' => 'Admin', 103 | ]); 104 | 105 | $createUsers = $this->createPermission([ 106 | 'name' => 'users.create', 107 | 'label' => 'Create Users', 108 | ]); 109 | 110 | $this->assertFalse($user->hasRole($admin)); 111 | $this->assertFalse($user->hasPermission($createUsers)); 112 | 113 | $user->assignRole($admin); 114 | 115 | $this->assertTrue($user->hasRole($admin)); 116 | 117 | $this->assertSame($createUsers, $admin->grant($createUsers)); 118 | 119 | $this->assertTrue($user->hasPermission($createUsers)); 120 | $this->assertTrue($admin->hasPermission($createUsers)); 121 | } 122 | 123 | public function test_grant_only_permission() 124 | { 125 | $user = $this->createUser([ 126 | 'name' => 'John Doe', 127 | ]); 128 | 129 | $admin = $this->createRole([ 130 | 'name' => 'administrator', 131 | 'label' => 'Admin', 132 | ]); 133 | 134 | $createUsers = $this->createPermission([ 135 | 'name' => 'users.create', 136 | 'label' => 'Create Users', 137 | ]); 138 | 139 | $editUsers = $this->createPermission([ 140 | 'name' => 'users.edit', 141 | 'label' => 'Edit Users', 142 | ]); 143 | 144 | $this->assertFalse($user->hasRole($admin)); 145 | $this->assertFalse($user->hasPermission($createUsers)); 146 | $this->assertFalse($user->hasPermission($editUsers)); 147 | 148 | $this->assertFalse($admin->hasPermission($createUsers)); 149 | $this->assertFalse($admin->hasPermission($editUsers)); 150 | 151 | $user->assignRole($admin); 152 | 153 | $this->assertTrue($user->hasRole($admin)); 154 | 155 | $this->assertSame($createUsers, $admin->grantOnly($createUsers)); 156 | 157 | $this->assertTrue($user->hasPermission($createUsers)); 158 | $this->assertTrue($admin->hasPermission($createUsers)); 159 | 160 | $this->assertFalse($user->hasPermission($editUsers)); 161 | $this->assertFalse($admin->hasPermission($editUsers)); 162 | } 163 | 164 | public function test_grant_permission_on_user() 165 | { 166 | $user = $this->createUser([ 167 | 'name' => 'John Doe', 168 | ]); 169 | 170 | $createUsers = $this->createPermission([ 171 | 'name' => 'users.create', 172 | 'label' => 'Create Users', 173 | ]); 174 | 175 | $this->assertTrue($createUsers->is($user->grant('users.create'))); 176 | $this->assertTrue($user->hasPermission('users.create')); 177 | $this->assertSame($createUsers, $user->grant($createUsers)); 178 | 179 | $user->revoke('users.create'); 180 | 181 | $this->assertFalse($user->hasPermission('users.create')); 182 | } 183 | 184 | public function test_grant_only_permission_on_user() 185 | { 186 | $user = $this->createUser([ 187 | 'name' => 'John Doe', 188 | ]); 189 | 190 | $createUsers = $this->createPermission([ 191 | 'name' => 'users.create', 192 | 'label' => 'Create Users', 193 | ]); 194 | 195 | $editUsers = $this->createPermission([ 196 | 'name' => 'users.edit', 197 | 'label' => 'Edit Users', 198 | ]); 199 | 200 | $user->grant([$createUsers, $editUsers]); 201 | 202 | $this->assertTrue($user->hasPermission('users.create')); 203 | $this->assertTrue($user->hasPermission('users.edit')); 204 | 205 | $user->grantOnly($createUsers); 206 | 207 | $this->assertFalse($user->hasPermission('users.edit')); 208 | $this->assertTrue($user->hasPermission('users.create')); 209 | } 210 | 211 | public function test_grant_multiple_permissions() 212 | { 213 | $user = $this->createUser([ 214 | 'name' => 'John Doe', 215 | ]); 216 | 217 | $admin = $this->createRole([ 218 | 'name' => 'administrator', 219 | 'label' => 'Admin', 220 | ]); 221 | 222 | $this->assertFalse($user->hasRole($admin)); 223 | $this->assertFalse($user->hasRole('administrator')); 224 | 225 | $user->assignRole($admin); 226 | 227 | $this->assertTrue($user->hasRole($admin)); 228 | $this->assertTrue($user->hasRole('administrator')); 229 | 230 | $createUsers = $this->createPermission([ 231 | 'name' => 'users.create', 232 | 'label' => 'Create Users', 233 | ]); 234 | 235 | $editUsers = $this->createPermission([ 236 | 'name' => 'users.edit', 237 | 'label' => 'Edit Users', 238 | ]); 239 | 240 | $granted = $admin->grant([$createUsers, $editUsers]); 241 | 242 | $this->assertInstanceOf(Collection::class, $granted); 243 | $this->assertCount(2, $granted); 244 | 245 | $this->assertTrue($user->hasPermission($createUsers)); 246 | $this->assertTrue($user->hasPermission($editUsers)); 247 | 248 | $this->assertTrue($admin->hasPermission($createUsers)); 249 | $this->assertTrue($admin->hasPermission($editUsers)); 250 | } 251 | 252 | public function test_grant_multiple_permissions_on_user() 253 | { 254 | $user = $this->createUser([ 255 | 'name' => 'John Doe', 256 | ]); 257 | 258 | $createUsers = $this->createPermission([ 259 | 'name' => 'users.create', 260 | 'label' => 'Create Users', 261 | ]); 262 | 263 | $editUsers = $this->createPermission([ 264 | 'name' => 'users.edit', 265 | 'label' => 'Edit Users', 266 | ]); 267 | 268 | $granted = $user->grant([$createUsers, $editUsers, 'invalid']); 269 | 270 | $this->assertInstanceOf(Collection::class, $granted); 271 | $this->assertSame($createUsers, $granted->get(0)); 272 | $this->assertSame($editUsers, $granted->get(1)); 273 | $this->assertCount(2, $granted); 274 | 275 | $this->assertTrue($user->hasPermission($createUsers)); 276 | $this->assertTrue($user->hasPermission($editUsers)); 277 | } 278 | 279 | public function test_grant_multiple_permissions_with_non_existent_permission() 280 | { 281 | $user = $this->createUser([ 282 | 'name' => 'John Doe', 283 | ]); 284 | 285 | $admin = $this->createRole([ 286 | 'name' => 'administrator', 287 | 'label' => 'Admin', 288 | ]); 289 | 290 | $user->assignRole($admin); 291 | 292 | $createUsers = $this->createPermission([ 293 | 'name' => 'users.create', 294 | 'label' => 'Create Users', 295 | ]); 296 | 297 | $editUsers = $this->createPermission([ 298 | 'name' => 'users.edit', 299 | 'label' => 'Edit Users', 300 | ]); 301 | 302 | $this->assertEquals(2, $admin->grant([$createUsers, $editUsers, 'invalid'])->count()); 303 | } 304 | 305 | public function test_revoke_permission() 306 | { 307 | $user = $this->createUser([ 308 | 'name' => 'John Doe', 309 | ]); 310 | 311 | $admin = $this->createRole([ 312 | 'name' => 'administrator', 313 | 'label' => 'Admin', 314 | ]); 315 | 316 | $user->assignRole($admin); 317 | 318 | $createUsers = $this->createPermission([ 319 | 'name' => 'users.create', 320 | 'label' => 'Create Users', 321 | ]); 322 | 323 | $admin->grant($createUsers); 324 | 325 | $this->assertTrue($admin->hasPermission($createUsers)); 326 | $this->assertTrue($user->hasPermission($createUsers)); 327 | 328 | $this->assertSame($createUsers, $admin->revoke($createUsers)); 329 | 330 | $this->assertFalse($admin->hasPermission($createUsers)); 331 | $this->assertFalse($user->hasPermission($createUsers)); 332 | } 333 | 334 | public function test_revoke_permission_on_user() 335 | { 336 | $user = $this->createUser([ 337 | 'name' => 'John Doe', 338 | ]); 339 | 340 | $createUsers = $this->createPermission([ 341 | 'name' => 'users.create', 342 | 'label' => 'Create Users', 343 | ]); 344 | 345 | $user->grant($createUsers); 346 | 347 | $this->assertTrue($user->hasPermission($createUsers)); 348 | 349 | $this->assertSame($createUsers, $user->revoke($createUsers)); 350 | 351 | $this->assertFalse($user->hasPermission($createUsers)); 352 | } 353 | 354 | public function test_revoke_multiple_permissions() 355 | { 356 | $user = $this->createUser([ 357 | 'name' => 'John Doe', 358 | ]); 359 | 360 | $admin = $this->createRole([ 361 | 'name' => 'administrator', 362 | 'label' => 'Admin', 363 | ]); 364 | 365 | $user->assignRole($admin); 366 | 367 | $createUsers = $this->createPermission([ 368 | 'name' => 'users.create', 369 | 'label' => 'Create Users', 370 | ]); 371 | 372 | $editUsers = $this->createPermission([ 373 | 'name' => 'users.edit', 374 | 'label' => 'Edit Users', 375 | ]); 376 | 377 | $admin->grant([$createUsers, $editUsers]); 378 | 379 | $this->assertTrue($admin->hasPermission($editUsers)); 380 | $this->assertTrue($admin->hasPermission($createUsers)); 381 | 382 | $this->assertTrue($user->hasPermission($editUsers)); 383 | $this->assertTrue($user->hasPermission($createUsers)); 384 | 385 | $this->assertCount(2, $admin->revoke([$createUsers, $editUsers])); 386 | 387 | $this->assertFalse($admin->hasPermission($editUsers)); 388 | $this->assertFalse($admin->hasPermission($createUsers)); 389 | 390 | $this->assertFalse($user->hasPermission($editUsers)); 391 | $this->assertFalse($user->hasPermission($createUsers)); 392 | } 393 | 394 | public function test_revoke_multiple_permissions_on_user() 395 | { 396 | $user = $this->createUser([ 397 | 'name' => 'John Doe', 398 | ]); 399 | 400 | $createUsers = $this->createPermission([ 401 | 'name' => 'users.create', 402 | 'label' => 'Create Users', 403 | ]); 404 | 405 | $editUsers = $this->createPermission([ 406 | 'name' => 'users.edit', 407 | 'label' => 'Edit Users', 408 | ]); 409 | 410 | $user->grant([$createUsers, $editUsers]); 411 | 412 | $this->assertTrue($user->hasPermission($editUsers)); 413 | $this->assertTrue($user->hasPermission($createUsers)); 414 | 415 | $this->assertCount(2, $user->revoke([$createUsers, $editUsers, 'invalid'])); 416 | 417 | $this->assertFalse($user->hasPermission($editUsers)); 418 | $this->assertFalse($user->hasPermission($createUsers)); 419 | } 420 | 421 | public function test_revoke_all() 422 | { 423 | $user = $this->createUser([ 424 | 'name' => 'John Doe', 425 | ]); 426 | 427 | $createUsers = $this->createPermission([ 428 | 'name' => 'users.create', 429 | 'label' => 'Create Users', 430 | ]); 431 | 432 | $editUsers = $this->createPermission([ 433 | 'name' => 'users.edit', 434 | 'label' => 'Edit Users', 435 | ]); 436 | 437 | $admin = $this->createRole([ 438 | 'name' => 'admin', 439 | 'label' => 'Administrator', 440 | ]); 441 | 442 | $admin->grant([$createUsers, $editUsers]); 443 | 444 | $user->assignRole($admin); 445 | 446 | $this->assertTrue($admin->hasPermissions([$createUsers, $editUsers])); 447 | $this->assertTrue($user->hasPermissions([$createUsers, $editUsers])); 448 | 449 | $admin->revokeAll(); 450 | 451 | $this->assertFalse($admin->hasPermissions([$createUsers, $editUsers])); 452 | $this->assertFalse($admin->hasPermission('users.create')); 453 | $this->assertFalse($admin->hasPermission('users.edit')); 454 | } 455 | 456 | public function test_revoke_all_on_user() 457 | { 458 | $user = $this->createUser([ 459 | 'name' => 'John Doe', 460 | ]); 461 | 462 | $createUsers = $this->createPermission([ 463 | 'name' => 'users.create', 464 | 'label' => 'Create Users', 465 | ]); 466 | 467 | $editUsers = $this->createPermission([ 468 | 'name' => 'users.edit', 469 | 'label' => 'Edit Users', 470 | ]); 471 | 472 | $user->grant([$createUsers, $editUsers]); 473 | 474 | $this->assertTrue($user->hasPermissions([$createUsers, $editUsers])); 475 | 476 | $user->revokeAll(); 477 | 478 | $this->assertFalse($user->hasPermissions([$createUsers, $editUsers])); 479 | $this->assertFalse($user->hasPermission('users.create')); 480 | $this->assertFalse($user->hasPermission('users.edit')); 481 | } 482 | 483 | public function test_revoke_multiple_permissions_with_non_existent_permission() 484 | { 485 | $user = $this->createUser([ 486 | 'name' => 'John Doe', 487 | ]); 488 | 489 | $admin = $this->createRole([ 490 | 'name' => 'administrator', 491 | 'label' => 'Admin', 492 | ]); 493 | 494 | $user->assignRole($admin); 495 | 496 | $createUsers = $this->createPermission([ 497 | 'name' => 'users.create', 498 | 'label' => 'Create Users', 499 | ]); 500 | 501 | $editUsers = $this->createPermission([ 502 | 'name' => 'users.edit', 503 | 'label' => 'Edit Users', 504 | ]); 505 | 506 | $admin->grant([$createUsers, $editUsers]); 507 | 508 | $this->assertEquals(2, $admin->revoke([$createUsers, $editUsers, 'invalid'])->count()); 509 | } 510 | 511 | public function test_has_permission() 512 | { 513 | $admin = $this->createRole([ 514 | 'name' => 'administrator', 515 | 'label' => 'Admin', 516 | ]); 517 | 518 | $permission = $this->createPermission([ 519 | 'name' => 'users.create', 520 | 'label' => 'Create Users', 521 | ]); 522 | 523 | $user = $this->createUser([ 524 | 'name' => 'John Doe', 525 | ]); 526 | 527 | $this->assertFalse($user->hasPermission(null)); 528 | 529 | $user->assignRole($admin); 530 | 531 | $admin->grant($permission); 532 | 533 | $this->assertTrue($user->hasPermission($permission)); 534 | $this->assertTrue($user->hasPermission('users.create')); 535 | $this->assertFalse($user->hasPermission('non-existent')); 536 | } 537 | 538 | public function test_has_multiple_permissions() 539 | { 540 | $admin = $this->createRole([ 541 | 'name' => 'administrator', 542 | 'label' => 'Admin', 543 | ]); 544 | 545 | $createUsers = $this->createPermission([ 546 | 'name' => 'users.create', 547 | 'label' => 'Create Users', 548 | ]); 549 | 550 | $editUsers = $this->createPermission([ 551 | 'name' => 'users.edit', 552 | 'label' => 'Edit Users', 553 | ]); 554 | 555 | $admin->grant([$createUsers, $editUsers]); 556 | 557 | $user = $this->createUser([ 558 | 'name' => 'John Doe', 559 | ]); 560 | 561 | $this->assertFalse($user->hasPermission(null)); 562 | $this->assertFalse($user->hasPermissions([])); 563 | $this->assertFalse($user->hasPermissions([null])); 564 | $this->assertFalse($user->hasPermissions(collect())); 565 | 566 | $user->assignRole($admin); 567 | 568 | $this->assertTrue($user->hasPermissions([$createUsers, $editUsers])); 569 | } 570 | 571 | public function test_does_not_have_permission() 572 | { 573 | $admin = $this->createRole([ 574 | 'name' => 'administrator', 575 | 'label' => 'Admin', 576 | ]); 577 | 578 | $createUsers = $this->createPermission([ 579 | 'name' => 'users.create', 580 | 'label' => 'Create Users', 581 | ]); 582 | 583 | $admin->grant($createUsers); 584 | 585 | $user = $this->createUser([ 586 | 'name' => 'John Doe', 587 | ]); 588 | 589 | $this->assertTrue($user->doesNotHavePermission($createUsers)); 590 | $this->assertFalse($user->hasPermission($createUsers)); 591 | } 592 | 593 | public function test_does_not_have_permission_multiple() 594 | { 595 | $admin = $this->createRole([ 596 | 'name' => 'administrator', 597 | 'label' => 'Admin', 598 | ]); 599 | 600 | $createUsers = $this->createPermission([ 601 | 'name' => 'users.create', 602 | 'label' => 'Create Users', 603 | ]); 604 | 605 | $editUsers = $this->createPermission([ 606 | 'name' => 'users.edit', 607 | 'label' => 'Edit Users', 608 | ]); 609 | 610 | $admin->grant($createUsers); 611 | 612 | $user = $this->createUser([ 613 | 'name' => 'John Doe', 614 | ]); 615 | 616 | $user->assignRole($admin); 617 | 618 | $this->assertTrue($user->doesNotHavePermissions([$createUsers, $editUsers])); 619 | } 620 | 621 | public function test_has_any_permissions() 622 | { 623 | $admin = $this->createRole([ 624 | 'name' => 'administrator', 625 | 'label' => 'Admin', 626 | ]); 627 | 628 | $createUsers = $this->createPermission([ 629 | 'name' => 'users.create', 630 | 'label' => 'Create Users', 631 | ]); 632 | 633 | $editUsers = $this->createPermission([ 634 | 'name' => 'users.edit', 635 | 'label' => 'Edit Users', 636 | ]); 637 | 638 | $nonGrantedPermission = $this->createPermission([ 639 | 'name' => 'other', 640 | 'label' => 'Other Permission', 641 | ]); 642 | 643 | $admin->grant([$createUsers, $editUsers]); 644 | 645 | $user = $this->createUser([ 646 | 'name' => 'John Doe', 647 | ]); 648 | 649 | $user->assignRole($admin); 650 | 651 | $this->assertTrue($user->hasAnyPermissions([$editUsers, $nonGrantedPermission])); 652 | $this->assertFalse($user->hasAnyPermissions([$nonGrantedPermission])); 653 | } 654 | 655 | public function test_role_has_permission() 656 | { 657 | $admin = $this->createRole([ 658 | 'name' => 'administrator', 659 | 'label' => 'Admin', 660 | ]); 661 | 662 | $createUsers = $this->createPermission([ 663 | 'name' => 'users.create', 664 | 'label' => 'Create Users', 665 | ]); 666 | 667 | $editUsers = $this->createPermission([ 668 | 'name' => 'users.edit', 669 | 'label' => 'Edit Users', 670 | ]); 671 | 672 | $admin->grant($createUsers); 673 | 674 | $this->assertTrue($admin->hasPermission('users.create')); 675 | $this->assertTrue($admin->hasPermission($createUsers)); 676 | $this->assertFalse($admin->hasPermission('non-existent')); 677 | $this->assertFalse($admin->hasPermission($editUsers)); 678 | } 679 | 680 | public function test_role_has_permissions() 681 | { 682 | $admin = $this->createRole([ 683 | 'name' => 'administrator', 684 | 'label' => 'Admin', 685 | ]); 686 | 687 | $createUsers = $this->createPermission([ 688 | 'name' => 'users.create', 689 | 'label' => 'Create Users', 690 | ]); 691 | 692 | $editUsers = $this->createPermission([ 693 | 'name' => 'users.edit', 694 | 'label' => 'Edit Users', 695 | ]); 696 | 697 | $deleteUsers = $this->createPermission([ 698 | 'name' => 'users.destroy', 699 | 'label' => 'Delete Users', 700 | ]); 701 | 702 | $admin->grant([$createUsers, $editUsers]); 703 | 704 | $this->assertTrue($admin->hasPermissions([$createUsers, $editUsers])); 705 | $this->assertFalse($admin->hasPermissions([$createUsers, $editUsers, $deleteUsers])); 706 | } 707 | 708 | public function test_role_has_any_permissions() 709 | { 710 | $admin = $this->createRole([ 711 | 'name' => 'administrator', 712 | 'label' => 'Admin', 713 | ]); 714 | 715 | $createUsers = $this->createPermission([ 716 | 'name' => 'users.create', 717 | 'label' => 'Create Users', 718 | ]); 719 | 720 | $editUsers = $this->createPermission([ 721 | 'name' => 'users.edit', 722 | 'label' => 'Edit Users', 723 | ]); 724 | 725 | $deleteUsers = $this->createPermission([ 726 | 'name' => 'users.destroy', 727 | 'label' => 'Delete Users', 728 | ]); 729 | 730 | $admin->grant([$createUsers, $editUsers]); 731 | 732 | $this->assertTrue($admin->hasAnyPermissions([$createUsers, $editUsers, 'non-existent'])); 733 | $this->assertFalse($admin->hasAnyPermissions(['non-existent', 'unknown', $deleteUsers])); 734 | } 735 | 736 | public function test_user_has_role() 737 | { 738 | $admin = $this->createRole([ 739 | 'name' => 'administrator', 740 | 'label' => 'Admin', 741 | ]); 742 | 743 | $member = $this->createRole([ 744 | 'name' => 'member', 745 | 'label' => 'Member', 746 | ]); 747 | 748 | $user = $this->createUser([ 749 | 'name' => 'John Doe', 750 | ]); 751 | 752 | $this->assertFalse($user->hasRole(null)); 753 | $this->assertFalse($user->hasRole('')); 754 | 755 | $user->assignRole($admin); 756 | 757 | $this->assertTrue($user->hasRole('administrator')); 758 | $this->assertTrue($user->hasRole($admin)); 759 | $this->assertFalse($user->hasRole('non-existent')); 760 | $this->assertFalse($user->hasRole($member)); 761 | } 762 | 763 | public function test_user_has_roles() 764 | { 765 | $admin = $this->createRole([ 766 | 'name' => 'administrator', 767 | 'label' => 'Admin', 768 | ]); 769 | 770 | $member = $this->createRole([ 771 | 'name' => 'member', 772 | 'label' => 'Member', 773 | ]); 774 | 775 | $guest = $this->createRole([ 776 | 'name' => 'guest', 777 | 'label' => 'Guest', 778 | ]); 779 | 780 | $user = $this->createUser([ 781 | 'name' => 'John Doe', 782 | ]); 783 | 784 | $this->assertFalse($user->hasRoles(null)); 785 | $this->assertFalse($user->hasRoles([])); 786 | $this->assertFalse($user->hasRoles([null])); 787 | $this->assertFalse($user->hasRoles(collect())); 788 | 789 | $user->assignRole($admin); 790 | $user->assignRole($member); 791 | 792 | $this->assertTrue($user->hasRoles(['administrator', 'member'])); 793 | $this->assertTrue($user->hasRoles([$admin, $member])); 794 | $this->assertFalse($user->hasRoles(['non-existent', $admin])); 795 | $this->assertFalse($user->hasRoles([$admin, $member, $guest])); 796 | } 797 | 798 | public function test_user_has_any_roles() 799 | { 800 | $admin = $this->createRole([ 801 | 'name' => 'administrator', 802 | 'label' => 'Admin', 803 | ]); 804 | 805 | $member = $this->createRole([ 806 | 'name' => 'member', 807 | 'label' => 'Member', 808 | ]); 809 | 810 | $guest = $this->createRole([ 811 | 'name' => 'guest', 812 | 'label' => 'Guest', 813 | ]); 814 | 815 | $user = $this->createUser([ 816 | 'name' => 'John Doe', 817 | ]); 818 | 819 | $user->assignRole($admin); 820 | $user->assignRole($member); 821 | 822 | $this->assertTrue($user->hasAnyRoles(['administrator', 'member', 'non-existent'])); 823 | $this->assertTrue($user->hasAnyRoles([$admin, $member, 'non-existent'])); 824 | $this->assertTrue($user->hasAnyRoles([$admin, $member, $guest])); 825 | } 826 | 827 | public function test_user_specific_permissions() 828 | { 829 | $user = $this->createUser([ 830 | 'name' => 'John Doe', 831 | ]); 832 | 833 | $editUser = $this->createPermission([ 834 | 'name' => 'users.edit.1', 835 | 'label' => 'Edit Specific User', 836 | ]); 837 | 838 | $user->permissions()->save($editUser); 839 | 840 | $this->assertTrue($user->hasPermission('users.edit.1')); 841 | $this->assertTrue($user->hasPermission($editUser)); 842 | } 843 | 844 | public function test_granting_same_permissions() 845 | { 846 | $admin = $this->createRole([ 847 | 'name' => 'administrator', 848 | 'label' => 'Admin', 849 | ]); 850 | 851 | $editUser = $this->createPermission([ 852 | 'name' => 'users.edit.1', 853 | 'label' => 'Edit Specific User', 854 | ]); 855 | 856 | $this->assertInstanceOf(Authorization::permissionModel(), $admin->grant($editUser)); 857 | $this->assertInstanceOf(Authorization::permissionModel(), $admin->grant($editUser)); 858 | } 859 | 860 | public function test_revoking_same_permissions() 861 | { 862 | $admin = $this->createRole([ 863 | 'name' => 'administrator', 864 | 'label' => 'Admin', 865 | ]); 866 | 867 | $editUser = $this->createPermission([ 868 | 'name' => 'users.edit.1', 869 | 'label' => 'Edit Specific User', 870 | ]); 871 | 872 | $admin->grant($editUser); 873 | 874 | $this->assertInstanceOf(Authorization::permissionModel(), $admin->revoke($editUser)); 875 | $this->assertInstanceOf(Authorization::permissionModel(), $admin->revoke($editUser)); 876 | } 877 | 878 | public function test_permissions_are_cached_in_registrar() 879 | { 880 | $permissions = collect([ 881 | $this->createPermission([ 882 | 'name' => 'create', 883 | 'label' => 'Create', 884 | ]), 885 | $this->createPermission([ 886 | 'name' => 'edit', 887 | 'label' => 'Edit', 888 | ]), 889 | ]); 890 | 891 | $keys = $permissions->map->id; 892 | 893 | $this->assertEquals($keys, app(PermissionRegistrar::class)->getPermissions()->map->getKey()); 894 | $this->assertEquals($keys, cache(Authorization::cacheKey())->map->getKey()); 895 | } 896 | 897 | public function test_permission_cache_is_flushed_when_permissions_are_created() 898 | { 899 | $p = $this->createPermission([ 900 | 'name' => 'create', 901 | 'label' => 'Create', 902 | ]); 903 | 904 | $this->assertEquals($p->getKey(), app(PermissionRegistrar::class)->getPermissions()->first()->getKey()); 905 | $this->assertEquals($p->getKey(), cache(Authorization::cacheKey())->first()->getKey()); 906 | 907 | $this->createPermission([ 908 | 'name' => 'edit', 909 | 'label' => 'Edit', 910 | ]); 911 | 912 | $this->assertNull(cache(Authorization::cacheKey())); 913 | } 914 | 915 | public function test_permission_cache_is_flushed_when_permissions_are_updated() 916 | { 917 | $p = $this->createPermission([ 918 | 'name' => 'create', 919 | 'label' => 'Create', 920 | ]); 921 | 922 | $this->assertEquals($p->getKey(), app(PermissionRegistrar::class)->getPermissions()->first()->getKey()); 923 | 924 | $p->update(['name' => 'edit', 'label' => 'Edit']); 925 | 926 | $this->assertNull(cache(Authorization::cacheKey())); 927 | } 928 | 929 | public function test_permission_cache_is_flushed_when_permissions_are_deleted() 930 | { 931 | $p = $this->createPermission([ 932 | 'name' => 'create', 933 | 'label' => 'Create', 934 | ]); 935 | 936 | $this->assertEquals($p->getKey(), app(PermissionRegistrar::class)->getPermissions()->first()->getKey()); 937 | 938 | $p->delete(); 939 | 940 | $this->assertNull(cache(Authorization::cacheKey())); 941 | } 942 | 943 | public function test_permission_cache_is_flushed_when_permissions_are_added_to_role() 944 | { 945 | $p = $this->createPermission(['name' => 'create', 'label' => 'Create']); 946 | 947 | $r = $this->createRole(['name' => 'admin', 'label' => 'Administrator']); 948 | 949 | $this->assertEquals($p->getKey(), app(PermissionRegistrar::class)->getPermissions()->first()->getKey()); 950 | $this->assertEquals($p->getKey(), cache(Authorization::cacheKey())->first()->getKey()); 951 | 952 | $r->permissions()->save($p); 953 | 954 | $this->assertNull(cache(Authorization::cacheKey())); 955 | } 956 | 957 | public function test_user_does_not_have_permission_with_cached_permissions() 958 | { 959 | $user = $this->createUser(['name' => 'John Doe']); 960 | 961 | $permission = $this->createPermission(['name' => 'create', 'label' => 'Create']); 962 | 963 | $role = $this->createRole(['name' => 'admin', 'label' => 'Administrator']); 964 | 965 | // Attach the permission to the role. 966 | $role->permissions()->attach($permission); 967 | 968 | // Attach the role to the user. 969 | $user->roles()->attach($role); 970 | 971 | // User has permission. 972 | $this->assertTrue($user->hasPermission($permission)); 973 | $this->assertTrue($user->hasPermission('create')); 974 | 975 | $this->assertEquals($permission->getKey(), app(PermissionRegistrar::class)->getPermissions()->first()->getKey()); 976 | $this->assertEquals($permission->getKey(), cache(Authorization::cacheKey())->first()->getKey()); 977 | 978 | // Detach the permission from the role. 979 | $role->permissions()->detach($permission); 980 | 981 | // User no longer has permission. 982 | $this->assertFalse($user->hasPermission($permission)); 983 | $this->assertFalse($user->hasPermission('create')); 984 | } 985 | 986 | public function test_permissions_are_not_cached_when_disabled() 987 | { 988 | Authorization::disablePermissionCache(); 989 | 990 | $permission = $this->createPermission(['name' => 'create', 'label' => 'Create']); 991 | 992 | $this->assertEquals($permission->getKey(), app(PermissionRegistrar::class)->getPermissions()->first()->getKey()); 993 | $this->assertNull(cache(Authorization::cacheKey())); 994 | } 995 | } 996 | -------------------------------------------------------------------------------- /tests/Stubs/User.php: -------------------------------------------------------------------------------- 1 | increments('id'); 23 | $table->string('name'); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | protected function getPackageProviders($app) 32 | { 33 | return [ 34 | AuthorizationServiceProvider::class, 35 | ]; 36 | } 37 | 38 | /** 39 | * Returns a new stub user instance. 40 | * 41 | * @param array $attributes 42 | * 43 | * @return User 44 | */ 45 | protected function createUser($attributes = []) 46 | { 47 | return User::create($attributes); 48 | } 49 | 50 | /** 51 | * Returns a new role instance. 52 | * 53 | * @param array $attributes 54 | * 55 | * @return \DirectoryTree\Authorization\Role 56 | */ 57 | protected function createRole($attributes = []) 58 | { 59 | $role = Authorization::role(); 60 | 61 | return $role::create($attributes); 62 | } 63 | 64 | /** 65 | * Returns a new permission instance. 66 | * 67 | * @param array $attributes 68 | * 69 | * @return \DirectoryTree\Authorization\Permission 70 | */ 71 | protected function createPermission($attributes = []) 72 | { 73 | $permission = Authorization::permission(); 74 | 75 | return $permission::create($attributes); 76 | } 77 | } 78 | --------------------------------------------------------------------------------