In Laravel, roles and permissions have been one of the most confusing topics over the years. Mostly, because there is no documentation about it: the same things "hide" under other terms in the framework, like "gates", "policies", "guards", etc. In this article, I will try to explain them all in "human language".
Gate is the same as Permission
One of the biggest confusions, in my opinion, is the term "gate". I think developers would have avoided a lot of confusion if they were called what they are.
Gates are Permissions, just called by another word.
What are the typical actions we need to perform with permissions?
- Define the permission, ex. "manage_users"
- Check the permission on the front-end, ex. show/hide the button
- Check the permission on the back-end, ex. can/can't update the data
So yeah, replace the word "permission" with "gate", and you understand it all.
A simple Laravel example would be this:
app/Providers/AppServiceProvider.php:
use App\Models\User;
use Illuminate\Support\Facades\Gate;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
// Should return TRUE or FALSE
Gate::define('manage_users', function(User $user) {
return $user->is_admin == 1;
});
}
}
resources/views/navigation.blade.php:
<ul>
<li>
<a href="{{ route('projects.index') }}">Projects</a>
</li>
@can('manage_users')
<li>
<a href="{{ route('users.index') }}">Users</a>
</li>
@endcan
</ul>
routes/web.php:
Route::resource('users', UserController::class)->middleware('can:manage_users');
Now, I know that, technically, Gate may mean more than one permission.So, instead of "manage_users", you could define something like "admin_area". But in most examples I've seen, Gate is a synonym for Permission.
Also, in some cases, the permissions are called "abilities", like in the Bouncer package. It also means the same thing - ability/permission for some action.We'll get to the packages, later in this article.
Various Ways to Check Gate Permission
Another source of confusion is how/where to check the Gate. It's so flexible that you may find very different examples. Let's run through them:
Option 1. Routes: middleware('can:xxxxxx')
This is the example from above.Directly on the route/group, you may assign the middleware:
Route::post('users', [UserController::class, 'store'])
->middleware('can:create_users');
Option 2.
In other words, a role is an entity OUTSIDE of the Laravel framework, so we need to build the role structure ourselves.It may be a part of the overall auth confusion, but it makes perfect sense because we should control how roles are defined: Let's discuss another confusion: in Laravel docs, you won't find any section about User Roles.
Even shorter option, and my favorite one, is to use In other words, a role is an entity OUTSIDE of the Laravel framework, so we need to build the role structure ourselves.It may be a part of the overall auth confusion, but it makes perfect sense because we should control how roles are defined: Let's discuss another confusion: in Laravel docs, you won't find any section about User Roles.
Even shorter option, and my favorite one, is to use In other words, a role is an entity OUTSIDE of the Laravel framework, so we need to build the role structure ourselves.It may be a part of the overall auth confusion, but it makes perfect sense because we should control how roles are defined: Let's discuss another confusion: in Laravel docs, you won't find any section about User Roles.
Oh, those. They cause so much confusion over the years.Many developers thought that Guards are Roles, and started creating separate DB tables like "administrators", and then assigning those as Guards.Partly, because in the documentation you may find code snippets like In other words, a role is an entity OUTSIDE of the Laravel framework, so we need to build the role structure ourselves.It may be a part of the overall auth confusion, but it makes perfect sense because we should control how roles are defined: Let's discuss another confusion: in Laravel docs, you won't find any section about User Roles.
Oh, those. They cause so much confusion over the years.Many developers thought that Guards are Roles, and started creating separate DB tables like "administrators", and then assigning those as Guards.Partly, because in the documentation you may find code snippets like In other words, a role is an entity OUTSIDE of the Laravel framework, so we need to build the role structure ourselves.It may be a part of the overall auth confusion, but it makes perfect sense because we should control how roles are defined: Let's discuss another confusion: in Laravel docs, you won't find any section about User Roles.
Oh, those. They cause so much confusion over the years.Many developers thought that Guards are Roles, and started creating separate DB tables like "administrators", and then assigning those as Guards.Partly, because in the documentation you may find code snippets like In other words, a role is an entity OUTSIDE of the Laravel framework, so we need to build the role structure ourselves.It may be a part of the overall auth confusion, but it makes perfect sense because we should control how roles are defined: Let's discuss another confusion: in Laravel docs, you won't find any section about User Roles.
Oh, those. They cause so much confusion over the years.Many developers thought that Guards are Roles, and started creating separate DB tables like "administrators", and then assigning those as Guards.Partly, because in the documentation you may find code snippets like In other words, a role is an entity OUTSIDE of the Laravel framework, so we need to build the role structure ourselves.It may be a part of the overall auth confusion, but it makes perfect sense because we should control how roles are defined: Let's discuss another confusion: in Laravel docs, you won't find any section about User Roles.
Oh, those. They cause so much confusion over the years.Many developers thought that Guards are Roles, and started creating separate DB tables like "administrators", and then assigning those as Guards.Partly, because in the documentation you may find code snippets like In other words, a role is an entity OUTSIDE of the Laravel framework, so we need to build the role structure ourselves.It may be a part of the overall auth confusion, but it makes perfect sense because we should control how roles are defined: Oh, those. They cause so much confusion over the years.Many developers thought that Guards are Roles, and started creating separate DB tables like "administrators", and then assigning those as Guards.Partly, because in the documentation you may find code snippets like So, guards are a more global concept than roles.
Oh, those. They cause so much confusion over the years.Many developers thought that Guards are Roles, and started creating separate DB tables like "administrators", and then assigning those as Guards.Partly, because in the documentation you may find code snippets like So, guards are a more global concept than roles.
Oh, those. They cause so much confusion over the years.Many developers thought that Guards are Roles, and started creating separate DB tables like "administrators", and then assigning those as Guards.Partly, because in the documentation you may find code snippets like So, guards are a more global concept than roles.
Oh, those. They cause so much confusion over the years.Many developers thought that Guards are Roles, and started creating separate DB tables like "administrators", and then assigning those as Guards.Partly, because in the documentation you may find code snippets like So, guards are a more global concept than roles.
Looks complicated?
public function store(Request $request)
{
if (!$request->user()->can('create_users'))
abort(403);
}
}
public function store(Request $request)
{
if ($request->user()->cannot('create_users'))
abort(403);
}
}
authorize()
in Controllers.In case of failure, it would return a 403 page, automatically.public function create()
{
if (!auth()->user()->can('create_users'))
abort(403);
}
}
public function store(Request $request)
{
if (!Gate::allows('create_users')) {
abort(403);
}
}
authorize()
in Controllers.In case of failure, it would return a 403 page, automatically.public function store(Request $request)
{
if (Gate::denies('create_users')) {
abort(403);
}
}
public function store(Request $request)
{
abort_if(Gate::denies('create_users'), 403);
}
Auth::guard('admin')->attempt($credentials))
public function store(Request $request)
{
$this->authorize('create_users');
}
Auth::guard('admin')->attempt($credentials))
public function store(StoreUserRequest $request)
{
// No check is needed in the Controller method
}
class StoreProjectRequest extends FormRequest
{
public function authorize()
{
return Gate::allows('create_users');
}
public function rules()
{
return [
// ...
];
}
}
Auth::guard('admin')->attempt($credentials))
php artisan make:policy ProductPolicy --model=Product
use App\Models\Product;
use App\Models\User;
class ProductPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user)
{
//
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Product $product)
{
//
}
/**
* Determine whether the user can create models.
*/
public function create(User $user)
{
//
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Product $product)
{
//
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Product $product)
{
//
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Product $product)
{
//
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Product $product)
{
//
}
}
Auth::guard('admin')->attempt($credentials))
class ProductPolicy
{
public function create(User $user)
{
return $user->is_admin == 1;
}
public function store(Request $request)
{
$this->authorize('create', Product::class);
}
Auth::guard('admin')->attempt($credentials))
Auth::guard('admin')->attempt($credentials))
Auth::guard('admin')->attempt($credentials))
class ProductPolicy
{
public function create(User $user)
{
return $user->is_admin == 1;
}
class ProductPolicy
{
public function create(User $user)
{
return $user->role_id == Role::ADMIN;
}
Auth::guard('admin')->attempt($credentials))
class ProductPolicy
{
public function create(User $user)
{
return $user->role->name == 'Administrator';
}
Auth::guard('admin')->attempt($credentials))
foreach
loop from all permissions from DB, and run a Gate::define()
statement for each of them, returning true/false based on the role;@can('permission_name')
and $this->authorize('permission_name')
, like in the examples above.$roles = Role::with('permissions')->get();
$permissionsArray = [];
foreach ($roles as $role) {
foreach ($role->permissions as $permissions) {
$permissionsArray[$permissions->title][] = $role->id;
}
}
// Every permission may have multiple roles assigned
foreach ($permissionsArray as $title => $roles) {
Gate::define($title, function ($user) use ($roles) {
// We check if we have the needed roles among current user's roles
return count(array_intersect($user->roles->pluck('id')->toArray(), $roles)) > 0;
});
}
$user->givePermissionTo('edit articles');
$user->assignRole('writer');
$role->givePermissionTo('edit articles');
$user->can('edit articles');
Bouncer::allow($user)->to('create', Post::class);
Bouncer::allow('admin')->to('ban-users');
Bouncer::assign('admin')->to($user);