I'm writing a server based solution. In database there are many users with different permissions and I have to check if they have permission to access module they are trying to.
In every Controller I have included something like:
protected $module = "moduleName";
I tried to solve it like:
function __construct()
{
$perm = session()->get('perm');
if (!isset($perm[$this->module]) || !$perm[$this->module]) {
Session::flash('message_error', "<span class='glyphicon glyphicon-warning-sign'></span> Access denined!");
return back();
}
}
It shows the message but it still displays the page not redirects back.
As you see I'm reading permissions from session and modules name is saved in controller so I don't think this could be solved by middleware unless I'm making middleware for each module (I'm talking about 30 modules).
Thanks for taking the time to read this
Middleware actually solved this.
Route:
Route::group(['middleware' => 'module:moduleName'], function () {
// Routes...
});
Custom middleware:
public function handle($request, Closure $next, $module)
{
$perm = session()->get('perm');
if (!isset($perm[$module]) || !$perm[$module]) {
Session::flash('message_error', "<span class='glyphicon glyphicon-warning-sign'></span> Access denined!");
return redirect()->back();
}
return $next($request);
}
Also I'll mention that Route groups can be nested. So you can wrap multiple groups with something like auth middleware as well
There is a very easy fix to your code, you forgot the define the redirect, so instead of using
return back();
use
return redirect()->back();
This will do the redirect.
There is also a mistake in your reasoning, you could and probably should use middleware.
Middleware does have access to the user, session and can be passed parameters. These are the necessary requirements for your system.
You can also assign middleware on a controller basis.
Related
I have a requirement where there is an API method (guarded by the well-known package tymon/jwt-auth) and I need to also be able to access it using the basic session based web middleware.
I don't want to repeat the route in both api.php and web.php even though that would totally work.
I tried adding both to the route but they simply don't work, like: ['auth:api', 'web']
I also tried creating a new middleware with the intention of checking both api and web like so:
class CombinedAuth
{
public function handle($request, Closure $next)
{
$web = Auth::guard('web')->user();
if ($web) {
return $next($request);
}
$api = Auth::guard('api')->user();
if ($api) {
return $next($request);
}
if(!$web && !$api) {
throw new AuthorizationException;
}
}
}
and that also doesn't work. The api middleware works fine but the web middleware doesn't and it always signs me out and redirects to the login page.
So Is there a neat way of protecting a route with api and web middlewares at the same time in Laravel 5.8?
You can use 'auth:api,web' to check for multiple guards.
Using multiple can middleware calls in Laravel 9 route;
<?php
Route::get('/', function () {
return view('money');
})
->middleware([
'can:manage,App\Models\Payment',
'can:manage,App\Models\Withdraw',
]);
?>
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 am using a session separately other than the default authentication sessions. If an user try to access my secured page, he should have the session set. If anyone without that session try to access means, they will be redirected to error page. I am using Laravel 5.3
The user can view the below two pages only if the session variable named 'secured_user' is set. Otherwise they will be redirect to the error page
Route::get('/secured-page1', 'ValidationController#CheckSecuredLogin_1');
Route::get('/secured-page2', 'ValidationController#CheckSecuredLogin_2');
The best option would be a policy.
You can create certain constrains and couple it with your models. Policies are especially suitable for changing your logic later on.
See here: Create Policy
Within you PagesPolicy, you can add this function:
public function before(User $user, $ability)
{
if ($user->isSuperAdmin()) {
return true;
}
}
public function seeSecurePage(User $user)
{
// Your custom Code and session handling
if(session("secured_user")) return true;
return false;
}
and in your controller.
$user->can("seeSecurePage","Pages");
If "can" fails, it will automatically redirect to error 403.
P.S.: Another possibility are Gates
You should use Laravel Middlewares to achieve this, I think middlewares are made for the work you need:
First create a new middleware by running the artisan command:
php artisan make:middleware CheckSesison
Then the CheckSession would look like this:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckSession
{
public function handle($request, Closure $next)
{
if ($session_value != 'YOUR_DESIRED_VALUE') {
return redirect('home');
}
return $next($request);
}
}
Now in your routes file you can use laravel's route middleware() method to implement it like this:
Route::get('/secured-page1', 'ValidationController#CheckSecuredLogin_1')
->middleware(CheckSession::class);
Hope this helps!
In addition to the awnser above, you could also use middleware that's used on the routes and even group them if required. It is a simple, quick and clean solution. Inside the middelware you simple check if the session you require is there and depending on the result you take any action necessary.
Laravel middleware docs
I am using Laravel 5.1. My controller is specifically for admin users. So I check whether user is admin or not.This is my code.
public function getAdminData()
{
$this->checkAdminStatus();
return response()->json(array('admin-data'));
}
public function checkAdminStatus()
{
$userManager = new UserManager();
if(!$userManager->isAdmin())
{
return redirect()->route('returnForbiddenAccess');
}
}
My route is
Route::any('api/app/forbidden',['uses' =>'ErrorController#returnNonAdminErrorStatus','as'=>'returnForbiddenAccess']);
Now if user is not admin, then it should not return admin-data yet it returns. Shouldn't it stop processing logic after redirect()->route call?
Also this is purely REST application.
Why don't you use Laravel Middleware solution for your need ? You can link a middleware to your controller, checking if the current user is an administrator, and redirect if not :
//You Middleware Handle method
public function handle($request, Closure $next)
{
if ($this->auth->guest() || !($this->auth->user()->isAdmin))
{
return redirect('your/url')->with('error','no admin');;
}
return $next($request);
}
You can add on or multiple middleware for a controller in his construct method
//your controller
public function __construct(Guard $auth, Request $request){
$this->middleware('auth', ['except' => ['index', 'show']]); //here one 'auth' middleware
$this->middleware('admin', ['only' => ['index', 'show', 'create','store']]); //here the admin middleware
}
Notice the onlyand except parameters that allow or disallow the middleware for some controller methods
Check laravel documentation on Middleware for more information :)
No, your logic is slightly flawed. The return value you are sending back from checkAdminStatus() is simply being ignored and thrown away:
public function getAdminData()
{
// You don't have $redirectValue in your code, but imagine it
// is there. To actually redirect, you need to return this value
// from your controller method
$redirectValue = $this->checkAdminStatus();
Nothing is being done with that redirect. The only time something is being returned from your controller is happening on every single request. I would suggest something more like this:
public function getAdminData(UserManager $userManager)
{
if($userManager->isAdmin()) {
return response()->json(array('admin-data'));
}
return redirect()->route('forbidden-access');
}
I think this captures the spirit of your question: the only time anything is being returned here is if the user is an admin.
As an aside, you are returning JSON data in one case, and a redirect in another. This may not be a very good idea. My reasoning is because, normally, JSON data is returned in response to AJAX requests, which in my experience are seldom followed up with an actual redirect in the event of failure. (YMMV)
For some reason I have had a mind block and can't figure out what is probably a very simple fix.
I have a Laravel 5 App and am using Zizaco's Entrust package for Access Control.
I want to protect a route so am using route Protection in routes.php as follows:
Entrust::routeNeedsRole('passtypes', array('admin'), null, false);
Which works as expected, apart from when a user's session has expired or they are not logged in and try to access the route.
In this case I would want Laravel's Authentication to be checked first, and redirect to the login page; however Entrust redirects to the 403 error first; which is confusing for a user that has ability to view that page, but is told they do not have access, rather than that they are not logged in/session has expired.
I initiate the Authentication in the Controller rather than in the route:
public function __construct()
{
$this->middleware('auth');
}
So just need to know how to get the same functionality, but by having auth get checked before the route permission requirement.
Thanks
I think that Entrust::routeNeedsRole fires before controller. Can you move Entrust to middleware? You could then check in middleware if user is logged in and then check if he has required role.
It's been a while, but I had a similar problem. The only difference, my entire app had to be protected. I ended up modifying Authenticate Middleware handle method:
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
/**
* This is to protect the entire app, except login form,
* to avoid loop
*/
if($request->path() != 'auth/login')
return redirect()->guest('auth/login');
}
}
return $next($request);
}
And inside Kernel.php moved Authenticate from $routeMiddleware to $middleware
Then you can protect your routes with Entrust.