I'm trying to protect a route via middleware as described in the doc
When I hit the url, I get:
ReflectionException in Container.php line 749:
Class can does not exist
Here's the relevant part from routes.php:
Route::get('{user}/profile/edit/{type?}', [
'as' => 'edit',
'uses' => 'User\UserController#edit',
'middleware' => ['can:edit-user,user'],
]);
AuthServiceProvider.php:
public function boot()
{
$this->registerPolicies();
// ... other definitions
Gate::define('edit-user', function ($user, $subjectUser) {
return
$user->hasRole('manage.user') // if the user has this role, green
||
($user->isAdmin && $user->id == $subjectUser->id) // otherwise if is admin, can edit her own profile
;
});
Maybe it's because I'm not using a separate policy class for defining the gate?
According to the documentation on using Middleware with Routes - you need to register the definition in app/Http/Kernel.php
If you want a middleware to run during every HTTP request to your application, simply list the middleware class in the $middleware property of your app/Http/Kernel.php class.
The error you're seeing shows that this definition is missing. You need to add something like;
// Within App\Http\Kernel Class...
protected $routeMiddleware = [
//...
'can' => \Path\To\Your\Middleware::class,
];
Related
I have a Laravel middleware which accepts parameters. This middleware is assigned to a group of routes on the group level. I need to overwrite the parameters specifically for a single route inside this group. How do I do this?
If I add ->middleware('my_middleware:new_param') to the specific route, then the middleware is executed twice: first with the default parameters from the group level, second with the new parameter.
If I add ->withoutMiddleware('my_middleware')->middleware('my_middleware:new_param') then the middleware is not executed at all.
Example
\App\Http\Kernel:
class Kernel extends HttpKernel {
protected $middleware = [
...
];
protected $middlewareGroups = [
'my_middleware_group' => [
'my_middlware:default_param',
...,
],
];
protected $routeMiddleware = [
'my_middlware' => \App\Http\Middleware\MyMiddleware::class,
...
];
}
\App\Providers\RouteServiceProvider:
class RouteServiceProvider extends ServiceProvider {
public function boot() {
$this->routes(function () {
Route::middleware('my_middleware_group')
->group(base_path('routes/my_routing_group.php'));
});
}
}
routes/my_routing_group.php:
// Neither the following line
Route::get('/my-url', [MyController::class, 'getSomething'])->middleware(['my_middlware:new_param']);
// nor this line works as expected
Route::get('/my-url', [MyController::class, 'getSomething'])->withoutMiddleware('my_middleware')->middleware(['my_middlware:new_param']);
The answer is simple: One must also repeat the exact parameters in ->withoutMiddleware which one want not to use. This means
routes/my_routing_group.php:
Route::get('/my-url', [MyController::class, 'getSomething'])
->withoutMiddleware(['my_middlware:default_param']) // the original parameters must be repeated, too
->middleware(['my_middlware:new_param']);
does the trick.
I have read almost everything in web and documentation but i can't find solution for my Problem.
I have a variable stored in Session , then I want to put this variable in every url generated by route('some-route') .
In Session I have sub = "mysubid"
When I generate Route route('my-route') I want to pass this sub parameter in query string: http://domain.dom/my-route-parameter?sub=mysubid
Can you help me to solve This problem? Any helpful answer will be appreciated;
You can use the Default Values feature.
First create a new middleware php artisan make:middleware SetSubIdFromSession. Then do the following:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\URL;
class SetSubIdFromSession
{
public function handle($request, Closure $next)
{
URL::defaults(['sub' => \Session::get('sub')]);
return $next($request);
}
}
At the end register your new middleware in app/Http/Kernel.php by adding it to $routeMiddleware.
protected $routeMiddleware = [
// other Middlewares
'sessionDefaultValue' => App\Http\Middleware\SetSubIdFromSession::class,
];
Add {sub} and the middleware to your route definition:
Route::get('/{sub}/path', function () {
//
})
->name('my-route')
->middleware('sessionDefaultValue');
Since you want this on every web route you can also add the middleware to the web middleware group:
protected $middlewareGroups = [
'web' => [
// other Middlewares
'sessionDefaultValue',
],
'api' => [
//
]
];
Try this , You need to create middleware php artisan make:middleware SetSubSession
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\URL;
class SetSubsSession
{
public function handle($request, Closure $next)
{
if(session('sub')){
$url = url()->full();
return redirect($url.'?sub='.session('sub'));
}
return $next($request);
}
}
in app/http/Kernel.php
protected $routeMiddleware = [
........
'setsubsession' => \App\Http\Middleware\SetSubsSession::class,
]
in route.php add
Route::group(['middleware' => 'setsubsession'], function(){
//and define all the route you want to add sub parameter
});
using this you don't need to change all your routes.This will automatic add "sub" in the route define in that middleware.
I created a policy in laravel 5.3 with this two actions:
class ProjectPolicy {
...
public function index(User $user)
{
return true;
}
public function create(User $user)
{
return true;
}
...
}
and then I tried to make authorization via route group middleware:
Route::group(['middleware' => ['can:index,create,App\Project']], function () {
Route::resource('projects', 'ProjectController');
});
ofcourse I have created Project model and controller correctly, but calling index and create actions always returns 403 forbidden response. where is the problem?
Update:
removing one of actions from route middleware, results correct response. something like this:
Route::group(['middleware' => ['can:index,App\Project']], function () {
Route::resource('projects', 'ProjectController');
});
Looking through the docs the can middleware doesn't really lend itself to resources. You could use multiple middleware calls on the group but this would mean that your use would require all privileges to access the routes.
Your alternatives are:
Add $this->authorize(new App\Project) to your index and create methods in your controller. Laravel will use reflection to figure out what policy to use based on the method it is called from.
Or
In the __construct() method of your controller you could use:
$this->authorizeResource(App\Project::class);
This will require you to
create update, view and delete methods inside your Policy class. Each of these methods will be passed User $user, Project $project e.g.
public function view(User $user, Project $project)
{
return true;
}
FYI, if you leave off the method name with authorize() or you use authorizeResource() Laravel will map certain method names to different policy methods i.e. :
[
//ControllerMethod => PolicyMethod
'show' => 'view',
'create' => 'create',
'store' => 'create',
'edit' => 'update',
'update' => 'update',
'destroy' => 'delete',
];
You can override this by adding a resourceAbilityMap() method to your controller and returning a different array to the one above.
Hope this helps!
Spend hours on that ...
If your controller method does not take a Model as parameter, you also have to override the resourceMethodsWithoutModels().
protected function resourceAbilityMap()
{
Log::info("Inside resourceAbilityMap()");
return array_merge(parent::resourceAbilityMap(), [
//ControllerMethod => PolicyMethod
'index' => 'index',
'search' => 'search'
]);
}
protected function resourceMethodsWithoutModels(){
return array_merge(parent::resourceMethodsWithoutModels(), [
'search'
]);
}
I've implemented the cashier / billing feature from Laravel 5 and I'm trying to protect a group of routes using middleware which checks for a subscription.
I'm getting the following error:
Argument 2 passed to App\Http\Middleware\HasSubscription::handle() must be an instance of App\Http\Middleware\Closure, instance of Closure given
Heres my Middleware
<?php
namespace App\Http\Middleware;
class HasSubscription
{
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed()) {
// This user is not a paying customer...
return redirect('subscription');
}
return $next($request);
}
}
Heres my protected route
Route::get('home', 'PagesController#index')->middleware('subscription');
Heres my applications route declaration
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'subscription' => \App\Http\Middleware\HasSubscription::class,
];
Any idea why I get the error at the top?
Just add
use Closure;
to the top of your middleware, just before class definition:
namespace App\Http\Middleware;
use Closure;
class HasSubscription
{
...
Take a look on the the example in manual: http://laravel.com/docs/5.1/middleware#defining-middleware
I follow this tutorial : https://www.youtube.com/watch?v=kmJYVhG6UzM Currently I can check in my blade if user is a admin or not like this:
{{ Auth::user()->roles->toArray()[0]['role'] }}
HI ADMIN
#endif
How can I make my route only available for admin user?
You need to create a middleware for your route.
Use: php artisan make:middleware AdminMiddleware.
You will find in your middleware folder a new file with this name.
Put your logic in your middleware, e.g.
public function handle($request, Closure $next)
{
if(Auth::check())
{
return $next($request);
}
else
{
return view('auth.login')->withErrors('You are not logged in');
}
}
Once you have done your logic in your middleware, you can either call it in the route or make the middleware apply to all routes.
If you want to add it to all routes, go to Kernel.php and add it to the $middleware array, e.g.
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'App\Http\Middleware\VerifyCsrfToken',
'App\Http\Middleware\AdminMiddleware',
];
If you want to add it to specific routes only, add it to the $routeMiddleware variable and add the alias to the route. E.g.
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
'admin' => 'App\Http\Middleware\AdminMiddleware',
];
You can then add it to a route, as a filter, e.g.
Route::get('admin/profile', ['middleware' => 'admin', function()
{
}]);
For additional info visit the docs:
http://laravel.com/docs/master/middleware
EDIT
An improvement on this would be to use variadic functions which was introduced in PHP 5.6
http://php.net/manual/en/migration56.new-features.php
Instead of having to make a middleware for each permission set you can do the following
PermissionMiddleware
namespace App\Http\Middleware;
use Closure;
use \App\Models\Role;
class PermissionMiddleware
{
// Pass parameters to this middleware
public function handle($request, Closure $next, ...$permitted_roles)
{
//Get a users role
$role = new Role;
$role_name = $role->getUserRoleByName();
foreach($permitted_roles as $permitted_role) {
if($permitted_role == $role_name) {
return $next($request);
}
}
return redirect()->back()->withErrors('You do not have the required permission');
}
}
Notice the ...$permitted_roles
Route::get('admin/profile', ['middleware' => 'PermissionMiddleware:Admin,Marketing', function()
{
}]);
You can now specify as many roles as required for one middleware rather than creating multiple by using middleware parameters
Docs
https://laravel.com/docs/5.3/middleware#middleware-parameters
Let's assume you have a column in your users table with isAdmin name which has a default value of 0 (false)
You can give special access using middleware in laravel like you give access to logged in users using auth middleware in laravel.
Now you need to create a middleware using the command :
php artisan make:middleware AdminMiddleware
In your Kernel.php you need to add this line to protected $routeMiddleware
'admin' => \App\Http\Middleware\AdminMiddleware::class,
In your middleware folder you have the AdminMiddleware file.
In that you need to put your logic
In this case this is how it might look like depending upon you
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RoleMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(Auth::user()->isAdmin == '1') // is an admin
{
return $next($request); // pass the admin
}
return redirect('/'); // not admin. redirect whereever you like
}
}
Now in your route you have to pass the url using this middleware
Here is how it might look like
Route::get('/iamanadmin', ['middleware' => 'admin', function() {
return view('iamanadmin');
}]);
use middleware and check for admin user.
Route::get('admin', ['middleware' => 'checkadmin', function()
{
}]);
now create middleware and validate admin user.