I am working on a blogging application in Laravel 8.
The application gives the users rights by assigning them roles. Every role has a set of permissions. There is a many-to-many relationship between roles and permissions.
In the user-rights view, I output each user’s permissions successfully:
@foreach ($user->role->permissions as $permission)
<span class="badge bg-primary">{{ $permission->slug }}</span>
@endforeach
The goal
I am trying to restrict access to the Site settings section of the application, like this:
// Settings routes
Route::group(['prefix' => 'settings', 'middleware' => ['checkUserPermissions:edit-settings']], function() {
Route::get('/', [SettingsController::class, 'index'])->name('dashboard.settings');
});
For this purpose, I have created the checkUserPermissions middleware:
class CheckUserPermissions
{
/**
* Handle an incoming request.
*
* @param IlluminateHttpRequest $request
* @param Closure(IlluminateHttpRequest): (IlluminateHttpResponse|IlluminateHttpRedirectResponse) $next
* @return IlluminateHttpResponse|IlluminateHttpRedirectResponse
*/
// Permissions checker
public function hasPermissionTo($permission) {
return in_array($permission, Auth::user()->role->permissions->toArray());
}
public function handle(Request $request, Closure $next, ...$permissions)
{
// Check user permissions
foreach ($permissions as $permission) {
if (!$this->hasPermissionTo($permission)) {
$permission_label = join(' ', explode('-', $permission));
return redirect()->back()->with('error', 'You do not have permission to ' . $permission_label);
}
}
return $next($request);
}
}
The problem
Although the super admin does have the permission to edit settings, dd(in_array($permission, Auth::user()->role->permissions->toArray()))
returns false.
That means that the restriction(s) apply when they should not.
NOTE
dd(Auth::user()->role->permissions->toArray())
returns:
Questions
- What causes this bug?
- What is the easiest fix?
2
Answers
In your custom middleware, you need to compare the permission to the slugs
Since you’re eager loading
permissions
, Eqloquent will return a collection of models. Inside of theCollection
instance, there is apluck
method that will create a new collection of columns.Using the
toArray
method, you will then end up with the expected array that your$permission
will match to:An alternative way would be to use PHP native methods (
array_column
) to achieve this. You could then return a list back to the view of all of the missing permissions as apposed to a single missing permission. This is untested but should just work out the box:For a vanilla PHP mock-up example, See it 100% working over at 3v4l.org