I have two types of user roles. Admin and manager. An admin can to to all the routes, but a manager can only access some of them. Most of my routes are resource route. Currently I have this route group for admin:
Route::middleware(['auth', 'admin'])->prefix('admin')->group(function () {
Route::resource('post','PostController')
}
Here is my 'admin' middleware if you need to check:
if (!Auth::user()->isAdmin())
{
return redirect('/home');
}
return $next($request);
Here, all the routes are accessible by the admin. But I want to allow access some of the routes such as post.index, post.show, post.edit for the manager.
What should I do now?
Below I am explaining my question elaborately with and example
I have three middleware, auth, manager, admin. As the name suggests, auth middleware checks if a user is authenticated, manager middleware checks if the user is manager and certainly admin middleware checks if the user is admin.
Now, for Route::resource('post','PostController') route,
auth has access to post.index, post.view
manager has access to post.index, post.view, post.edit
admin has access to all the routes.
What is the best way to apply middleware to solve the problem?
Laravel allows for multiple routes in your controllers
Follow the following steps:
Remove the 'admin' middleware from your route group, leaving just 'auth'.
Route::middleware(['auth'])->prefix('admin')->group(function()
{
Route::resource('post','PostController');
}
In your 'manager.php' route file now, you can use and point to the same PostController
Route::middleware(['auth'])->prefix('manager')->group(function()
{
Route::resource('post','PostController');
}
then add a __construct() method at the top of the PostController like this
class PostController extends Controller
{
public function __construct()
{
$this->middleware('admin')->except(['index', 'show', 'edit']);
$this->middleware('manager');
}
}
You can define partial ressource routes.
https://laravel.com/docs/7.x/controllers#restful-partial-resource-routes
So you can define some of them in your middleware group and the other ones outside of it.
Route::middleware(['auth'])->group(function(){
Route::middleware(['admin'])->prefix('admin')->group(function () {
Route::resource('post','PostController')->except([
'index', 'show', 'edit'
]);
}
Route::middleware(['manager'])->prefix('manager')->group(function () {
Route::resource('post','PostController')->only([
'index', 'show', 'edit'
]);
}
}
Related
I am wondering if there is any adifference between asiigning middleware in a route like this :
Route::patch('/edit/{column}/{id}',['middleware' => 'auth', 'uses' => 'ResourceController#editCompany']);
and launching it in controller's constructor
public function __construct()
{
$this->middleware('auth');
}
Is it the same? Does it do anything else then checking if I am logged in?
It is completely the same. The problem is when you add that in the constructor you need to remember to add it in every new controller that you want to be auth protected.
While in the routes file, you can group multiple endpoints and apply the middleware in all of them:
Route::group(['middleware' => 'auth'], function() {
// all routes here that need to be auth protected.
});
What I'm trying to test is to access some routes but these routes are in laratrust role middleware this role is the auth user must be super admin to go in this routes my problem is I don't know how to write this function.
I tried to make the user super admin in the test function like this
public function Test()
{
$user = factory(User::class)->create();
$user->attachRole('superadministrator');
$this->actingAs($user, 'api');
$response = $this->json('GET', 'api/users');
$response->assertStatus(200);
}
but it didn't work even I checked the data base this user is superadministrator and the test give like I'm not super admin
This is my api routes:
Route::group(['middleware' => ['auth:api', 'role:superadministrator']],
function()
{
Route::apiResource('users', 'UserController');
}
This is my index function in UserController:
public function index()
{
return Response()->json(User::all, 200);
}
What I'm expect is a function can access this route because there is more routes in this group and the rest of the tests depends on this function
I've never used Laratrust, but after a quick look at its source code, it looks like the issue is that you need to specify the api guard on your role middleware check. Add ,guard:api to your role middleware:
Route::group(['middleware' => ['auth:api', 'role:superadministrator,guard:api']], function() {
Route::apiResource('users', 'UserController');
}
In the role middleware check, if you don't specify the guard, it will use the default guard defined in your auth config (which is web if you haven't changed it). So, the middleware will be looking for the user from the web auth guard, which doesn't exist.
The good news is, your test worked! It found a bug in your route definition.
Laravel newbie here (obviously :D ). I've set up a new model & controller for a model named Pages.
Every User has many Pages.
Each Page has a single User.
I've created the following functioning controller actions (& views):
PagesController::index
PagesController::create
PagesController::store
PagesController::show
PagesController::edit
PagesController::delete
So you can edit a Page by going to url.dev/pages/{id}/edit.
The problem is, you can access all of these routes regardless of your session status. So random users can edit any given Page. Which, obviously, is terrible.
Can anyone point me in the direction of what I should read up on, to limit the ability to access my model's controller actions based on whether or not the user is logged in (and if it's the correct user, at all)?
To force a specific route to be only accessible by authenticated users you can specify middleware auth in the controller constructor, like so:
public function __construct()
{
$this->middleware('auth');
}
Also you can restrict which methods you want auth to be applied to in the controller, using the only or except parameters. Using only you could do:
public function __construct()
{
$this->middleware('auth', ['only' => ['create', 'store', 'edit', 'delete']]);
}
You´re looking for middleware..
You can read more here
public function __construct()
{
$this->middleware('auth')->only('index');
$this->middleware('admin')->except('store');
}
Other answers are good, but I prefer to use middleware on route groups.
So when I have several routes like this:
Route::get('pages', 'PagesController#index')->name('pages.index');
Route::get('pages/{id}/edit', 'PagesController#edit')->name('pages.edit');
I would add them inside Laravel Route group. Like this:
Route::group(['middleware' => 'auth'], function() {
Route::get('pages', 'PagesController#index')->name('pages.index');
Route::get('pages/{id}/edit', 'PagesController#edit')->name('pages.edit');
});
I use Laravel 5.3 and I have the following problem.
[UPDATE]
My initial trouble was the appearance of an error when performing actions on the site when the user was not logged in the system.
This happened when the browser is started, where cached information is displayed by default on the page. Site interface displayed for logged users, and in his system was not. At the same time, producing some action, I get an error that the user is not authorized.
I also have group auth middleware for all my routes. When I reboot page of the site, the middleware is activated and redirectedme to the login page. The main problem is the browser shows the cached information.
So, in addition to middleware for routes I decided to make auth check in controllers.
[/UPDATE]
I want to check user's auth in every controller's action. Making the auth check in every controllers' action manually isn't a solution, because there are many controllers and actions.
So I decided to make it globally.
As all controllers extends Main Controller (App\Http\Controllers\Controller.php), I decided write the
auth()->check() in constructor:
function __construct()
{
if(auth()->check()) dd('success');
}
But... nothing happened((( Then I found the callAction method in BaseController which Main Controller extends and made checking here:
public function callAction($method, $parameters)
{
if(auth()->check()) dd('success');
return call_user_func_array([$this, $method], $parameters);
}
This time everything's OK, but I don't like this solution, because editing the core files isn't good.
Finally, I redeclared callAction method in Main Controller with auth checking, but I don't like this way too.
Is any solution?
You should use middleware:
Route::get('profile', ['middleware' => 'auth', 'uses' => 'UserController#showProfile']);
Or:
Route::get('profile', 'UserController#show')->middleware('auth');
Or using middleware groups:
Route::group(['middleware' => ['auth']], function () {
// Controllers here.
});
Or using controller's construct:
public function __construct()
{
$this->middleware('auth');
}
You can use auth middleware in your controller
public function __construct()
{
$this->middleware('auth');
}
check here : https://laravel.com/docs/5.3/authentication
if there is a group of routes this would be the easiest way
Route::group(['middleware' => ['auth']], function()
{
// here all of the routes that requires auth to be checked like this
Route::resource('user','UsersController');
}
another ways
function __construct()
{
$this->middleware('auth');
}
another way is specified on controller routes
Route::get('profile', [
'middleware' => 'auth',
'uses' => 'UserController#showProfile'
]);
see documentation
https://laravel.com/docs/5.0/controllers#controller-middleware
I have multi middleware (studen, parent, admin) and create some route group with that middleware. But some route can access if user is in any of that groups and belong in any of that middleware but not if is in other middleware fot example teacher. I use something like that in docs: http://laravel.com/docs/5.1/routing#route-groups But it's work when I put one route, when add another route group with another middleware it doesn't work. Is that possible and how to make it?
When I execute php artisan route it gives me an error
[Symfony\Component\Debug\Exception\FatalErrorException]
Call to a member function inRole() on null
Laravel's route middleware is executed one by one as declared in routes.php file. Therefore, if one of them denies access by throwing an exception or returning some respoise, the next middlewares won't be executed.
In order to make that work, you'll need a single middleware that would check if current user has any of required roles. Luckily, as of Laravel 5.1 you are able to pass parameters to middleware from your routes.php file (see http://laravel.com/docs/5.1/middleware#middleware-parameters), so you'll only need one middleware class to handle all cases.
Example middleware class could look like that:
class HasAnyRole
{
public function handle($request, Closure $next, $roles)
{
// Return Not Authorized error, if user has not logged in
if (!$request->user) {
App::abort(401);
}
$roles = explode(',', $roles);
foreach ($roles as $role) {
// if user has given role, continue processing the request
if ($request->user->hasRole($role)) {
return $next($request);
}
}
// user didn't have any of required roles, return Forbidden error
App::abort(403);
}
}
Register the middleware in your Kernel.php:
protected $routeMiddleware = [
'has_any_role' => 'App\Http\Middleware\HasAnyRole',
];
Now, in your routes.php you can apply the middleware to a group like that:
//this route is available only to users with role admin or author
Route::put('post/{id}', ['middleware' => 'has_any_role:admin,author', function ($id) {
//
}]);
This should do the trick, just make sure that your User class has a hasRole method.