Roles And Permissions For Laravel 5.4
, (*1)
A Powerful package for handling roles and permissions in Laravel 5.4., (*2)
Installation
This package is very easy to set up. There are only couple of steps., (*3)
Composer
Pull this package in through Composer, (*4)
composer require jeremykenedy/laravel-roles
Service Provider
Add the package to your application service providers in config/app.php
file., (*5)
'providers' => [
...
/**
* Third Party Service Providers...
*/
jeremykenedy\LaravelRoles\RolesServiceProvider::class,
],
Config File
Publish the package config file and migrations to your application. Run these commands inside your terminal., (*6)
php artisan vendor:publish --provider="jeremykenedy\LaravelRoles\RolesServiceProvider" --tag=config
php artisan vendor:publish --provider="jeremykenedy\LaravelRoles\RolesServiceProvider" --tag=migrations
php artisan vendor:publish --provider="jeremykenedy\LaravelRoles\RolesServiceProvider" --tag=seeds
HasRoleAndPermission Trait And Contract
-
Include HasRoleAndPermission
trait and also implement HasRoleAndPermission
contract inside your User
model. See example below., (*7)
-
Include use jeremykenedy\LaravelRoles\Traits\HasRoleAndPermission;
in the top of your User
model below the namespace and implement the HasRoleAndPermission
trait. See example below., (*8)
Example User
model Trait And Contract:, (*9)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use jeremykenedy\LaravelRoles\Traits\HasRoleAndPermission;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
use HasRoleAndPermission;
// rest of your model ...
}
Migrations and seeds
This uses the default users table which is in Laravel. You should already have the migration file for the users table available and migrated., (*10)
-
Setup the needed tables:, (*11)
php artisan migrate
, (*12)
-
Update database\seeds\DatabaseSeeder.php
to include the seeds. See example below., (*13)
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
$this->call('PermissionsTableSeeder');
$this->call('RolesTableSeeder');
$this->call('ConnectRelationshipsSeeder');
//$this->call('UsersTableSeeder');
Model::reguard();
}
}
- Seed an initial set of Permissions, Roles, and Users with roles.
composer dump-autoload
php artisan db:seed
Roles Seeded
Property |
Value |
Name |
Admin |
Slug |
admin |
Description |
Admin Role |
Level |
5 |
Property |
Value |
Name |
User |
Slug |
user |
Description |
User Role |
Level |
1 |
Property |
Value |
Name |
Unverified |
Slug |
unverified |
Description |
Unverified Role |
Level |
0 |
Permissions Seeded:
Property |
Value |
name |
Can View Users |
slug |
view.users |
description |
Can view users |
model |
Permission |
Property |
Value |
name |
Can Create Users |
slug |
create.users |
description |
Can create new users |
model |
Permission |
Property |
Value |
name |
Can Edit Users |
slug |
edit.users |
description |
Can edit users |
model |
Permission |
Property |
Value |
name |
Can Delete Users |
slug |
delete.users |
description |
Can delete users |
model |
Permission |
And that's it!
Migrate from bican roles
If you migrate from bican/roles to jeremykenedy/LaravelRoles you will need to update a few things.
- Change all calls to can
, canOne
and canAll
to hasPermission
, hasOnePermission
, hasAllPermissions
.
- Change all calls to is
, isOne
and isAll
to hasRole
, hasOneRole
, hasAllRoles
., (*14)
Usage
Creating Roles
use jeremykenedy\LaravelRoles\Models\Role;
$adminRole = Role::create([
'name' => 'Admin',
'slug' => 'admin',
'description' => '',
'level' => 5,
]);
$moderatorRole = Role::create([
'name' => 'Forum Moderator',
'slug' => 'forum.moderator',
]);
Because of Slugable
trait, if you make a mistake and for example leave a space in slug parameter, it'll be replaced with a dot automatically, because of str_slug
function., (*15)
Attaching, Detaching and Syncing Roles
It's really simple. You fetch a user from database and call attachRole
method. There is BelongsToMany
relationship between User
and Role
model., (*16)
use App\User;
$user = User::find($id);
$user->attachRole($adminRole); // you can pass whole object, or just an id
$user->detachRole($adminRole); // in case you want to detach role
$user->detachAllRoles(); // in case you want to detach all roles
$user->syncRoles($roles); // you can pass Eloquent collection, or just an array of ids
Assign a user role to new registered users
You can assign the user a role upon the users registration by updating the file app\Http\Controllers\Auth\RegisterController.php
.
You can assign a role to a user upon registration by including the needed models and modifying the create()
method to attach a user role. See example below:, (*17)
- Update the top of
app\Http\Controllers\Auth\RegisterController.php
:
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use jeremykenedy\LaravelRoles\Models\Role;
use jeremykenedy\LaravelRoles\Models\Permission;
use Illuminate\Foundation\Auth\RegistersUsers;
- Updated
create()
method of app\Http\Controllers\Auth\RegisterController.php
:
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
$role = Role::where('name', '=', 'User')->first(); //choose the default role upon user creation.
$user->attachRole($role);
return $user;
}
Checking For Roles
You can now check if the user has required role., (*18)
if ($user->hasRole('admin')) { // you can pass an id or slug
//
}
You can also do this:, (*19)
if ($user->isAdmin()) {
//
}
And of course, there is a way to check for multiple roles:, (*20)
if ($user->hasRole(['admin', 'moderator'])) {
/*
| Or alternatively:
| $user->hasRole('admin, moderator'), $user->hasRole('admin|moderator'),
| $user->hasOneRole('admin, moderator'), $user->hasOneRole(['admin', 'moderator']), $user->hasOneRole('admin|moderator')
*/
// The user has at least one of the roles
}
if ($user->hasRole(['admin', 'moderator'], true)) {
/*
| Or alternatively:
| $user->hasRole('admin, moderator', true), $user->hasRole('admin|moderator', true),
| $user->hasAllRoles('admin, moderator'), $user->hasAllRoles(['admin', 'moderator']), $user->hasAllRoles('admin|moderator')
*/
// The user has all roles
}
Levels
When you are creating roles, there is optional parameter level
. It is set to 1
by default, but you can overwrite it and then you can do something like this:, (*21)
if ($user->level() > 4) {
//
}
If user has multiple roles, method level
returns the highest one., (*22)
Level
has also big effect on inheriting permissions. About it later., (*23)
Creating Permissions
It's very simple thanks to Permission
model., (*24)
use jeremykenedy\LaravelRoles\Models\Permission;
$createUsersPermission = Permission::create([
'name' => 'Create users',
'slug' => 'create.users',
'description' => '', // optional
]);
$deleteUsersPermission = Permission::create([
'name' => 'Delete users',
'slug' => 'delete.users',
]);
Attaching, Detaching and Syncing Permissions
You can attach permissions to a role or directly to a specific user (and of course detach them as well)., (*25)
use App\User;
use jeremykenedy\LaravelRoles\Models\Role;
$role = Role::find($roleId);
$role->attachPermission($createUsersPermission); // permission attached to a role
$user = User::find($userId);
$user->attachPermission($deleteUsersPermission); // permission attached to a user
$role->detachPermission($createUsersPermission); // in case you want to detach permission
$role->detachAllPermissions(); // in case you want to detach all permissions
$role->syncPermissions($permissions); // you can pass Eloquent collection, or just an array of ids
$user->detachPermission($deleteUsersPermission);
$user->detachAllPermissions();
$user->syncPermissions($permissions); // you can pass Eloquent collection, or just an array of ids
Checking For Permissions
if ($user->hasPermission('create.users') { // you can pass an id or slug
//
}
if ($user->canDeleteUsers()) {
//
}
You can check for multiple permissions the same way as roles. You can make use of additional methods like hasOnePermission
or hasAllPermissions
., (*26)
Permissions Inheriting
Role with higher level is inheriting permission from roles with lower level., (*27)
There is an example of this magic
:, (*28)
You have three roles: user
, moderator
and admin
. User has a permission to read articles, moderator can manage comments and admin can create articles. User has a level 1, moderator level 2 and admin level 3. It means, moderator and administrator has also permission to read articles, but administrator can manage comments as well., (*29)
If you don't want permissions inheriting feature in you application, simply ignore level
parameter when you're creating roles., (*30)
Entity Check
Let's say you have an article and you want to edit it. This article belongs to a user (there is a column user_id
in articles table)., (*31)
use App\Article;
use jeremykenedy\LaravelRoles\Models\Permission;
$editArticlesPermission = Permission::create([
'name' => 'Edit articles',
'slug' => 'edit.articles',
'model' => 'App\Article',
]);
$user->attachPermission($editArticlesPermission);
$article = Article::find(1);
if ($user->allowed('edit.articles', $article)) { // $user->allowedEditArticles($article)
//
}
This condition checks if the current user is the owner of article. If not, it will be looking inside user permissions for a row we created before., (*32)
if ($user->allowed('edit.articles', $article, false)) { // now owner check is disabled
//
}
Blade Extensions
There are four Blade extensions. Basically, it is replacement for classic if statements., (*33)
@role('admin') // @if(Auth::check() && Auth::user()->hasRole('admin'))
// user has admin role
@endrole
@permission('edit.articles') // @if(Auth::check() && Auth::user()->hasPermission('edit.articles'))
// user has edit articles permissison
@endpermission
@level(2) // @if(Auth::check() && Auth::user()->level() >= 2)
// user has level 2 or higher
@endlevel
@allowed('edit', $article) // @if(Auth::check() && Auth::user()->allowed('edit', $article))
// show edit button
@endallowed
@role('admin|moderator', true) // @if(Auth::check() && Auth::user()->hasRole('admin|moderator', true))
// user has admin and moderator role
@else
// something else
@endrole
Middleware
This package comes with VerifyRole
, VerifyPermission
and VerifyLevel
middleware. You must add them inside your app/Http/Kernel.php
file., (*34)
/**
* The application's route middleware.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'role' => \jeremykenedy\LaravelRoles\Middleware\VerifyRole::class,
'permission' => \jeremykenedy\LaravelRoles\Middleware\VerifyPermission::class,
'level' => \jeremykenedy\LaravelRoles\Middleware\VerifyLevel::class,
];
Now you can easily protect your routes., (*35)
$router->get('/example', [
'as' => 'example',
'middleware' => 'role:admin',
'uses' => 'ExampleController@index',
]);
$router->post('/example', [
'as' => 'example',
'middleware' => 'permission:edit.articles',
'uses' => 'ExampleController@index',
]);
$router->get('/example', [
'as' => 'example',
'middleware' => 'level:2', // level >= 2
'uses' => 'ExampleController@index',
]);
It throws \jeremykenedy\LaravelRoles\Exceptions\RoleDeniedException
, \jeremykenedy\LaravelRoles\Exceptions\PermissionDeniedException
or \jeremykenedy\LaravelRoles\Exceptions\LevelDeniedException
exceptions if it goes wrong., (*36)
You can catch these exceptions inside app/Exceptions/Handler.php
file and do whatever you want., (*37)
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
$userLevelCheck = $exception instanceof \jeremykenedy\LaravelRoles\Exceptions\RoleDeniedException ||
$exception instanceof \jeremykenedy\LaravelRoles\Exceptions\RoleDeniedException ||
$exception instanceof \jeremykenedy\LaravelRoles\Exceptions\PermissionDeniedException ||
$exception instanceof \jeremykenedy\LaravelRoles\Exceptions\LevelDeniedException;
if ($userLevelCheck) {
if ($request->expectsJson()) {
return Response::json(array(
'error' => 403,
'message' => 'Unauthorized.'
), 403);
}
abort(403);
}
return parent::render($request, $exception);
}
Config File
You can change connection for models, slug separator, models path and there is also a handy pretend feature. Have a look at config file for more information., (*38)
For more information, please have a look at HasRoleAndPermission contract., (*39)
Credit Note
This package is an adaptation of romanbican/roles and ultraware/roles., (*40)
License
This package is free software distributed under the terms of the MIT license., (*41)