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.
Related
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 know this may seem duplicated. I have already checked these threads:
https://laracasts.com/discuss/channels/laravel/authuser-returns-null-in-laravel-52
https://medium.com/#mshanak/laravel-5-token-based-authentication-ae258c12cfea#.8qeglhfnq
Auth::user() returns null in Laravel 5.2
Laravel : Auth::user() returns null
But I haven't found the solution to my problem
After successfully getting the access_token for a user using the credentials Auth::user() returns null within the controllers.
Here is my Kernel.php
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\LucaDegasperi\OAuth2Server\Middleware\OAuthExceptionHandlerMiddleware::class,
];
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'oauth' => \LucaDegasperi\OAuth2Server\Middleware\OAuthMiddleware::class,
'oauth-user' => \LucaDegasperi\OAuth2Server\Middleware\OAuthUserOwnerMiddleware::class,
'oauth-client' => \LucaDegasperi\OAuth2Server\Middleware\OAuthClientOwnerMiddleware::class,
'check-authorization-params' => \LucaDegasperi\OAuth2Server\Middleware\CheckAuthCodeRequestMiddleware::class,
'csrf' => \App\Http\Middleware\VerifyCsrfToken::class,
];
Here is my routes.php
Route::group(['prefix' => $api_prefix, 'middleware' => 'oauth', 'namespace' => 'Api'], function () {
Route::resource('user', 'UserController', ['except' => ['create', 'store']]);
Route::resource('post', 'PostController');
Route::post('follow/{user}', 'UserRelationsController#follow');
Route::post('unfollow/{user}', 'UserRelationsController#unfollow');
Route::post('trade/{user}', 'UserRelationsController#trade');
Route::post('untrade/{user}', 'UserRelationsController#untrade');
Route::post('capturetime', 'TimeCaptureController#store');
});
Any help would be appreciated
You need to use Authorizer::getResourceOwnerId() to get the user id. After that you should be able to use Auth::loginUsingId($userId) to log in the user for that request. You could set up a middleware to do this for you, would be something like this:
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$userId = Authorizer::getResourceOwnerId();
if($userId) {
Auth::loginUsingId($userId);
}
return $next($request);
}
It's kinda strange because I followed everything that's online about getting route parameters in the middleware and all of them returns NULL or an error, That's what I already tried and didn't work:
public function handle($request, Closure $next)
{
$token = $request->access_token;
// Do something with $token
}
This one returns NULL.
public function handle($request, Closure $next)
{
$request->route('parameter_name');
// Do something with $token
}
And this one above reurns NULL.
public function handle($request, Closure $next)
{
$request->route()->parameters();
// Do something with $token
}
And this one above returns:
FatalThrowableError in CheckOwner.php line 22: Call to a member
function parameters() on null
What I want is to check if the current user trying to edit a product is the owner of this product based on two thing the id from the 'id' parameter of the URL and the 'user_id' in the products row in the database. and that's my code:
the middleware:
class CheckOwner
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
//Get product from $id to edit
$product = Product::find($request->parameter('product'));
if ($product->user_id != Auth::user()->id) {
return redirect('home');
}
return $next($request);
}
}
and that's my route which it's a resource route:
//Products controller show, store, edit, update, destroy
Route::resource('products', 'ProductsController');
and that's how I registered my middleware in the kernel page:
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* #var array
*/
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\CheckOwner::class,
];
/**
* The application's route middleware groups.
*
* #var array
*/
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',
],
];
/**
* 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,
/**** OTHER MIDDLEWARE ****/
'localize' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
'localizationRedirect' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
'localeSessionRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class
// REDIRECTION MIDDLEWARE
];
}
Right now based on the code above it returns this error:
BadMethodCallException in Macroable.php line 74: Method parameter does
not exist.
UPDATE: That's where I use the middleware, in the constructor of the controller:
public function __construct()
{
//Kick him/her out if he is not logged in
$this->middleware('auth', ['except' => ['show']]);
$this->middleware('checkowner', ['except' => ['create', 'store', 'show', 'update', 'delete']]);
}
The global middleware run before the route is resolved.
If you need access to route parameters, use your middleware as route middleware:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\CheckOwner::class,
// the rest of your middleware...
],
];
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.
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).