I am working on a Laravel 8 app with users, roles and permissions. I use Microsoft Azure for user sign-in.
I began by following this tutorial on their website.
I get a list of comma-separated user permissions specific to every user role, from a permissions
MySQL table.
I want the current user to have access to certain routes, depending on the permissions they have.
I store the userPermissions
variable in a session like this:
public function storeTokens($accessToken, $user, $user_role, $user_permissions) {
session([
'accessToken' => $accessToken->getToken(),
'refreshToken' => $accessToken->getRefreshToken(),
'tokenExpires' => $accessToken->getExpires(),
'userName' => $user->getDisplayName(),
'firstName' => $user->getGivenName(),
'lastName' => $user->getSurname(),
'userRole' => $user_role,
'userPermissions' => $user_permissions,
'userEmail' => null !== $user->getMail() ? $user->getMail() : $user->getUserPrincipalName(),
'userTimeZone' => $user->getMailboxSettings()->getTimeZone()
]);
}
I have created a new middleware, called CheckUserPermissions
:
class CheckUserPermissions
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
private $permission;
// Permissions checker
public function hasPermissionTo($permission) {
return in_array($permission, session('userPermissions'));
}
public function handle(Request $request, Closure $next)
{
// Check user permissions
if (!$this->hasPermissionTo($this->permission)) {
return redirect('/')->with('error', 'You do not have permission to access to this section of the application');
}
return $next($request);
}
}
I registered the above middleware in app\Http\Kernel.php
:
protected $routeMiddleware = [
//More middleware
'checkSignedIn' => \App\Http\Middleware\CheckSignedIn::class,
'checkUserPermissions' => \App\Http\Middleware\CheckUserPermissions::class,
];
The problem
The problem is that I am unable to use the policy/gate outside the controller (even though the current user does have the permission to view users).
Doing Route::get('/users', [UsersContoller::class, 'index'])->middleware('checkUserPermissions')->hasPermissionTo('view-users')
in routes\web.php
does not work:
// Dashboard routes
Route::group(['prefix' => 'dashboard', 'middleware' => ['checkSignedIn']], function() {
Route::get('/', [DashboardContoller::class, 'index'])->name('dashboard');
Route::get('/users', [UsersContoller::class, 'index'])->middleware('checkUserPermissions')->hasPermissionTo('view-users');
});
It results in a Method Illuminate\Routing\Route::hasPermissionTo does not exist
error being displayed in the browser
What am I doing wrong?
CodePudding user response:
You can't access middleware methods in the route definitions. The middleware only runs the handle
method so you should write your code with that in mind. Here's how you can solve this:
class CheckUserPermissions
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
private $permission;
// Permissions checker
public function hasPermissionTo($permission) {
return in_array($permission, session('userPermissions'));
}
public function handle(Request $request, Closure $next, ...$permissions)
{
// Check user permissions
foreach ($permissions as $permission) {
if (!$this->hasPermissionTo($permission)) {
return redirect('/')->with('error', 'You do not have permission to access to this section of the application');
}
}
return $next($request);
}
}
and use the middleware as:
Route::group(['prefix' => 'dashboard', 'middleware' => ['checkSignedIn']], function() {
Route::get('/', [DashboardContoller::class, 'index'])->name('dashboard');
Route::get('/users', [UsersContoller::class, 'index'])->middleware('checkUserPermissions:view-users');
});
Anything after the :
in the checkUserPermissions:view-users
is sent as an additional argument of the handle
method. You can also send multiple ones:
Route::get('/users', [UsersContoller::class, 'index'])->middleware('checkUserPermissions:view-users,see-users,glance-at-users');
each item in the comma separated list is an additional array entry in the $permissions
array of the middleware