I'm developing a Laravel ACL System. My base Table's are users,roles,permissions and pivot tables are role_user,role_permission,user_permission.
I want to check User Permissions using my custom middleware HasPermission. I have tried this way but it's not working properly. every user can access the all the permissions which have or have not.
Now, How can I solve the issue. Please see my code sample.
My Controller.
function __construct()
{
$this->middleware('auth');
$this->middleware('HasPermission:Role_Read|Role_Update|Role_Delete');
}
My Middleware.
class HasPermission
{
public function handle($request, Closure $next,$permissions)
{
$permissions_array = explode('|', $permissions);
// $user = $this->auth->user();
foreach($permissions_array as $permission){
if(!$request->user()->hasPermission($permission)){
return $next($request);
}
}
return redirect()->back();
}
}
and, my User Model method.
public function user_permissions()
{
return $this->belongsToMany(Permission::class,'user_permission');
}
public function hasPermission(string $permission)
{
if($this->user_permissions()->where('name', $permission)->first())
{
return true;
}
else
{
return false;
}
}
Best way to do is that you need to introduce an new service provider and in that you can check the authorization and permissions.
I made a test project (last year) for db driven permission and I used service provider.
That's the perfect way to implement.
Basically !$request->user()->hasPermission($permission) is saying if the user associated with the request does not have this permission the middleware passes, however this is not what you want. Here's what you should do:
If you need the user to have one of the stated permissions you need to do:
class HasPermission
{
public function handle($request, Closure $next,$permissions)
{
$permissions_array = explode('|', $permissions);
foreach($permissions_array as $permission){
if ($request->user()->hasPermission($permission)){
return $next($request);
}
}
return redirect()->back();
}
}
If you want the user to have all stated permissions you need to do:
class HasPermission
{
public function handle($request, Closure $next,$permissions)
{
$permissions_array = explode('|', $permissions);
foreach($permissions_array as $permission){
if (!$request->user()->hasPermission($permission)){
return redirect()->back();
}
}
return $next($request);
}
}
As an added note if you want to do this in a more elegant way you can do:
class HasPermission
{
public function handle($request, Closure $next, ...$permissions_array)
{
//Function body from above without the explode part
}
}
And
function __construct()
{
$this->middleware('auth');
$this->middleware('HasPermission:Role_Read,Role_Update,Role_Delete');
}
If you use commas then the framework will split the string into arguments for you .
In my case i just added simple function to get permissions from database and then check it Middleware. Check this code:
// Add new function to get permissions from database
public static function user_permissions($user) {
$permissions=DB::table('permissions')->where('user_id', $user)->first();
return $permissions;
}
// In Middleware check your permissions
if(Auth::guest())
{
return redirect('/');
}
elseif(Functions::user_permissions(Auth::user()->id)->user_managment != 1) {
return redirect('/');
} else {
return $next($request);
}
In web.php/api.php:
Route::middleware('hasPermission')->group(function() { // for all routes
Route::get('/article', [ArticleController::class, 'index'])->name('article.index');
});
in middleWare:
class HasPermission
{
public function handle($request, Closure $next)
{
$routeName = Request::route()->getName();
$permission = $user->permissions()->where('route_name', $routeName)->first();
if ( ! empty($permission)){
return redirect()->back();
}
return $next($request);
}
}
Related
Actually I wanted that my application will go to admin_panel.blade.php only if the user login. I don't want to go there directly. So I implemented middleware and session but it's not working because if I directly goes to 'admin_panel' then it does not restrict me. Without or with using login information it grants me to go to admin_panel.
Kindly solve my issue.
Web.php
Route::get('/admin_log', function () {
return view('Admin.admin_login');
});
Route::group(['middleware'=>'session_auth'],function(){
Route::get('/admin_panel','LoginController#admin_panel');
LoginController
public function admin_panel(){
return view('Admin.admin_panel');
}
public function admin_login(Request $req){
$login=AdminLogin::first();
if ($login->Admin_Name==$req->admin_name && $login->Admin_Password==$req->admin_password ){
$req->session()->put('session_name',$req->admin_name);
return redirect('admin_panel');
}
else{
return redirect('admin_log')->with('error','Invalid UserName or Password!');
}
}
Middleware
public function handle($request, Closure $next)
{
if(is_null($request->session()->get('session_name'))){
return redirect('/admin_log');
}
return $next($request);
}
I'm new to laravel I have created middleware for my each role but when I add it to my route it won't work.
If I add single middleware to my route it works fine but when I add second and third one It will not work.
It won't shows the route to authorized user it redirect it to home,
My User Model:
public function IsAdmin()
{
if($this->role_id =='1')
{
return true;
}
else
{
return false;
}
}
public function IsManager()
{
if($this->role_id =='2')
{
return true;
}
else
{
return false;
}
}
public function IsUser()
{
if($this->role_id =='3')
{
return true;
}
else
{
return false;
}
}
My Kernal:
'IsAdmin' => \App\Http\Middleware\IsAdmin::class,
'IsManager' => \App\Http\Middleware\IsManager::class,
'IsUser' => \App\Http\Middleware\IsUser::class,
My IsAdmin Middlewares:
public function handle($request, Closure $next)
{
$user =Auth::User();
if(!$user->IsAdmin())
{
return redirect('stock');
}
return $next($request);
}
My IsManager
public function handle($request, Closure $next)
{
$user =Auth::User();
if(!$user->IsManager())
{
return redirect('stock');
}
return $next($request);
}
and IsUser
public function handle($request, Closure $next)
{
$user =Auth::User();
if(!$user->IsUser())
{
return redirect('stock');
}
return $next($request);
}
and finally my Route
Route::get('approv',['middleware'=>['IsManager','IsAdmin'],function(){
return view('approv');
}]);
This will not work as you'd expect. All middleware need to pass in order for the request to be processed which means that your user will need to be both a manager and an admin at the same time which based on your setup is impossible.
You can get around this (kind of) by making a different kind of middleware:
Kernel:
'roles' => \App\Http\Middleware\Roles::class,
And the Roles middleware:
class Roles {
private function checkRole($role) {
switch ($role) {
case 'user': return \Auth::user()->IsUser();
case 'manager': return \Auth::user()->IsManager();
case 'admin': return \Auth::user()->IsAdmin();
}
return false;
}
public function handle($request, Closure $next, ...$roles)
{
foreach ($roles as $role) {
if ($this->checkRole($role)) {
//At least one role passes
return $next($request);
}
}
//All checks failed so user does not have any of the required roles
return redirect('stock');
}
}
Then to use this you simply do:
Route::get('approv',['middleware'=>['roles:manager,admin'],function(){
return view('approv');
}]);
This works because Laravel Middleware support parameters. You can pass parameters as a comma separated list of strings where you declare the middleware. In this case this was done as roles:manager,admin
Laravel will then send these parameters as additional parameters in the handle method. These can be accessed using PHPs syntax for variadic arguments. In this particular case it's by using the array spread operator. This is documented as an example in the function arguments section of the PHP manual.
Note that this is actually equivalent to saying :
public function handle($request, Closure $next, $role1=null, $role2=null, $role3=null)
but using the spread operator is much more convenient since ...$roles would be an array which contains only the roles that were passed in the middleware.
I have two login forms with two different tables.One is default with /login route and the other has route /myportal. I have extra logincontroller
protected $redirectTo = '/student-home';
public function showLoginForm()
{
return view('my_portal');
}
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->flush();
$request->session()->regenerate();
return redirect('/my_portal');
}
protected function guard()
{
return Auth::guard('web_student');
}
public function username ()
{
return 'username';
}
This login is working fine. But, I am having problem with RedirectIfAuthenticated
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
else if(Auth::guard('web_student')->check())
{
return redirect('student-home');
}
return $next($request);
}
Now, if the user is already logged in, it is redirected to /student-home only if the route is /login and not /my-portal. i.e only if i click on regular form not this extra form I created. How can I redirect to student-home if user clicked on /my-portal?
You can connect a controller to the my-portal route with :
Route::get('test', 'exampleController#example') ;
Then in the controller function, you can check if the user is already logged in by
public function example() {
if(Auth::check()) {
//This condition will run if the user is logged in !
return redirect('student-home');
}
//Do whatever you want if user is not logged in!
}
Hopefully, this answers your question!
Please change your RedirectIfAuthenticated middleware like this
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
if(guard == 'web_student') {
return redirect('student-home');
}else return redirect('/home');
}
return $next($request);
}
The problem with your code is that the following segment will always true if a user is logged in. You have to check for whether or not a specific guard is set, inside this if statement if you want to redirect them accordingly.
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
So i'm trying to achieve something, that seems is impossible. I want some routes in my application to use different controller based on user role. This is the approach i'm trying, but it doesn't work well. The user routes work, but admin routes return and Trying to get property on non object error in the VerifyCsrfToken.php file*
Route::group(array('middleware' => 'isAdmin'), function() {
Route::get('/', 'Admin\TestController#getIndex');
});
Route::group(array('middleware' => 'isUser'), function() {
Route::get('/', 'User\TestController#getIndex');
});
My middlewares
public function handle($request, Closure $next)
{
if(Auth::user()->isAdmin()) {
return $next($request);
}
}
public function handle($request, Closure $next)
{
if(Auth::user()->isUser()) {
return $next($request);
}
}
I've seen some handle this kind of situation, by just handling this in the controllers or even checking the use role inside the routes file, but I would rather use middlwares, so my routes file would be cleaner
You can do something like this:
Route::get('/', function () {
if (auth()->check()) {
if (auth()->user()->isAdmin()) {
return redirect()->route('');
} elseif (auth()->user()->isUser()) {
return redirect()->route();
} else {
return view('index');
}
}
return redirect()->to('login');
});
The error message has probably nothing to do with the code your show.
But using multiple controllers on one route is impossible, I asked the same question once.
But you could just use one controller and handle the authorization in that controller.
For example:
public function getIndex()
{
if(Auth::user()->isAdmin()) {
//Admin
return $this->getAdminIndex();
} else {
//No admin
return $this->getUserIndex();
}
}
protected function getAdminIndex()
{
return view('admin.index');
}
protected function getUserIndex()
{
return view('user.index');
}
But the cleanest way to do it is to just have 2 routes.
I have a laravel application in this application i have following function for login user
public function login() {
try {
$inputs = Input::except('_token');
$validator = Validator::make($inputs, User::$login);
if ($validator->fails()) {
return Redirect::to('/')->with('message', 'Please Enter Valid Credentials');
} else {
$respones = \UserHelper::processLogin($inputs);
if ($respones) {
return Redirect::to('/dashboard')->with('success_message', 'Welcome to Tressly Admin Dashboard');
} else {
return Redirect::to('/')->with('message', 'Please Enter Valid Information ');
}
}
} catch (Exception $ex) {
return CommonHelper::AdminExceptions($ex);
}
}
Now as user logout and presses the back button , browser show previous page as it is present in cache. Now on this page as user tries to access any protected route application It shows following error
I want to redirect it to '/'( home route)as logged out user tries to acess any protect routes following error comes
Class App\Illuminate\Auth\Middleware\AdminAuthenticate does not exist
I have made a custom Authentication Middle , handle function of the middleware is
public function handle($request, Closure $next, $guard = null) {
if (Auth::check()) {
return $next($request);
}
return redirect('/');
}
I have also registered it in kernal.php in $routeMiddleware like
'authAdmin' => \Illuminate\Auth\Middleware\AdminAuthenticate::class,
and protected my route like
Route::group(['middleware' => 'authAdmin'], function () {
///routes
});
Any ideas ?
use
'authAdmin' => \App\Http\Middleware\AdminAuthenticate::class,
Instead of
'authAdmin' =>\Illuminate\Auth\Middleware\AdminAuthenticate::class,
I hope it it will work
Is there a reason you made a custom middleware class that does exactly the same thing as the already present 'auth' middleware?
RedirectifAuthenticated.php does this;
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
return $next($request);
}
https://laravel.com/docs/5.3/authentication#protecting-routes