Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Laravel 5 Simple ACL - Protect routes by an account / role type

#Laravel 5 Simple ACL manager

Protect your routes with user roles. Simply add a 'role_id' to the User model, install the roles table and seed if you need some example roles to get going.

If the user has a 'Root' role, then they can perform any actions.

Installation

Simply copy the files across into the appropriate directories, and register the middleware in App\Http\Kernel.php

Then specify a 'roles' middleware on the route you'd like to protect, and specify the individual roles as an array:

Route::get('user/{user}', [
     'middleware' => ['auth', 'roles'],
     'uses' => 'UserController@index',
     'roles' => ['administrator', 'manager']
]);

If you found this ACL manager helpful please give this repo a star, and give me a follow. Any questions, please leave a comment.

<?php namespace App\Http\Middleware;
// First copy this file into your middleware directoy
use Closure;
class CheckRole{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// Get the required roles from the route
$roles = $this->getRequiredRoleForRoute($request->route());
// Check if a role is required for the route, and
// if so, ensure that the user has that role.
if($request->user()->hasRole($roles) || !$roles)
{
return $next($request);
}
return response([
'error' => [
'code' => 'INSUFFICIENT_ROLE',
'description' => 'You are not authorized to access this resource.'
]
], 401);
}
private function getRequiredRoleForRoute($route)
{
$actions = $route->getAction();
return isset($actions['roles']) ? $actions['roles'] : null;
}
}
<?php
// Register the new route middleware
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
'roles' => 'App\Http\Middleware\CheckRole',
];
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model {
protected $table = 'roles';
public function users()
{
return $this->hasMany('App\User', 'role_id', 'id');
}
}
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateRolesTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('role', function($table) {
$table->increments('id');
$table->string('name', 40);
$table->string('description', 255);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('role');
}
}
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use App\Role;
class RoleTableSeeder extends Seeder{
public function run()
{
if (App::environment() === 'production') {
exit('I just stopped you getting fired. Love, Amo.');
}
DB::table('role')->truncate();
Role::create([
'id' => 1,
'name' => 'Root',
'description' => 'Use this account with extreme caution. When using this account it is possible to cause irreversible damage to the system.'
]);
Role::create([
'id' => 2,
'name' => 'Administrator',
'description' => 'Full access to create, edit, and update companies, and orders.'
]);
Role::create([
'id' => 3,
'name' => 'Manager',
'description' => 'Ability to create new companies and orders, or edit and update any existing ones.'
]);
Role::create([
'id' => 4,
'name' => 'Company Manager',
'description' => 'Able to manage the company that the user belongs to, including adding sites, creating new users and assigning licences.'
]);
Role::create([
'id' => 5,
'name' => 'User',
'description' => 'A standard user that can have a licence assigned to them. No administrative features.'
]);
}
}
<?php
Route::get('user/{user}', [
'middleware' => ['auth', 'roles'], // A 'roles' middleware must be specified
'uses' => 'UserController@index',
'roles' => ['administrator', 'manager'] // Only an administrator, or a manager can access this route
]);
<?php
// The User model
public function role()
{
return $this->hasOne('App\Role', 'id', 'role_id');
}
public function hasRole($roles)
{
$this->have_role = $this->getUserRole();
// Check if the user is a root account
if($this->have_role->name == 'Root') {
return true;
}
if(is_array($roles)){
foreach($roles as $need_role){
if($this->checkIfUserHasRole($need_role)) {
return true;
}
}
} else{
return $this->checkIfUserHasRole($roles);
}
return false;
}
private function getUserRole()
{
return $this->role()->getResults();
}
private function checkIfUserHasRole($need_role)
{
return (strtolower($need_role)==strtolower($this->have_role->name)) ? true : false;
}
@z900collector
Copy link

z900collector commented May 23, 2016

I found a few issues.

  1. You need to add the syntax for the 'users' table changes to your doco: alter table users add role_id int unsigned not null;
  2. Then set the users role_id's otherwise it does not work.
  3. Role.php has "roles" (as mentioned earlier).

Seams to work ok after this.

@benmclendon
Copy link

benmclendon commented Jun 7, 2016

Laravel newbee, apologies in advance. Laravel 5.2.35

My Route:

Route::get('portal', [
    'middleware' => ['auth', 'roles'], // A 'roles' middleware must be specified
    'uses' => 'UserController@index',
    'roles' => ['user'] // Only an administrator, or a manager can access this route
]);

I'm getting this: "ReflectionException in Route.php line 280: Method App\Http\Controllers\UserController::index() does not exist"
UserController.php is your User.php

If I add an index function to the controller I get: "BadMethodCallException in Builder.php line 2345: Call to undefined method Illuminate\Database\Query\Builder::hasRole()"

I've made the adjustments mentioned above.

Suggestions?

@gkimpson
Copy link

gkimpson commented Jun 17, 2016

I am using Laravel 5.2.38 and have followed the steps above included the fixes and when I try to run php artisan migrate I get the following error:

[Symfony\Component\Debug\Exception\FatalErrorException] Class '' not found

Any ideas?

@dldelante
Copy link

dldelante commented Jun 22, 2016

Just learned laravel and php and still a lot to learn. I just want to share that I use spatie-permissions to create roles and permissions then through this Simple ACL Manager I was able to understand a thing or two about authentication.
So instead of using the 'handle' method in VerifyRole.php I used the 'handle' method from zizaco/entrust and now I can use this in route groups. See zizaco/entrust on how to use the 'role' on route groups.

@mkillua
Copy link

mkillua commented Jul 22, 2016

gkimpson your problem is because this migrate copy and pasted don't have a date.
so, try this - > php artisan make:migration create_roles_table
after, you need modify the date of generated migration, 2016 to 2013 for example.(because exist foreign key in users)
now copy and past the original migrate method

@aacook
Copy link

aacook commented Jul 24, 2016

Thanks - any suggestions on how best to set a user's role? Here's how I'm currently doing it.

$role = Role::where('name', '=', 'client')->first();
$user->role_id = $role->id;

@amochohan
Copy link
Author

amochohan commented Jul 28, 2016

@aacook if you have a role() relationship on the User model you can simply do $user->role()->associate($role);

@lcdss
Copy link

lcdss commented Jul 29, 2016

@aacook $user->role()->associate(Role::whereName('client')->first());

@ssirjann
Copy link

ssirjann commented Aug 19, 2016

can anyone suggest on this, http://stackoverflow.com/questions/38827474/laravel-5-polymorphic-relationship-with-foreign-key .
I need to add polymorphic relationship between user types, as well as have a separate role table for setting permissions.

@kyoukhana
Copy link

kyoukhana commented Aug 22, 2016

I currently have a users.php with the following code under App/user.php. Was wondering where the User code is suppose to go. Also I didn't see any examples on how to use this using route::

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

/**
 * App\User
 *
 * @mixin \Eloquent
 */
class User extends Authenticatable
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

@blorange2
Copy link

blorange2 commented Aug 22, 2016

Can you grab the name of the role in a blade?

@amochohan
Copy link
Author

amochohan commented Aug 22, 2016

@blorange2 Yes, simply inject the User object into your template and then you can use $user->role->name

@nicolasdanelon
Copy link

nicolasdanelon commented Aug 23, 2016

can you re-write this code in go-lang ?
hahaha just kidding.
Grate job, I'll use this with my REST API ;)

@skysanket
Copy link

skysanket commented Aug 30, 2016

How can I use with resource routes.?

@LexPr
Copy link

LexPr commented Aug 30, 2016

TANKS

@AngeloMerlo
Copy link

AngeloMerlo commented Oct 5, 2016

I'm using 5.3 laravel and I have a problem I do not know how to solve. Could anyone help me, please?

Route: Route::get('/', function () { return view('welcome'); });
error: FatalThrowableError in CheckRole.php line 20: Call to a member function hasRole() on null

@greatsami
Copy link

greatsami commented Oct 13, 2016

Update:
I solved my issue. by adding this codes in app\Exceptions\Handler.php @ unauthenticated function:

$route = Route::getCurrentRoute()->getPath();
switch($route) {
     case 'admin':
             $login = '/admin/login';
             break;
     default:
             $login = '/login';
             break;
}

return redirect()->guest($login);

Now its working :)

Hi

I used your tutorial and every thing is OK.
i built admin layouts as separate theme, when I write in url: (http://multiauth.dev/admin) (and not logged) it redirect me to original login.
I need to redirect me to (http://multiauth.dev/admin/login).

i have a note if logged to admin i can see (http://multiauth.dev/admin/login) page.

How i can do it?
note: I'm newbie with laravel

@JoseCage
Copy link

JoseCage commented Oct 19, 2016

Thank you o much for this snnipet..

Works for me.. 👍 📦 💯

@Shkeeny
Copy link

Shkeeny commented Nov 24, 2016

Hello, I'm new to Laravel, how do I add a restriction to this route: Route::resource('users', 'UserController'); ? Thanks in advance!

@amochohan
Copy link
Author

amochohan commented Nov 28, 2016

@Shkeeny this code won't work with route resources out of the box.

@k9uma
Copy link

k9uma commented Dec 7, 2016

Hai, I used this method a couple months ago L5.0, just tried it with 5.3 and i keep getting the same error about not finding the CheckRole class, something like this "ReflectionException in Container.php line 749:
Class App\Http\Middleware\CheckRole does not exist"
Any ideas??

@yourzed
Copy link

yourzed commented Dec 7, 2016

Hi @drawmyattention,
your code works great with my project. The thing is that it only works for two roles, when I do a thirth, it passes the autenthication, but the view shows a 500 error with no more info. My routes are like this:

Route::group(['middleware' => ['auth','roles'], 'roles' => ['Administrator']], function() {
Route::controller('/system/dashboard', 'AdminController');
.... THIS WORKS
});

Route::group(['middleware' => ['auth','roles'], 'roles' => ['Role1']], function() {
Route::controller('/system/dashboard', 'AdminController');
.... THIS WORKS
});

Route::group(['middleware' => ['auth','roles'], 'roles' => ['Role2']], function() {
Route::controller('/system/dashboard', 'AdminController');
.... IT Goes to the controller, but when showing the view: error 500...
});

Any idea of why? Of course I have all the roles on the database. And If I delete the last route group and try to login with that customer, it shows the Insuficient role, you are not allowed, correctly.
Thanks in advance

@amochohan
Copy link
Author

amochohan commented Dec 8, 2016

@yourzed if it gets to the controller, the middleware has allowed the request. If you're getting a 500 error from the view, I don't see how that'd related. You may be referring to a value that doesn't exist, which causes blade to thrown an exception.

@yourzed
Copy link

yourzed commented Dec 9, 2016

Many thanks @drawmyattention, finally I managed to work it ! I had to made some changes on the app.php and that was all. Thanks, it works like charm on laravel 5.1 ;)

@daino92
Copy link

daino92 commented Dec 13, 2016

@drawmyattention Hello mate! So I get this error:

BadMethodCallException in Builder.php line 2405:
Call to undefined method Illuminate\Database\Query\Builder::role()

This is my sample route:

Route::get('/admin/products', [
'uses' => 'AdminController@adminProducts',
'as' => 'admin.products',
'middleware' => ['auth', 'roles'],
'roles' => ['admin'],
]);

Can you tell me what's going on? Also I have added the roles() relationship function on the User model.

@rutvij2292
Copy link

rutvij2292 commented Jan 17, 2017

@drawmyattention Thanks for the simple solution for ACL. I would like to add few lines in controller to work with Route::group.

Here's my suggestion.

`
Route::group(['middleware' => ['auth', 'roles'], 'roles' => ['admin']], function() {
Route::get('admin/{user_id}/edit/{course_id}/reasons/{reason_id}', [
'uses' => 'AdminController@reasonsShowAndEdit'
]);

    Route::get('admin/{user_id}/edit/{course_id}', [
        'uses' => 'AdminController@journalsShow',
    ]);

    Route::get('admin/home', [
        'uses' => 'AdminController@index',
    ]);
});

`

This works like a charm. Thanks once again.

@rexdarel
Copy link

rexdarel commented Sep 14, 2017

Hi, im using the make:auth and I'm new to laravel. I have added a column "type" in the users table to determine if the user is an admin or super admin or just a user. I was able to protect the components of my view files (like buttons, tables) using the "type" column but the problem is the routes of the super admin can still access by the admin... My question is, how can I use your solution on my current situation? Or do you have any idea how to protect the routes base on the column "type"?

@candrasetiadi
Copy link

candrasetiadi commented Mar 3, 2018

Hello...
Everyhting was work for me.
But How i make the roles specified on controller? you make it on route file like this 'roles' => ['admin', 'superadmin']
because i have dynamic roles on my roles table.

please help me. thanks

@gitongacollin
Copy link

gitongacollin commented Oct 10, 2018

Hi, I'm new to Laravel
Every time I try to check if the roles are working I'm getting this error
Call to a member function hasRole() on null
I create a route to try check
Kindly help

Here is the snippet of my route
`Route::group(['middleware'=>['authen','roles'],'roles'=>['Root']],function(){
//for Root

Route::get('/createUser',function(){
	echo "This is for Root test";
});`

@miladjamali
Copy link

miladjamali commented Aug 25, 2019

nice this helped me much thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment