it should forbid that the user grabs this url:
?main_title=banner
?main_title=law
?main_title=faq
with this
if(\Auth::user()->hasRole(['super_admin']))
I am going to assume that you are using spatie/laravel-permission based on your example code.
Laravel Permission comes with built-in role middlewares
One of the ways you could use them is by grouping the routes you want to be accessible only by super admins
Route::group(['middleware' => ['role:super_admin']], function () {
// YOUR ROUTES HERE
});
It's always good to using the middlewares ,
So in your case first create a Trait for roles
public function isSuperadmin(){
return Auth::user()->role->role=='superadmin';
}
After that create a middlewar like superadmin for the superuser and in that first include your trait
use App\Traits\Roles;
after that
use Roles;
public function handle($request, Closure $next)
{
if(!$this->isSuperadmin())
{
return back();
}
return $next($request);
}
and just register the middleware in the app/http/kernal.php in protected $routeMiddleware function
'superadmin' => \App\Http\Middleware\superadmin::class,
so it's make your life very easy now you don't need to check the url or role every time , for any url you want to block for other users just use
Route::get('/?main_title=law', 'HomeController#function')->middleware('superadmin')->name('admin-dashboard-home');
so if the user role is superadmin then he is allow to assess the url you can redirect the other users or show the error message :)
Related
I'm really new to laravel and have been reading the documentations and tutorials. I'm planning on making an app that has 2 roles, admin and user. I modified my User model to have the column 'isAdmin' with boolean value since I only need 2 roles. How do I perform a check on this attribute during auth? Thank you.
TO answer your question, first of all to make protect any route using the auth middleware which ensures a user is authenticated (logged in) before they can access the route, you simply need to add the auth middleware.
e.g
web.php
<?php
Route::middleware('auth')->group(function(){
//All Routes which needs user to be logged in
});
or
//Individiual Route Middleware
Route::get('/path/to', 'controller#instance')->middleware('auth');
As for checking user role, you can basically create a middleware for this using the following steps:
run your php artisan make:middleware IsAdminMiddleware
open your IsAdminMiddleware and add this code inside the handle function:
public function handle($request, Closure $next)
{
if(!Auth::check()){
return redirect()->route('login');
}
if(Auth::user()->isAdmin == true){
return $next($request);
}
return redirect()->back()->with('unauthorised', 'You are
unauthorised to access this page');
}
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.
I wonder if I can do this in Laravel Route. Let's say I have Admin, Premium and User (which can be login too by using Auth) Middleware. Also, I have controller with methods like this: index, create, edit, delete and I want Admin to be able do all those things, but Premium can only be able to access index method, and User can't access anything in this controller (he can access another controller). I know I can use except or only middleware method like this:
public function __construct()
{
$this->middleware('premium')->only('index');
$this->middleware('admin');
// or maybe $this->middleware('admin')->except('index');
}
but when I try to put these two middlewares in __construct method they will start to conflict each other, it makes sense because index method can be access by Premium but then can't be access by the Admin itself. By the way, my middleware is simply checking:
if (Auth::check()) {
if (Auth::user()->role == 'Admin') {
return $next($request);
}
}
return redirect('/home');
So, back to my question, can I have OR Middleware so I can avoid conflict from multiple middleware (which is must be AND condition when they written at the same controller constructor)?
If you change up the way your logic is thinking a little bit, the answer becomes pretty easy. You can create new middleware that checks if it can access the specific method.
So create the following middleware 'CanAccessIndex':
if (Auth::check()) {
if (Auth::user()->role == 'Admin' || Auth::user()->role == 'Premium') {
return $next($request);
}
}
return redirect('/home');
Then, you can put that middleware on the index function (instead of the premium middleware) and put your admin middleware on everything EXCEPT index. Like so:
public function __construct()
{
$this->middleware('canAccessIndex')->only('index');
$this->middleware('admin')->except('index');
}
That's one way to do it.
You need middleware group for this, and to manage these hierarchy of access layer, you can simply use Route grouping.
I will demo an example of what I mean:
Say you have auth middleware general for authenticated users (i.e everybody), then another called premium for premium member, and admin for the Admin.
Then you'll group based on the access level:
Route::middleware('auth')->group(function(){
Route::middleware('premium')->group(function(){
Route::post('/create', 'HomeController#create')->middleware('admin');
Route::put('/update/{id}', 'HomeController#update')->middleware('admin');
Route::get('/index', 'HomeController#index');
Route::put('/delete/{id}', 'HomeController#delete')->middleware('admin');
});
});
So you can have general check in your middleware with a check. It would have been much easier if you have role level say 3 for admin and 2 for premium member. So we can have for the premium middleware:
public function handle($request, Closure $next)
{
return auth()->user->role >= 2
? $next($request)
: redirect('/home');
}
This is just an example. You can do further check based on your need but more importantly, ensure your admin middleware to checks the exact role level that is allowed.
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.
I have an application which uses Laravel 5's out of the box authentication. I need to require authentication on the show method of a controller ONLY when the field called "approved" is equal to 1.
How can I require authentication using middlewares on a conditional basis such that unauthenticated users can access entries whose approved column is equal to 1, but only authenticated users can see entries where approved is equal to 0.
My constructor currently looks like this:
public function __construct(){
$this->middleware('auth', ['only' => ['edit', 'destroy', 'approve', 'markRecovered']]);
}
You may create your own middleware instead of using Laravel's default auth middleware and in that case, you may create a middleware such as 'checkApproval' using something like this:
php artisan make:middleware checkApproval
Then in your app/Http/Middleware directory you'll find the new middleware created and it'll contain the basic structure including handle method, so now you may erite code inside this middleware's handle method to check the user's state and the approved field and then either redirect to login page if condition doesn't match or allow access. here is a basic idea:
use Illuminate\Contracts\Auth\Guard;
class CheckPermission implements Middleware {
protected $auth;
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
public function handle($request, Closure $next)
{
if($this->auth->guest() && !$this->checkApproval($request))
{
return redirect('login');
}
return $next($request);
}
protected function checkApproval($request)
{
// Get the auth/logged in user
// $user = $request->user();
// Get a parameter from route
// $id = $request->route()->parameter('id')
// Check the approved field here and return true or false
}
}
Now assign the middleware a short-hand key in your app/Http/Kernel.php file. By default, the $routeMiddleware property of this class contains entries for the middleware included with Laravel. To add your own, simply append it to this list and assign it a key to use in your route/controller, for example, checkApproval so in the place of auth you may use checkApproval for the the method view in your controller.
This is an abstract idea, but you can implement one of your own now so check the documentation for more information.