I am following a tutorial for making a access level restriction:
https://gist.github.com/amochohan/8cb599ee5dc0af5f4246
I was able to make it work somehow but there's something I need to get working which is not in the tutorial.
Provided I have followed the tutorial. I have setup this resource route:
Route::group(['middleware' => ['auth', 'roles'], 'roles' => ['Administrator']], function()
{
Route::resource('changeschedule', 'ChangeScheduleController', ['only' => ['index'], 'except' => ['create']]);
});
So what I wanted is just to apply the roles middleware to a resource route but with specific route in that resource only let's say I want to be applied in the index only so I have that route above.
When I go to:
http://localhost/hrs/public/changeschedule
It works fine and the middleware roles is working fine. But why is that when I go to:
http://localhost/hrs/public/changeschedule/create
I am getting
NotFoundHttpException in RouteCollection.php line 161:
So I have a no found route error. Why is that? But when I do
Route::group(['middleware' => ['auth', 'roles'], 'roles' => ['Administrator']], function()
{
Route::resource('changeschedule', 'ChangeScheduleController');
});
Then it works fine but the middleware is applied to all:
index, create, update, edit, delete
I want it to be in index only.
My code:
Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'roles' => \App\Http\Middleware\CheckRole::class,
];
CheckRole.php
<?php namespace App\Http\Middleware;
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;
}
}
You may try this, create a constructor function and add the middleware from there, for example:
public function __construct()
{
$this->middleware('auth');
$this->middleware('roles:administrator', ['only' => ['index']]);
}
Read the documentation.
Update (The third parameter in the middleware::handle method can take the argument):
public function handle($request, Closure $next, $role)
{
// $role will catch the administrator or whatever you pass
}
You may also check these examples/tutorials on my blog (about middleware).
Related
I know this is very common question but in am stuck in this. I am using Laravel 5.5 and developing an ERP. In ERP some URL only access by Super admin so i create a middleware.
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class CheckAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = Auth::user();
if($user->user_role_id == 1) {
return $next($request);
} else {
return redirect('/');
}
}
}
I have register this middleware in Kernel.php
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'admin' => \App\Http\Middleware\CheckAdmin::class
];
and usign this middleware in routes
$routes = [
"students" => "StudentsController",
"teachers" => "TeachersController",
"courses" => "CoursesController",
"subjects" => "SubjectsController",
"colleges" => "CollegesController",
"branches" => "BranchesController",
];
Route::group(['middleware' => ['web', 'auth']], function () use ($routes){
Route::match(["get","post"],'/users','UsersController#index')->middleware('admin');
Route::get('/users/status/{id}','UsersController#setStatus')->middleware('admin');
Route::get('/users/delete/{id}','UsersController#delete')->middleware('admin');
Route::match(["get","post"],'/users/add','UsersController#add')->middleware('admin');
Route::match(["get","post"],'/users/edit/{id}','UsersController#edit')->middleware('admin');
Route::match(["get","post"],'/users/view/{id}','UsersController#view')->middleware('admin');
foreach($routes as $route => $class){
Route::match(["get","post"],'/'.$route,$class.'#index');
Route::get('/'.$route.'/status/{id}',$class.'#setStatus');
Route::get('/'.$route.'/delete/{id}',$class.'#delete');
Route::match(["get","post"],'/'.$route.'/add',$class.'#add');
Route::match(["get","post"],'/'.$route.'/edit/{id}',$class.'#edit');
Route::match(["get","post"],'/'.$route.'/view/{id}',$class.'#view');
}
// additions routes
Route::match(["get","post"],'/courses/assign-subjects','CoursesController#assign_subjects');
Route::match(["get"],'/courses/already-assign-subjects/{id}','CoursesController#already_assigned_subjects');
});
This middleware is not called with users/* routes. When i register this $middleware in kernel.php then its works but Auth::user() return null every time. So how can i check logged in user role?
I read somewhere that in L5.5 version, Session not works in constructor then what is the best approach to check user role in middleware.
For checking admin middleware you can use a nested middleware
Route::group(['middleware' => ['web', 'auth']], function () use ($routes){
Route::match(["get","post"],'/users','UsersController#index')->middleware('admin');
Route::group(['middleware' => ['admin'], function () use ($routes){
Route::get('/users/status/{id}','UsersController#setStatus')->middleware('admin');
Route::get('/users/delete/{id}','UsersController#delete')->middleware('admin');
});
Route::match(["get","post"],'/users/add','UsersController#add')->middleware('admin');
Route::match(["get","post"],'/users/edit/{id}','UsersController#edit')->middleware('admin');
Route::match(["get","post"],'/users/view/{id}','UsersController#view')->middleware('admin');
foreach($routes as $route => $class){
Route::match(["get","post"],'/'.$route,$class.'#index');
Route::get('/'.$route.'/status/{id}',$class.'#setStatus');
Route::get('/'.$route.'/delete/{id}',$class.'#delete');
Route::match(["get","post"],'/'.$route.'/add',$class.'#add');
Route::match(["get","post"],'/'.$route.'/edit/{id}',$class.'#edit');
Route::match(["get","post"],'/'.$route.'/view/{id}',$class.'#view');
}
// additions routes
Route::match(["get","post"],'/courses/assign-subjects','CoursesController#assign_subjects');
Route::match(["get"],'/courses/already-assign-subjects/{id}','CoursesController#already_assigned_subjects');
});
When the admin middleware is outside the web and auth it will always return null
Hope this helps
I'm attempting to bind a function to the routing so it takes effect globally.
Basically I'm using Hashids to obfuscate the IDs, and want to be able to decode the ID on the route level so I don't need to do it everywhere the ID is uses in different controllers.
I've attempted to do the following at the top of the api routes file:
api.php
<?php
use Dingo\Api\Routing\Router;
use Hashids\Hashids;
Route::bind('id', function ($id) {
return Hasher::decode($id);
});
/** #var Router $api */
$api = app(Router::class);
But it doesn't seem to have any effect.
I have a couple of routes that use the ID I want to decode at the bottom of the routes file:
$api->get('leads/{id}', 'App\\Api\\V1\\Controllers\\LeadController#show');
$api->put('leads/update/{id}', 'App\\Api\\V1\\Controllers\\LeadController#update');
Really at a loss as to how to get this to work, I've tried using $api->bind and others but they all call undefined functions.
Sure this is an easy thing, but I'm just starting out with Laravel so this is a bit beyond me at this point.
Many thanks!
Based on the hint that Serge gave me, I've attempted to move this functionality into Middleware, but still due to a full lack of understanding, this isn't working.
I have the following middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Junity\Hashids\Facades\Hashids;
class DecodeHashids
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if($request->has('id'))
$request->id = Hasher::decode($request->id);
return $next($request);
}
}
I've added it to Kernal.php:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'bindings',
'decode',
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'jwt.auth' => GetUserFromToken::class,
'jwt.refresh' => RefreshToken::class,
'decode' => \App\Http\Middleware\DecodeHashids::class,
];
}
and added it in the api routes file as so:
$api->group(['middleware' => 'jwt.auth'], function(Router $api) {
$api->get('protected', function() {
return response()->json([
'message' => 'Access to protected resources granted! You are seeing this text as you provided the token correctly.'
]);
});
$api->get('refresh', [
'middleware' => 'jwt.refresh',
function() {
return response()->json([
'message' => 'By accessing this endpoint, you can refresh your access token at each request. Check out this response headers!'
]);
}
]);
$api->group(['middleware' => 'decode'], function(Router $api) {
$api->get('leads/{id}', 'App\\Api\\V1\\Controllers\\LeadController#show');
});
I get no errors, but the ID is not decoded when it passes through to the controller.
Thanks to the help from Serge, I managed to complete the Middleware.
Middleware as below, it updates the Route ID Parameter with the decoded value, and this Middleware is added to the Kernal.
<?php
namespace App\Http\Middleware;
use Closure;
use Hashids;
class DecodeHashids
{
public function handle($request, Closure $next)
{
if($request->route()->parameters('id'))
$request->route()->setParameter('id', Hashids::decode($request->id));
return $next($request);
}
}
Then in the API route file, I added a new group that uses the 'decode' Middleware:
$api->group(['middleware' => 'decode'], function(Router $api) {
$api->get('leads/{id}', 'App\\Api\\V1\\Controllers\\LeadController#show');
});
Can then of course add as many routes to this group where parameters need decoded.
Thanks Serge and the Laravel community for the help and responses on here and other sites. Hopefully this will help others.
I'm trying to add a simple middleware to check if a user matches a role. I'm running into an issue when I use the middleware, I get an Exception:
ReflectionException: class role does not exist
I do not attempt to call a class named role so I assume this is happening magically in Laravel somewhere.
My middleware:
class RoleMiddleware
{
/**
* Run the request filter.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string $role
* #return mixed
*/
public function handle($request, Closure $next, $role)
{
if (! $request->user()->is($role)) {
return redirect('/login');
}
return $next($request);
}
}
In the users table, I have a role field and in the User model I have:
/**
* Check if a user is a certain role
*
* #param $role
* #return bool
*/
function is($role) {
return ($this->role == $role);
}
The route group:
Route::group(['prefix' => 'support', 'middleware' => ['role:admin', 'web']], function() {
Route::get('threads', 'ThreadController#index');
});
In Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
],
'role' => [
RoleMiddleware::class,
],
];
Anyone have any ideas on what could be causing this ReflectionException?
For Spatie/Laravel-Permissions to work properly we have to register the two route middleware (role and permission) in app/Http/Kernel.php as follows, along with the other two auth middleware:
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
];
In Http/Kernel.php, you need to include the full path to RoleMiddleware. E.g.:
...
'role' => [
\App\Http\Middleware\RoleMiddleware::class,
],
...
Try to change this
Route::group(['prefix' => 'support', 'middleware' => ['role:admin', 'web']], function() {
Route::get('threads', 'ThreadController#index');
});
to
Route::group(['prefix' => 'support', 'middlewareGroups' => ['role:admin', 'web']], function() {
Route::get('threads', 'ThreadController#index');
});
I'm a beginner in laravel i was facing this same problem and fixed by changing the name from middleware to "your-own-middelware-name".
This came down to two problems:
$middlewareGroups may not allow for parameters. (Needs to be confirmed) Dropping RoleMiddleware down to $routeMiddleware got rid of the exception.
Having 'web' after 'role:admin' resulted in a null $request->user(). So for future users, you may need to consider placement of your middleware and a check to see if $request->user() is null.
Hope this helps someone else.
I am trying to filter route using 'auth' and 'auth.admin' middleware which should be like laravel 4.2's Route::filter. But it's not working.
Here is my route
Route::group(['prefix' => 'admin', 'middleware' => ['auth', 'auth.admin']], function()
{
// ...
});
Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'auth.admin' => \App\Http\Middleware\RedirectIfAdmin::class,
'role' => Zizaco\Entrust\Middleware\EntrustRole::class,
'permission' => Zizaco\Entrust\Middleware\EntrustPermission::class,
'ability' => Zizaco\Entrust\Middleware\EntrustAbility::class,
];
RedirectIfAdmin.php
<?php
namespace App\Http\Middleware;
use Closure;
use Entrust;
class RedirectIfAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!Entrust::hasRole(config('customConfig.roles.admin'))) {
return redirect()->route('dashboard')
->with('error', 'Access Denied');
}
return $next($request);
}
}
As u said that ur dashboard route is for authenticated user,
But ur checking if user is not in admin role send to dashboard, and when he is sent to dashboard he is redirected back, probably due to another middleware kick in, and that send back to login and from login again to dashboard, so just remove ! from ur if condition.
How can I separate auth user group into 3 groups, I need admin, client and worker group.
Here is my route for auth users:
Route::group(['middleware' => 'auth'], function(){
Route::get('home', array(
'as' => 'home',
'uses' => 'HomeController#index'
));
Route::get('logout', array(
'as' => 'logout',
'uses' => 'UserController#logout'
));
});
I've written a middleware that can do basic role based authentication, as you've described.
Route::get('home', [
'middleware' => ['auth', 'roles'], //use the roles middleware
'uses' => 'HomeController#index',
'roles' => ['admin', 'client'] // only admin and client roles are allowed
]);
Instructions
In App\Http\Middleware, create a file called 'CheckRole.php'
<?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;
}
}
In kernel, enable the 'roles' middleware:
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
'roles' => 'App\Http\Middleware\CheckRole',
];
You will also need to set up a roles table, with some role data and then assign the relationships to the role on the User model.
The full code is available here: https://gist.github.com/amochohan/8cb599ee5dc0af5f4246
Hope this helps.