├── .editorconfig ├── .gitignore ├── .php_cs ├── .styleci.yml ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── config └── roles.php ├── migrations ├── 2015_01_15_105324_create_roles_table.php ├── 2015_01_15_114412_create_role_user_table.php ├── 2015_01_26_115212_create_permissions_table.php ├── 2015_01_26_115523_create_permission_role_table.php └── 2015_02_09_132439_create_permission_user_table.php ├── phpunit.xml ├── src ├── Contracts │ ├── HasRoleAndPermission.php │ ├── PermissionHasRelations.php │ └── RoleHasRelations.php ├── Exceptions │ ├── AccessDeniedException.php │ ├── LevelDeniedException.php │ ├── PermissionDeniedException.php │ └── RoleDeniedException.php ├── Middleware │ ├── VerifyLevel.php │ ├── VerifyPermission.php │ └── VerifyRole.php ├── Models │ ├── Permission.php │ └── Role.php ├── RolesServiceProvider.php └── Traits │ ├── HasRoleAndPermission.php │ ├── PermissionHasRelations.php │ ├── RoleHasRelations.php │ └── Slugable.php └── tests ├── TestCase.php ├── User.php ├── database ├── factories │ └── ModelFactory.php └── migrations │ └── 2014_10_12_000000_create_users_table.php └── src ├── Middleware ├── VerifyPermissionTest.php └── VerifyRoleTest.php └── Traits ├── HasRoleAndPermissionTest.php └── PermissionHasRelationsTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.yml] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.lock 3 | .DS_Store 4 | .php_cs.cache 5 | .idea 6 | /log 7 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | finder(DefaultFinder::create()->in(__DIR__)) 73 | ->fixers($fixers) 74 | ->level(FixerInterface::NONE_LEVEL) 75 | ->setUsingCache(true); 76 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: psr2 2 | 3 | enabled: 4 | - short_array_syntax 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | sudo: false 3 | php: 4 | - 7.1 5 | - 7.2 6 | 7 | env: 8 | global: 9 | - setup=basic 10 | - coverage=no 11 | 12 | matrix: 13 | fast_finish: true 14 | include: 15 | - php: 7.1 16 | env: setup=lowest 17 | - php: 7.1 18 | env: setup=stable 19 | - php: 7.1 20 | env: coverage=yes 21 | 22 | cache: 23 | directories: 24 | - $HOME/.composer/cache 25 | 26 | install: 27 | - if [[ $setup = 'basic' ]]; then travis_retry composer install --no-interaction --prefer-dist --no-suggest; fi 28 | - if [[ $setup = 'stable' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-stable --no-suggest; fi 29 | - if [[ $setup = 'lowest' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-lowest --prefer-stable --no-suggest; fi 30 | 31 | script: 32 | - if [[ $coverage = 'yes' ]]; then ./vendor/bin/phpunit -c phpunit.xml --coverage-clover build/logs/clover.xml; fi 33 | - if [[ $coverage = 'no' ]]; then ./vendor/bin/phpunit -c phpunit.xml; fi 34 | 35 | after_script: 36 | - if [[ $coverage = 'yes' ]]; then php vendor/bin/coveralls -v; fi 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ultraware 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/ultraware/roles.svg?branch=master)](https://travis-ci.org/ultraware/roles) 2 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/ultraware/roles/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/ultraware/roles/?branch=5.1) 3 | [![StyleCI](https://styleci.io/repos/74971525/shield?branch=master)](https://styleci.io/repos/74971525) 4 | [![Coverage Status](https://coveralls.io/repos/github/ultraware/roles/badge.svg?branch=master)](https://coveralls.io/github/ultraware/roles?branch=5.1) 5 | 6 | #Abandoned 7 | The Roles and permissions for laraval 5 package is abandoned and will no longer be maintained by Ultraware. 8 | Feel free to fork the project and submit it to packagist. We can share the url to your package on this page if you send us a message. 9 | 10 | # Roles And Permissions For Laravel 5 11 | 12 | Powerful package for handling roles and permissions in Laravel 5. 13 | 14 | - [Installation](#installation) 15 | - [Composer](#composer) 16 | - [Service Provider](#service-provider) 17 | - [Config File And Migrations](#config-file-and-migrations) 18 | - [HasRoleAndPermission Trait And Contract](#hasroleandpermission-trait-and-contract) 19 | - [Migrate from Bican roles](#Migrate-from-bican-roles) 20 | - [Usage](#usage) 21 | - [Creating Roles](#creating-roles) 22 | - [Attaching, Detaching and Syncing Roles](#attaching-detaching-and-syncing-roles) 23 | - [Checking For Roles](#checking-for-roles) 24 | - [Levels](#levels) 25 | - [Creating Permissions](#creating-permissions) 26 | - [Attaching, Detaching and Syncing Permissions](#attaching-detaching-and-syncing-permissions) 27 | - [Checking For Permissions](#checking-for-permissions) 28 | - [Permissions Inheriting](#permissions-inheriting) 29 | - [Entity Check](#entity-check) 30 | - [Blade Extensions](#blade-extensions) 31 | - [Middleware](#middleware) 32 | - [Config File](#config-file) 33 | - [More Information](#more-information) 34 | - [License](#license) 35 | 36 | ## Installation 37 | 38 | This package is very easy to set up. There are only couple of steps. 39 | 40 | ### Composer 41 | 42 | Pull this package in through Composer 43 | ``` 44 | composer require ultraware/roles 45 | ``` 46 | 47 | > If you are still using Laravel 5.0, you must pull in version `1.7.*`. 48 | 49 | 50 | ### Service Provider 51 | 52 | Add the package to your application service providers in `config/app.php` file. 53 | 54 | ```php 55 | 'providers' => [ 56 | 57 | ... 58 | 59 | /** 60 | * Third Party Service Providers... 61 | */ 62 | Ultraware\Roles\RolesServiceProvider::class, 63 | 64 | ], 65 | ``` 66 | 67 | ### Config File And Migrations 68 | 69 | Publish the package config file and migrations to your application. Run these commands inside your terminal. 70 | 71 | php artisan vendor:publish --provider="Ultraware\Roles\RolesServiceProvider" --tag=config 72 | php artisan vendor:publish --provider="Ultraware\Roles\RolesServiceProvider" --tag=migrations 73 | 74 | And also run migrations. 75 | 76 | php artisan migrate 77 | 78 | > This uses the default users table which is in Laravel. You should already have the migration file for the users table available and migrated. 79 | 80 | ### HasRoleAndPermission Trait And Contract 81 | 82 | Include `HasRoleAndPermission` trait and also implement `HasRoleAndPermission` contract inside your `User` model. 83 | 84 | ## Migrate from bican roles 85 | If you migrate from bican/roles to ultraware/roles yoe need to update a few things. 86 | - Change all calls to `can`, `canOne` and `canAll` to `hasPermission`, `hasOnePermission`, `hasAllPermissions`. 87 | - Change all calls to `is`, `isOne` and `isAll` to `hasRole`, `hasOneRole`, `hasAllRoles`. 88 | 89 | ## Usage 90 | 91 | ### Creating Roles 92 | 93 | ```php 94 | use Ultraware\Roles\Models\Role; 95 | 96 | $adminRole = Role::create([ 97 | 'name' => 'Admin', 98 | 'slug' => 'admin', 99 | 'description' => '', // optional 100 | 'level' => 1, // optional, set to 1 by default 101 | ]); 102 | 103 | $moderatorRole = Role::create([ 104 | 'name' => 'Forum Moderator', 105 | 'slug' => 'forum.moderator', 106 | ]); 107 | ``` 108 | 109 | > Because of `Slugable` trait, if you make a mistake and for example leave a space in slug parameter, it'll be replaced with a dot automatically, because of `str_slug` function. 110 | 111 | ### Attaching, Detaching and Syncing Roles 112 | 113 | It's really simple. You fetch a user from database and call `attachRole` method. There is `BelongsToMany` relationship between `User` and `Role` model. 114 | 115 | ```php 116 | use App\User; 117 | 118 | $user = User::find($id); 119 | 120 | $user->attachRole($adminRole); // you can pass whole object, or just an id 121 | $user->detachRole($adminRole); // in case you want to detach role 122 | $user->detachAllRoles(); // in case you want to detach all roles 123 | $user->syncRoles($roles); // you can pass Eloquent collection, or just an array of ids 124 | ``` 125 | 126 | ### Checking For Roles 127 | 128 | You can now check if the user has required role. 129 | 130 | ```php 131 | if ($user->hasRole('admin')) { // you can pass an id or slug 132 | // 133 | } 134 | ``` 135 | 136 | You can also do this: 137 | 138 | ```php 139 | if ($user->isAdmin()) { 140 | // 141 | } 142 | ``` 143 | 144 | And of course, there is a way to check for multiple roles: 145 | 146 | ```php 147 | if ($user->hasRole(['admin', 'moderator'])) { 148 | /* 149 | | Or alternatively: 150 | | $user->hasRole('admin, moderator'), $user->hasRole('admin|moderator'), 151 | | $user->hasOneRole('admin, moderator'), $user->hasOneRole(['admin', 'moderator']), $user->hasOneRole('admin|moderator') 152 | */ 153 | 154 | // The user has at least one of the roles 155 | } 156 | 157 | if ($user->hasRole(['admin', 'moderator'], true)) { 158 | /* 159 | | Or alternatively: 160 | | $user->hasRole('admin, moderator', true), $user->hasRole('admin|moderator', true), 161 | | $user->hasAllRoles('admin, moderator'), $user->hasAllRoles(['admin', 'moderator']), $user->hasAllRoles('admin|moderator') 162 | */ 163 | 164 | // The user has all roles 165 | } 166 | ``` 167 | 168 | ### Levels 169 | 170 | When you are creating roles, there is optional parameter `level`. It is set to `1` by default, but you can overwrite it and then you can do something like this: 171 | 172 | ```php 173 | if ($user->level() > 4) { 174 | // 175 | } 176 | ``` 177 | 178 | > If user has multiple roles, method `level` returns the highest one. 179 | 180 | `Level` has also big effect on inheriting permissions. About it later. 181 | 182 | ### Creating Permissions 183 | 184 | It's very simple thanks to `Permission` model. 185 | 186 | ```php 187 | use Ultraware\Roles\Models\Permission; 188 | 189 | $createUsersPermission = Permission::create([ 190 | 'name' => 'Create users', 191 | 'slug' => 'create.users', 192 | 'description' => '', // optional 193 | ]); 194 | 195 | $deleteUsersPermission = Permission::create([ 196 | 'name' => 'Delete users', 197 | 'slug' => 'delete.users', 198 | ]); 199 | ``` 200 | 201 | ### Attaching, Detaching and Syncing Permissions 202 | 203 | You can attach permissions to a role or directly to a specific user (and of course detach them as well). 204 | 205 | ```php 206 | use App\User; 207 | use Ultraware\Roles\Models\Role; 208 | 209 | $role = Role::find($roleId); 210 | $role->attachPermission($createUsersPermission); // permission attached to a role 211 | 212 | $user = User::find($userId); 213 | $user->attachPermission($deleteUsersPermission); // permission attached to a user 214 | ``` 215 | 216 | ```php 217 | $role->detachPermission($createUsersPermission); // in case you want to detach permission 218 | $role->detachAllPermissions(); // in case you want to detach all permissions 219 | $role->syncPermissions($permissions); // you can pass Eloquent collection, or just an array of ids 220 | 221 | $user->detachPermission($deleteUsersPermission); 222 | $user->detachAllPermissions(); 223 | $user->syncPermissions($permissions); // you can pass Eloquent collection, or just an array of ids 224 | ``` 225 | 226 | ### Checking For Permissions 227 | 228 | ```php 229 | if ($user->hasPermission('create.users')) { // you can pass an id or slug 230 | // 231 | } 232 | 233 | if ($user->canDeleteUsers()) { 234 | // 235 | } 236 | ``` 237 | 238 | You can check for multiple permissions the same way as roles. You can make use of additional methods like `hasOnePermission` or `hasAllPermissions`. 239 | 240 | ### Permissions Inheriting 241 | 242 | Role with higher level is inheriting permission from roles with lower level. 243 | 244 | There is an example of this `magic`: 245 | 246 | You have three roles: `user`, `moderator` and `admin`. User has a permission to read articles, moderator can manage comments and admin can create articles. User has a level 1, moderator level 2 and admin level 3. It means, moderator and administrator has also permission to read articles, but administrator can manage comments as well. 247 | 248 | > If you don't want permissions inheriting feature in you application, simply ignore `level` parameter when you're creating roles. 249 | 250 | ### Entity Check 251 | 252 | Let's say you have an article and you want to edit it. This article belongs to a user (there is a column `user_id` in articles table). 253 | 254 | ```php 255 | use App\Article; 256 | use Ultraware\Roles\Models\Permission; 257 | 258 | $editArticlesPermission = Permission::create([ 259 | 'name' => 'Edit articles', 260 | 'slug' => 'edit.articles', 261 | 'model' => 'App\Article', 262 | ]); 263 | 264 | $user->attachPermission($editArticlesPermission); 265 | 266 | $article = Article::find(1); 267 | 268 | if ($user->allowed('edit.articles', $article)) { // $user->allowedEditArticles($article) 269 | // 270 | } 271 | ``` 272 | 273 | This condition checks if the current user is the owner of article. If not, it will be looking inside user permissions for a row we created before. 274 | 275 | ```php 276 | if ($user->allowed('edit.articles', $article, false)) { // now owner check is disabled 277 | // 278 | } 279 | ``` 280 | 281 | ### Blade Extensions 282 | 283 | There are four Blade extensions. Basically, it is replacement for classic if statements. 284 | 285 | ```php 286 | @role('admin') // @if(Auth::check() && Auth::user()->hasRole('admin')) 287 | // user has admin role 288 | @endrole 289 | 290 | @permission('edit.articles') // @if(Auth::check() && Auth::user()->hasPermission('edit.articles')) 291 | // user has edit articles permissison 292 | @endpermission 293 | 294 | @level(2) // @if(Auth::check() && Auth::user()->level() >= 2) 295 | // user has level 2 or higher 296 | @endlevel 297 | 298 | @allowed('edit', $article) // @if(Auth::check() && Auth::user()->allowed('edit', $article)) 299 | // show edit button 300 | @endallowed 301 | 302 | @role('admin|moderator', true) // @if(Auth::check() && Auth::user()->hasRole('admin|moderator', true)) 303 | // user has admin and moderator role 304 | @else 305 | // something else 306 | @endrole 307 | ``` 308 | 309 | ### Middleware 310 | 311 | This package comes with `VerifyRole`, `VerifyPermission` and `VerifyLevel` middleware. You must add them inside your `app/Http/Kernel.php` file. 312 | 313 | ```php 314 | /** 315 | * The application's route middleware. 316 | * 317 | * @var array 318 | */ 319 | protected $routeMiddleware = [ 320 | 'auth' => \App\Http\Middleware\Authenticate::class, 321 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 322 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 323 | 'role' => \Ultraware\Roles\Middleware\VerifyRole::class, 324 | 'permission' => \Ultraware\Roles\Middleware\VerifyPermission::class, 325 | 'level' => \Ultraware\Roles\Middleware\VerifyLevel::class, 326 | ]; 327 | ``` 328 | 329 | Now you can easily protect your routes. 330 | 331 | ```php 332 | $router->get('/example', [ 333 | 'as' => 'example', 334 | 'middleware' => 'role:admin', 335 | 'uses' => 'ExampleController@index', 336 | ]); 337 | 338 | $router->post('/example', [ 339 | 'as' => 'example', 340 | 'middleware' => 'permission:edit.articles', 341 | 'uses' => 'ExampleController@index', 342 | ]); 343 | 344 | $router->get('/example', [ 345 | 'as' => 'example', 346 | 'middleware' => 'level:2', // level >= 2 347 | 'uses' => 'ExampleController@index', 348 | ]); 349 | ``` 350 | 351 | It throws `\Ultraware\Roles\Exceptions\RoleDeniedException`, `\Ultraware\Roles\Exceptions\PermissionDeniedException` or `\Ultraware\Roles\Exceptions\LevelDeniedException` exceptions if it goes wrong. 352 | 353 | You can catch these exceptions inside `app/Exceptions/Handler.php` file and do whatever you want. 354 | 355 | ```php 356 | /** 357 | * Render an exception into an HTTP response. 358 | * 359 | * @param \Illuminate\Http\Request $request 360 | * @param \Exception $e 361 | * @return \Illuminate\Http\Response 362 | */ 363 | public function render($request, Exception $e) 364 | { 365 | if ($e instanceof \Ultraware\Roles\Exceptions\RoleDeniedException) { 366 | // you can for example flash message, redirect... 367 | return redirect()->back(); 368 | } 369 | 370 | return parent::render($request, $e); 371 | } 372 | ``` 373 | 374 | ## Config File 375 | 376 | You can change connection for models, slug separator, models path and there is also a handy pretend feature. Have a look at config file for more information. 377 | 378 | ## More Information 379 | 380 | For more information, please have a look at [HasRoleAndPermission](https://github.com/ultraware/roles/blob/master/src/Contracts/HasRoleAndPermission.php) contract. 381 | 382 | ## License 383 | 384 | This package is free software distributed under the terms of the MIT license. 385 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ultraware/roles", 3 | "description": "Powerful package for handling roles and permissions in Laravel 5.", 4 | "abandoned": true, 5 | "keywords": [ 6 | "roles", 7 | "permissions", 8 | "acl", 9 | "auth", 10 | "laravel", 11 | "illuminate" 12 | ], 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "Ultraware" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=7.1.3", 21 | "illuminate/support": "5.7.*", 22 | "illuminate/database": "5.7.*" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Ultraware\\Roles\\": "src/" 27 | } 28 | }, 29 | "require-dev": { 30 | "orchestra/testbench": "^3.6", 31 | "orchestra/database": "^3.6", 32 | "mockery/mockery": "^1.0", 33 | "phpunit/phpunit": "~7.0", 34 | "satooshi/php-coveralls": "^2.0", 35 | "sebastian/environment": "^3.1" 36 | }, 37 | "autoload-dev": { 38 | "classmap": [ 39 | "tests/TestCase.php", 40 | "tests/User.php" 41 | ] 42 | }, 43 | "minimum-stability": "dev", 44 | "extra": { 45 | "laravel": { 46 | "providers": [ 47 | "Ultraware\\Roles\\RolesServiceProvider" 48 | ] 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /config/roles.php: -------------------------------------------------------------------------------- 1 | null, 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Slug Separator 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you can change the slug separator. This is very important in matter 24 | | of magic method __call() and also a `Slugable` trait. The default value 25 | | is a dot. 26 | | 27 | */ 28 | 29 | 'separator' => '.', 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Models 34 | |-------------------------------------------------------------------------- 35 | | 36 | | If you want, you can replace default models from this package by models 37 | | you created. Have a look at `Ultraware\Roles\Models\Role` model and 38 | | `Ultraware\Roles\Models\Permission` model. 39 | | 40 | */ 41 | 42 | 'models' => [ 43 | 'role' => Ultraware\Roles\Models\Role::class, 44 | 'permission' => Ultraware\Roles\Models\Permission::class, 45 | ], 46 | 47 | /* 48 | |-------------------------------------------------------------------------- 49 | | Roles, Permissions and Allowed "Pretend" 50 | |-------------------------------------------------------------------------- 51 | | 52 | | You can pretend or simulate package behavior no matter what is in your 53 | | database. It is really useful when you are testing you application. 54 | | Set up what will methods hasRole(), hasPermission() and allowed() return. 55 | | 56 | */ 57 | 58 | 'pretend' => [ 59 | 60 | 'enabled' => false, 61 | 62 | 'options' => [ 63 | 'hasRole' => true, 64 | 'hasPermission' => true, 65 | 'allowed' => true, 66 | ], 67 | 68 | ], 69 | 70 | ]; 71 | -------------------------------------------------------------------------------- /migrations/2015_01_15_105324_create_roles_table.php: -------------------------------------------------------------------------------- 1 | increments('id')->unsigned(); 17 | $table->string('name'); 18 | $table->string('slug')->unique(); 19 | $table->string('description')->nullable(); 20 | $table->integer('level')->default(1); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::drop('roles'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /migrations/2015_01_15_114412_create_role_user_table.php: -------------------------------------------------------------------------------- 1 | increments('id')->unsigned(); 17 | $table->integer('role_id')->unsigned()->index(); 18 | $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade'); 19 | $table->integer('user_id')->unsigned()->index(); 20 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::drop('role_user'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /migrations/2015_01_26_115212_create_permissions_table.php: -------------------------------------------------------------------------------- 1 | increments('id')->unsigned(); 17 | $table->string('name'); 18 | $table->string('slug')->unique(); 19 | $table->string('description')->nullable(); 20 | $table->string('model')->nullable(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::drop('permissions'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /migrations/2015_01_26_115523_create_permission_role_table.php: -------------------------------------------------------------------------------- 1 | increments('id')->unsigned(); 17 | $table->integer('permission_id')->unsigned()->index(); 18 | $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade'); 19 | $table->integer('role_id')->unsigned()->index(); 20 | $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::drop('permission_role'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /migrations/2015_02_09_132439_create_permission_user_table.php: -------------------------------------------------------------------------------- 1 | increments('id')->unsigned(); 17 | $table->integer('permission_id')->unsigned()->index(); 18 | $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade'); 19 | $table->integer('user_id')->unsigned()->index(); 20 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::drop('permission_user'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | 20 | ./src/ 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Contracts/HasRoleAndPermission.php: -------------------------------------------------------------------------------- 1 | message = sprintf("You don't have a required [%s] level.", $level); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Exceptions/PermissionDeniedException.php: -------------------------------------------------------------------------------- 1 | message = sprintf("You don't have a required ['%s'] permission.", $permission); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Exceptions/RoleDeniedException.php: -------------------------------------------------------------------------------- 1 | message = sprintf("You don't have a required ['%s'] role.", $role); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Middleware/VerifyLevel.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 25 | } 26 | 27 | /** 28 | * Handle an incoming request. 29 | * 30 | * @param Request $request 31 | * @param \Closure $next 32 | * @param int $level 33 | * @return mixed 34 | * @throws \Ultraware\Roles\Exceptions\LevelDeniedException 35 | */ 36 | public function handle($request, Closure $next, $level) 37 | { 38 | if ($this->auth->check() && $this->auth->user()->level() >= $level) { 39 | return $next($request); 40 | } 41 | 42 | throw new LevelDeniedException($level); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Middleware/VerifyPermission.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 25 | } 26 | 27 | /** 28 | * Handle an incoming request. 29 | * 30 | * @param Request $request 31 | * @param \Closure $next 32 | * @param int|string $permission 33 | * @return mixed 34 | * @throws \Ultraware\Roles\Exceptions\PermissionDeniedException 35 | */ 36 | public function handle($request, Closure $next, $permission) 37 | { 38 | if ($this->auth->check() && $this->auth->user()->hasPermission($permission)) { 39 | return $next($request); 40 | } 41 | 42 | throw new PermissionDeniedException($permission); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Middleware/VerifyRole.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 25 | } 26 | 27 | /** 28 | * Handle an incoming request. 29 | * 30 | * @param Request $request 31 | * @param \Closure $next 32 | * @param int|string $role 33 | * @return mixed 34 | * @throws RoleDeniedException 35 | */ 36 | public function handle($request, Closure $next, $role) 37 | { 38 | if ($this->auth->check() && $this->auth->user()->hasRole($role)) { 39 | return $next($request); 40 | } 41 | 42 | throw new RoleDeniedException($role); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Models/Permission.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Models/Role.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/RolesServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 17 | __DIR__ . '/../config/roles.php' => config_path('roles.php'), 18 | ], 'config'); 19 | 20 | $this->publishes([ 21 | __DIR__ . '/../migrations/' => base_path('/database/migrations'), 22 | ], 'migrations'); 23 | 24 | $this->registerBladeExtensions(); 25 | } 26 | 27 | /** 28 | * Register any application services. 29 | * 30 | * @return void 31 | */ 32 | public function register() 33 | { 34 | $this->mergeConfigFrom(__DIR__ . '/../config/roles.php', 'roles'); 35 | } 36 | 37 | /** 38 | * Register Blade extensions. 39 | * 40 | * @return void 41 | */ 42 | protected function registerBladeExtensions() 43 | { 44 | $blade = $this->app['view']->getEngineResolver()->resolve('blade')->getCompiler(); 45 | 46 | $blade->directive('role', function ($expression) { 47 | return "hasRole({$expression})): ?>"; 48 | }); 49 | 50 | $blade->directive('endrole', function () { 51 | return ''; 52 | }); 53 | 54 | $blade->directive('permission', function ($expression) { 55 | return "hasPermission({$expression})): ?>"; 56 | }); 57 | 58 | $blade->directive('endpermission', function () { 59 | return ''; 60 | }); 61 | 62 | $blade->directive('level', function ($expression) { 63 | $level = trim($expression, '()'); 64 | 65 | return "level() >= {$level}): ?>"; 66 | }); 67 | 68 | $blade->directive('endlevel', function () { 69 | return ''; 70 | }); 71 | 72 | $blade->directive('allowed', function ($expression) { 73 | return "allowed({$expression})): ?>"; 74 | }); 75 | 76 | $blade->directive('endallowed', function () { 77 | return ''; 78 | }); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Traits/HasRoleAndPermission.php: -------------------------------------------------------------------------------- 1 | belongsToMany(config('roles.models.role'))->withTimestamps(); 38 | } 39 | 40 | /** 41 | * Get all roles as collection. 42 | * 43 | * @return Collection 44 | */ 45 | public function getRoles() 46 | { 47 | return (!$this->roles) ? $this->roles = $this->roles()->get() : $this->roles; 48 | } 49 | 50 | /** 51 | * Check if the user has a role or roles. 52 | * 53 | * @param int|string|array $role 54 | * @param bool $all 55 | * @return bool 56 | */ 57 | public function hasRole($role, $all = false) 58 | { 59 | if ($this->isPretendEnabled()) { 60 | return $this->pretend('hasRole'); 61 | } 62 | 63 | if (!$all) { 64 | return $this->hasOneRole($role); 65 | } 66 | 67 | return $this->hasAllRoles($role); 68 | } 69 | 70 | /** 71 | * Check if the user has at least one of the given roles. 72 | * 73 | * @param int|string|array $role 74 | * @return bool 75 | */ 76 | public function hasOneRole($role) 77 | { 78 | foreach ($this->getArrayFrom($role) as $role) { 79 | if ($this->checkRole($role)) { 80 | return true; 81 | } 82 | } 83 | 84 | return false; 85 | } 86 | 87 | /** 88 | * Check if the user has all roles. 89 | * 90 | * @param int|string|array $role 91 | * @return bool 92 | */ 93 | public function hasAllRoles($role) 94 | { 95 | foreach ($this->getArrayFrom($role) as $role) { 96 | if (!$this->checkRole($role)) { 97 | return false; 98 | } 99 | } 100 | 101 | return true; 102 | } 103 | 104 | /** 105 | * Check if the user has role. 106 | * 107 | * @param int|string $role 108 | * @return bool 109 | */ 110 | public function checkRole($role) 111 | { 112 | return $this->getRoles()->contains(function ($value) use ($role) { 113 | return $role == $value->id || Str::is($role, $value->slug); 114 | }); 115 | } 116 | 117 | /** 118 | * Attach role to a user. 119 | * 120 | * @param int|Role $role 121 | * @return null|bool 122 | */ 123 | public function attachRole($role) 124 | { 125 | if ($this->getRoles()->contains($role)) { 126 | return true; 127 | } 128 | $this->roles = null; 129 | return $this->roles()->attach($role); 130 | } 131 | 132 | /** 133 | * Detach role from a user. 134 | * 135 | * @param int|Role $role 136 | * @return int 137 | */ 138 | public function detachRole($role) 139 | { 140 | $this->roles = null; 141 | 142 | return $this->roles()->detach($role); 143 | } 144 | 145 | /** 146 | * Detach all roles from a user. 147 | * 148 | * @return int 149 | */ 150 | public function detachAllRoles() 151 | { 152 | $this->roles = null; 153 | 154 | return $this->roles()->detach(); 155 | } 156 | 157 | /** 158 | * Sync roles for a user. 159 | * 160 | * @param array|\Ultraware\Roles\Models\Role[]|\Illuminate\Database\Eloquent\Collection $roles 161 | * @return array 162 | */ 163 | public function syncRoles($roles) 164 | { 165 | $this->roles = null; 166 | 167 | return $this->roles()->sync($roles); 168 | } 169 | 170 | /** 171 | * Get role level of a user. 172 | * 173 | * @return int 174 | */ 175 | public function level() 176 | { 177 | return ($role = $this->getRoles()->sortByDesc('level')->first()) ? $role->level : 0; 178 | } 179 | 180 | /** 181 | * Get all permissions from roles. 182 | * 183 | * @return Builder 184 | */ 185 | public function rolePermissions() 186 | { 187 | $permissionModel = app(config('roles.models.permission')); 188 | 189 | if (!$permissionModel instanceof Model) { 190 | throw new InvalidArgumentException('[roles.models.permission] must be an instance of \Illuminate\Database\Eloquent\Model'); 191 | } 192 | 193 | return $permissionModel 194 | ::select(['permissions.*', 'permission_role.created_at as pivot_created_at', 'permission_role.updated_at as pivot_updated_at']) 195 | ->join('permission_role', 'permission_role.permission_id', '=', 'permissions.id') 196 | ->join('roles', 'roles.id', '=', 'permission_role.role_id') 197 | ->whereIn('roles.id', $this->getRoles()->pluck('id')->toArray()) 198 | ->orWhere('roles.level', '<', $this->level()) 199 | ->groupBy(['permissions.id', 'permissions.name', 'permissions.slug', 'permissions.description', 'permissions.model', 'permissions.created_at', 'permissions.updated_at', 'permission_role.created_at', 'permission_role.updated_at']); 200 | } 201 | 202 | /** 203 | * User belongs to many permissions. 204 | * 205 | * @return BelongsToMany 206 | */ 207 | public function userPermissions() 208 | { 209 | return $this->belongsToMany(config('roles.models.permission'))->withTimestamps(); 210 | } 211 | 212 | /** 213 | * Get all permissions as collection. 214 | * 215 | * @return Collection 216 | */ 217 | public function getPermissions() 218 | { 219 | return (!$this->permissions) ? $this->permissions = $this->rolePermissions()->get()->merge($this->userPermissions()->get()) : $this->permissions; 220 | } 221 | 222 | /** 223 | * Check if the user has a permission or permissions. 224 | * 225 | * @param int|string|array $permission 226 | * @param bool $all 227 | * @return bool 228 | */ 229 | public function hasPermission($permission, $all = false) 230 | { 231 | if ($this->isPretendEnabled()) { 232 | return $this->pretend('hasPermission'); 233 | } 234 | 235 | if (!$all) { 236 | return $this->hasOnePermission($permission); 237 | } 238 | 239 | return $this->hasAllPermissions($permission); 240 | } 241 | 242 | /** 243 | * Check if the user has at least one of the given permissions. 244 | * 245 | * @param int|string|array $permission 246 | * @return bool 247 | */ 248 | public function hasOnePermission($permission) 249 | { 250 | foreach ($this->getArrayFrom($permission) as $permission) { 251 | if ($this->checkPermission($permission)) { 252 | return true; 253 | } 254 | } 255 | 256 | return false; 257 | } 258 | 259 | /** 260 | * Check if the user has all permissions. 261 | * 262 | * @param int|string|array $permission 263 | * @return bool 264 | */ 265 | public function hasAllPermissions($permission) 266 | { 267 | foreach ($this->getArrayFrom($permission) as $permission) { 268 | if (!$this->checkPermission($permission)) { 269 | return false; 270 | } 271 | } 272 | 273 | return true; 274 | } 275 | 276 | /** 277 | * Check if the user has a permission. 278 | * 279 | * @param int|string $permission 280 | * @return bool 281 | */ 282 | public function checkPermission($permission) 283 | { 284 | return $this->getPermissions()->contains(function ($value) use ($permission) { 285 | return $permission == $value->id || Str::is($permission, $value->slug); 286 | }); 287 | } 288 | 289 | /** 290 | * Check if the user is allowed to manipulate with entity. 291 | * 292 | * @param string $providedPermission 293 | * @param Model $entity 294 | * @param bool $owner 295 | * @param string $ownerColumn 296 | * @return bool 297 | */ 298 | public function allowed($providedPermission, Model $entity, $owner = true, $ownerColumn = 'user_id') 299 | { 300 | if ($this->isPretendEnabled()) { 301 | return $this->pretend('allowed'); 302 | } 303 | 304 | if ($owner === true && $entity->{$ownerColumn} == $this->id) { 305 | return true; 306 | } 307 | 308 | return $this->isAllowed($providedPermission, $entity); 309 | } 310 | 311 | /** 312 | * Check if the user is allowed to manipulate with provided entity. 313 | * 314 | * @param string $providedPermission 315 | * @param Model $entity 316 | * @return bool 317 | */ 318 | protected function isAllowed($providedPermission, Model $entity) 319 | { 320 | foreach ($this->getPermissions() as $permission) { 321 | if ($permission->model != '' && get_class($entity) == $permission->model 322 | && ($permission->id == $providedPermission || $permission->slug === $providedPermission) 323 | ) { 324 | return true; 325 | } 326 | } 327 | 328 | return false; 329 | } 330 | 331 | /** 332 | * Attach permission to a user. 333 | * 334 | * @param int|Permission $permission 335 | * @return null|bool 336 | */ 337 | public function attachPermission($permission) 338 | { 339 | if ($this->getPermissions()->contains($permission)) { 340 | return true; 341 | } 342 | $this->permissions = null; 343 | return $this->userPermissions()->attach($permission); 344 | } 345 | 346 | /** 347 | * Detach permission from a user. 348 | * 349 | * @param int|Permission $permission 350 | * @return int 351 | */ 352 | public function detachPermission($permission) 353 | { 354 | $this->permissions = null; 355 | 356 | return $this->userPermissions()->detach($permission); 357 | } 358 | 359 | /** 360 | * Detach all permissions from a user. 361 | * 362 | * @return int 363 | */ 364 | public function detachAllPermissions() 365 | { 366 | $this->permissions = null; 367 | 368 | return $this->userPermissions()->detach(); 369 | } 370 | 371 | /** 372 | * Sync permissions for a user. 373 | * 374 | * @param array|\Ultraware\Roles\Models\Permission[]|\Illuminate\Database\Eloquent\Collection $permissions 375 | * @return array 376 | */ 377 | public function syncPermissions($permissions) 378 | { 379 | $this->permissions = null; 380 | 381 | return $this->userPermissions()->sync($permissions); 382 | } 383 | 384 | /** 385 | * Check if pretend option is enabled. 386 | * 387 | * @return bool 388 | */ 389 | private function isPretendEnabled() 390 | { 391 | return (bool) config('roles.pretend.enabled'); 392 | } 393 | 394 | /** 395 | * Allows to pretend or simulate package behavior. 396 | * 397 | * @param string $option 398 | * @return bool 399 | */ 400 | private function pretend($option) 401 | { 402 | return (bool) config('roles.pretend.options.' . $option); 403 | } 404 | 405 | /** 406 | * Get an array from argument. 407 | * 408 | * @param int|string|array $argument 409 | * @return array 410 | */ 411 | private function getArrayFrom($argument) 412 | { 413 | return (!is_array($argument)) ? preg_split('/ ?[,|] ?/', $argument) : $argument; 414 | } 415 | 416 | public function callMagic($method, $parameters) 417 | { 418 | if (starts_with($method, 'is')) { 419 | return $this->hasRole(snake_case(substr($method, 2), config('roles.separator'))); 420 | } elseif (starts_with($method, 'can')) { 421 | return $this->hasPermission(snake_case(substr($method, 3), config('roles.separator'))); 422 | } elseif (starts_with($method, 'allowed')) { 423 | return $this->allowed(snake_case(substr($method, 7), config('roles.separator')), $parameters[0], (isset($parameters[1])) ? $parameters[1] : true, (isset($parameters[2])) ? $parameters[2] : 'user_id'); 424 | } 425 | 426 | return parent::__call($method, $parameters); 427 | } 428 | 429 | public function __call($method, $parameters) 430 | { 431 | return $this->callMagic($method, $parameters); 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /src/Traits/PermissionHasRelations.php: -------------------------------------------------------------------------------- 1 | belongsToMany(config('roles.models.role'))->withTimestamps(); 17 | } 18 | 19 | /** 20 | * Permission belongs to many users. 21 | * 22 | * @return BelongsToMany 23 | */ 24 | public function users() 25 | { 26 | return $this->belongsToMany(config('auth.providers.users.model'))->withTimestamps(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Traits/RoleHasRelations.php: -------------------------------------------------------------------------------- 1 | belongsToMany(config('roles.models.permission'))->withTimestamps(); 19 | } 20 | 21 | /** 22 | * Role belongs to many users. 23 | * 24 | * @return BelongsToMany 25 | */ 26 | public function users() 27 | { 28 | return $this->belongsToMany(config('auth.providers.users.model'))->withTimestamps(); 29 | } 30 | 31 | /** 32 | * Attach permission to a role. 33 | * 34 | * @param int|Permission $permission 35 | * @return int|bool 36 | */ 37 | public function attachPermission($permission) 38 | { 39 | return (!$this->permissions()->get()->contains($permission)) ? $this->permissions()->attach($permission) : true; 40 | } 41 | 42 | /** 43 | * Detach permission from a role. 44 | * 45 | * @param int|Permission $permission 46 | * @return int 47 | */ 48 | public function detachPermission($permission) 49 | { 50 | return $this->permissions()->detach($permission); 51 | } 52 | 53 | /** 54 | * Detach all permissions. 55 | * 56 | * @return int 57 | */ 58 | public function detachAllPermissions() 59 | { 60 | return $this->permissions()->detach(); 61 | } 62 | 63 | /** 64 | * Sync permissions for a role. 65 | * 66 | * @param array|Permission[]|Collection $permissions 67 | * @return array 68 | */ 69 | public function syncPermissions($permissions) 70 | { 71 | return $this->permissions()->sync($permissions); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Traits/Slugable.php: -------------------------------------------------------------------------------- 1 | attributes['slug'] = Str::slug($value, config('roles.separator')); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | singleton(Illuminate\Contracts\Console\Kernel::class, Orchestra\Testbench\Console\Kernel::class); 27 | } 28 | 29 | protected function setupDbConfig($app) 30 | { 31 | // Setup default database to use sqlite :memory: 32 | $app['config']->set('database.default', 'testbench'); 33 | $app['config']->set('database.connections.testbench', [ 34 | 'driver' => 'sqlite', 35 | 'database' => ':memory:', 36 | 'prefix' => '', 37 | ]); 38 | } 39 | 40 | protected function runMigrations() 41 | { 42 | $this->loadMigrationsFrom([ 43 | '--database' => 'testbench', 44 | ]); 45 | } 46 | } 47 | 48 | class TestMigrationsServiceProvider extends ServiceProvider 49 | { 50 | public function boot() 51 | { 52 | $this->loadMigrationsFrom([ 53 | realpath(__DIR__ . '/../migrations'), 54 | realpath(__DIR__ . '/database/migrations') 55 | ]); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/User.php: -------------------------------------------------------------------------------- 1 | define(App\User::class, function (Faker\Generator $faker) { 5 | return [ 6 | 'id' => $faker->randomNumber(), 7 | 'name' => $faker->name, 8 | 'email' => $faker->unique()->safeEmail, 9 | 'password' => bcrypt('secret'), 10 | 'remember_token' => str_random(10), 11 | ]; 12 | }); 13 | 14 | $factory->define(Ultraware\Roles\Models\Role::class, function (Faker\Generator $faker) { 15 | return [ 16 | 'name' => $faker->words(2, true), 17 | 'slug' => $faker->slug(2), 18 | 'description' => '', 19 | ]; 20 | }); 21 | 22 | $factory->define(Ultraware\Roles\Models\Permission::class, function (Faker\Generator $faker) { 23 | return [ 24 | 'name' => $faker->words(2, true), 25 | 'slug' => $faker->slug(2), 26 | 'description' => '', 27 | 'model' => $faker->words(1, true), 28 | ]; 29 | }); 30 | -------------------------------------------------------------------------------- /tests/database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->string('password'); 21 | $table->rememberToken(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('users'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/src/Middleware/VerifyPermissionTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('check')->once()->withNoArgs()->andReturn(true); 17 | $guard->shouldReceive('user')->once()->withNoArgs()->andReturn($user); 18 | $user->shouldReceive('hasPermission')->once()->with('permission1')->andReturn(true); 19 | 20 | $verifyPermission = new VerifyPermission($guard); 21 | $result = $verifyPermission->handle($request, function (Request $request) { 22 | return 'next was called'; 23 | }, 'permission1'); 24 | $this->assertEquals('next was called', $result); 25 | } 26 | 27 | public function testUserHasPermission_throwsException() 28 | { 29 | $guard = \Mockery::mock(Guard::class); 30 | $user = \Mockery::mock(User::class); 31 | $request = new Illuminate\Http\Request(); 32 | $guard->shouldReceive('check')->once()->withNoArgs()->andReturn(true); 33 | $guard->shouldReceive('user')->once()->withNoArgs()->andReturn($user); 34 | $user->shouldReceive('hasPermission')->once()->with('permission1')->andReturn(false); 35 | 36 | $this->expectException(PermissionDeniedException::class); 37 | $verifyPermission = new VerifyPermission($guard); 38 | $verifyPermission->handle($request, function (Request $request) { 39 | }, 'permission1'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/src/Middleware/VerifyRoleTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('check')->once()->withNoArgs()->andReturn(true); 16 | $guard->shouldReceive('user')->once()->withNoArgs()->andReturn($user); 17 | $user->shouldReceive('hasRole')->once()->with('role1')->andReturn(true); 18 | 19 | $verifyRole = new VerifyRole($guard); 20 | $result = $verifyRole->handle($request, function (Request $request) { 21 | return 'next was called'; 22 | }, 'role1'); 23 | $this->assertEquals('next was called', $result); 24 | } 25 | 26 | public function testUserHasPermission_throwsException() 27 | { 28 | $guard = \Mockery::mock(Guard::class); 29 | $user = \Mockery::mock(User::class); 30 | $request = new Request(); 31 | $guard->shouldReceive('check')->once()->withNoArgs()->andReturn(true); 32 | $guard->shouldReceive('user')->once()->withNoArgs()->andReturn($user); 33 | $user->shouldReceive('hasRole')->once()->with('role1')->andReturn(false); 34 | 35 | $this->expectException(RoleDeniedException::class); 36 | $verifyRole = new VerifyRole($guard); 37 | $verifyRole->handle($request, function (Request $request) { 38 | }, 'role1'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/src/Traits/HasRoleAndPermissionTest.php: -------------------------------------------------------------------------------- 1 | withFactories(__DIR__ . '/../../database/factories'); 14 | } 15 | 16 | /** 17 | * Define environment setup. 18 | * 19 | * @param \Illuminate\Foundation\Application $app 20 | * @return void 21 | */ 22 | protected function getEnvironmentSetUp($app) 23 | { 24 | $this->setupDbConfig($app); 25 | parent::getEnvironmentSetUp($app); 26 | } 27 | 28 | public function testRolePermissions() 29 | { 30 | $this->runMigrations(); 31 | 32 | /** @var User $user */ 33 | $user = factory(User::class)->make(); 34 | 35 | // roles 36 | $roles = new Collection([ 37 | factory(Role::class)->create(), 38 | factory(Role::class)->create(), 39 | factory(Role::class)->create(['level' => 2]), 40 | factory(Role::class)->create(['level' => 3]), 41 | ]); 42 | // permissions 43 | /** @var Collection $permissions */ 44 | $permissions = factory(Permission::class, 8)->create(); 45 | 46 | // attach permissions to role 47 | $permissions->each(function ($permission, $key) use ($roles) { 48 | switch ($key) { 49 | case 0: 50 | case 1: 51 | $roles->get(0)->attachPermission($permission); 52 | break; 53 | case 2: 54 | case 3: 55 | $roles->get(1)->attachPermission($permission); 56 | break; 57 | case 4: 58 | case 5: 59 | $roles->get(2)->attachPermission($permission); 60 | break; 61 | case 6: 62 | case 7: 63 | $roles->get(3)->attachPermission($permission); 64 | break; 65 | } 66 | }); 67 | // attach role 0 (without level) 68 | $user->roles()->attach($roles->get(0)); 69 | 70 | // only permissions of role 0 should be found 71 | $this->assertEquals( 72 | $permissions->toBase()->only([0, 1])->pluck('id')->toArray(), 73 | $user->rolePermissions()->get()->pluck('id')->toArray()); 74 | 75 | // reset cache 76 | $user->detachRole(null); 77 | 78 | // attach role 2 wich has a level and all lower role.permissions with lower level should also be found 79 | $user->roles()->attach($roles->get(2)); 80 | 81 | $this->assertEquals( 82 | $permissions->toBase()->only([0, 1, 2, 3, 4, 5])->pluck('id')->toArray(), 83 | $user->rolePermissions()->get()->pluck('id')->toArray() 84 | ); 85 | } 86 | 87 | public function testHasRole() 88 | { 89 | $user = \Mockery::mock(User::class . '[hasOneRole]'); 90 | $user->shouldReceive('hasOneRole') 91 | ->with('role1') 92 | ->once() 93 | ->andReturn(true); 94 | $this->assertTrue($user->hasRole('role1')); 95 | } 96 | 97 | public function testHasRole_all() 98 | { 99 | $user = \Mockery::mock(User::class . '[hasAllRoles]'); 100 | $user->shouldReceive('hasAllRoles') 101 | ->with(['role1', 'role2']) 102 | ->once() 103 | ->andReturn(true); 104 | $this->assertTrue($user->hasRole(['role1', 'role2'], true)); 105 | } 106 | 107 | public function testHasOneRole_true() 108 | { 109 | $user = \Mockery::mock(User::class . '[checkRole]'); 110 | $user->shouldReceive('checkRole') 111 | ->once() 112 | ->with('role1') 113 | ->andReturn(false); 114 | 115 | $user->shouldReceive('checkRole') 116 | ->once() 117 | ->with('role2') 118 | ->andReturn(true); 119 | 120 | $this->assertTrue($user->hasOneRole(['role1', 'role2'])); 121 | } 122 | 123 | public function testHasOneRole_false() 124 | { 125 | $user = \Mockery::mock(User::class . '[checkRole]'); 126 | $user->shouldReceive('checkRole') 127 | ->once() 128 | ->with('role1') 129 | ->andReturn(false); 130 | 131 | $user->shouldReceive('checkRole') 132 | ->once() 133 | ->with('role2') 134 | ->andReturn(false); 135 | 136 | $this->assertFalse($user->hasOneRole(['role1', 'role2'])); 137 | } 138 | 139 | public function testHasAllRoles_true() 140 | { 141 | $user = \Mockery::mock(User::class . '[checkRole]'); 142 | $user->shouldReceive('checkRole') 143 | ->once() 144 | ->with('role1') 145 | ->andReturn(true); 146 | 147 | $user->shouldReceive('checkRole') 148 | ->once() 149 | ->with('role2') 150 | ->andReturn(true); 151 | 152 | $this->assertTrue($user->hasAllRoles(['role1', 'role2'])); 153 | } 154 | 155 | public function testHasOAllRole_false() 156 | { 157 | $user = \Mockery::mock(User::class . '[checkRole]'); 158 | $user->shouldReceive('checkRole') 159 | ->once() 160 | ->with('role1') 161 | ->andReturn(true); 162 | 163 | $user->shouldReceive('checkRole') 164 | ->once() 165 | ->with('role2') 166 | ->andReturn(false); 167 | 168 | $this->assertFalse($user->hasAllRoles(['role1', 'role2'])); 169 | } 170 | 171 | public function testHasAllRoles_csv() 172 | { 173 | $user = \Mockery::mock(User::class . '[checkRole]'); 174 | $user->shouldReceive('checkRole') 175 | ->once() 176 | ->with('role1') 177 | ->andReturn(true); 178 | 179 | $user->shouldReceive('checkRole') 180 | ->once() 181 | ->with('role2') 182 | ->andReturn(true); 183 | 184 | $this->assertTrue($user->hasAllRoles('role1,role2')); 185 | } 186 | 187 | public function testHasAllRoles_pipe() 188 | { 189 | $user = \Mockery::mock(User::class . '[checkRole]'); 190 | $user->shouldReceive('checkRole') 191 | ->once() 192 | ->with('role1') 193 | ->andReturn(true); 194 | 195 | $user->shouldReceive('checkRole') 196 | ->once() 197 | ->with('role2') 198 | ->andReturn(true); 199 | 200 | $this->assertTrue($user->hasAllRoles('role1| role2')); 201 | } 202 | 203 | public function testCheckRole() 204 | { 205 | $user = \Mockery::mock(User::class . '[getRoles]'); 206 | $roles = factory(Role::class, 4)->make(); 207 | $user->shouldReceive('getRoles') 208 | ->once() 209 | ->withNoArgs() 210 | ->andReturn($roles); 211 | 212 | $this->assertTrue($user->checkRole($roles->first()->id)); 213 | } 214 | 215 | public function testHasPermission() 216 | { 217 | $user = \Mockery::mock(User::class . '[hasOnePermission]'); 218 | $user->shouldReceive('hasOnePermission') 219 | ->with('permission1') 220 | ->once() 221 | ->andReturn(true); 222 | $this->assertTrue($user->hasPermission('permission1')); 223 | } 224 | 225 | public function testHasPermission_all() 226 | { 227 | $user = \Mockery::mock(User::class . '[hasAllPermissions]'); 228 | $user->shouldReceive('hasAllPermissions') 229 | ->with(['permission1', 'permission2']) 230 | ->once() 231 | ->andReturn(true); 232 | $this->assertTrue($user->hasPermission(['permission1', 'permission2'], true)); 233 | } 234 | 235 | public function testHasOnePermission_true() 236 | { 237 | $user = \Mockery::mock(User::class . '[checkPermission]'); 238 | $user->shouldReceive('checkPermission') 239 | ->once() 240 | ->with('permission1') 241 | ->andReturn(false); 242 | 243 | $user->shouldReceive('checkPermission') 244 | ->once() 245 | ->with('permission2') 246 | ->andReturn(true); 247 | 248 | $this->assertTrue($user->hasOnePermission(['permission1', 'permission2'])); 249 | } 250 | 251 | public function testHasOnePermission_false() 252 | { 253 | $user = \Mockery::mock(User::class . '[checkPermission]'); 254 | $user->shouldReceive('checkPermission') 255 | ->once() 256 | ->with('permission1') 257 | ->andReturn(false); 258 | 259 | $user->shouldReceive('checkPermission') 260 | ->once() 261 | ->with('permission2') 262 | ->andReturn(false); 263 | 264 | $this->assertFalse($user->hasOnePermission(['permission1', 'permission2'])); 265 | } 266 | 267 | public function testHasAllPermissions_true() 268 | { 269 | $user = \Mockery::mock(User::class . '[checkPermission]'); 270 | $user->shouldReceive('checkPermission') 271 | ->once() 272 | ->with('permission1') 273 | ->andReturn(true); 274 | 275 | $user->shouldReceive('checkPermission') 276 | ->once() 277 | ->with('permission2') 278 | ->andReturn(true); 279 | 280 | $this->assertTrue($user->hasAllPermissions(['permission1', 'permission2'])); 281 | } 282 | 283 | public function testHasOAllPermission_false() 284 | { 285 | $user = \Mockery::mock(User::class . '[checkPermission]'); 286 | $user->shouldReceive('checkPermission') 287 | ->once() 288 | ->with('permission1') 289 | ->andReturn(true); 290 | 291 | $user->shouldReceive('checkPermission') 292 | ->once() 293 | ->with('permission2') 294 | ->andReturn(false); 295 | 296 | $this->assertFalse($user->hasAllPermissions(['permission1', 'permission2'])); 297 | } 298 | 299 | public function testHasAllPermissions_csv() 300 | { 301 | $user = \Mockery::mock(User::class . '[checkPermission]'); 302 | $user->shouldReceive('checkPermission') 303 | ->once() 304 | ->with('permission1') 305 | ->andReturn(true); 306 | 307 | $user->shouldReceive('checkPermission') 308 | ->once() 309 | ->with('permission2') 310 | ->andReturn(true); 311 | 312 | $this->assertTrue($user->hasAllPermissions('permission1,permission2')); 313 | } 314 | 315 | public function testHasAllPermissions_pipe() 316 | { 317 | $user = \Mockery::mock(User::class . '[checkPermission]'); 318 | $user->shouldReceive('checkPermission') 319 | ->once() 320 | ->with('permission1') 321 | ->andReturn(true); 322 | 323 | $user->shouldReceive('checkPermission') 324 | ->once() 325 | ->with('permission2') 326 | ->andReturn(true); 327 | 328 | $this->assertTrue($user->hasAllPermissions('permission1| permission2')); 329 | } 330 | 331 | public function testCheckPermission() 332 | { 333 | $user = \Mockery::mock(User::class . '[getPermissions]'); 334 | $permissions = factory(Permission::class, 4)->make(); 335 | $user->shouldReceive('getPermissions') 336 | ->once() 337 | ->withNoArgs() 338 | ->andReturn($permissions); 339 | 340 | $this->assertTrue($user->checkPermission($permissions->first()->id)); 341 | } 342 | 343 | public function testMagicCall() 344 | { 345 | $user = \Mockery::mock(User::class . '[hasRole,hasPermission]'); 346 | 347 | //isMyRole 348 | $user->shouldReceive('hasRole') 349 | ->once() 350 | ->with('my.role') 351 | ->andReturn(true); 352 | $this->assertTrue($user->callMagic('isMyRole', [])); 353 | 354 | //canMyPermission 355 | $user->shouldReceive('hasPermission') 356 | ->once() 357 | ->with('my.permission') 358 | ->andReturn(true); 359 | $this->assertTrue($user->callMagic('canMyPermission', [])); 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /tests/src/Traits/PermissionHasRelationsTest.php: -------------------------------------------------------------------------------- 1 | withFactories(__DIR__ . '/../../database/factories'); 13 | $this->runMigrations(); 14 | } 15 | 16 | /** 17 | * Define environment setup. 18 | * 19 | * @param \Illuminate\Foundation\Application $app 20 | * @return void 21 | */ 22 | protected function getEnvironmentSetUp($app) 23 | { 24 | $this->setupDbConfig($app); 25 | parent::getEnvironmentSetUp($app); 26 | } 27 | 28 | public function testPermissionHasRoles() 29 | { 30 | $role = factory(Role::class)->create(); 31 | $permission = factory(Permission::class)->create(); 32 | $role->permissions()->attach($permission); 33 | $this->assertEquals($role->id, $permission->roles->first()->id); 34 | $this->assertEquals($role->slug, $permission->roles->first()->slug); 35 | } 36 | 37 | public function testPermissionHasUsers() 38 | { 39 | /** @var User $user */ 40 | $user = factory(User::class)->create(); 41 | $permission = factory(Permission::class)->create(); 42 | $user->userPermissions()->attach($permission); 43 | $this->assertEquals($user->id, $permission->users->first()->id); 44 | $this->assertEquals($user->name, $permission->users->first()->name); 45 | } 46 | } 47 | --------------------------------------------------------------------------------