I'm working on Laravel and try to make anACL system. I have a Role Middleware From myController I've sent Role to my RoleMiddleware two way.
first one is send string.
$this->middleware('HasRole:User|Admin|Author');
this way i'm get a string when use dd() function.
and the second way is.
$this->middleware('HasRole:User,Admin,Author');
this way i'm get an array when use dd() function.
but this array only contains a single value.
result like this.
array:1 [▼
0 => "User"
]
other two value Admin & Author doesn't appear in this array.
How can i work both way string & array
Here is my middleware.
public function handle($request, Closure $next,$role='')
{
$roles=is_array($role)? $role: explode('|', $role);
dd($roles);
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
I know maybe not exactly the answer to your question, but I recommend you to use laravel authorization instead middleware. see this doc
I prefer to use this code for your problem in the controller:
$this->authorize('update', $post);
or:
if ($user->can('create', Post::class)) {
// Executes the "create" method on the relevant policy...
}
then inside the policy file, you should specify the statement like:
if ($user->isSuperAdmin() || $user->id === $post->user_id) {
return true;
}
if you want middleware to automatically convert your string to array then middleware $role params should be like this
public function handle($request, Closure $next, ...$roles)
{
dd($roles);
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
Then use it like this $this->middleware('HasRole:User,Admin,Author');
OR
But if you want some other character to separate your roles like | then the code should be
public function handle($request, Closure $next, $role = '')
{
$roles = explode('|', $role);
dd($roles);
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
And use it like $this->middleware('HasRole:User|Admin|Author');
You can use both, just add some code in your middleware.
public function handle($request, Closure $next,...$role)
{
//add two line
$str_role=explode('|',$role[0]);
$roleString=$str_role;
$roles=is_array($role)? $role : is_array($roleString)? $roleString : null;
//dd($roles);
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
NOW, you can use.
$this->middleware('HasRole:User|Admin|Author');
OR,
$this->middleware('HasRole:User,Admin,Author');
Related
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 a role middleware. when i pass to role from my controller as a string it's work properly but when pass as an array it's not working. This way middleware work properly.
$this->middleware('HasRole:User|Admin|Author')->except(['userEdit','roleEdit','permissionEdit','userUpdate']);
But when I change like this, it's not working.
$this->middleware('HasRole:User,Admin,Author')->except(['userEdit','roleEdit','permissionEdit','userUpdate']);
Here, is my middleware.
public function handle($request, Closure $next, ...$role)
{
$roles=is_array($role)? $role: explode('|', $role);
//dd($roles);
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
controller
public function __construct()
{
$this->middleware('HasRole:User,Admin,Author')->except(['userEdit','roleEdit','permissionEdit','userUpdate']);
}
your middleware
read here http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list about the ...$
public function handle($request, Closure $next, ...$roles)
{
// $roles is array('User', 'Admin', 'Author')
// 0 => 'User',
// 1 => 'Admin',
// 2 => 'Author'
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
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);
}
}
I created two Middleware called "MustBeAdmin" and "MustBeUser" to make sure depending on the user login I redirect them to the right page and restrict unauthorized content. Currently everything is working fine and redirects work well too. But the Logic I wrote behind the scene seems wrong to me and its weird it still works. If I write the logic that seems right to me atleast, it does not seem to work as expected.
Users table
id (1,2,3,...)
name
role (1,2,3,...)
Roles table
id (1,2,3,...)
role (Student, Admin,...)
MustBeAdmin middleware
public function handle($request, Closure $next)
{
if($request->user()->role == 2)
{
return $next($request);
}
else
{
return redirect('/admin/users');
}
}
MustBeUser middleware:
public function handle($request, Closure $next)
{
if($request->user()->role == 1)
{
return $next($request);
}
else
{
return redirect('/admin/users');
}
}
kernel.php
'admin' => \App\Http\Middleware\MustBeAdmin::class,
'user' => \App\Http\Middleware\MustBeUser::class,
As you can see I have registered middlewares in kernel.
I am getting results exactly what I need but I doubt if the logic in middleware is correct?
1 = Student
2 = Admin
if you see in MustBeAdmin middleware I am comparing if user role is 2 (admin) then do next($request) and in MustBeUser middleware I am comparing if user role is 1 (Student) then do next($request) and I set else to /Admin directory.
I feel its wrong, what do you think?
You are not checking the authenticated users details in your Middleware. The middleware should be something like:
//for student
public function handle($request, Closure $next)
{
if ( Auth::check() && Auth::user()->role == 1 )
{
return $next($request);
}
return redirect('/admin');
}
//for admin
public function handle($request, Closure $next)
{
if ( Auth::check() && Auth::user()->role == 2 )
{
return $next($request);
}
return redirect('/student');
}
You should check my detailed answer on the same topic here
Yes, It can be handled in one common file.
Here is the code
public function handle($request, Closure $next)
{
$user = User::find(Auth::id());
$roles = [];
foreach ($user->roles as $key => $value) {
array_push($roles, $value->pivot->role_id);
}
$routeName = Route::getFacadeRoot()->current()->uri();
$route = explode('/', $routeName);
if ($route[0] == "teacher") {
if (in_array(2, $roles)) {
return $next($request);
} else {
return response('Unauthorized.', 401);
}
} elseif ($route[0] == "student") {
if (in_array(1, $roles)) {
return $next($request);
} else {
return response('Unauthorized.', 401);
}
} elseif ($route[0] == "admin") {
if (Auth::user()->admin == 1) {
return $next($request);
} else {
return response('Unauthorized.', 401);
}
} else {
if (!Auth::user()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('admin-panel/auth/login');
}
}
}
return $next($request);
}
You can alter the logic according to your need.
I created a middleware to check if a user is admin or editor, but for some reason in my if statement when i use the OR operator to check if the user has access, it doesnt work, it accepts the first property statement, but not after the OR operator.
To work i need to separate each condition of account type.
For example:
Code dont work:
public function handle($request, Closure $next)
{
if(Auth::user()->account_type_id == '1' || Auth::user()->account_type_id == '2') // is an admin
{
return $next($request); // pass the admin
}
return redirect('/admin'); // not admin. redirect whereever you like
}
Code that Works:
public function handle($request, Closure $next)
{
// dd($request->all());
if(Auth::user()->account_type_id == '1') // is an admin
{
return $next($request); // pass the admin
}
if(Auth::user()->account_type_id == '2') // is an admin
{
return $next($request); // pass the admin
}
return redirect('/admin'); // not admin. redirect whereever you like
}
Does anybody no whats wrong?
I would create an array and check the condition again the array of possible solutions.
public function handle($request, Closure $next)
{
$accountTypes = array('1','2');
if (in_array(Auth::user()->account_type_id, $accountTypes)) {
return $next($request); // pass the admin
}
return redirect('/admin'); // not admin
}
Try using the in_array function, this will also make your code more readable and easy to maintain.
public function handle($request, Closure $next)
{
if (in_array(Auth::user()->account_type_id, ['1', '2'])) // is an admin
{
return $next($request); // pass the admin
}
return redirect('/admin'); // not admin. redirect whereever you like
}
I don't know why the second one work but here is mine and it works fine give it a try
public function handle($request, Closure $next)
{
$user = $request->user();
if ($user->role == 1 || $user->role == 2) {
return $next($request);
}
else{
return redirect(url('/admin'));
}
}