├── CHANGELOG.md ├── LICENSE ├── README.md ├── VERSION ├── composer.json ├── config ├── laratrust.php └── laratrust_seeder.php ├── package-lock.json ├── package.json ├── public ├── img │ └── logo.png ├── laratrust.css └── mix-manifest.json ├── resources ├── css │ └── styles.css └── views │ ├── migration.blade.php │ ├── panel │ ├── edit.blade.php │ ├── layout.blade.php │ ├── pagination.blade.php │ ├── permissions │ │ └── index.blade.php │ ├── roles-assignment │ │ ├── edit.blade.php │ │ └── index.blade.php │ └── roles │ │ ├── index.blade.php │ │ └── show.blade.php │ ├── setup-teams.blade.php │ └── upgrade-migration.blade.php ├── routes └── web.php ├── src ├── Checkers │ ├── CheckersManager.php │ ├── Role │ │ ├── RoleChecker.php │ │ ├── RoleDefaultChecker.php │ │ └── RoleQueryChecker.php │ └── User │ │ ├── UserChecker.php │ │ ├── UserDefaultChecker.php │ │ └── UserQueryChecker.php ├── Console │ ├── AddLaratrustUserTraitUseCommand.php │ ├── MakePermissionCommand.php │ ├── MakeRoleCommand.php │ ├── MakeSeederCommand.php │ ├── MakeTeamCommand.php │ ├── MigrationCommand.php │ ├── SetupCommand.php │ ├── SetupTeamsCommand.php │ └── UpgradeCommand.php ├── Contracts │ ├── LaratrustUser.php │ ├── Permission.php │ ├── Role.php │ └── Team.php ├── Helper.php ├── Http │ └── Controllers │ │ ├── PermissionsController.php │ │ ├── RolesAssignmentController.php │ │ └── RolesController.php ├── Laratrust.php ├── LaratrustFacade.php ├── LaratrustServiceProvider.php ├── Middleware │ ├── Ability.php │ ├── LaratrustMiddleware.php │ ├── Permission.php │ └── Role.php ├── Models │ ├── Permission.php │ ├── Role.php │ └── Team.php └── Traits │ ├── DynamicUserRelationshipCalls.php │ ├── HasLaratrustEvents.php │ ├── HasLaratrustScopes.php │ └── HasRolesAndPermissions.php ├── stubs ├── permission.stub ├── role.stub ├── seeder.stub └── team.stub ├── tailwind.config.js └── webpack.mix.js /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 6.1.0 (mayo 29, 2020) 2 | - Merge branch 'limit-roles-in-panel' into 6.x 3 | - Update docs 4 | - Update docs 5 | - Use a default model when entering the roles assignment view 6 | - Use display name when available in the panel 7 | - Build for production 8 | - Little improvements on the not removable roles and defaults for previously installed versions 9 | - Add show view for the not editable roles 10 | - Update docs 11 | - Add config file structure 12 | - Add possibility to avoid having roles removed from an user 13 | - Add the possibility to block roles for edit and delete 14 | 15 | ## 6.0.2 (mayo 11, 2020) 16 | - Merge pull request #411 from siarheipashkevich/fix-config-typos 17 | - Fixed config typos 18 | - Update docs 19 | - Merge branch '6.x' 20 | - Fix broken links and update sitemap 21 | - Merge branch '6.x' 22 | - Add some screenshots to the docs 23 | - Merge branch '6.x' 24 | 25 | ## 6.0.1 (mayo 07, 2020) 26 | - Don't register the panel by default 27 | 28 | ## 6.0.0 (mayo 06, 2020) 29 | - Add simple admin panel to manage roles, permissions and roles/permissions assignment to the users 30 | - Change how the Seeder works, in order to only use the role structure we had before 31 | - Remove the method `can` so we now support gates and policies out of the box 32 | - Add `withoutRole` and `withoutPermission` scopes 33 | - Add support to receive multiple roles and permisions in the `whereRoleIs` and `wherePermissionIs` methods. 34 | - Laratrust is now using semver. 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 santigarcor 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 |

2 | 3 | # Laratrust (Laravel Package) 4 | 5 | [![Sponsor](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#white)](//github.com/sponsors/santigarcor) 6 | [![Latest Stable Version](https://poser.pugx.org/santigarcor/laratrust/v?style=for-the-badge)](//packagist.org/packages/santigarcor/laratrust) 7 | [![Total Downloads](https://poser.pugx.org/santigarcor/laratrust/downloads?style=for-the-badge)](//packagist.org/packages/santigarcor/laratrust) 8 | [![License](https://poser.pugx.org/santigarcor/laratrust/license?style=for-the-badge)](//packagist.org/packages/santigarcor/laratrust) 9 | [![tests](https://github.com/santigarcor/laratrust/workflows/tests/badge.svg)](https://github.com/santigarcor/laratrust/actions?query=workflow%3Atests) 10 | 11 | ## Version Compatibility 12 | 13 | | Laravel | Laratrust | 14 | | :--------------- | :-------------------------------------------------- | 15 | | 10.x, 11.X, 12.X | [8.x](//laratrust.santigarcor.me/docs/8.x/) | 16 | | 9.x-10.x | [7.x](//laratrust.santigarcor.me/docs/7.x/) | 17 | | 8.x | [6.x](//laratrust.santigarcor.me/docs/6.x/) | 18 | | 7.x | [6.x](//laratrust.santigarcor.me/docs/6.x/) | 19 | | 6.x | [6.x](//laratrust.santigarcor.me/docs/6.x/) | 20 | | 5.6.x - 5.8.x | [5.2](//laratrust.santigarcor.me/docs/5.2/) | 21 | | 5.3.x - 5.5.x | [5.1](//laratrust.santigarcor.me/docs/5.1/) | 22 | | 5.0.x - 5.2.x | [4.0](//github.com/santigarcor/laratrust/tree/4.0). | 23 | 24 | ## Installation, Configuration and Usage 25 | 26 | To install, configure and learn how to use Laratrust please go to the [Documentation](https://laratrust.santigarcor.me/). 27 | 28 | ## What does Laratrust support? 29 | 30 | - Multiple user models. 31 | - Multiple roles and permissions assignable to users. 32 | - Multiple permissions assignable to roles. 33 | - Roles and permissions verification. 34 | - Roles and permissions caching. 35 | - Events when roles and permissions are added, removed or synced. 36 | - Multiple roles and permissions can be added to users within teams. 37 | - Multiple guards for the middleware. 38 | - A simple administration panel for roles and permissions. 39 | - Laravel gates and policies. 40 | 41 | ## License 42 | 43 | Laratrust is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). 44 | 45 | ## Contributing 46 | 47 | Please report any issue you find in the issues page. Pull requests are more than welcome. 48 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 6.1.0 2 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "santigarcor/laratrust", 3 | "description": "This package provides a flexible way to add Role-based Permissions to Laravel", 4 | "keywords": [ 5 | "acl", 6 | "authorization", 7 | "laravel", 8 | "laratrust", 9 | "permissions", 10 | "php", 11 | "rbac", 12 | "roles", 13 | "multiusers", 14 | "teams" 15 | ], 16 | "license": "MIT", 17 | "authors": [ 18 | { 19 | "name": "Santiago Garcia", 20 | "homepage": "http://santigarcor.me" 21 | } 22 | ], 23 | "require": { 24 | "php": ">=8.1", 25 | "kkszymanowski/traitor": "^1.0", 26 | "laravel/framework": "^10.0|^11.0|^12.0" 27 | }, 28 | "require-dev": { 29 | "mockery/mockery": "^1.3.2", 30 | "orchestra/testbench": "^8.0|^9.0|^10.0", 31 | "phpunit/phpunit": "^8.4|^9.0|^10.5|^11.5.3" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Laratrust\\": "src/" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "Laratrust\\Tests\\": "tests/" 41 | } 42 | }, 43 | "config": { 44 | "sort-packages": true 45 | }, 46 | "prefer-stable": true, 47 | "minimum-stability": "dev", 48 | "extra": { 49 | "laravel": { 50 | "providers": [ 51 | "Laratrust\\LaratrustServiceProvider" 52 | ], 53 | "aliases": { 54 | "Laratrust": "Laratrust\\LaratrustFacade" 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /config/laratrust.php: -------------------------------------------------------------------------------- 1 | false, 14 | 15 | /* 16 | |-------------------------------------------------------------------------- 17 | | Checkers 18 | |-------------------------------------------------------------------------- 19 | | 20 | | Manage Laratrust's role and permissions checkers configurations. 21 | | 22 | */ 23 | 'checkers' => [ 24 | 25 | /* 26 | |-------------------------------------------------------------------------- 27 | | Which permissions checker to use. 28 | |-------------------------------------------------------------------------- 29 | | 30 | | Defines if you want to use the roles and permissions checker. 31 | | Available: 32 | | - default: Check for the roles and permissions using the method that Laratrust 33 | | has always used. 34 | | - query: Check for the roles and permissions using direct queries to the database. 35 | | This method doesn't support cache yet. 36 | | - class that extends Laratrust\Checkers\User\UserChecker 37 | */ 38 | 'user' => 'default', 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Which role checker to use. 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Defines if you want to use the roles and permissions checker. 46 | | Available: 47 | | - default: Check for the roles and permissions using the method that Laratrust 48 | has always used. 49 | | - query: Check for the roles and permissions using direct queries to the database. 50 | | This method doesn't support cache yet. 51 | | - class that extends Laratrust\Checkers\Role\RoleChecker 52 | */ 53 | 'role' => 'default', 54 | ], 55 | 56 | /* 57 | |-------------------------------------------------------------------------- 58 | | Cache 59 | |-------------------------------------------------------------------------- 60 | | 61 | | Manage Laratrust's cache configurations. It uses the driver defined in the 62 | | config/cache.php file. 63 | | 64 | */ 65 | 'cache' => [ 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | Use cache in the package 69 | |-------------------------------------------------------------------------- 70 | | 71 | | Defines if Laratrust will use Laravel's Cache to cache the roles and permissions. 72 | | NOTE: Currently the database check does not use cache. 73 | | 74 | */ 75 | 'enabled' => env('LARATRUST_ENABLE_CACHE', env('APP_ENV') === 'production'), 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Time to store in cache Laratrust's roles and permissions. 80 | |-------------------------------------------------------------------------- 81 | | 82 | | Determines the time in SECONDS to store Laratrust's roles and permissions in the cache. 83 | | 84 | */ 85 | 'expiration_time' => 3600, 86 | ], 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | Laratrust User Models 91 | |-------------------------------------------------------------------------- 92 | | 93 | | This is the array that contains the information of the user models. 94 | | This information is used in the add-trait command, for the roles and 95 | | permissions relationships with the possible user models, and the 96 | | administration panel to add roles and permissions to the users. 97 | | 98 | | The key in the array is the name of the relationship inside the roles and permissions. 99 | | 100 | */ 101 | 'user_models' => [ 102 | 'users' => \App\Models\User::class, 103 | ], 104 | 105 | /* 106 | |-------------------------------------------------------------------------- 107 | | Laratrust Models 108 | |-------------------------------------------------------------------------- 109 | | 110 | | These are the models used by Laratrust to define the roles, permissions and teams. 111 | | If you want the Laratrust models to be in a different namespace or 112 | | to have a different name, you can do it here. 113 | | 114 | */ 115 | 'models' => [ 116 | 117 | 'role' => \App\Models\Role::class, 118 | 119 | 'permission' => \App\Models\Permission::class, 120 | 121 | /** 122 | * Will be used only if the teams functionality is enabled. 123 | */ 124 | 'team' => \App\Models\Team::class, 125 | ], 126 | 127 | /* 128 | |-------------------------------------------------------------------------- 129 | | Laratrust Tables 130 | |-------------------------------------------------------------------------- 131 | | 132 | | These are the tables used by Laratrust to store all the authorization data. 133 | | 134 | */ 135 | 'tables' => [ 136 | 137 | 'roles' => 'roles', 138 | 139 | 'permissions' => 'permissions', 140 | 141 | /** 142 | * Will be used only if the teams functionality is enabled. 143 | */ 144 | 'teams' => 'teams', 145 | 146 | 'role_user' => 'role_user', 147 | 148 | 'permission_user' => 'permission_user', 149 | 150 | 'permission_role' => 'permission_role', 151 | ], 152 | 153 | /* 154 | |-------------------------------------------------------------------------- 155 | | Laratrust Foreign Keys 156 | |-------------------------------------------------------------------------- 157 | | 158 | | These are the foreign keys used by laratrust in the intermediate tables. 159 | | 160 | */ 161 | 'foreign_keys' => [ 162 | /** 163 | * User foreign key on Laratrust's role_user and permission_user tables. 164 | */ 165 | 'user' => 'user_id', 166 | 167 | /** 168 | * Role foreign key on Laratrust's role_user and permission_role tables. 169 | */ 170 | 'role' => 'role_id', 171 | 172 | /** 173 | * Role foreign key on Laratrust's permission_user and permission_role tables. 174 | */ 175 | 'permission' => 'permission_id', 176 | 177 | /** 178 | * Role foreign key on Laratrust's role_user and permission_user tables. 179 | */ 180 | 'team' => 'team_id', 181 | ], 182 | 183 | /* 184 | |-------------------------------------------------------------------------- 185 | | Laratrust Middleware 186 | |-------------------------------------------------------------------------- 187 | | 188 | | This configuration helps to customize the Laratrust middleware behavior. 189 | | 190 | */ 191 | 'middleware' => [ 192 | /** 193 | * Define if the laratrust middleware are registered automatically in the service provider. 194 | */ 195 | 'register' => true, 196 | 197 | /** 198 | * Method to be called in the middleware return case. 199 | * Available: abort|redirect. 200 | */ 201 | 'handling' => 'abort', 202 | 203 | /** 204 | * Handlers for the unauthorized method in the middlewares. 205 | * The name of the handler must be the same as the handling. 206 | */ 207 | 'handlers' => [ 208 | /** 209 | * Aborts the execution with a 403 code and allows you to provide the response text. 210 | */ 211 | 'abort' => [ 212 | 'code' => 403, 213 | 'message' => 'User does not have any of the necessary access rights.', 214 | ], 215 | 216 | /** 217 | * Redirects the user to the given url. 218 | * If you want to flash a key to the session, 219 | * you can do it by setting the key and the content of the message 220 | * If the message content is empty it won't be added to the redirection. 221 | */ 222 | 'redirect' => [ 223 | 'url' => '/home', 224 | 'message' => [ 225 | 'key' => 'error', 226 | 'content' => '', 227 | ], 228 | ], 229 | ], 230 | ], 231 | 232 | 'teams' => [ 233 | /* 234 | |-------------------------------------------------------------------------- 235 | | Use teams feature in the package 236 | |-------------------------------------------------------------------------- 237 | | 238 | | Defines if Laratrust will use the teams feature. 239 | | Please check the docs to see what you need to do in case you have the package already configured. 240 | | 241 | */ 242 | 'enabled' => false, 243 | 244 | /* 245 | |-------------------------------------------------------------------------- 246 | | Strict check for roles/permissions inside teams 247 | |-------------------------------------------------------------------------- 248 | | 249 | | Determines if a strict check should be done when checking if a role or permission is added inside a team. 250 | | If it's false, when checking a role/permission without specifying the team, 251 | | it will check only if the user has added that role/permission ignoring the team. 252 | | 253 | */ 254 | 'strict_check' => false, 255 | ], 256 | 257 | /* 258 | |-------------------------------------------------------------------------- 259 | | Laratrust Permissions as Gates 260 | |-------------------------------------------------------------------------- 261 | | 262 | | Determines if you can check if a user has a permission using the "can" method. 263 | | 264 | */ 265 | 'permissions_as_gates' => false, 266 | 267 | /* 268 | |-------------------------------------------------------------------------- 269 | | Laratrust Panel 270 | |-------------------------------------------------------------------------- 271 | | 272 | | Section to manage everything related with the admin panel for the roles and permissions. 273 | | 274 | */ 275 | 'panel' => [ 276 | /* 277 | |-------------------------------------------------------------------------- 278 | | Laratrust Panel Register 279 | |-------------------------------------------------------------------------- 280 | | 281 | | This manages if routes used for the admin panel should be registered. 282 | | Turn this value to false if you don't want to use Laratrust admin panel 283 | | 284 | */ 285 | 'register' => false, 286 | 287 | /* 288 | |-------------------------------------------------------------------------- 289 | | Laratrust Panel Domain 290 | |-------------------------------------------------------------------------- 291 | | 292 | | This is the Domain Laratrust panel for roles and permissions 293 | | will be accessible from. 294 | | 295 | */ 296 | 'domain' => env('LARATRUST_PANEL_DOMAIN'), 297 | 298 | /* 299 | |-------------------------------------------------------------------------- 300 | | Laratrust Panel Path 301 | |-------------------------------------------------------------------------- 302 | | 303 | | This is the URI path where Laratrust panel for roles and permissions 304 | | will be accessible from. 305 | | 306 | */ 307 | 'path' => 'laratrust', 308 | 309 | /* 310 | |-------------------------------------------------------------------------- 311 | | Laratrust Panel Path 312 | |-------------------------------------------------------------------------- 313 | | 314 | | The route where the go back link should point 315 | | 316 | */ 317 | 'go_back_route' => '/', 318 | 319 | /* 320 | |-------------------------------------------------------------------------- 321 | | Laratrust Panel Route Middleware 322 | |-------------------------------------------------------------------------- 323 | | 324 | | These middleware will get added onto each Laratrust panel route. 325 | | 326 | */ 327 | 'middleware' => ['web'], 328 | 329 | /* 330 | |-------------------------------------------------------------------------- 331 | | Enable permissions assignment 332 | |-------------------------------------------------------------------------- 333 | | 334 | | Enable/Disable the permissions assignment to the users. 335 | | 336 | */ 337 | 'assign_permissions_to_user' => true, 338 | 339 | /* 340 | |-------------------------------------------------------------------------- 341 | | Enable permissions creation 342 | |-------------------------------------------------------------------------- 343 | | 344 | | Enable/Disable the possibility to create permissions from the panel. 345 | | 346 | */ 347 | 'create_permissions' => true, 348 | 349 | /* 350 | |-------------------------------------------------------------------------- 351 | | Add restriction to roles in the panel 352 | |-------------------------------------------------------------------------- 353 | | 354 | | Configure which roles can not be editable, deletable and removable. 355 | | To add a role to the restriction, use name of the role here. 356 | | 357 | */ 358 | 'roles_restrictions' => [ 359 | // The user won't be able to remove roles already assigned to users. 360 | 'not_removable' => [], 361 | 362 | // The user won't be able to edit the role and the permissions assigned. 363 | 'not_editable' => [], 364 | 365 | // The user won't be able to delete the role. 366 | 'not_deletable' => [], 367 | ], 368 | ], 369 | ]; 370 | -------------------------------------------------------------------------------- /config/laratrust_seeder.php: -------------------------------------------------------------------------------- 1 | false, 8 | 9 | /** 10 | * Control if all the laratrust tables should be truncated before running the seeder. 11 | */ 12 | 'truncate_tables' => true, 13 | 14 | 'roles_structure' => [ 15 | 'superadministrator' => [ 16 | 'users' => 'c,r,u,d', 17 | 'payments' => 'c,r,u,d', 18 | 'profile' => 'r,u', 19 | ], 20 | 'administrator' => [ 21 | 'users' => 'c,r,u,d', 22 | 'profile' => 'r,u', 23 | ], 24 | 'user' => [ 25 | 'profile' => 'r,u', 26 | ], 27 | 'role_name' => [ 28 | 'module_1_name' => 'c,r,u,d', 29 | ], 30 | ], 31 | 32 | 'permissions_map' => [ 33 | 'c' => 'create', 34 | 'r' => 'read', 35 | 'u' => 'update', 36 | 'd' => 'delete', 37 | ], 38 | ]; 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laratrust", 3 | "version": "5.1.0", 4 | "description": "Laratrust Docs", 5 | "directories": { 6 | "doc": "docs", 7 | "test": "tests" 8 | }, 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "@tailwindcss/custom-forms": "^0.2.1", 12 | "cross-env": "^7.0.2", 13 | "laravel-mix": "^5.0.4", 14 | "tailwindcss": "^1.4.2", 15 | "vuepress": "^0.14.4" 16 | }, 17 | "scripts": { 18 | "docs:dev": "vuepress dev docs", 19 | "docs:build": "vuepress build docs", 20 | "dev": "npm run development", 21 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 22 | "watch": "npm run development -- --watch", 23 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 24 | "prod": "npm run production", 25 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 26 | }, 27 | "author": "Santiago Garcia C", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/santigarcor/laratrust/issues" 31 | }, 32 | "homepage": "https://github.com/santigarcor/laratrust#readme" 33 | } 34 | -------------------------------------------------------------------------------- /public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santigarcor/laratrust/f0b90fca6b378906c3522f9f40ca1691c4a6fd84/public/img/logo.png -------------------------------------------------------------------------------- /public/laratrust.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none;padding:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#a0aec0}input::-moz-placeholder,textarea::-moz-placeholder{color:#a0aec0}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#a0aec0}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:#a0aec0}input::placeholder,textarea::placeholder{color:#a0aec0}[role=button],button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}.form-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#e2e8f0;border-width:1px;border-radius:.25rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5}.form-input::-webkit-input-placeholder{color:#a0aec0;opacity:1}.form-input::-moz-placeholder{color:#a0aec0;opacity:1}.form-input:-ms-input-placeholder{color:#a0aec0;opacity:1}.form-input::-ms-input-placeholder{color:#a0aec0;opacity:1}.form-input::placeholder{color:#a0aec0;opacity:1}.form-input:focus{outline:none;box-shadow:0 0 0 3px rgba(66,153,225,.5);border-color:#63b3ed}.form-textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#e2e8f0;border-width:1px;border-radius:.25rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5}.form-textarea::-webkit-input-placeholder{color:#a0aec0;opacity:1}.form-textarea::-moz-placeholder{color:#a0aec0;opacity:1}.form-textarea:-ms-input-placeholder{color:#a0aec0;opacity:1}.form-textarea::-ms-input-placeholder{color:#a0aec0;opacity:1}.form-textarea::placeholder{color:#a0aec0;opacity:1}.form-textarea:focus{outline:none;box-shadow:0 0 0 3px rgba(66,153,225,.5);border-color:#63b3ed}.form-multiselect{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#e2e8f0;border-width:1px;border-radius:.25rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5}.form-multiselect:focus{outline:none;box-shadow:0 0 0 3px rgba(66,153,225,.5);border-color:#63b3ed}.form-select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23a0aec0'%3E%3Cpath d='M15.3 9.3a1 1 0 011.4 1.4l-4 4a1 1 0 01-1.4 0l-4-4a1 1 0 011.4-1.4l3.3 3.29 3.3-3.3z'/%3E%3C/svg%3E");-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;background-repeat:no-repeat;background-color:#fff;border-color:#e2e8f0;border-width:1px;border-radius:.25rem;padding:.5rem 2.5rem .5rem .75rem;font-size:1rem;line-height:1.5;background-position:right .5rem center;background-size:1.5em 1.5em}.form-select::-ms-expand{color:#a0aec0;border:none}@media not print{.form-select::-ms-expand{display:none}}@media print and (-ms-high-contrast:active),print and (-ms-high-contrast:none){.form-select{padding-right:.75rem}}.form-select:focus{outline:none;box-shadow:0 0 0 3px rgba(66,153,225,.5);border-color:#63b3ed}.form-checkbox{-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;flex-shrink:0;height:1em;width:1em;color:#4299e1;background-color:#fff;border-color:#e2e8f0;border-width:1px;border-radius:.25rem}.form-checkbox:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.707 7.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4a1 1 0 00-1.414-1.414L7 8.586 5.707 7.293z'/%3E%3C/svg%3E");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media not print{.form-checkbox::-ms-check{border-width:1px;color:transparent;background:inherit;border-color:inherit;border-radius:inherit}}.form-checkbox:focus{outline:none;box-shadow:0 0 0 3px rgba(66,153,225,.5);border-color:#63b3ed}.form-radio{-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;flex-shrink:0;border-radius:100%;height:1em;width:1em;color:#4299e1;background-color:#fff;border-color:#e2e8f0;border-width:1px}.form-radio:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media not print{.form-radio::-ms-check{border-width:1px;color:transparent;background:inherit;border-color:inherit;border-radius:inherit}}.form-radio:focus{outline:none;box-shadow:0 0 0 3px rgba(66,153,225,.5);border-color:#63b3ed}.nav-button,.nav-button-active{padding:.5rem .75rem;border-radius:.375rem;font-size:.875rem;font-weight:500;--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.nav-button-active{background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.nav-button-active,.nav-button-active:focus,.nav-button:focus{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity));--bg-opacity:1}.nav-button-active:focus,.nav-button:focus{outline:0}.nav-button-active:focus,.nav-button:focus,.nav-button:hover{background-color:#4a5568;background-color:rgba(74,85,104,var(--bg-opacity))}.nav-button:hover{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity));--bg-opacity:1}.th{padding:.75rem 1.5rem;--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity));text-align:left;font-size:.75rem;line-height:1rem;font-weight:500;--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity));text-transform:uppercase;letter-spacing:.05em}.td,.th{border-bottom-width:1px;--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.td{padding:1rem 1.5rem;white-space:nowrap}.btn{font-weight:700;padding:.5rem 1rem;border-radius:.25rem}.btn-blue{--bg-opacity:1;background-color:#4299e1;background-color:rgba(66,153,225,var(--bg-opacity));--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.btn-blue:hover{--bg-opacity:1;background-color:#2b6cb0;background-color:rgba(43,108,176,var(--bg-opacity))}.btn-red{--bg-opacity:1;background-color:#f56565;background-color:rgba(245,101,101,var(--bg-opacity));--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.btn-red:hover{--bg-opacity:1;background-color:#c53030;background-color:rgba(197,48,48,var(--bg-opacity))}.alert-warning{background-color:#fffaf0;background-color:rgba(255,250,240,var(--bg-opacity));border-left-width:4px;--border-opacity:1;border-color:#ed8936;border-color:rgba(237,137,54,var(--border-opacity));color:#c05621;color:rgba(192,86,33,var(--text-opacity))}.alert-success,.alert-warning{--bg-opacity:1;--text-opacity:1;padding:1rem}.alert-success{background-color:#f0fff4;background-color:rgba(240,255,244,var(--bg-opacity));border-left-width:4px;--border-opacity:1;border-color:#48bb78;border-color:rgba(72,187,120,var(--border-opacity));color:#2f855a;color:rgba(47,133,90,var(--text-opacity))}.alert-error{--bg-opacity:1;background-color:#fff5f5;background-color:rgba(255,245,245,var(--bg-opacity));border-left-width:4px;--border-opacity:1;border-color:#f56565;border-color:rgba(245,101,101,var(--border-opacity));--text-opacity:1;color:#c53030;color:rgba(197,48,48,var(--text-opacity));padding:1rem}.bg-transparent{background-color:transparent}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-200{--bg-opacity:1;background-color:#edf2f7;background-color:rgba(237,242,247,var(--bg-opacity))}.bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.hover\:bg-gray-700:hover{--bg-opacity:1;background-color:#4a5568;background-color:rgba(74,85,104,var(--bg-opacity))}.hover\:bg-blue-500:hover{--bg-opacity:1;background-color:#4299e1;background-color:rgba(66,153,225,var(--bg-opacity))}.focus\:bg-gray-700:focus{--bg-opacity:1;background-color:#4a5568;background-color:rgba(74,85,104,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-gray-300{--border-opacity:1;border-color:#e2e8f0;border-color:rgba(226,232,240,var(--border-opacity))}.border-red-500{--border-opacity:1;border-color:#f56565;border-color:rgba(245,101,101,var(--border-opacity))}.border-blue-500{--border-opacity:1;border-color:#4299e1;border-color:rgba(66,153,225,var(--border-opacity))}.focus\:border-transparent:focus,.hover\:border-transparent:hover{border-color:transparent}.focus\:border-blue-300:focus{--border-opacity:1;border-color:#90cdf4;border-color:rgba(144,205,244,var(--border-opacity))}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-l-md{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.border{border-width:1px}.border-t{border-top-width:1px}.border-b{border-bottom-width:1px}.cursor-not-allowed{cursor:not-allowed}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-baseline{align-items:baseline}.self-end{align-self:flex-end}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-10{height:2.5rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-base{font-size:1rem}.text-3xl{font-size:1.875rem}.leading-5{line-height:1.25rem}.leading-tight{line-height:1.25}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mx-auto{margin-left:auto;margin-right:auto}.-my-2{margin-top:-.5rem;margin-bottom:-.5rem}.mt-1{margin-top:.25rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.ml-4{margin-left:1rem}.mr-6{margin-right:1.5rem}.ml-10{margin-left:2.5rem}.-mr-2{margin-right:-.5rem}.-ml-px{margin-left:-1px}.max-w-6xl{max-width:72rem}.min-w-full{min-width:100%}.focus\:outline-none:focus{outline:0}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.p-2{padding:.5rem}.p-4{padding:1rem}.p-8{padding:2rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-2{padding-top:.5rem}.pb-3{padding-bottom:.75rem}.relative{position:relative}.shadow-sm{box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.focus\:shadow-none:focus{box-shadow:none}.text-right{text-align:right}.text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-800{--text-opacity:1;color:#2d3748;color:rgba(45,55,72,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.text-red-500{--text-opacity:1;color:#f56565;color:rgba(245,101,101,var(--text-opacity))}.text-red-600{--text-opacity:1;color:#e53e3e;color:rgba(229,62,62,var(--text-opacity))}.text-blue-600{--text-opacity:1;color:#3182ce;color:rgba(49,130,206,var(--text-opacity))}.text-blue-700{--text-opacity:1;color:#2b6cb0;color:rgba(43,108,176,var(--text-opacity))}.hover\:text-white:hover{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.hover\:text-gray-400:hover{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.hover\:text-gray-700:hover{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.hover\:text-red-900:hover{--text-opacity:1;color:#742a2a;color:rgba(116,42,42,var(--text-opacity))}.hover\:text-blue-900:hover{--text-opacity:1;color:#2a4365;color:rgba(42,67,101,var(--text-opacity))}.focus\:text-white:focus{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.align-middle{vertical-align:middle}.whitespace-no-wrap{white-space:nowrap}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-auto{width:auto}.w-3\/12{width:25%}.w-4\/12{width:33.333333%}.w-full{width:100%}.z-0{z-index:0}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-150{transition-duration:.15s}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:flex{display:flex}.sm\:items-center{align-items:center}.sm\:justify-end{justify-content:flex-end}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-3{padding-left:.75rem;padding-right:.75rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width:768px){.md\:block{display:block}.md\:hidden{display:none}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width:1024px){.lg\:-mx-8{margin-left:-2rem;margin-right:-2rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:px-32{padding-left:8rem;padding-right:8rem}} -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/laratrust.css": "/laratrust.css?id=8c7ac59946cfc7c35e3f", 3 | "/img/logo.png": "/img/logo.png?id=b16a11382c0453bdb8a3" 4 | } 5 | -------------------------------------------------------------------------------- /resources/css/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | .nav-button,.nav-button-active { 6 | @apply px-3 py-2 rounded-md text-sm font-medium text-gray-300; 7 | } 8 | .nav-button-active { 9 | @apply text-white bg-gray-900; 10 | } 11 | 12 | .nav-button:focus, .nav-button-active:focus { 13 | @apply outline-none text-white bg-gray-700; 14 | } 15 | 16 | .nav-button:hover { 17 | @apply text-white bg-gray-700; 18 | } 19 | 20 | .th { 21 | @apply px-6 py-3 border-b border-gray-200 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider; 22 | } 23 | 24 | .td { 25 | @apply px-6 py-4 whitespace-no-wrap border-b border-gray-200; 26 | } 27 | 28 | .btn { 29 | @apply font-bold py-2 px-4 rounded; 30 | } 31 | .btn-blue { 32 | @apply bg-blue-500 text-white; 33 | } 34 | .btn-blue:hover { 35 | @apply bg-blue-700; 36 | } 37 | .btn-red { 38 | @apply bg-red-500 text-white; 39 | } 40 | .btn-red:hover { 41 | @apply bg-red-700; 42 | } 43 | 44 | .alert-warning { 45 | @apply bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4; 46 | } 47 | 48 | .alert-success { 49 | @apply bg-green-100 border-l-4 border-green-500 text-green-700 p-4; 50 | } 51 | 52 | .alert-error { 53 | @apply bg-red-100 border-l-4 border-red-500 text-red-700 p-4; 54 | } 55 | 56 | @tailwind utilities; -------------------------------------------------------------------------------- /resources/views/migration.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | use Illuminate\Database\Migrations\Migration; 5 | use Illuminate\Database\Schema\Blueprint; 6 | use Illuminate\Support\Facades\Schema; 7 | 8 | class LaratrustSetupTables extends Migration 9 | { 10 | /** 11 | * Run the migrations. 12 | * 13 | * @return void 14 | */ 15 | public function up() 16 | { 17 | // Create table for storing roles 18 | Schema::create('{{ $laratrust['tables']['roles'] }}', function (Blueprint $table) { 19 | $table->bigIncrements('id'); 20 | $table->string('name')->unique(); 21 | $table->string('display_name')->nullable(); 22 | $table->string('description')->nullable(); 23 | $table->timestamps(); 24 | }); 25 | 26 | // Create table for storing permissions 27 | Schema::create('{{ $laratrust['tables']['permissions'] }}', function (Blueprint $table) { 28 | $table->bigIncrements('id'); 29 | $table->string('name')->unique(); 30 | $table->string('display_name')->nullable(); 31 | $table->string('description')->nullable(); 32 | $table->timestamps(); 33 | }); 34 | 35 | @if ($laratrust['teams']['enabled']) 36 | // Create table for storing teams 37 | Schema::create('{{ $laratrust['tables']['teams'] }}', function (Blueprint $table) { 38 | $table->bigIncrements('id'); 39 | $table->string('name')->unique(); 40 | $table->string('display_name')->nullable(); 41 | $table->string('description')->nullable(); 42 | $table->timestamps(); 43 | }); 44 | 45 | @endif 46 | // Create table for associating roles to users and teams (Many To Many Polymorphic) 47 | Schema::create('{{ $laratrust['tables']['role_user'] }}', function (Blueprint $table) { 48 | $table->unsignedBigInteger('{{ $laratrust['foreign_keys']['role'] }}'); 49 | $table->unsignedBigInteger('{{ $laratrust['foreign_keys']['user'] }}'); 50 | $table->string('user_type'); 51 | @if ($laratrust['teams']['enabled']) 52 | $table->unsignedBigInteger('{{ $laratrust['foreign_keys']['team'] }}')->nullable(); 53 | @endif 54 | 55 | $table->foreign('{{ $laratrust['foreign_keys']['role'] }}')->references('id')->on('{{ $laratrust['tables']['roles'] }}') 56 | ->onUpdate('cascade')->onDelete('cascade'); 57 | @if ($laratrust['teams']['enabled']) 58 | $table->foreign('{{ $laratrust['foreign_keys']['team'] }}')->references('id')->on('{{ $laratrust['tables']['teams'] }}') 59 | ->onUpdate('cascade')->onDelete('cascade'); 60 | 61 | $table->unique(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['role'] }}', 'user_type', '{{ $laratrust['foreign_keys']['team'] }}']); 62 | @else 63 | 64 | $table->primary(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['role'] }}', 'user_type']); 65 | @endif 66 | }); 67 | 68 | // Create table for associating permissions to users (Many To Many Polymorphic) 69 | Schema::create('{{ $laratrust['tables']['permission_user'] }}', function (Blueprint $table) { 70 | $table->unsignedBigInteger('{{ $laratrust['foreign_keys']['permission'] }}'); 71 | $table->unsignedBigInteger('{{ $laratrust['foreign_keys']['user'] }}'); 72 | $table->string('user_type'); 73 | @if ($laratrust['teams']['enabled']) 74 | $table->unsignedBigInteger('{{ $laratrust['foreign_keys']['team'] }}')->nullable(); 75 | @endif 76 | 77 | $table->foreign('{{ $laratrust['foreign_keys']['permission'] }}')->references('id')->on('{{ $laratrust['tables']['permissions'] }}') 78 | ->onUpdate('cascade')->onDelete('cascade'); 79 | @if ($laratrust['teams']['enabled']) 80 | $table->foreign('{{ $laratrust['foreign_keys']['team'] }}')->references('id')->on('{{ $laratrust['tables']['teams'] }}') 81 | ->onUpdate('cascade')->onDelete('cascade'); 82 | 83 | $table->unique(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['permission'] }}', 'user_type', '{{ $laratrust['foreign_keys']['team'] }}']); 84 | @else 85 | 86 | $table->primary(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['permission'] }}', 'user_type']); 87 | @endif 88 | }); 89 | 90 | // Create table for associating permissions to roles (Many-to-Many) 91 | Schema::create('{{ $laratrust['tables']['permission_role'] }}', function (Blueprint $table) { 92 | $table->unsignedBigInteger('{{ $laratrust['foreign_keys']['permission'] }}'); 93 | $table->unsignedBigInteger('{{ $laratrust['foreign_keys']['role'] }}'); 94 | 95 | $table->foreign('{{ $laratrust['foreign_keys']['permission'] }}')->references('id')->on('{{ $laratrust['tables']['permissions'] }}') 96 | ->onUpdate('cascade')->onDelete('cascade'); 97 | $table->foreign('{{ $laratrust['foreign_keys']['role'] }}')->references('id')->on('{{ $laratrust['tables']['roles'] }}') 98 | ->onUpdate('cascade')->onDelete('cascade'); 99 | 100 | $table->primary(['{{ $laratrust['foreign_keys']['permission'] }}', '{{ $laratrust['foreign_keys']['role'] }}']); 101 | }); 102 | } 103 | 104 | /** 105 | * Reverse the migrations. 106 | * 107 | * @return void 108 | */ 109 | public function down() 110 | { 111 | Schema::dropIfExists('{{ $laratrust['tables']['permission_user'] }}'); 112 | Schema::dropIfExists('{{ $laratrust['tables']['permission_role'] }}'); 113 | Schema::dropIfExists('{{ $laratrust['tables']['permissions'] }}'); 114 | Schema::dropIfExists('{{ $laratrust['tables']['role_user'] }}'); 115 | Schema::dropIfExists('{{ $laratrust['tables']['roles'] }}'); 116 | @if ($laratrust['teams']['enabled']) 117 | Schema::dropIfExists('{{ $laratrust['tables']['teams'] }}'); 118 | @endif 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /resources/views/panel/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laratrust::panel.layout') 2 | 3 | @section('title', $model ? "Edit {$type}" : "New {$type}") 4 | 5 | @section('content') 6 |
7 |
8 |
9 |
10 |
getKey()) : route("laratrust.{$type}s.store")}}" 15 | class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200 p-8" 16 | > 17 | @csrf 18 | @if ($model) 19 | @method('PUT') 20 | @endif 21 | 35 | 36 | 46 | 47 | 56 | @if($type == 'role') 57 | Permissions 58 |
59 | @foreach ($permissions as $permission) 60 | 70 | @endforeach 71 |
72 | @endif 73 |
74 | 78 | Cancel 79 | 80 | 81 |
82 |
83 |
84 |
85 | 104 | @endsection -------------------------------------------------------------------------------- /resources/views/panel/layout.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Laratrust - @yield('title') 10 | 11 | 12 | 13 | 14 |
15 | 77 | 78 |
79 |
80 |

81 | @yield('title') 82 |

83 |
84 |
85 |
86 |
87 | @foreach (['error', 'warning', 'success'] as $msg) 88 | @if(Session::has('laratrust-' . $msg)) 89 | 92 | @endif 93 | @endforeach 94 |
95 | @yield('content') 96 |
97 |
98 |
99 |
100 | 101 | -------------------------------------------------------------------------------- /resources/views/panel/pagination.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | <{!!$paginator->onFirstPage() 6 | ? 'span' 7 | : 'a href="' . $paginator->previousPageUrl() . '"' 8 | !!} 9 | class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm leading-5 font-medium {{$paginator->onFirstPage() ? 'text-gray-500' : 'text-gray-700'}} hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" 10 | > 11 | 12 | 13 | 14 | Previous 15 | onFirstPage() ? 'span' : 'a' }}> 16 | <{!!$paginator->hasMorePages() 17 | ? 'a href="' . $paginator->nextPageUrl() . '"' 18 | : 'span' 19 | !!} 20 | class="-ml-px relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm leading-5 font-medium {{$paginator->hasMorePages() ? 'text-gray-700' : 'text-gray-500'}} hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" 21 | > 22 | Next 23 | 24 | 25 | 26 | hasMorePages() ? 'a' : 'span' }}> 27 | 28 |
29 |
30 |
-------------------------------------------------------------------------------- /resources/views/panel/permissions/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laratrust::panel.layout') 2 | 3 | @section('title', 'Permissions') 4 | 5 | @section('content') 6 |
7 | @if (config('laratrust.panel.create_permissions')) 8 | 12 | + New Permission 13 | 14 | @endif 15 |
16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | @foreach ($permissions as $permission) 29 | 30 | 33 | 36 | 39 | 42 | 45 | 46 | @endforeach 47 | 48 |
IdName/CodeDisplay NameDescription
31 | {{$permission->getKey()}} 32 | 34 | {{$permission->name}} 35 | 37 | {{$permission->display_name}} 38 | 40 | {{$permission->description}} 41 | 43 | Edit 44 |
49 |
50 |
51 |
52 | {{ $permissions->links('laratrust::panel.pagination') }} 53 | @endsection -------------------------------------------------------------------------------- /resources/views/panel/roles-assignment/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laratrust::panel.layout') 2 | 3 | @section('title', "Edit {$modelKey}") 4 | 5 | @section('content') 6 |
7 |
8 |
9 |
10 |
15 | @csrf 16 | @method('PUT') 17 | 28 | Roles 29 |
30 | @foreach ($roles as $role) 31 | 48 | @endforeach 49 |
50 | @if ($permissions) 51 | Permissions 52 |
53 | @foreach ($permissions as $permission) 54 | 64 | @endforeach 65 |
66 | @endif 67 | 76 |
77 |
78 |
79 | @endsection -------------------------------------------------------------------------------- /resources/views/panel/roles-assignment/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laratrust::panel.layout') 2 | 3 | @section('title', 'Roles Assignment') 4 | 5 | @section('content') 6 |
7 |
8 |
13 | User model to assign roles/permissions 14 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | @if(config('laratrust.panel.assign_permissions_to_user'))@endif 30 | 31 | 32 | 33 | 34 | @foreach ($users as $user) 35 | 36 | 39 | 42 | 45 | @if(config('laratrust.panel.assign_permissions_to_user')) 46 | 49 | @endif 50 | 56 | 57 | @endforeach 58 | 59 |
IdName# Roles# Permissions
37 | {{$user->getKey()}} 38 | 40 | {{$user->name ?? 'The model doesn\'t have a `name` attribute'}} 41 | 43 | {{$user->roles_count}} 44 | 47 | {{$user->permissions_count}} 48 | 51 | Edit 55 |
60 |
61 | @if ($modelKey) 62 | {{ $users->appends(['model' => $modelKey])->links('laratrust::panel.pagination') }} 63 | @endif 64 | 65 |
66 |
67 |
68 | @endsection 69 | -------------------------------------------------------------------------------- /resources/views/panel/roles/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laratrust::panel.layout') 2 | 3 | @section('title', 'Roles') 4 | 5 | @section('content') 6 |
7 | 11 | + New Role 12 | 13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | @foreach ($roles as $role) 27 | 28 | 31 | 34 | 37 | 40 | 60 | 61 | @endforeach 62 | 63 |
IdDisplay NameName# Permissions
29 | {{$role->getKey()}} 30 | 32 | {{$role->display_name}} 33 | 35 | {{$role->name}} 36 | 38 | {{$role->permissions_count}} 39 | 41 | @if (\Laratrust\Helper::roleIsEditable($role)) 42 | Edit 43 | @else 44 | Details 45 | @endif 46 |
51 | @method('DELETE') 52 | @csrf 53 | 58 |
59 |
64 |
65 |
66 |
67 | {{ $roles->links('laratrust::panel.pagination') }} 68 | @endsection 69 | -------------------------------------------------------------------------------- /resources/views/panel/roles/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laratrust::panel.layout') 2 | 3 | @section('title', "Role details") 4 | 5 | @section('content') 6 |
7 |
8 |
11 | 15 | 16 | 20 | 21 | 25 | Permissions: 26 |
    27 | @foreach ($role->permissions as $permission) 28 |
  • {{$permission->display_name ?? $permission->name}}
  • 29 | @endforeach 30 |
31 | 39 |
40 |
41 |
42 | @endsection -------------------------------------------------------------------------------- /resources/views/setup-teams.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | use Illuminate\Database\Migrations\Migration; 4 | use Illuminate\Database\Schema\Blueprint; 5 | use Illuminate\Support\Facades\Schema; 6 | 7 | class LaratrustSetupTeams extends Migration 8 | { 9 | /** 10 | * Run the migrations. 11 | * 12 | * @return void 13 | */ 14 | public function up() 15 | { 16 | // Create table for storing teams 17 | Schema::create('{{ $laratrust['tables']['teams'] }}', function (Blueprint $table) { 18 | $table->increments('id'); 19 | $table->string('name')->unique(); 20 | $table->string('display_name')->nullable(); 21 | $table->string('description')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | 25 | Schema::table('{{ $laratrust['tables']['role_user'] }}', function (Blueprint $table) { 26 | // Drop role foreign key and primary key 27 | $table->dropForeign(['{{ $laratrust['foreign_keys']['role'] }}']); 28 | $table->dropPrimary(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['role'] }}', 'user_type']); 29 | 30 | // Add {{ $laratrust['foreign_keys']['team'] }} column 31 | $table->unsignedInteger('{{ $laratrust['foreign_keys']['team'] }}')->nullable(); 32 | 33 | // Create foreign keys 34 | $table->foreign('{{ $laratrust['foreign_keys']['role'] }}')->references('id')->on('{{ $laratrust['tables']['roles'] }}') 35 | ->onUpdate('cascade')->onDelete('cascade'); 36 | $table->foreign('{{ $laratrust['foreign_keys']['team'] }}')->references('id')->on('{{ $laratrust['tables']['teams'] }}') 37 | ->onUpdate('cascade')->onDelete('cascade'); 38 | 39 | // Create a unique key 40 | $table->unique(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['role'] }}', 'user_type', '{{ $laratrust['foreign_keys']['team'] }}']); 41 | }); 42 | 43 | Schema::table('{{ $laratrust['tables']['permission_user'] }}', function (Blueprint $table) { 44 | // Drop permission foreign key and primary key 45 | $table->dropForeign(['{{ $laratrust['foreign_keys']['permission'] }}']); 46 | $table->dropPrimary(['{{ $laratrust['foreign_keys']['permission'] }}', '{{ $laratrust['foreign_keys']['user'] }}', 'user_type']); 47 | 48 | $table->foreign('{{ $laratrust['foreign_keys']['permission'] }}')->references('id')->on('{{ $laratrust['tables']['permissions'] }}') 49 | ->onUpdate('cascade')->onDelete('cascade'); 50 | 51 | // Add {{ $laratrust['foreign_keys']['team'] }} column 52 | $table->unsignedInteger('{{ $laratrust['foreign_keys']['team'] }}')->nullable(); 53 | 54 | $table->foreign('{{ $laratrust['foreign_keys']['team'] }}')->references('id')->on('{{ $laratrust['tables']['teams'] }}') 55 | ->onUpdate('cascade')->onDelete('cascade'); 56 | 57 | $table->unique(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['permission'] }}', 'user_type', '{{ $laratrust['foreign_keys']['team'] }}']); 58 | }); 59 | } 60 | 61 | /** 62 | * Reverse the migrations. 63 | * 64 | * @return void 65 | */ 66 | public function down() 67 | { 68 | Schema::table('{{ $laratrust['tables']['role_user'] }}', function (Blueprint $table) { 69 | $table->dropForeign(['{{ $laratrust['foreign_keys']['team'] }}']); 70 | }); 71 | 72 | Schema::table('{{ $laratrust['tables']['permission_user'] }}', function (Blueprint $table) { 73 | $table->dropForeign(['{{ $laratrust['foreign_keys']['team'] }}']); 74 | }); 75 | 76 | Schema::dropIfExists('{{ $laratrust['tables']['teams'] }}'); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /resources/views/upgrade-migration.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | use Illuminate\Database\Migrations\Migration; 4 | use Illuminate\Database\Schema\Blueprint; 5 | use Illuminate\Support\Facades\Schema; 6 | 7 | class LaratrustUpgradeTables extends Migration 8 | { 9 | /** 10 | * Run the migrations. 11 | * 12 | * @return void 13 | */ 14 | public function up() 15 | { 16 | @if ($laratrust['teams']['enabled']) 17 | // Create table for storing teams 18 | Schema::create('{{ $laratrust['tables']['teams'] }}', function (Blueprint $table) { 19 | $table->increments('id'); 20 | $table->string('name')->unique(); 21 | $table->string('display_name')->nullable(); 22 | $table->string('description')->nullable(); 23 | $table->timestamps(); 24 | }); 25 | 26 | Schema::table('{{ $laratrust['tables']['role_user'] }}', function (Blueprint $table) { 27 | // Drop role foreign key and primary key 28 | $table->dropForeign(['{{ $laratrust['foreign_keys']['role'] }}']); 29 | $table->dropPrimary(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['role'] }}', 'user_type']); 30 | 31 | // Add {{ $laratrust['foreign_keys']['team'] }} column 32 | $table->unsignedInteger('{{ $laratrust['foreign_keys']['team'] }}')->nullable(); 33 | 34 | // Create foreign keys 35 | $table->foreign('{{ $laratrust['foreign_keys']['role'] }}')->references('id')->on('{{ $laratrust['tables']['roles'] }}') 36 | ->onUpdate('cascade')->onDelete('cascade'); 37 | $table->foreign('{{ $laratrust['foreign_keys']['team'] }}')->references('id')->on('{{ $laratrust['tables']['teams'] }}') 38 | ->onUpdate('cascade')->onDelete('cascade'); 39 | 40 | // Create a unique key 41 | $table->unique(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['role'] }}', 'user_type', '{{ $laratrust['foreign_keys']['team'] }}']); 42 | }); 43 | 44 | @endif 45 | Schema::table('{{ $laratrust['tables']['permission_user'] }}', function (Blueprint $table) { 46 | // Drop permission foreign key and primary key 47 | $table->dropForeign(['{{ $laratrust['foreign_keys']['permission'] }}']); 48 | $table->dropPrimary(['{{ $laratrust['foreign_keys']['permission'] }}', '{{ $laratrust['foreign_keys']['user'] }}', 'user_type']); 49 | 50 | $table->foreign('{{ $laratrust['foreign_keys']['permission'] }}')->references('id')->on('{{ $laratrust['tables']['permissions'] }}') 51 | ->onUpdate('cascade')->onDelete('cascade'); 52 | 53 | @if ($laratrust['teams']['enabled']) 54 | // Add {{ $laratrust['foreign_keys']['team'] }} column 55 | $table->unsignedInteger('{{ $laratrust['foreign_keys']['team'] }}')->nullable(); 56 | 57 | $table->foreign('{{ $laratrust['foreign_keys']['team'] }}')->references('id')->on('{{ $laratrust['tables']['teams'] }}') 58 | ->onUpdate('cascade')->onDelete('cascade'); 59 | 60 | $table->unique(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['permission'] }}', 'user_type', '{{ $laratrust['foreign_keys']['team'] }}']); 61 | @else 62 | $table->primary(['{{ $laratrust['foreign_keys']['user'] }}', '{{ $laratrust['foreign_keys']['permission'] }}', 'user_type']); 63 | @endif 64 | }); 65 | } 66 | 67 | /** 68 | * Reverse the migrations. 69 | * 70 | * @return void 71 | */ 72 | public function down() 73 | { 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | 'laratrust']) 6 | ->only(['index', 'create', 'store', 'edit', 'update']); 7 | 8 | Route::resource('/roles', 'RolesController', ['as' => 'laratrust']); 9 | 10 | Route::resource('/roles-assignment', 'RolesAssignmentController', ['as' => 'laratrust']) 11 | ->only(['index', 'edit', 'update']); 12 | -------------------------------------------------------------------------------- /src/Checkers/CheckersManager.php: -------------------------------------------------------------------------------- 1 | model); 34 | case 'query': 35 | return new UserQueryChecker($this->model); 36 | default: 37 | if (! is_a($checker, UserChecker::class, true)) { 38 | throw new \RuntimeException('User checker must extend UserChecker'); 39 | } 40 | 41 | return app()->make($checker, ['user' => $this->model]); 42 | } 43 | } 44 | 45 | /** 46 | * Return the right checker according to the configuration. 47 | */ 48 | public function getRoleChecker(): RoleChecker 49 | { 50 | $checker = Config::get('laratrust.checkers.role', Config::get('laratrust.checker', 'default')); 51 | 52 | switch ($checker) { 53 | case 'default': 54 | return new RoleDefaultChecker($this->model); 55 | case 'query': 56 | return new RoleQueryChecker($this->model); 57 | default: 58 | if (! is_a($checker, RoleChecker::class, true)) { 59 | throw new \RuntimeException('Role checker must extend RoleChecker'); 60 | } 61 | 62 | return app()->make($checker, ['role' => $this->model]); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Checkers/Role/RoleChecker.php: -------------------------------------------------------------------------------- 1 | currentRoleHasPermission($permissionName); 27 | 28 | if ($hasPermission && ! $requireAll) { 29 | return true; 30 | } elseif (! $hasPermission && $requireAll) { 31 | return false; 32 | } 33 | } 34 | 35 | // If we've made it this far and $requireAll is FALSE, then NONE of the permissions were found. 36 | // If we've made it this far and $requireAll is TRUE, then ALL of the permissions were found. 37 | // Return the value of $requireAll. 38 | return $requireAll; 39 | } 40 | 41 | foreach ($this->currentRoleCachedPermissions() as $perm) { 42 | if (Str::is(Helper::ensureString($permission), $perm['name'])) { 43 | return true; 44 | } 45 | } 46 | 47 | return false; 48 | } 49 | 50 | /** 51 | * Flush the role's cache. 52 | */ 53 | public function currentRoleFlushCache(): void 54 | { 55 | Cache::forget('laratrust_permissions_for_role_'.$this->role->getKey()); 56 | } 57 | 58 | /** 59 | * Tries to return all the cached permissions of the role. 60 | * If it can't bring the permissions from the cache, 61 | * it brings them back from the DB. 62 | */ 63 | public function currentRoleCachedPermissions(): array 64 | { 65 | $cacheKey = 'laratrust_permissions_for_role_'.$this->role->getKey(); 66 | 67 | if (! Config::get('laratrust.cache.enabled')) { 68 | return $this->role->permissions()->get()->toArray(); 69 | } 70 | 71 | return Cache::remember($cacheKey, Config::get('laratrust.cache.expiration_time', 60), function () { 72 | return $this->role->permissions()->get()->toArray(); 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Checkers/Role/RoleQueryChecker.php: -------------------------------------------------------------------------------- 1 | role->permissions() 28 | ->whereIn('name', $permissionsNoWildcard) 29 | ->when($permissionsWildcard, function ($query) use ($permissionsWildcard) { 30 | foreach ($permissionsWildcard as $permission) { 31 | $query->orWhere('name', 'like', $permission); 32 | } 33 | 34 | return $query; 35 | }) 36 | ->count(); 37 | 38 | return $requireAll 39 | ? $permissionsCount >= count($permissionsNames) 40 | : $permissionsCount > 0; 41 | } 42 | 43 | /** 44 | * Flush the role's cache. 45 | */ 46 | public function currentRoleFlushCache(): void 47 | { 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Checkers/User/UserChecker.php: -------------------------------------------------------------------------------- 1 | ($methodToEvaluate($team) ? null : $team), 56 | 'options' => $requireAllOrOptions, 57 | 'require_all' => $requireAllOrOptions, 58 | ]; 59 | } 60 | 61 | /** 62 | * Checks role(s) and permission(s). 63 | * 64 | * @param array $options validate_all (true|false) or return_type (boolean|array|both) 65 | * 66 | * @throws \InvalidArgumentException 67 | */ 68 | public function currentUserHasAbility( 69 | string|array|BackedEnum $roles, 70 | string|array|BackedEnum $permissions, 71 | mixed $team = null, 72 | array $options = [] 73 | ): array|bool { 74 | ['team' => $team, 'options' => $options] = $this->getRealValues($team, $options, 'is_array'); 75 | // Convert string to array if that's what is passed in. 76 | $roles = Helper::standardize($roles, true); 77 | $permissions = Helper::standardize($permissions, true); 78 | 79 | // Setup default values and validate options. 80 | $options = $this->validateAndSetOptions($options, [ 81 | 'validate_all' => [ 82 | 'available' => [false, true], 83 | 'default' => false, 84 | ], 85 | 'return_type' => [ 86 | 'available' => ['boolean', 'array', 'both'], 87 | 'default' => 'boolean', 88 | ], 89 | ]); 90 | 91 | if ($options['return_type'] == 'boolean') { 92 | $hasRoles = $this->currentUserHasRole($roles, $team, $options['validate_all']); 93 | $hasPermissions = $this->currentUserHasPermission($permissions, $team, $options['validate_all']); 94 | 95 | return $options['validate_all'] 96 | ? $hasRoles && $hasPermissions 97 | : $hasRoles || $hasPermissions; 98 | } 99 | 100 | // Loop through roles and permissions and check each. 101 | $checkedRoles = []; 102 | $checkedPermissions = []; 103 | foreach ($roles as $role) { 104 | $checkedRoles[$role] = $this->currentUserHasRole($role, $team); 105 | } 106 | foreach ($permissions as $permission) { 107 | $checkedPermissions[$permission] = $this->currentUserHasPermission($permission, $team); 108 | } 109 | 110 | // If validate all and there is a false in either. 111 | // Check that if validate all, then there should not be any false. 112 | // Check that if not validate all, there must be at least one true. 113 | if (($options['validate_all'] && ! (in_array(false, $checkedRoles) || in_array(false, $checkedPermissions))) || (! $options['validate_all'] && (in_array(true, $checkedRoles) || in_array(true, $checkedPermissions)))) { 114 | $validateAll = true; 115 | } else { 116 | $validateAll = false; 117 | } 118 | 119 | // Return based on option. 120 | if ($options['return_type'] == 'array') { 121 | return ['roles' => $checkedRoles, 'permissions' => $checkedPermissions]; 122 | } 123 | 124 | return [$validateAll, ['roles' => $checkedRoles, 'permissions' => $checkedPermissions]]; 125 | } 126 | 127 | /** 128 | * Checks if the option exists inside the array, 129 | * otherwise, it sets the first option inside the default values array. 130 | */ 131 | private function validateAndSetOptions(array $options, array $toVerify): array 132 | { 133 | foreach ($toVerify as $option => $config) { 134 | if (! isset($options[$option])) { 135 | $options[$option] = $config['default']; 136 | 137 | continue; 138 | } 139 | 140 | $ignoredOptions = ['team', 'foreignKeyName']; 141 | 142 | if ( 143 | ! in_array($option, $ignoredOptions) 144 | && ! in_array($options[$option], $config['available'], true) 145 | ) { 146 | throw new InvalidArgumentException(); 147 | } 148 | } 149 | 150 | return $options; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/Checkers/User/UserDefaultChecker.php: -------------------------------------------------------------------------------- 1 | userCachedRoles()); 24 | 25 | if ( 26 | Config::get('laratrust.teams.enabled') === false || 27 | ($team === null && Config::get('laratrust.teams.strict_check') === false) 28 | ) { 29 | return $roles->pluck('name')->toArray(); 30 | } 31 | 32 | if ($team === null) { 33 | return $roles->filter(function ($role) { 34 | return $role['pivot'][Team::modelForeignKey()] === null; 35 | })->pluck('name')->toArray(); 36 | } 37 | 38 | $teamId = Helper::getIdFor($team, 'team'); 39 | 40 | return $roles 41 | ->filter(fn ($role) => $role['pivot'][Team::modelForeignKey()] == $teamId) 42 | ->pluck('name') 43 | ->toArray(); 44 | } 45 | 46 | public function currentUserHasRole( 47 | string|array|BackedEnum $name, 48 | mixed $team = null, 49 | bool $requireAll = false 50 | ): bool { 51 | $name = Helper::standardize($name); 52 | [ 53 | 'team' => $team, 54 | 'require_all' => $requireAll 55 | ] = $this->getRealValues($team, $requireAll, 'is_bool'); 56 | 57 | if (is_array($name)) { 58 | if (empty($name)) { 59 | return true; 60 | } 61 | 62 | foreach ($name as $roleName) { 63 | $hasRole = $this->currentUserHasRole($roleName, $team); 64 | 65 | if ($hasRole && ! $requireAll) { 66 | return true; 67 | } elseif (! $hasRole && $requireAll) { 68 | return false; 69 | } 70 | } 71 | 72 | // If we've made it this far and $requireAll is FALSE, then NONE of the roles were found. 73 | // If we've made it this far and $requireAll is TRUE, then ALL of the roles were found. 74 | // Return the value of $requireAll. 75 | return $requireAll; 76 | } 77 | 78 | $teamId = Helper::getIdFor($team, 'team'); 79 | 80 | foreach ($this->userCachedRoles() as $role) { 81 | if ($role['name'] == $name && $this->isInSameTeam($role, $teamId)) { 82 | return true; 83 | } 84 | } 85 | 86 | return false; 87 | } 88 | 89 | public function currentUserHasPermission( 90 | string|array|BackedEnum $permission, 91 | mixed $team = null, 92 | bool $requireAll = false 93 | ): bool { 94 | $permission = Helper::standardize($permission); 95 | [ 96 | 'team' => $team, 97 | 'require_all' => $requireAll 98 | ] = $this->getRealValues($team, $requireAll, 'is_bool'); 99 | 100 | if (is_array($permission)) { 101 | if (empty($permission)) { 102 | return true; 103 | } 104 | 105 | foreach ($permission as $permissionName) { 106 | $hasPermission = $this->currentUserHasPermission($permissionName, $team); 107 | 108 | if ($hasPermission && ! $requireAll) { 109 | return true; 110 | } elseif (! $hasPermission && $requireAll) { 111 | return false; 112 | } 113 | } 114 | 115 | // If we've made it this far and $requireAll is FALSE, then NONE of the perms were found. 116 | // If we've made it this far and $requireAll is TRUE, then ALL of the perms were found. 117 | // Return the value of $requireAll. 118 | return $requireAll; 119 | } 120 | 121 | $teamId = Helper::getIdFor($team, 'team'); 122 | 123 | foreach ($this->userCachedPermissions() as $perm) { 124 | if ($this->isInSameTeam($perm, $teamId) && Str::is($permission, $perm['name'])) { 125 | return true; 126 | } 127 | } 128 | 129 | foreach ($this->userCachedRoles() as $role) { 130 | $role = $this->hidrateRole(Config::get('laratrust.models.role'), $role); 131 | 132 | if ($this->isInSameTeam($role, $teamId) && $role->hasPermission($permission)) { 133 | return true; 134 | } 135 | } 136 | 137 | return false; 138 | } 139 | 140 | public function currentUserFlushCache() 141 | { 142 | Cache::forget('laratrust_roles_for_'.$this->userModelCacheKey().'_'.$this->user->getKey()); 143 | Cache::forget('laratrust_permissions_for_'.$this->userModelCacheKey().'_'.$this->user->getKey()); 144 | } 145 | 146 | /** 147 | * Tries to return all the cached roles of the user. 148 | * If it can't bring the roles from the cache, 149 | * it brings them back from the DB. 150 | */ 151 | protected function userCachedRoles(): array 152 | { 153 | $cacheKey = 'laratrust_roles_for_'.$this->userModelCacheKey().'_'.$this->user->getKey(); 154 | 155 | if (! Config::get('laratrust.cache.enabled')) { 156 | return $this->user->roles()->get()->toArray(); 157 | } 158 | 159 | return Cache::remember($cacheKey, Config::get('laratrust.cache.expiration_time', 60), function () { 160 | return $this->user->roles()->get()->toArray(); 161 | }); 162 | } 163 | 164 | /** 165 | * Tries to return all the cached permissions of the user 166 | * and if it can't bring the permissions from the cache, 167 | * it brings them back from the DB. 168 | */ 169 | public function userCachedPermissions(): array 170 | { 171 | $cacheKey = 'laratrust_permissions_for_'.$this->userModelCacheKey().'_'.$this->user->getKey(); 172 | 173 | if (! Config::get('laratrust.cache.enabled')) { 174 | return $this->user->permissions()->get()->toArray(); 175 | } 176 | 177 | return Cache::remember($cacheKey, Config::get('laratrust.cache.expiration_time', 60), function () { 178 | return $this->user->permissions()->get()->toArray(); 179 | }); 180 | } 181 | 182 | /** 183 | * Tries return key name for user_models. 184 | * 185 | * @return string|void default key user 186 | */ 187 | public function userModelCacheKey(): string 188 | { 189 | foreach (Config::get('laratrust.user_models') as $key => $model) { 190 | if ($this->user instanceof $model) { 191 | return $key; 192 | } 193 | } 194 | 195 | $modelClass = get_class($this); 196 | 197 | throw new UnexpectedValueException("Class '{$modelClass}' is not defined in the laratrust.user_models"); 198 | } 199 | 200 | /** 201 | * Creates a model from an array filled with the class data. 202 | */ 203 | private function hidrateRole(string $class, Model|array $data): Role 204 | { 205 | if ($data instanceof Model) { 206 | return $data; 207 | } 208 | 209 | if (! isset($data['pivot'])) { 210 | throw new \Exception("The 'pivot' attribute in the {$class} is hidden"); 211 | } 212 | 213 | $role = new $class; 214 | $primaryKey = $role->getKeyName(); 215 | 216 | $role 217 | ->setAttribute($primaryKey, $data[$primaryKey]) 218 | ->setAttribute('name', $data['name']) 219 | ->setRelation( 220 | 'pivot', 221 | MorphPivot::fromRawAttributes($role, $data['pivot'], 'pivot_table') 222 | ); 223 | 224 | return $role; 225 | } 226 | 227 | /** 228 | * Check if a role or permission is added to the user in a same team. 229 | */ 230 | private function isInSameTeam($rolePermission, int|string|null $teamId = null): bool 231 | { 232 | if ( 233 | ! Config::get('laratrust.teams.enabled') 234 | || (! Config::get('laratrust.teams.strict_check') && ! $teamId) 235 | ) { 236 | return true; 237 | } 238 | 239 | return $rolePermission['pivot'][Team::modelForeignKey()] == $teamId; 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/Checkers/User/UserQueryChecker.php: -------------------------------------------------------------------------------- 1 | user->roles->pluck('name')->toArray(); 17 | } 18 | 19 | if ($team === null && config('laratrust.teams.strict_check') === false) { 20 | return $this->user->roles->pluck('name')->toArray(); 21 | } 22 | 23 | $teamId = Helper::getIdFor($team, 'team'); 24 | 25 | return $this->user 26 | ->roles() 27 | ->wherePivot(config('laratrust.foreign_keys.team'), $teamId) 28 | ->pluck('name') 29 | ->toArray(); 30 | } 31 | 32 | public function currentUserHasRole( 33 | string|array|BackedEnum $name, 34 | mixed $team = null, 35 | bool $requireAll = false 36 | ): bool { 37 | if (empty($name)) { 38 | return true; 39 | } 40 | 41 | $name = Helper::standardize($name); 42 | $rolesNames = is_array($name) ? $name : [$name]; 43 | [ 44 | 'team' => $team, 45 | 'require_all' => $requireAll 46 | ] = $this->getRealValues($team, $requireAll, 'is_bool'); 47 | $useTeams = Config::get('laratrust.teams.enabled'); 48 | $teamStrictCheck = Config::get('laratrust.teams.strict_check'); 49 | 50 | $rolesCount = $this->user->roles() 51 | ->whereIn('name', $rolesNames) 52 | ->when($useTeams && ($teamStrictCheck || ! is_null($team)), function ($query) use ($team) { 53 | $teamId = Helper::getIdFor($team, 'team'); 54 | 55 | return $query->where(Config::get('laratrust.foreign_keys.team'), $teamId); 56 | }) 57 | ->count(); 58 | 59 | return $requireAll ? $rolesCount == count($rolesNames) : $rolesCount > 0; 60 | } 61 | 62 | public function currentUserHasPermission( 63 | string|array|BackedEnum $permission, 64 | mixed $team = null, 65 | bool $requireAll = false 66 | ): bool { 67 | if (empty($permission)) { 68 | return true; 69 | } 70 | 71 | $permission = Helper::standardize($permission); 72 | $permissionsNames = is_array($permission) ? $permission : [$permission]; 73 | [ 74 | 'team' => $team, 75 | 'require_all' => $requireAll 76 | ] = $this->getRealValues($team, $requireAll, 'is_bool'); 77 | $useTeams = Config::get('laratrust.teams.enabled'); 78 | $teamStrictCheck = Config::get('laratrust.teams.strict_check'); 79 | 80 | [$permissionsWildcard, $permissionsNoWildcard] = 81 | Helper::getPermissionWithAndWithoutWildcards($permissionsNames); 82 | 83 | $rolesPermissionsCount = $this->user->roles() 84 | ->withCount(['permissions' => function ($query) use ($permissionsNoWildcard, $permissionsWildcard) { 85 | $query->whereIn('name', $permissionsNoWildcard); 86 | foreach ($permissionsWildcard as $permission) { 87 | $query->orWhere('name', 'like', $permission); 88 | } 89 | }, 90 | ]) 91 | ->when($useTeams && ($teamStrictCheck || ! is_null($team)), function ($query) use ($team) { 92 | $teamId = Helper::getIdFor($team, 'team'); 93 | 94 | return $query->where(Config::get('laratrust.foreign_keys.team'), $teamId); 95 | }) 96 | ->pluck('permissions_count') 97 | ->sum(); 98 | 99 | $directPermissionsCount = $this->user->permissions() 100 | ->whereIn('name', $permissionsNoWildcard) 101 | ->when($permissionsWildcard, function ($query) use ($permissionsWildcard) { 102 | foreach ($permissionsWildcard as $permission) { 103 | $query->orWhere('name', 'like', $permission); 104 | } 105 | 106 | return $query; 107 | }) 108 | ->when($useTeams && ($teamStrictCheck || ! is_null($team)), function ($query) use ($team) { 109 | $teamId = Helper::getIdFor($team, 'team'); 110 | 111 | return $query->where(Config::get('laratrust.foreign_keys.team'), $teamId); 112 | }) 113 | ->count(); 114 | 115 | return $requireAll 116 | ? $rolesPermissionsCount + $directPermissionsCount >= count($permissionsNames) 117 | : $rolesPermissionsCount + $directPermissionsCount > 0; 118 | } 119 | 120 | public function currentUserFlushCache() 121 | { 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/Console/AddLaratrustUserTraitUseCommand.php: -------------------------------------------------------------------------------- 1 | getUserModels(); 35 | 36 | foreach ($models as $model) { 37 | if (! class_exists($model)) { 38 | $this->error("Class $model does not exist."); 39 | 40 | return; 41 | } 42 | 43 | if ($this->alreadyUsesLaratrustUserTrait($model)) { 44 | $this->error("Class $model already uses LaratrustUserTrait."); 45 | continue; 46 | } 47 | 48 | Traitor::addTrait($this->targetTrait)->toClass($model); 49 | } 50 | 51 | $this->info("LaratrustUserTrait added successfully to {$models->implode(', ')}"); 52 | } 53 | 54 | /** 55 | * Check if the class already uses LaratrustUserTrait. 56 | * 57 | * @param string $model 58 | * @return bool 59 | */ 60 | protected function alreadyUsesLaratrustUserTrait($model) 61 | { 62 | return in_array(LaratrustUserTrait::class, class_uses($model)); 63 | } 64 | 65 | /** 66 | * Get the description of which clases the LaratrustUserTrait was added. 67 | * 68 | * @return string 69 | */ 70 | public function getDescription(): string 71 | { 72 | return "Add LaratrustUserTrait to {$this->getUserModels()->implode(', ')} class"; 73 | } 74 | 75 | /** 76 | * Return the User models array. 77 | * 78 | * @return array 79 | */ 80 | protected function getUserModels() 81 | { 82 | return new Collection(Config::get('laratrust.user_models', [])); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Console/MakePermissionCommand.php: -------------------------------------------------------------------------------- 1 | seederPath())) { 34 | $this->line(''); 35 | 36 | $this->warn('The LaratrustSeeder file already exists. Delete the existing one if you want to create a new one.'); 37 | $this->line(''); 38 | 39 | return; 40 | } 41 | 42 | try { 43 | $seederClass = $this->createSeederClass(); 44 | $this->files->put($this->seederPath(), $seederClass); 45 | $this->info('Seeder successfully created!'); 46 | } catch (Exception $exception) { 47 | $folder = $this->files->dirname($this->seederPath()); 48 | $where = substr($folder, strpos($folder, 'database')); 49 | 50 | $this->error( 51 | "Couldn't create seeder.\n". 52 | "Check the write permissions within the $where directory." 53 | ); 54 | } 55 | 56 | $this->line(''); 57 | } 58 | 59 | /** 60 | * Create the seeder. 61 | * 62 | * @return string 63 | */ 64 | protected function createSeederClass(): string 65 | { 66 | $stub = $this->files->get($this->getStub()); 67 | 68 | $this->replaceSeederNamespace($stub); 69 | $this->replaceModelClassNames($stub); 70 | $this->replaceTableNames($stub); 71 | 72 | return $stub; 73 | } 74 | 75 | /** 76 | * Replace the namespace of the seeder. 77 | * 78 | * @param string $stub 79 | */ 80 | protected function replaceSeederNamespace(string &$stub) 81 | { 82 | $namespace = ''; 83 | 84 | if (version_compare($this->getLaravel()->version(), '8.0') >= 0) { 85 | $namespace = "\nnamespace Database\Seeders;\n"; 86 | } 87 | 88 | $this->replaceStubParameter($stub, 'namespace', $namespace); 89 | } 90 | 91 | /** 92 | * Replace the models class names in the stub. 93 | * 94 | * @param string $stub 95 | */ 96 | protected function replaceModelClassNames(string &$stub) 97 | { 98 | $role = Config::get('laratrust.models.role', 'App\Role'); 99 | $this->replaceStubParameter($stub, 'roleConfiguredModelClass', '\\'.ltrim($role, '\\')); 100 | 101 | $permission = Config::get('laratrust.models.permission', 'App\Permission'); 102 | $this->replaceStubParameter($stub, 'permissionConfiguredModelClass', '\\'.ltrim($permission, '\\')); 103 | 104 | $user = new Collection(Config::get('laratrust.user_models', ['App\User'])); 105 | $user = $user->first(); 106 | $this->replaceStubParameter($stub, 'userConfiguredModelClass', '\\'.ltrim($user, '\\')); 107 | } 108 | 109 | /** 110 | * Replace the table names in the stub. 111 | * 112 | * @param string $stub 113 | */ 114 | protected function replaceTableNames(string &$stub) 115 | { 116 | $rolePermission = Config::get('laratrust.tables.permission_role'); 117 | $this->replaceStubParameter($stub, 'permission_roleConfiguredTableName', $rolePermission); 118 | 119 | $permissionUser = Config::get('laratrust.tables.permission_user'); 120 | $this->replaceStubParameter($stub, 'permission_userConfiguredTableName', $permissionUser); 121 | 122 | $roleUser = Config::get('laratrust.tables.role_user'); 123 | $this->replaceStubParameter($stub, 'role_userConfiguredTableName', $roleUser); 124 | 125 | $rolesTable = Config::get('laratrust.tables.roles'); 126 | $this->replaceStubParameter($stub, 'rolesTableName', $rolesTable); 127 | 128 | $permissionsTable = Config::get('laratrust.tables.permissions'); 129 | $this->replaceStubParameter($stub, 'permissionsTableName', $permissionsTable); 130 | } 131 | 132 | /** 133 | * Replace a placeholder parameter in the stub. 134 | * 135 | * @param string $stub 136 | * @param string $parameter 137 | * @param string $value 138 | */ 139 | protected function replaceStubParameter(string &$stub, string $parameter, string $value) 140 | { 141 | $stub = str_replace('{{'.$parameter.'}}', $value, $stub); 142 | } 143 | 144 | /** 145 | * Get the seeder stub file. 146 | * 147 | * @return string 148 | */ 149 | protected function getStub() 150 | { 151 | return $this->resolveStubPath('/stubs/seeder.stub'); 152 | } 153 | 154 | /** 155 | * Resolve the fully-qualified path to the stub. 156 | * 157 | * @param string $stub 158 | * @return string 159 | */ 160 | protected function resolveStubPath($stub) 161 | { 162 | return __DIR__."/../../$stub"; 163 | } 164 | 165 | /** 166 | * Get the seeder path. 167 | * 168 | * @return string 169 | */ 170 | protected function seederPath() 171 | { 172 | return $this->getPath('LaratrustSeeder'); 173 | } 174 | 175 | /** 176 | * Get the console command arguments. 177 | * 178 | * @return array 179 | */ 180 | protected function getArguments() 181 | { 182 | return []; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/Console/MakeTeamCommand.php: -------------------------------------------------------------------------------- 1 | line(''); 40 | $this->info('Laratrust Migration Creation.'); 41 | if (Config::get('laratrust.teams.enabled')) { 42 | $this->comment('You are using the teams feature.'); 43 | } 44 | $this->line(''); 45 | $this->comment($this->generateMigrationMessage()); 46 | 47 | $existingMigrations = $this->alreadyExistingMigrations(); 48 | $defaultAnswer = true; 49 | 50 | if ($existingMigrations) { 51 | $this->line(''); 52 | 53 | $this->warn($this->getExistingMigrationsWarning($existingMigrations)); 54 | 55 | $defaultAnswer = false; 56 | } 57 | 58 | $this->line(''); 59 | 60 | if (! $this->confirm('Proceed with the migration creation?', $defaultAnswer)) { 61 | return; 62 | } 63 | 64 | $this->line(''); 65 | 66 | $this->line('Creating migration'); 67 | 68 | if ($this->createMigration()) { 69 | $this->info('Migration created successfully.'); 70 | } else { 71 | $this->error( 72 | "Couldn't create migration.\n". 73 | 'Check the write permissions within the database/migrations directory.' 74 | ); 75 | } 76 | 77 | $this->line(''); 78 | } 79 | 80 | /** 81 | * Create the migration. 82 | * 83 | * @return bool 84 | */ 85 | protected function createMigration() 86 | { 87 | $migrationPath = $this->getMigrationPath(); 88 | 89 | $output = $this->laravel->view 90 | ->make('laratrust::migration') 91 | ->with(['laratrust' => Config::get('laratrust')]) 92 | ->render(); 93 | 94 | if (! file_exists($migrationPath) && $fs = fopen($migrationPath, 'x')) { 95 | fwrite($fs, $output); 96 | fclose($fs); 97 | 98 | return true; 99 | } 100 | 101 | return false; 102 | } 103 | 104 | /** 105 | * Generate the message to display when running the 106 | * console command showing what tables are going 107 | * to be created. 108 | * 109 | * @return string 110 | */ 111 | protected function generateMigrationMessage() 112 | { 113 | $tables = Collection::make(Config::get('laratrust.tables')) 114 | ->reject(function ($value, $key) { 115 | return $key == 'teams' && ! Config::get('laratrust.teams.enabled'); 116 | }) 117 | ->sort(); 118 | 119 | return "A migration that creates {$tables->implode(', ')} " 120 | .'tables will be created in database/migrations directory'; 121 | } 122 | 123 | /** 124 | * Build a warning regarding possible duplication 125 | * due to already existing migrations. 126 | * 127 | * @param array $existingMigrations 128 | * @return string 129 | */ 130 | protected function getExistingMigrationsWarning(array $existingMigrations) 131 | { 132 | if (count($existingMigrations) > 1) { 133 | $base = "Laratrust migrations already exist.\nFollowing files were found: "; 134 | } else { 135 | $base = "Laratrust migration already exists.\nFollowing file was found: "; 136 | } 137 | 138 | return $base.array_reduce($existingMigrations, function ($carry, $fileName) { 139 | return $carry."\n - ".$fileName; 140 | }); 141 | } 142 | 143 | /** 144 | * Check if there is another migration 145 | * with the same suffix. 146 | * 147 | * @return array 148 | */ 149 | protected function alreadyExistingMigrations() 150 | { 151 | $matchingFiles = glob($this->getMigrationPath('*')); 152 | 153 | return array_map(function ($path) { 154 | return basename($path); 155 | }, $matchingFiles); 156 | } 157 | 158 | /** 159 | * Get the migration path. 160 | * 161 | * The date parameter is optional for ability 162 | * to provide a custom value or a wildcard. 163 | * 164 | * @param string|null $date 165 | * @return string 166 | */ 167 | protected function getMigrationPath($date = null) 168 | { 169 | $date = $date ?: date('Y_m_d_His'); 170 | 171 | return database_path("migrations/{$date}_{$this->migrationSuffix}.php"); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/Console/SetupCommand.php: -------------------------------------------------------------------------------- 1 | 'Creating migration', 31 | 'laratrust:role' => 'Creating Role model', 32 | 'laratrust:permission' => 'Creating Permission model', 33 | // 'laratrust:add-trait' => 'Adding LaratrustUserTrait to User model' 34 | ]; 35 | 36 | /** 37 | * Create a new command instance. 38 | * 39 | * @return void 40 | */ 41 | public function __construct() 42 | { 43 | if (Config::get('laratrust.teams.enabled')) { 44 | $this->calls['laratrust:team'] = 'Creating Team model'; 45 | } 46 | 47 | parent::__construct(); 48 | } 49 | 50 | /** 51 | * Execute the console command. 52 | * 53 | * @return void 54 | */ 55 | public function handle() 56 | { 57 | foreach ($this->calls as $command => $info) { 58 | $this->line(PHP_EOL.$info); 59 | $this->call($command); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Console/SetupTeamsCommand.php: -------------------------------------------------------------------------------- 1 | error('Not using teams in your Laratrust configuration file.'); 40 | $this->warn('Please enable the teams usage in your configuration.'); 41 | 42 | return; 43 | } 44 | 45 | $this->line(''); 46 | $this->info('The Laratrust teams feature setup is going to add a migration and a model'); 47 | 48 | $existingMigrations = $this->alreadyExistingMigrations(); 49 | 50 | if ($existingMigrations) { 51 | $this->line(''); 52 | 53 | $this->warn($this->getExistingMigrationsWarning($existingMigrations)); 54 | } 55 | 56 | $this->line(''); 57 | 58 | if (! $this->confirm('Proceed with the migration creation?', 'yes')) { 59 | return; 60 | } 61 | 62 | $this->line(''); 63 | 64 | $this->line('Creating migration'); 65 | 66 | if ($this->createMigration()) { 67 | $this->info('Migration created successfully.'); 68 | } else { 69 | $this->error( 70 | "Couldn't create migration.\n". 71 | 'Check the write permissions within the database/migrations directory.' 72 | ); 73 | } 74 | 75 | $this->line('Creating Team model'); 76 | $this->call('laratrust:team'); 77 | 78 | $this->line(''); 79 | } 80 | 81 | /** 82 | * Create the migration. 83 | * 84 | * @return bool 85 | */ 86 | protected function createMigration() 87 | { 88 | $migrationPath = $this->getMigrationPath(); 89 | 90 | $this->call('view:clear'); 91 | $output = $this->laravel->view 92 | ->make('laratrust::setup-teams') 93 | ->with(['laratrust' => Config::get('laratrust')]) 94 | ->render(); 95 | 96 | if (! file_exists($migrationPath) && $fs = fopen($migrationPath, 'x')) { 97 | fwrite($fs, $output); 98 | fclose($fs); 99 | 100 | return true; 101 | } 102 | 103 | return false; 104 | } 105 | 106 | /** 107 | * Build a warning regarding possible duplication 108 | * due to already existing migrations. 109 | * 110 | * @param array $existingMigrations 111 | * @return string 112 | */ 113 | protected function getExistingMigrationsWarning(array $existingMigrations) 114 | { 115 | if (count($existingMigrations) > 1) { 116 | $base = "Laratrust setup teams migrations already exist.\nFollowing files were found: "; 117 | } else { 118 | $base = "Laratrust setup teams migration already exists.\nFollowing file was found: "; 119 | } 120 | 121 | return $base.array_reduce($existingMigrations, function ($carry, $fileName) { 122 | return $carry."\n - ".$fileName; 123 | }); 124 | } 125 | 126 | /** 127 | * Check if there is another migration 128 | * with the same suffix. 129 | * 130 | * @return array 131 | */ 132 | protected function alreadyExistingMigrations() 133 | { 134 | $matchingFiles = glob($this->getMigrationPath('*')); 135 | 136 | return array_map(function ($path) { 137 | return basename($path); 138 | }, $matchingFiles); 139 | } 140 | 141 | /** 142 | * Get the migration path. 143 | * 144 | * The date parameter is optional for ability 145 | * to provide a custom value or a wildcard. 146 | * 147 | * @param string|null $date 148 | * @return string 149 | */ 150 | protected function getMigrationPath($date = null) 151 | { 152 | $date = $date ?: date('Y_m_d_His'); 153 | 154 | return database_path("migrations/{$date}_{$this->migrationSuffix}.php"); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Console/UpgradeCommand.php: -------------------------------------------------------------------------------- 1 | info('There is nothing to upgrade through the command.'); 39 | 40 | return; 41 | 42 | $this->line(''); 43 | $this->info('The Laratrust upgrade migration will be created in the database/migration directory'); 44 | 45 | $existingMigrations = $this->alreadyExistingMigrations(); 46 | 47 | if ($existingMigrations) { 48 | $this->line(''); 49 | 50 | $this->warn($this->getExistingMigrationsWarning($existingMigrations)); 51 | } 52 | 53 | $this->line(''); 54 | 55 | if (! $this->confirm('Proceed with the migration creation?', 'yes')) { 56 | return; 57 | } 58 | 59 | $this->line(''); 60 | 61 | $this->info('Creating migration...'); 62 | 63 | if ($this->createMigration()) { 64 | $this->info('Migration successfully created!'); 65 | } else { 66 | $this->error( 67 | "Couldn't create migration.\n". 68 | 'Check the write permissions within the database/migrations directory.' 69 | ); 70 | } 71 | 72 | $this->line(''); 73 | } 74 | 75 | /** 76 | * Create the migration. 77 | * 78 | * @return bool 79 | */ 80 | protected function createMigration() 81 | { 82 | $migrationPath = $this->getMigrationPath(); 83 | 84 | $this->call('view:clear'); 85 | $output = $this->laravel->view 86 | ->make('laratrust::upgrade-migration') 87 | ->with(['laratrust' => Config::get('laratrust')]) 88 | ->render(); 89 | 90 | if (! file_exists($migrationPath) && $fs = fopen($migrationPath, 'x')) { 91 | fwrite($fs, $output); 92 | fclose($fs); 93 | 94 | return true; 95 | } 96 | 97 | return false; 98 | } 99 | 100 | /** 101 | * Build a warning regarding possible duplication 102 | * due to already existing migrations. 103 | * 104 | * @param array $existingMigrations 105 | * @return string 106 | */ 107 | protected function getExistingMigrationsWarning(array $existingMigrations) 108 | { 109 | if (count($existingMigrations) > 1) { 110 | $base = "Laratrust upgrade migrations already exist.\nFollowing files were found: "; 111 | } else { 112 | $base = "Laratrust upgrade migration already exists.\nFollowing file was found: "; 113 | } 114 | 115 | return $base.array_reduce($existingMigrations, function ($carry, $fileName) { 116 | return $carry."\n - ".$fileName; 117 | }); 118 | } 119 | 120 | /** 121 | * Check if there is another migration 122 | * with the same suffix. 123 | * 124 | * @return array 125 | */ 126 | protected function alreadyExistingMigrations() 127 | { 128 | $matchingFiles = glob($this->getMigrationPath('*')); 129 | 130 | return array_map(function ($path) { 131 | return basename($path); 132 | }, $matchingFiles); 133 | } 134 | 135 | /** 136 | * Get the migration path. 137 | * 138 | * The date parameter is optional for ability 139 | * to provide a custom value or a wildcard. 140 | * 141 | * @param string|null $date 142 | * @return string 143 | */ 144 | protected function getMigrationPath($date = null) 145 | { 146 | $date = $date ?: date('Y_m_d_His'); 147 | 148 | return database_path("migrations/{$date}_{$this->migrationSuffix}.php"); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/Contracts/LaratrustUser.php: -------------------------------------------------------------------------------- 1 | 152 | */ 153 | public function allPermissions(?array $columns = null, mixed $team = false): Collection; 154 | } 155 | -------------------------------------------------------------------------------- /src/Contracts/Permission.php: -------------------------------------------------------------------------------- 1 | value; 29 | } 30 | 31 | if ($object instanceof UuidInterface) { 32 | return (string) $object; 33 | } 34 | 35 | if (is_object($object)) { 36 | return $object->getKey(); 37 | } 38 | 39 | if (is_array($object)) { 40 | return $object['id']; 41 | } 42 | 43 | if (is_numeric($object)) { 44 | return $object; 45 | } 46 | 47 | if (is_string($object)) { 48 | return call_user_func_array([ 49 | Config::get("laratrust.models.{$type}"), 'where', 50 | ], ['name', $object])->firstOrFail()->getKey(); 51 | } 52 | 53 | throw new InvalidArgumentException( 54 | 'getIdFor function only supports UuidInterface, Model, BackedEnum, array{id: string}, int, string' 55 | ); 56 | } 57 | 58 | /** 59 | * Checks if the string passed contains a pipe '|' and explodes the string to an array. 60 | */ 61 | public static function standardize( 62 | string|array|BackedEnum $value, 63 | bool $toArray = false 64 | ): string|array { 65 | if ($value instanceof BackedEnum) { 66 | return $toArray ? [$value->value] : $value->value; 67 | } 68 | 69 | if (is_array($value)) { 70 | return Collection::make($value)->map(function ($item) { 71 | return $item instanceof BackedEnum 72 | ? $item->value 73 | : $item; 74 | })->toArray(); 75 | } 76 | 77 | if ((strpos($value, '|') === false) && ! $toArray) { 78 | return $value; 79 | } 80 | 81 | return explode('|', $value); 82 | } 83 | 84 | public static function ensureString(mixed $enum): string 85 | { 86 | return $enum instanceof BackedEnum ? $enum->value : $enum; 87 | } 88 | 89 | /** 90 | * Return two arrays with the filtered permissions between the permissions 91 | * with wildcard and the permissions without it. 92 | * 93 | * @param array $permissions 94 | * @return array [$wildcard, $noWildcard] 95 | */ 96 | public static function getPermissionWithAndWithoutWildcards($permissions) 97 | { 98 | $wildcard = []; 99 | $noWildcard = []; 100 | 101 | foreach ($permissions as $permission) { 102 | if (strpos($permission, '*') === false) { 103 | $noWildcard[] = $permission; 104 | } else { 105 | $wildcard[] = str_replace('*', '%', $permission); 106 | } 107 | } 108 | 109 | return [$wildcard, $noWildcard]; 110 | } 111 | 112 | /** 113 | * Check if a role is editable in the admin panel. 114 | * 115 | * @param string|\Laratrust\Models\LaratrustRole $role 116 | * @return bool 117 | */ 118 | public static function roleIsEditable($role) 119 | { 120 | $roleName = is_string($role) ? $role : $role->name; 121 | 122 | return ! in_array( 123 | $roleName, 124 | Config::get('laratrust.panel.roles_restrictions.not_editable') ?? [] 125 | ); 126 | } 127 | 128 | /** 129 | * Check if a role is deletable in the admin panel. 130 | * 131 | * @param string|\Laratrust\Models\LaratrustRole $role 132 | * @return bool 133 | */ 134 | public static function roleIsDeletable($role) 135 | { 136 | $roleName = is_string($role) ? $role : $role->name; 137 | 138 | return ! in_array( 139 | $roleName, 140 | Config::get('laratrust.panel.roles_restrictions.not_deletable') ?? [] 141 | ); 142 | } 143 | 144 | /** 145 | * Check if a role is removable in the admin panel. 146 | * 147 | * @param string|\Laratrust\Models\LaratrustRole $role 148 | * @return bool 149 | */ 150 | public static function roleIsRemovable($role) 151 | { 152 | $roleName = is_string($role) ? $role : $role->name; 153 | 154 | return ! in_array( 155 | $roleName, 156 | Config::get('laratrust.panel.roles_restrictions.not_removable') ?? [] 157 | ); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/Http/Controllers/PermissionsController.php: -------------------------------------------------------------------------------- 1 | permissionModel = Config::get('laratrust.models.permission'); 17 | } 18 | 19 | public function index() 20 | { 21 | return View::make('laratrust::panel.permissions.index', [ 22 | 'permissions' => $this->permissionModel::simplePaginate(10), 23 | ]); 24 | } 25 | 26 | public function create() 27 | { 28 | return View::make('laratrust::panel.edit', [ 29 | 'model' => null, 30 | 'permissions' => null, 31 | 'type' => 'permission', 32 | ]); 33 | } 34 | 35 | public function store(Request $request) 36 | { 37 | $data = $request->validate([ 38 | 'name' => 'required|string|unique:'.config('laratrust.tables.permissions', 'permissions').',name', 39 | 'display_name' => 'nullable|string', 40 | 'description' => 'nullable|string', 41 | ]); 42 | 43 | $permission = $this->permissionModel::create($data); 44 | 45 | Session::flash('laratrust-success', 'Permission created successfully'); 46 | 47 | return redirect(route('laratrust.permissions.index')); 48 | } 49 | 50 | public function edit($id) 51 | { 52 | $permission = $this->permissionModel::findOrFail($id); 53 | 54 | return View::make('laratrust::panel.edit', [ 55 | 'model' => $permission, 56 | 'type' => 'permission', 57 | ]); 58 | } 59 | 60 | public function update(Request $request, $id) 61 | { 62 | $permission = $this->permissionModel::findOrFail($id); 63 | 64 | $data = $request->validate([ 65 | 'display_name' => 'nullable|string', 66 | 'description' => 'nullable|string', 67 | ]); 68 | 69 | $permission->update($data); 70 | 71 | Session::flash('laratrust-success', 'Permission updated successfully'); 72 | 73 | return redirect(route('laratrust.permissions.index')); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Http/Controllers/RolesAssignmentController.php: -------------------------------------------------------------------------------- 1 | rolesModel = Config::get('laratrust.models.role'); 20 | $this->permissionModel = Config::get('laratrust.models.permission'); 21 | $this->assignPermissions = Config::get('laratrust.panel.assign_permissions_to_user'); 22 | } 23 | 24 | public function index(Request $request) 25 | { 26 | $modelsKeys = array_keys(Config::get('laratrust.user_models')); 27 | $modelKey = $request->get('model') ?? $modelsKeys[0] ?? null; 28 | $userModel = Config::get('laratrust.user_models')[$modelKey] ?? null; 29 | 30 | if (! $userModel) { 31 | abort(404); 32 | } 33 | 34 | return View::make('laratrust::panel.roles-assignment.index', [ 35 | 'models' => $modelsKeys, 36 | 'modelKey' => $modelKey, 37 | 'users' => $userModel::query() 38 | ->withCount(['roles', 'permissions']) 39 | ->simplePaginate(10), 40 | ]); 41 | } 42 | 43 | public function edit(Request $request, $modelId) 44 | { 45 | $modelKey = $request->get('model'); 46 | $userModel = Config::get('laratrust.user_models')[$modelKey] ?? null; 47 | 48 | if (! $userModel) { 49 | Session::flash('laratrust-error', 'Model was not specified in the request'); 50 | 51 | return redirect(route('laratrust.roles-assignment.index')); 52 | } 53 | 54 | $user = $userModel::query() 55 | ->with(['roles:id,name', 'permissions:id,name']) 56 | ->findOrFail($modelId); 57 | 58 | $roles = $this->rolesModel::orderBy('name')->get(['id', 'name', 'display_name']) 59 | ->map(function ($role) use ($user) { 60 | $role->assigned = $user->roles 61 | ->pluck('id') 62 | ->contains($role->id); 63 | $role->isRemovable = Helper::roleIsRemovable($role); 64 | 65 | return $role; 66 | }); 67 | if ($this->assignPermissions) { 68 | $permissions = $this->permissionModel::orderBy('name') 69 | ->get(['id', 'name', 'display_name']) 70 | ->map(function ($permission) use ($user) { 71 | $permission->assigned = $user->permissions 72 | ->pluck('id') 73 | ->contains($permission->id); 74 | 75 | return $permission; 76 | }); 77 | } 78 | 79 | return View::make('laratrust::panel.roles-assignment.edit', [ 80 | 'modelKey' => $modelKey, 81 | 'roles' => $roles, 82 | 'permissions' => $this->assignPermissions ? $permissions : null, 83 | 'user' => $user, 84 | ]); 85 | } 86 | 87 | public function update(Request $request, $modelId) 88 | { 89 | $modelKey = $request->get('model'); 90 | $userModel = Config::get('laratrust.user_models')[$modelKey] ?? null; 91 | 92 | if (! $userModel) { 93 | Session::flash('laratrust-error', 'Model was not specified in the request'); 94 | 95 | return redirect()->back(); 96 | } 97 | 98 | $user = $userModel::findOrFail($modelId); 99 | $user->syncRoles($request->get('roles') ?? []); 100 | if ($this->assignPermissions) { 101 | $user->syncPermissions($request->get('permissions') ?? []); 102 | } 103 | 104 | Session::flash('laratrust-success', 'Roles and permissions assigned successfully'); 105 | 106 | return redirect(route('laratrust.roles-assignment.index', ['model' => $modelKey])); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Http/Controllers/RolesController.php: -------------------------------------------------------------------------------- 1 | rolesModel = Config::get('laratrust.models.role'); 20 | $this->permissionModel = Config::get('laratrust.models.permission'); 21 | } 22 | 23 | public function index() 24 | { 25 | return View::make('laratrust::panel.roles.index', [ 26 | 'roles' => $this->rolesModel::withCount('permissions') 27 | ->simplePaginate(10), 28 | ]); 29 | } 30 | 31 | public function create() 32 | { 33 | return View::make('laratrust::panel.edit', [ 34 | 'model' => null, 35 | 'permissions' => $this->permissionModel::all(['id', 'display_name']), 36 | 'type' => 'role', 37 | ]); 38 | } 39 | 40 | public function show(Request $request, $id) 41 | { 42 | $role = $this->rolesModel::query() 43 | ->with('permissions:id,name,display_name') 44 | ->findOrFail($id); 45 | 46 | return View::make('laratrust::panel.roles.show', ['role' => $role]); 47 | } 48 | 49 | public function store(Request $request) 50 | { 51 | $data = $request->validate([ 52 | 'name' => 'required|string|unique:'.config('laratrust.tables.roles', 'roles').',name', 53 | 'display_name' => 'nullable|string', 54 | 'description' => 'nullable|string', 55 | ]); 56 | 57 | $role = $this->rolesModel::create($data); 58 | $role->syncPermissions($request->get('permissions') ?? []); 59 | 60 | Session::flash('laratrust-success', 'Role created successfully'); 61 | 62 | return redirect(route('laratrust.roles.index')); 63 | } 64 | 65 | public function edit($id) 66 | { 67 | $role = $this->rolesModel::query() 68 | ->with('permissions:id') 69 | ->findOrFail($id); 70 | 71 | if (! Helper::roleIsEditable($role)) { 72 | Session::flash('laratrust-error', 'The role is not editable'); 73 | 74 | return redirect()->back(); 75 | } 76 | 77 | $permissions = $this->permissionModel::all(['id', 'name', 'display_name']) 78 | ->map(function ($permission) use ($role) { 79 | $permission->assigned = $role->permissions 80 | ->pluck('id') 81 | ->contains($permission->id); 82 | 83 | return $permission; 84 | }); 85 | 86 | return View::make('laratrust::panel.edit', [ 87 | 'model' => $role, 88 | 'permissions' => $permissions, 89 | 'type' => 'role', 90 | ]); 91 | } 92 | 93 | public function update(Request $request, $id) 94 | { 95 | $role = $this->rolesModel::findOrFail($id); 96 | 97 | if (! Helper::roleIsEditable($role)) { 98 | Session::flash('laratrust-error', 'The role is not editable'); 99 | 100 | return redirect()->back(); 101 | } 102 | 103 | $data = $request->validate([ 104 | 'display_name' => 'nullable|string', 105 | 'description' => 'nullable|string', 106 | ]); 107 | 108 | $role->update($data); 109 | $role->syncPermissions($request->get('permissions') ?? []); 110 | 111 | Session::flash('laratrust-success', 'Role updated successfully'); 112 | 113 | return redirect(route('laratrust.roles.index')); 114 | } 115 | 116 | public function destroy($id) 117 | { 118 | $usersAssignedToRole = DB::table(Config::get('laratrust.tables.role_user')) 119 | ->where(Config::get('laratrust.foreign_keys.role'), $id) 120 | ->count(); 121 | $role = $this->rolesModel::findOrFail($id); 122 | 123 | if (! Helper::roleIsDeletable($role)) { 124 | Session::flash('laratrust-error', 'The role is not deletable'); 125 | 126 | return redirect()->back(); 127 | } 128 | 129 | if ($usersAssignedToRole > 0) { 130 | Session::flash('laratrust-warning', 'Role is added to one or more users. It can not be deleted'); 131 | } else { 132 | Session::flash('laratrust-success', 'Role deleted successfully'); 133 | $this->rolesModel::destroy($id); 134 | } 135 | 136 | return redirect(route('laratrust.roles.index')); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/Laratrust.php: -------------------------------------------------------------------------------- 1 | user()) { 33 | return $user->hasRole($role, $team, $requireAll); 34 | } 35 | 36 | return false; 37 | } 38 | 39 | /** 40 | * Check if the current user has a permission by its name. 41 | */ 42 | public function hasPermission( 43 | string|array|BackedEnum $permission, 44 | mixed $team = null, 45 | bool $requireAll = false 46 | ): bool { 47 | if ($user = $this->user()) { 48 | return $user->hasPermission($permission, $team, $requireAll); 49 | } 50 | 51 | return false; 52 | } 53 | 54 | /** 55 | * Check if the current user has a permission by its name. 56 | * Alias to hasPermission. 57 | */ 58 | public function isAbleTo( 59 | string|array|BackedEnum $permission, 60 | mixed $team = null, 61 | bool $requireAll = false 62 | ): bool { 63 | return $this->hasPermission($permission, $team, $requireAll); 64 | } 65 | 66 | /** 67 | * Check if the current user has a role or permission by its name. 68 | * 69 | * @param array|string $roles The role(s) needed. 70 | * @param array|string $permissions The permission(s) needed. 71 | * @param array $options The Options. 72 | * @return bool 73 | */ 74 | public function ability($roles, $permissions, $team = null, $options = []) 75 | { 76 | if ($user = $this->user()) { 77 | return $user->ability($roles, $permissions, $team, $options); 78 | } 79 | 80 | return false; 81 | } 82 | 83 | /** 84 | * Get the currently authenticated user or null. 85 | */ 86 | protected function user(): ?LaratrustUser 87 | { 88 | return $this->app->auth->user(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/LaratrustFacade.php: -------------------------------------------------------------------------------- 1 | useMorphMapForRelationships(); 32 | $this->registerMiddlewares(); 33 | $this->registerBladeDirectives(); 34 | $this->registerRoutes(); 35 | $this->registerResources(); 36 | $this->registerPermissionsToGate(); 37 | $this->defineAssetPublishing(); 38 | } 39 | 40 | /** 41 | * If the user wants to use the morphMap it uses the morphMap. 42 | * 43 | * @return void 44 | */ 45 | protected function useMorphMapForRelationships() 46 | { 47 | if ($this->app['config']->get('laratrust.use_morph_map')) { 48 | Relation::morphMap($this->app['config']->get('laratrust.user_models')); 49 | } 50 | } 51 | 52 | /** 53 | * Register the middlewares automatically. 54 | * 55 | * @return void 56 | */ 57 | protected function registerMiddlewares() 58 | { 59 | if (! $this->app['config']->get('laratrust.middleware.register')) { 60 | return; 61 | } 62 | 63 | $router = $this->app['router']; 64 | 65 | if (method_exists($router, 'middleware')) { 66 | $registerMethod = 'middleware'; 67 | } elseif (method_exists($router, 'aliasMiddleware')) { 68 | $registerMethod = 'aliasMiddleware'; 69 | } else { 70 | return; 71 | } 72 | 73 | $middlewares = [ 74 | 'role' => \Laratrust\Middleware\Role::class, 75 | 'permission' => \Laratrust\Middleware\Permission::class, 76 | 'ability' => \Laratrust\Middleware\Ability::class, 77 | ]; 78 | 79 | foreach ($middlewares as $key => $class) { 80 | $router->$registerMethod($key, $class); 81 | } 82 | } 83 | 84 | /** 85 | * Register the blade directives. 86 | * 87 | * @return void 88 | */ 89 | private function registerBladeDirectives() 90 | { 91 | if (! class_exists('\Blade')) { 92 | return; 93 | } 94 | 95 | // Call to Laratrust::hasRole. 96 | Blade::directive('role', function ($expression) { 97 | return "hasRole({$expression})) : ?>"; 98 | }); 99 | 100 | // Call to Laratrust::permission. 101 | Blade::directive('permission', function ($expression) { 102 | return "hasPermission({$expression})) : ?>"; 103 | }); 104 | 105 | // Call to Laratrust::ability. 106 | Blade::directive('ability', function ($expression) { 107 | return "ability({$expression})) : ?>"; 108 | }); 109 | 110 | Blade::directive('endrole', function () { 111 | return "hasRole ?>"; 112 | }); 113 | 114 | Blade::directive('endpermission', function () { 115 | return "permission ?>"; 116 | }); 117 | 118 | Blade::directive('endability', function () { 119 | return "ability ?>"; 120 | }); 121 | } 122 | 123 | /** 124 | * Register the routes used by the Laratrust admin panel. 125 | * 126 | * @return void 127 | */ 128 | protected function registerRoutes() 129 | { 130 | if (! $this->app['config']->get('laratrust.panel.register')) { 131 | return; 132 | } 133 | 134 | Route::group([ 135 | 'domain' => config('laratrust.panel.domain', (app()->runningInConsole() === false) ? request()->getHost() : 'localhost'), 136 | 'prefix' => config('laratrust.panel.path'), 137 | 'namespace' => 'Laratrust\Http\Controllers', 138 | 'middleware' => config('laratrust.panel.middleware', 'web'), 139 | ], function () { 140 | Route::redirect('/', '/'.config('laratrust.panel.path').'/roles-assignment'); 141 | $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); 142 | }); 143 | } 144 | 145 | /** 146 | * Register all the possible views used by Laratrust. 147 | * 148 | * @return void 149 | */ 150 | protected function registerResources() 151 | { 152 | $this->loadViewsFrom(__DIR__.'/../resources/views', 'laratrust'); 153 | } 154 | 155 | /** 156 | * Register permissions to Laravel Gate. 157 | * 158 | * @return void 159 | */ 160 | protected function registerPermissionsToGate() 161 | { 162 | if (! $this->app['config']->get('laratrust.permissions_as_gates')) { 163 | return; 164 | } 165 | 166 | app(Gate::class)->before(function (Authorizable $user, mixed $ability, $attributes) { 167 | if (method_exists($user, 'hasPermission')) { 168 | $team = Collection::make($attributes) 169 | ->filter(fn ($attr) => ! is_bool($attr)) 170 | ->first(); 171 | $requireAll = Collection::make($attributes) 172 | ->filter(fn ($attr) => is_bool($attr)) 173 | ->first() || false; 174 | 175 | return $user->hasPermission($ability, $team, $requireAll) ?: null; 176 | } 177 | }); 178 | } 179 | 180 | /** 181 | * Register the assets that are publishable for the admin panel to work. 182 | * 183 | * @return void 184 | */ 185 | protected function defineAssetPublishing() 186 | { 187 | if (! $this->app['config']->get('laratrust.panel.register')) { 188 | return; 189 | } 190 | 191 | $this->publishes([ 192 | __DIR__.'/../public' => public_path('vendor/laratrust'), 193 | ], 'laratrust-assets'); 194 | } 195 | 196 | /** 197 | * Register the service provider. 198 | * 199 | * @return void 200 | */ 201 | public function register() 202 | { 203 | $this->configure(); 204 | $this->offerPublishing(); 205 | $this->registerLaratrust(); 206 | $this->registerCommands(); 207 | } 208 | 209 | /** 210 | * Setup the configuration for Laratrust. 211 | * 212 | * @return void 213 | */ 214 | protected function configure() 215 | { 216 | $this->mergeConfigFrom(__DIR__.'/../config/laratrust.php', 'laratrust'); 217 | } 218 | 219 | /** 220 | * Setup the resource publishing group for Laratrust. 221 | * 222 | * @return void 223 | */ 224 | protected function offerPublishing() 225 | { 226 | if ($this->app->runningInConsole()) { 227 | $this->publishes([ 228 | __DIR__.'/../config/laratrust.php' => config_path('laratrust.php'), 229 | ], 'laratrust'); 230 | 231 | $this->publishes([ 232 | __DIR__.'/../config/laratrust_seeder.php' => config_path('laratrust_seeder.php'), 233 | ], 'laratrust-seeder'); 234 | 235 | $this->publishes([ 236 | __DIR__.'/../resources/views/panel' => resource_path('views/vendor/laratrust/panel'), 237 | ], 'laratrust-views'); 238 | } 239 | } 240 | 241 | /** 242 | * Register the application bindings. 243 | * 244 | * @return void 245 | */ 246 | protected function registerLaratrust() 247 | { 248 | $this->app->bind('laratrust', function ($app) { 249 | return new Laratrust($app); 250 | }); 251 | 252 | $this->app->alias('laratrust', 'Laratrust\Laratrust'); 253 | } 254 | 255 | /** 256 | * Register the Laratrusts commands. 257 | * 258 | * @return void 259 | */ 260 | protected function registerCommands() 261 | { 262 | if ($this->app->runningInConsole()) { 263 | $this->commands([ 264 | Console\AddLaratrustUserTraitUseCommand::class, 265 | Console\MakePermissionCommand::class, 266 | Console\MakeRoleCommand::class, 267 | Console\MakeSeederCommand::class, 268 | Console\MakeTeamCommand::class, 269 | Console\MigrationCommand::class, 270 | Console\SetupCommand::class, 271 | Console\SetupTeamsCommand::class, 272 | Console\UpgradeCommand::class, 273 | ]); 274 | } 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/Middleware/Ability.php: -------------------------------------------------------------------------------- 1 | $team, 25 | 'require_all' => $validateAll, 26 | 'guard' => $guard, 27 | ] = $this->getValuesFromParameters($team, $options); 28 | 29 | $roles = Helper::standardize($roles, true); 30 | $permissions = Helper::standardize($permissions, true); 31 | 32 | if ( 33 | Auth::guard($guard)->guest() 34 | || ! Auth::guard($guard)->user() 35 | ->ability($roles, $permissions, $team, ['validate_all' => $validateAll]) 36 | ) { 37 | return $this->unauthorized(); 38 | } 39 | 40 | return $next($request); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Middleware/LaratrustMiddleware.php: -------------------------------------------------------------------------------- 1 | $team, 28 | 'require_all' => $requireAll, 29 | 'guard' => $guard, 30 | ] = $this->getValuesFromParameters($team, $options); 31 | $method = $type == 'roles' ? 'hasRole' : 'hasPermission'; 32 | $rolesPermissions = Helper::standardize($rolesPermissions, true); 33 | 34 | return ! Auth::guard($guard)->guest() 35 | && Auth::guard($guard)->user()->$method($rolesPermissions, $team, $requireAll); 36 | } 37 | 38 | /** 39 | * The request is unauthorized, so it handles the aborting/redirecting. 40 | */ 41 | protected function unauthorized(): mixed 42 | { 43 | $handling = Config::get('laratrust.middleware.handling'); 44 | $handler = Config::get("laratrust.middleware.handlers.{$handling}"); 45 | 46 | if ($handling == 'abort') { 47 | $defaultMessage = 'User does not have any of the necessary access rights.'; 48 | 49 | return App::abort($handler['code'], $handler['message'] ?? $defaultMessage); 50 | } 51 | 52 | $redirect = Redirect::to($handler['url']); 53 | if (! empty($handler['message']['content'])) { 54 | $redirect->with($handler['message']['key'], $handler['message']['content']); 55 | } 56 | 57 | return $redirect; 58 | } 59 | 60 | /** 61 | * Generate an array with the values of the parameters given to the middleware. 62 | */ 63 | protected function getValuesFromParameters(?string $team, ?string $options): array 64 | { 65 | return [ 66 | 'team' => Str::contains((string) $team, ['require_all', 'guard:']) ? null : $team, 67 | 'require_all' => Str::contains((string) $team, 'require_all') ?: Str::contains((string) $options, 'require_all'), 68 | 'guard' => Str::contains((string) $team, 'guard:') 69 | ? $this->extractGuard($team) 70 | : ( 71 | Str::contains((string) $options, 'guard:') 72 | ? $this->extractGuard($options) 73 | : Config::get('auth.defaults.guard') 74 | ), 75 | ]; 76 | } 77 | 78 | /** 79 | * Extract the guard type from the given string. 80 | */ 81 | protected function extractGuard(string $string): string 82 | { 83 | $options = Collection::make(explode('|', $string)); 84 | 85 | return $options 86 | ->reject(fn ($option) => ! Str::contains($option, 'guard:')) 87 | ->map(fn ($option) => Str::of($option)->explode(':')->get(1)) 88 | ->first(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Middleware/Permission.php: -------------------------------------------------------------------------------- 1 | authorization('permissions', $permissions, $team, $options)) { 21 | return $this->unauthorized(); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Middleware/Role.php: -------------------------------------------------------------------------------- 1 | authorization('roles', $roles, $team, $options)) { 21 | return $this->unauthorized(); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Models/Permission.php: -------------------------------------------------------------------------------- 1 | table = Config::get('laratrust.tables.permissions'); 38 | } 39 | 40 | protected static function booted(): void 41 | { 42 | static::deleting(function ($permission) { 43 | if (method_exists($permission, 'bootSoftDeletes') && ! $permission->forceDeleting) { 44 | return; 45 | } 46 | 47 | $permission->roles()->sync([]); 48 | 49 | foreach (array_keys(Config::get('laratrust.user_models')) as $key) { 50 | $permission->$key()->sync([]); 51 | } 52 | }); 53 | } 54 | 55 | /** 56 | * Many-to-Many relations with role model. 57 | */ 58 | public function roles(): BelongsToMany 59 | { 60 | return $this->belongsToMany( 61 | Config::get('laratrust.models.role'), 62 | Config::get('laratrust.tables.permission_role'), 63 | Config::get('laratrust.foreign_keys.permission'), 64 | Config::get('laratrust.foreign_keys.role') 65 | ); 66 | } 67 | 68 | /** 69 | * Morph by Many relationship between the permission and the one of the possible user models. 70 | */ 71 | public function getMorphByUserRelation(string $relationship): MorphToMany 72 | { 73 | return $this->morphedByMany( 74 | Config::get('laratrust.user_models')[$relationship], 75 | 'user', 76 | Config::get('laratrust.tables.permission_user'), 77 | Config::get('laratrust.foreign_keys.permission'), 78 | Config::get('laratrust.foreign_keys.user') 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Models/Role.php: -------------------------------------------------------------------------------- 1 | table = Config::get('laratrust.tables.roles'); 45 | } 46 | 47 | protected static function booted(): void 48 | { 49 | $flushCache = function (Role $role) { 50 | $role->flushCache(); 51 | }; 52 | 53 | // If the role doesn't use SoftDeletes. 54 | if (method_exists(static::class, 'restored')) { 55 | static::restored($flushCache); 56 | } 57 | 58 | static::deleted($flushCache); 59 | static::saved($flushCache); 60 | 61 | static::deleting(function ($role) { 62 | if (method_exists($role, 'bootSoftDeletes') && ! $role->forceDeleting) { 63 | return; 64 | } 65 | 66 | $role->permissions()->sync([]); 67 | 68 | foreach (array_keys(Config::get('laratrust.user_models')) as $key) { 69 | $role->$key()->sync([]); 70 | } 71 | }); 72 | } 73 | 74 | /** 75 | * Return the right checker for the role model. 76 | */ 77 | protected function laratrustRoleChecker(): RoleChecker 78 | { 79 | return (new CheckersManager($this))->getRoleChecker(); 80 | } 81 | 82 | public function getMorphByUserRelation(string $relationship): MorphToMany 83 | { 84 | return $this->morphedByMany( 85 | Config::get('laratrust.user_models')[$relationship], 86 | 'user', 87 | Config::get('laratrust.tables.role_user'), 88 | Config::get('laratrust.foreign_keys.role'), 89 | Config::get('laratrust.foreign_keys.user') 90 | ); 91 | } 92 | 93 | public function permissions(): BelongsToMany 94 | { 95 | return $this->belongsToMany( 96 | Config::get('laratrust.models.permission'), 97 | Config::get('laratrust.tables.permission_role'), 98 | Config::get('laratrust.foreign_keys.role'), 99 | Config::get('laratrust.foreign_keys.permission') 100 | ); 101 | } 102 | 103 | public function hasPermission(string|array|BackedEnum $permission, bool $requireAll = false): bool 104 | { 105 | return $this->laratrustRoleChecker() 106 | ->currentRoleHasPermission($permission, $requireAll); 107 | } 108 | 109 | public function syncPermissions(iterable $permissions): static 110 | { 111 | $mappedPermissions = []; 112 | 113 | foreach ($permissions as $permission) { 114 | $mappedPermissions[] = Helper::getIdFor($permission, 'permission'); 115 | } 116 | 117 | $changes = $this->permissions()->sync($mappedPermissions); 118 | $this->flushCache(); 119 | $this->fireLaratrustEvent('permission.synced', [$this, $changes]); 120 | 121 | return $this; 122 | } 123 | 124 | public function givePermission(array|string|int|Model|UuidInterface|BackedEnum $permission): static 125 | { 126 | $permission = Helper::getIdFor($permission, 'permission'); 127 | 128 | $this->permissions()->attach($permission); 129 | $this->flushCache(); 130 | $this->fireLaratrustEvent('permission.added', [$this, $permission]); 131 | 132 | return $this; 133 | } 134 | 135 | public function removePermission(array|string|int|Model|UuidInterface|BackedEnum $permission): static 136 | { 137 | $permission = Helper::getIdFor($permission, 'permission'); 138 | 139 | $this->permissions()->detach($permission); 140 | $this->flushCache(); 141 | $this->fireLaratrustEvent('permission.removed', [$this, $permission]); 142 | 143 | return $this; 144 | } 145 | 146 | public function givePermissions(iterable $permissions): static 147 | { 148 | foreach ($permissions as $permission) { 149 | $this->givePermission($permission); 150 | } 151 | 152 | return $this; 153 | } 154 | 155 | public function removePermissions(?iterable $permissions = null): static 156 | { 157 | if (! $permissions) { 158 | $this->syncPermissions([]); 159 | 160 | return $this; 161 | } 162 | 163 | foreach ($permissions as $permission) { 164 | $this->removePermission($permission); 165 | } 166 | 167 | return $this; 168 | } 169 | 170 | /** 171 | * Flush the role's cache. 172 | */ 173 | public function flushCache(): void 174 | { 175 | $this->laratrustRoleChecker()->currentRoleFlushCache(); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/Models/Team.php: -------------------------------------------------------------------------------- 1 | table = Config::get('laratrust.tables.teams'); 38 | } 39 | 40 | /** 41 | * Boots the team model and adds event listener to 42 | * remove the many-to-many records when trying to delete. 43 | * It WON'T delete any records if the team model uses soft deletes. 44 | */ 45 | protected static function booted(): void 46 | { 47 | static::deleting(function ($team) { 48 | if (method_exists($team, 'bootSoftDeletes') && ! $team->forceDeleting) { 49 | return; 50 | } 51 | 52 | foreach (array_keys(Config::get('laratrust.user_models')) as $key) { 53 | $team->$key()->sync([]); 54 | } 55 | }); 56 | } 57 | 58 | public function getMorphByUserRelation(string $relationship): MorphToMany 59 | { 60 | return $this->morphedByMany( 61 | Config::get('laratrust.user_models')[$relationship], 62 | 'user', 63 | Config::get('laratrust.tables.role_user'), 64 | Config::get('laratrust.foreign_keys.team'), 65 | Config::get('laratrust.foreign_keys.user') 66 | ); 67 | } 68 | 69 | /** 70 | * Returns the team's foreign key. 71 | */ 72 | public static function modelForeignKey(): string 73 | { 74 | return Config::get('laratrust.foreign_keys.team'); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Traits/DynamicUserRelationshipCalls.php: -------------------------------------------------------------------------------- 1 | relationLoaded($key)) { 18 | return $this->relations[$key]; 19 | } 20 | 21 | return $this->getRelationshipFromMethod($key); 22 | } 23 | 24 | /** 25 | * Dynamically retrieve the relationship value with the possible user models. 26 | * 27 | * @param string $key 28 | * @return mixed 29 | */ 30 | public function __get($key) 31 | { 32 | if (in_array($key, array_keys(Config::get('laratrust.user_models')))) { 33 | return $this->getUsersRelationValue($key); 34 | } 35 | 36 | return parent::__get($key); 37 | } 38 | 39 | /** 40 | * Handle dynamic method calls into the model. 41 | * 42 | * @param string $method 43 | * @param array $parameters 44 | * @return mixed 45 | */ 46 | public function __call($method, $parameters) 47 | { 48 | if (in_array($method, array_keys(Config::get('laratrust.user_models')))) { 49 | return $this->getMorphByUserRelation($method); 50 | } 51 | 52 | return parent::__call($method, $parameters); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Traits/HasLaratrustEvents.php: -------------------------------------------------------------------------------- 1 | forget("laratrust.{$event}: ".static::class); 40 | } 41 | } 42 | 43 | /** 44 | * Fire the given event for the model. 45 | */ 46 | protected function fireLaratrustEvent(string $event, array $payload) 47 | { 48 | if (! isset(static::$dispatcher)) { 49 | return true; 50 | } 51 | 52 | return static::$dispatcher->dispatch( 53 | "laratrust.{$event}: ".static::class, 54 | $payload 55 | ); 56 | } 57 | 58 | /** 59 | * Register a laratrust event with the dispatcher. 60 | */ 61 | public static function registerLaratrustEvent( 62 | string $event, 63 | Closure|string|array $callback 64 | ): void { 65 | if (isset(static::$dispatcher)) { 66 | $name = static::class; 67 | 68 | static::$dispatcher->listen("laratrust.{$event}: {$name}", $callback); 69 | } 70 | } 71 | 72 | /** 73 | * Register a role added laratrust event with the dispatcher. 74 | */ 75 | public static function roleAdded(Closure|string|array $callback): void 76 | { 77 | static::registerLaratrustEvent('role.added', $callback); 78 | } 79 | 80 | /** 81 | * Register a role removed laratrust event with the dispatcher. 82 | */ 83 | public static function roleRemoved(Closure|string|array $callback): void 84 | { 85 | static::registerLaratrustEvent('role.removed', $callback); 86 | } 87 | 88 | /** 89 | * Register a permission added laratrust event with the dispatcher. 90 | */ 91 | public static function permissionAdded(\Closure|string|array $callback): void 92 | { 93 | static::registerLaratrustEvent('permission.added', $callback); 94 | } 95 | 96 | /** 97 | * Register a permission removed laratrust event with the dispatcher. 98 | */ 99 | public static function permissionRemoved(\Closure|string|array $callback): void 100 | { 101 | static::registerLaratrustEvent('permission.removed', $callback); 102 | } 103 | 104 | /** 105 | * Register a role synced laratrust event with the dispatcher. 106 | */ 107 | public static function roleSynced(\Closure|string|array $callback): void 108 | { 109 | static::registerLaratrustEvent('role.synced', $callback); 110 | } 111 | 112 | /** 113 | * Register a permission synced laratrust event with the dispatcher. 114 | */ 115 | public static function permissionSynced(\Closure|string|array $callback): void 116 | { 117 | static::registerLaratrustEvent('permission.synced', $callback); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Traits/HasLaratrustScopes.php: -------------------------------------------------------------------------------- 1 | $method('roles', function ($roleQuery) use ($role, $team) { 41 | $teamsStrictCheck = Config::get('laratrust.teams.strict_check'); 42 | $method = is_array($role) ? 'whereIn' : 'where'; 43 | 44 | $roleQuery 45 | ->$method('name', $role) 46 | ->when( 47 | $team || $teamsStrictCheck, 48 | fn ($q) => $q->where( 49 | Team::modelForeignKey(), 50 | Helper::getIdFor($team, 'team') 51 | ) 52 | ); 53 | }); 54 | } 55 | 56 | /** 57 | * This scope allows to retrive the users with a specific role. 58 | */ 59 | public function scopeOrWhereHasRole( 60 | Builder $query, 61 | string|array|BackedEnum $role = '', 62 | mixed $team = null 63 | ): Builder { 64 | return $this->scopeWhereHasRole($query, $role, $team, 'or'); 65 | } 66 | 67 | /** 68 | * This scope allows to retrieve the users with a specific permission. 69 | */ 70 | public function scopeWhereHasPermission( 71 | Builder $query, 72 | string|array|BackedEnum $permission = '', 73 | mixed $team = null, 74 | string $boolean = 'and' 75 | ): Builder { 76 | $method = $boolean == 'and' ? 'where' : 'orWhere'; 77 | 78 | return $query->$method(function ($query) use ($permission, $team) { 79 | $teamsStrictCheck = Config::get('laratrust.teams.strict_check'); 80 | $method = is_array($permission) ? 'whereIn' : 'where'; 81 | 82 | $query 83 | ->whereHas( 84 | 'roles.permissions', 85 | fn ($permissionQuery) => $permissionQuery 86 | ->$method('name', $permission) 87 | ->when( 88 | $team || $teamsStrictCheck, 89 | fn ($q) => $q->where( 90 | Team::modelForeignKey(), 91 | Helper::getIdFor($team, 'team') 92 | ) 93 | ) 94 | ) 95 | ->orWhereHas( 96 | 'permissions', 97 | fn ($permissionQuery) => $permissionQuery 98 | ->$method('name', $permission) 99 | ->when( 100 | $team || $teamsStrictCheck, 101 | fn ($q) => $q->where( 102 | Team::modelForeignKey(), 103 | Helper::getIdFor($team, 'team') 104 | ) 105 | ) 106 | ); 107 | }); 108 | } 109 | 110 | /** 111 | * This scope allows to retrive the users with a specific permission. 112 | */ 113 | public function scopeOrWhereHasPermission( 114 | Builder $query, 115 | string|array|BackedEnum $permission = '', 116 | mixed $team = null 117 | ): Builder { 118 | return $this->scopeWhereHasPermission($query, $permission, $team, 'or'); 119 | } 120 | 121 | /** 122 | * Filter by the users that don't have roles assigned. 123 | */ 124 | public function scopeWhereDoesntHaveRoles(Builder $query): Builder 125 | { 126 | return $query->doesntHave('roles'); 127 | } 128 | 129 | /** 130 | * Filter by the users that don't have permissions assigned. 131 | */ 132 | public function scopeWhereDoesntHavePermissions(Builder $query): Builder 133 | { 134 | return $query->where(function ($query) { 135 | $query->doesntHave('permissions') 136 | ->orDoesntHave('roles.permissions'); 137 | }); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Traits/HasRolesAndPermissions.php: -------------------------------------------------------------------------------- 1 | flushCache(); 34 | }; 35 | 36 | // If the user doesn't use SoftDeletes. 37 | if (method_exists(static::class, 'restored')) { 38 | static::restored($flushCache); 39 | } 40 | 41 | static::deleted($flushCache); 42 | static::saved($flushCache); 43 | 44 | static::deleting(function ($user) { 45 | if (method_exists($user, 'bootSoftDeletes') && ! $user->forceDeleting) { 46 | return; 47 | } 48 | 49 | $user->roles()->sync([]); 50 | $user->permissions()->sync([]); 51 | }); 52 | } 53 | 54 | /** 55 | * Many-to-Many relations with Role. 56 | */ 57 | public function roles(): MorphToMany 58 | { 59 | $roles = $this->morphToMany( 60 | Config::get('laratrust.models.role'), 61 | 'user', 62 | Config::get('laratrust.tables.role_user'), 63 | Config::get('laratrust.foreign_keys.user'), 64 | Config::get('laratrust.foreign_keys.role') 65 | ); 66 | 67 | if (Config::get('laratrust.teams.enabled')) { 68 | $roles->withPivot(Config::get('laratrust.foreign_keys.team')); 69 | } 70 | 71 | return $roles; 72 | } 73 | 74 | /** 75 | * Many-to-Many relations with Team associated through the roles. 76 | */ 77 | public function rolesTeams(): ?MorphToMany 78 | { 79 | if (! Config::get('laratrust.teams.enabled')) { 80 | return null; 81 | } 82 | 83 | return $this->morphToMany( 84 | Config::get('laratrust.models.team'), 85 | 'user', 86 | Config::get('laratrust.tables.role_user'), 87 | Config::get('laratrust.foreign_keys.user'), 88 | Config::get('laratrust.foreign_keys.team') 89 | ) 90 | ->withPivot(Config::get('laratrust.foreign_keys.role')); 91 | } 92 | 93 | /** 94 | * Many-to-Many relations with Team associated through the permissions user is given. 95 | */ 96 | public function permissionsTeams(): ?MorphToMany 97 | { 98 | if (! Config::get('laratrust.teams.enabled')) { 99 | return null; 100 | } 101 | 102 | return $this->morphToMany( 103 | Config::get('laratrust.models.team'), 104 | 'user', 105 | Config::get('laratrust.tables.permission_user'), 106 | Config::get('laratrust.foreign_keys.user'), 107 | Config::get('laratrust.foreign_keys.team') 108 | ) 109 | ->withPivot(Config::get('laratrust.foreign_keys.permission')); 110 | } 111 | 112 | /** 113 | * Get a collection of all user teams. 114 | */ 115 | public function allTeams(?array $columns = null): Collection 116 | { 117 | $columns = is_array($columns) ? $columns : ['*']; 118 | if ($columns) { 119 | $columns[] = 'id'; 120 | $columns = array_unique($columns); 121 | } 122 | 123 | if (! Config::get('laratrust.teams.enabled')) { 124 | return collect([]); 125 | } 126 | $permissionTeams = $this->permissionsTeams()->get($columns); 127 | $roleTeams = $this->rolesTeams()->get($columns); 128 | 129 | return $roleTeams->merge($permissionTeams)->unique('id'); 130 | } 131 | 132 | /** 133 | * Many-to-Many relations with Permission. 134 | */ 135 | public function permissions(): MorphToMany 136 | { 137 | $permissions = $this->morphToMany( 138 | Config::get('laratrust.models.permission'), 139 | 'user', 140 | Config::get('laratrust.tables.permission_user'), 141 | Config::get('laratrust.foreign_keys.user'), 142 | Config::get('laratrust.foreign_keys.permission') 143 | ); 144 | 145 | if (Config::get('laratrust.teams.enabled')) { 146 | $permissions->withPivot(Config::get('laratrust.foreign_keys.team')); 147 | } 148 | 149 | return $permissions; 150 | } 151 | 152 | /** 153 | * Return the right checker for the user model. 154 | */ 155 | protected function laratrustUserChecker(): UserChecker 156 | { 157 | return (new CheckersManager($this))->getUserChecker(); 158 | } 159 | 160 | /** 161 | * Get the the names of the user's roles. 162 | */ 163 | public function getRoles(mixed $team = null): array 164 | { 165 | return $this->laratrustUserChecker()->getCurrentUserRoles($team); 166 | } 167 | 168 | /** 169 | * Checks if the user has a role by its name. 170 | */ 171 | public function hasRole( 172 | string|array|BackedEnum $name, 173 | mixed $team = null, 174 | bool $requireAll = false 175 | ): bool { 176 | return $this->laratrustUserChecker()->currentUserHasRole( 177 | $name, 178 | $team, 179 | $requireAll 180 | ); 181 | } 182 | 183 | /** 184 | * Check if user has a permission by its name. 185 | */ 186 | public function hasPermission( 187 | string|array|BackedEnum $permission, 188 | mixed $team = null, 189 | bool $requireAll = false 190 | ): bool { 191 | return $this->laratrustUserChecker()->currentUserHasPermission( 192 | $permission, 193 | $team, 194 | $requireAll 195 | ); 196 | } 197 | 198 | /** 199 | * Check if user has a permission by its name. 200 | */ 201 | public function isAbleTo( 202 | string|array|BackedEnum $permission, 203 | mixed $team = null, 204 | bool $requireAll = false 205 | ): bool { 206 | return $this->hasPermission($permission, $team, $requireAll); 207 | } 208 | 209 | /** 210 | * Checks role(s) and permission(s). 211 | * 212 | * @param array $options validate_all{true|false} or return_type{boolean|array|both} 213 | * 214 | * @throws \InvalidArgumentException 215 | */ 216 | public function ability( 217 | string|array|BackedEnum $roles, 218 | string|array|BackedEnum $permissions, 219 | mixed $team = null, 220 | array $options = [] 221 | ): array|bool { 222 | return $this->laratrustUserChecker()->currentUserHasAbility( 223 | $roles, 224 | $permissions, 225 | $team, 226 | $options 227 | ); 228 | } 229 | 230 | /** 231 | * Check if the given relationship is a valid laratrust relationship. 232 | */ 233 | private function isValidRelationship(string $relationship): bool 234 | { 235 | return in_array($relationship, ['roles', 'permissions']); 236 | } 237 | 238 | /** 239 | * Alias to eloquent many-to-many relation's attach() method. 240 | */ 241 | private function attachModel( 242 | string $relationship, 243 | array|string|int|Model|UuidInterface|BackedEnum $object, 244 | array|string|int|Model|UuidInterface|null $team 245 | ): static { 246 | if (! $this->isValidRelationship($relationship)) { 247 | throw new InvalidArgumentException; 248 | } 249 | 250 | $attributes = []; 251 | $objectType = Str::singular($relationship); 252 | $object = Helper::getIdFor($object, $objectType); 253 | 254 | if (Config::get('laratrust.teams.enabled')) { 255 | $team = Helper::getIdFor($team, 'team'); 256 | 257 | if ( 258 | $this->$relationship() 259 | ->wherePivot(Team::modelForeignKey(), $team) 260 | ->wherePivot(Config::get("laratrust.foreign_keys.{$objectType}"), $object) 261 | ->count() 262 | ) { 263 | return $this; 264 | } 265 | 266 | $attributes[Team::modelForeignKey()] = $team; 267 | } 268 | 269 | $this->$relationship()->attach( 270 | $object, 271 | $attributes 272 | ); 273 | $this->flushCache(); 274 | $this->fireLaratrustEvent("{$objectType}.added", [$this, $object, $team]); 275 | 276 | return $this; 277 | } 278 | 279 | /** 280 | * Alias to eloquent many-to-many relation's detach() method. 281 | */ 282 | private function detachModel( 283 | string $relationship, 284 | array|string|int|Model|UuidInterface|BackedEnum $object, 285 | array|string|int|Model|UuidInterface|null $team 286 | ): static { 287 | if (! $this->isValidRelationship($relationship)) { 288 | throw new InvalidArgumentException; 289 | } 290 | 291 | $objectType = Str::singular($relationship); 292 | $relationshipQuery = $this->$relationship(); 293 | 294 | if (Config::get('laratrust.teams.enabled')) { 295 | $relationshipQuery->wherePivot( 296 | Team::modelForeignKey(), 297 | Helper::getIdFor($team, 'team') 298 | ); 299 | } 300 | 301 | $object = Helper::getIdFor($object, $objectType); 302 | $relationshipQuery->detach($object); 303 | 304 | $this->flushCache(); 305 | $this->fireLaratrustEvent("{$objectType}.removed", [$this, $object, $team]); 306 | 307 | return $this; 308 | } 309 | 310 | /** 311 | * Alias to eloquent many-to-many relation's sync() method. 312 | */ 313 | private function syncModels( 314 | string $relationship, 315 | array|string|int|Model|UuidInterface|BackedEnum $objects, 316 | array|string|int|Model|UuidInterface|null $team, 317 | bool $detaching 318 | ): static { 319 | if (! $this->isValidRelationship($relationship)) { 320 | throw new InvalidArgumentException; 321 | } 322 | 323 | $objectType = Str::singular($relationship); 324 | $mappedObjects = []; 325 | $useTeams = Config::get('laratrust.teams.enabled'); 326 | $team = $useTeams ? Helper::getIdFor($team, 'team') : null; 327 | 328 | foreach ($objects as $object) { 329 | if ($useTeams && $team) { 330 | $mappedObjects[Helper::getIdFor($object, $objectType)] = [Team::modelForeignKey() => $team]; 331 | } else { 332 | $mappedObjects[] = Helper::getIdFor($object, $objectType); 333 | } 334 | } 335 | 336 | $relationshipToSync = $this->$relationship(); 337 | 338 | if ($useTeams) { 339 | $relationshipToSync->wherePivot(Team::modelForeignKey(), $team); 340 | } 341 | 342 | $result = $relationshipToSync->sync($mappedObjects, $detaching); 343 | 344 | $this->flushCache(); 345 | $this->fireLaratrustEvent("{$objectType}.synced", [$this, $result, $team]); 346 | 347 | return $this; 348 | } 349 | 350 | /** 351 | * Add a role to the user. 352 | */ 353 | public function addRole( 354 | array|string|int|Model|UuidInterface|BackedEnum $role, 355 | mixed $team = null 356 | ): static { 357 | return $this->attachModel('roles', $role, $team); 358 | } 359 | 360 | /** 361 | * Remove a role from the user. 362 | */ 363 | public function removeRole( 364 | array|string|int|Model|UuidInterface|BackedEnum $role, 365 | mixed $team = null 366 | ): static { 367 | return $this->detachModel('roles', $role, $team); 368 | } 369 | 370 | /** 371 | * Add multiple roles to a user. 372 | */ 373 | public function addRoles( 374 | array $roles = [], 375 | mixed $team = null 376 | ): static { 377 | foreach ($roles as $role) { 378 | $this->addRole($role, $team); 379 | } 380 | 381 | return $this; 382 | } 383 | 384 | /** 385 | * Remove multiple roles from a user. 386 | */ 387 | public function removeRoles( 388 | array $roles = [], 389 | mixed $team = null 390 | ): static { 391 | if (empty($roles)) { 392 | return $this->syncRoles([], $team); 393 | } 394 | 395 | foreach ($roles as $role) { 396 | $this->removeRole($role, $team); 397 | } 398 | 399 | return $this; 400 | } 401 | 402 | /** 403 | * Sync roles to the user. 404 | */ 405 | public function syncRoles( 406 | array $roles = [], 407 | mixed $team = null, 408 | bool $detaching = true 409 | ): static { 410 | return $this->syncModels('roles', $roles, $team, $detaching); 411 | } 412 | 413 | /** 414 | * Sync roles to the user without detaching. 415 | */ 416 | public function syncRolesWithoutDetaching( 417 | array $roles = [], 418 | mixed $team = null, 419 | ): static { 420 | return $this->syncRoles($roles, $team, false); 421 | } 422 | 423 | /** 424 | * Add direct permissions to the user. 425 | */ 426 | public function givePermission( 427 | array|string|int|Model|UuidInterface|BackedEnum $permission, 428 | mixed $team = null 429 | ): static { 430 | return $this->attachModel('permissions', $permission, $team); 431 | } 432 | 433 | /** 434 | * Remove direct permissions from the user. 435 | */ 436 | public function removePermission( 437 | array|string|int|Model|UuidInterface|BackedEnum $permission, 438 | mixed $team = null 439 | ): static { 440 | return $this->detachModel('permissions', $permission, $team); 441 | } 442 | 443 | /** 444 | * Add multiple permissions to the user. 445 | */ 446 | public function givePermissions( 447 | array $permissions = [], 448 | mixed $team = null 449 | ): static { 450 | foreach ($permissions as $permission) { 451 | $this->givePermission($permission, $team); 452 | } 453 | 454 | return $this; 455 | } 456 | 457 | /** 458 | * Remove multiple permissions from the user. 459 | */ 460 | public function removePermissions( 461 | array $permissions = [], 462 | mixed $team = null 463 | ): static { 464 | if (! $permissions) { 465 | return $this->syncPermissions([], $team); 466 | } 467 | 468 | foreach ($permissions as $permission) { 469 | $this->removePermission($permission, $team); 470 | } 471 | 472 | return $this; 473 | } 474 | 475 | /** 476 | * Sync permissions to the user. 477 | */ 478 | public function syncPermissions( 479 | array $permissions = [], 480 | mixed $team = null, 481 | bool $detaching = true 482 | ): static { 483 | return $this->syncModels('permissions', $permissions, $team, $detaching); 484 | } 485 | 486 | /** 487 | * Sync permissions to the user without detaching. 488 | */ 489 | public function syncPermissionsWithoutDetaching( 490 | array $permissions = [], 491 | mixed $team = null 492 | ): static { 493 | return $this->syncPermissions($permissions, $team, false); 494 | } 495 | 496 | /** 497 | * Return all the user permissions. 498 | * 499 | * @return Collection<\Laratrust\Contracts\Permission> 500 | */ 501 | public function allPermissions(?array $columns = null, mixed $team = false): Collection 502 | { 503 | $columns = is_array($columns) ? $columns : null; 504 | if ($columns) { 505 | $columns[] = 'id'; 506 | $columns = array_unique($columns); 507 | } 508 | $withColumns = $columns ? ':'.implode(',', $columns) : ''; 509 | 510 | $roles = $this->roles() 511 | ->when( 512 | Config::get('laratrust.teams.enabled') && $team !== false, 513 | fn ($query) => $query->whereHas('permissions', function ($permissionQuery) use ($team) { 514 | $permissionQuery->where( 515 | Config::get('laratrust.foreign_keys.team'), 516 | Helper::getIdFor($team, 'team') 517 | ); 518 | }) 519 | ) 520 | ->with("permissions{$withColumns}") 521 | ->get(); 522 | 523 | $rolesPermissions = $roles->flatMap(function ($role) { 524 | return $role->permissions; 525 | }); 526 | 527 | $directPermissions = $this->permissions() 528 | ->when( 529 | Config::get('laratrust.teams.enabled') && $team !== false, 530 | fn ($query) => $query->where( 531 | config('laratrust.foreign_keys.team'), 532 | Helper::getIdFor($team, 'team') 533 | ) 534 | ); 535 | 536 | return $directPermissions 537 | ->get($columns ?? ['*']) 538 | ->merge($rolesPermissions) 539 | ->unique('id'); 540 | } 541 | 542 | /** 543 | * Flush the user's cache. 544 | */ 545 | public function flushCache(): void 546 | { 547 | $this->laratrustUserChecker()->currentUserFlushCache(); 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /stubs/permission.stub: -------------------------------------------------------------------------------- 1 | truncateLaratrustTables(); 18 | 19 | $config = Config::get('laratrust_seeder.roles_structure'); 20 | 21 | if ($config === null) { 22 | $this->command->error("The configuration has not been published. Did you run `php artisan vendor:publish --tag=\"laratrust-seeder\"`"); 23 | $this->command->line(''); 24 | return false; 25 | } 26 | 27 | $mapPermission = collect(config('laratrust_seeder.permissions_map')); 28 | 29 | foreach ($config as $key => $modules) { 30 | 31 | // Create a new role 32 | $role = {{roleConfiguredModelClass}}::firstOrCreate([ 33 | 'name' => $key, 34 | 'display_name' => ucwords(str_replace('_', ' ', $key)), 35 | 'description' => ucwords(str_replace('_', ' ', $key)) 36 | ]); 37 | $permissions = []; 38 | 39 | $this->command->info('Creating Role '. strtoupper($key)); 40 | 41 | // Reading role permission modules 42 | foreach ($modules as $module => $value) { 43 | 44 | foreach (explode(',', $value) as $perm) { 45 | 46 | $permissionValue = $mapPermission->get($perm); 47 | 48 | $permissions[] = {{permissionConfiguredModelClass}}::firstOrCreate([ 49 | 'name' => $module . '-' . $permissionValue, 50 | 'display_name' => ucfirst($permissionValue) . ' ' . ucfirst($module), 51 | 'description' => ucfirst($permissionValue) . ' ' . ucfirst($module), 52 | ])->id; 53 | 54 | $this->command->info('Creating Permission to '.$permissionValue.' for '. $module); 55 | } 56 | } 57 | 58 | // Add all permissions to the role 59 | $role->permissions()->sync($permissions); 60 | 61 | if (Config::get('laratrust_seeder.create_users')) { 62 | $this->command->info("Creating '{$key}' user"); 63 | // Create default user for each role 64 | $user = {{userConfiguredModelClass}}::create([ 65 | 'name' => ucwords(str_replace('_', ' ', $key)), 66 | 'email' => $key.'@app.com', 67 | 'password' => bcrypt('password') 68 | ]); 69 | $user->addRole($role); 70 | } 71 | 72 | } 73 | } 74 | 75 | /** 76 | * Truncates all the laratrust tables and the users table 77 | * 78 | * @return void 79 | */ 80 | public function truncateLaratrustTables() 81 | { 82 | $this->command->info('Truncating User, Role and Permission tables'); 83 | Schema::disableForeignKeyConstraints(); 84 | 85 | DB::table('{{permission_roleConfiguredTableName}}')->truncate(); 86 | DB::table('{{permission_userConfiguredTableName}}')->truncate(); 87 | DB::table('{{role_userConfiguredTableName}}')->truncate(); 88 | 89 | if (Config::get('laratrust_seeder.truncate_tables')) { 90 | DB::table('{{rolesTableName}}')->truncate(); 91 | DB::table('{{permissionsTableName}}')->truncate(); 92 | 93 | if (Config::get('laratrust_seeder.create_users')) { 94 | $usersTable = (new {{userConfiguredModelClass}})->getTable(); 95 | DB::table($usersTable)->truncate(); 96 | } 97 | } 98 | 99 | Schema::enableForeignKeyConstraints(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /stubs/team.stub: -------------------------------------------------------------------------------- 1 |