I'am using Sentinel package, for example I've created middleware PCMiddleware
public function handle($request, Closure $next)
{
$roles = array_slice(func_get_args(), 2);
foreach ($roles as $role) {
if(Sentinel::check() && Sentinel::getUser()->inRole($role)){
return $next($request);
} else {
return redirect()->route('admin.errors');
}
}
}
Route:
$route->get('dashboard', ['as' => 'dashboard', 'uses' => 'AdminController#dashboard'])->middleware('role_check:member,hrd,super-admin');
and I've created user too with role hrd, which is any user with hrd role can access dashboard but when I loggedin endup with 404 how can I be able to access /dashboard even though I only have hrd role ?
maybe like this if user has any of these 3 role then he can access the route,
In Laravel already there is a way of transferring parameters for the middlewares. You can read about it here
public function handle($request, Closure $next)
{
$roles = array_slice(func_get_args(), 2);
$status = false;
foreach ($roles as $role) {
if(Sentinel::check() && Sentinel::getUser()->inRole($role)){
$status = true;
break;
}
}
if ($status) {
return $next($request);
}
return redirect()->route('admin.errors');
}
In this example, your code will work if at least one of the passed parameters exists
Related
I am trying to protect a route using two middle-wares so that both expert and user can access the same route but as soon a user tries to access the route he is logged out.
I had created two middle-wares for expert and user and protect the route using these middle-wares.
Web.php
Route::group(['middleware' => ['expert','user']], function () {
Route::post('/showForm','UserController#showFormFilled');
});
User Middle ware
public function handle($request, Closure $next)
{
//////////////////// check if user is logged in ///////////////////
if(Auth::check())
{
////////////////// check user role id //////////////////////////
if(auth()->user()->role_id == 3)
{
return $next($request);
}
else if (auth()->user()->role_id==2)
{
return redirect('/expert');
}
}
else
{
return redirect('/login');
}
}
Expert Middle ware
public function handle($request, Closure $next)
{
if(Auth::check()){
if(auth()->user()->role_id == 2)
return $next($request);
else if (auth()->user()->role_id==3)
return redirect('/dashboard');
}
else {
return redirect('/login');
}
}
Both the users should be able to access the same route.
#hamzahummam - there is no way to achieve what you are looking for using the above separate-middlware-for-each-type method. Each middleware prematurely redirects [either to /dashboard or to /expert etc] the request without allowing it to passthrough other middleware. Best would be to use a third-party package that provides a more comprehensive and fine-grained access control [example: https://github.com/Zizaco/entrust]
If that's not an option, the best case would be to implement a single middleware and pass the role as parameter. See: Laravel Middleware Parameters
A minimal example would look like:
public function handle($request, Closure $next, $role)
{
// Assuming Auth::check() passes
$roleId = auth()->user()->role_id;
if ($roleId == 2 && strpos($role, 'expert') !== false) {
// Logged in user is `expert` and route allows `expert` access
return $next($request);
} else if ($roleId == 3 && strpos($role, 'user') !== false) {
// Logged in user is `user` and route allows `user` access
return $next($request);
} // and so on...
// Handle failures here
if ($roleId == 2 && strpos($role, 'expert') === false) {
// an `expert` is trying to access route that can't be accessed
return redirect('/expert-dashboard');
} // and so on...
}
You'd define routes as:
Route::group(['middleware' => ['new_middleware:expert,user' ]], function () {
Route::post('/showForm','UserController#showFormFilled');
});
Hope this helps.
Inside an AuthServiceProvider Access Control is defined. I need to check permission to access page in the routes web.php.
If user is not admin then page should redirect error page or page not found .
How to create a middleware that redirect to 404 page if somebody tries to access the page from the url.
AuthServiceProvider
Gate::define('isAdmin',function($user){
return $user->type === 'admin';
});
Gate::define('isGeneralUser',function($user){
return $user->type === 'user';
});
Gate::define('isPaidUser',function($user){
return $user->type === 'paid';
});
Route web.php
if (Gate::allows('isAdmin') && Gate::allows('isPaidUser')) {
Route::get('/home-page', function () {
return view('pages.trades');
});
}
create middleware
class CheckIsTradeable
{
public function handle($request, Closure $next)
{
if ($request->user()->type !== 'admin' && $request->user()->type !== 'paid') {
abort(404);
}
return $next($request);
}
}
Register inside Kernal
protected $routeMiddleware = [
...
'isTradeable' => \App\Http\Middleware\CheckIsTradeable::class,
];
and check it in your route
Route::get('/home-page', function () {
return view('pages.trades');
})->middleware('isTradeable');
I have two levels in middleware and it does not work well, then how to write middleware in two levels at once?
public function __construct()
{
$this->middleware('auth');
$this->middleware('Admin');
$this->middleware('Teacher');
}
I also have this, i want to check that this page is only accessible by admin and teacher, but in writing the code it does not work well and how to write it right?
#if (Auth::check() && Auth::user()->level == 'Admin')
#elseif (Auth::check() && Auth::user()->level == 'Teacher')
#endif
If you can help me in solving this problem i am very grateful
in my experience i did that using a middleware ..
Middleware
public function handle($request, Closure $next, $roles)
{
$roles = explode('|',$roles);
$user = User::find($request->user()->id);
foreach($user->intRoles as $myRole)
{
if(in_array($myRole->role_id, $roles))
{
return $next($request);
}
}
return redirect()->back();
}
Route
Route::group( ['middleware' => 'rolePermissions:1|2|3'], function(){
Route::get('/users',['as'=>'users', 'uses'=>'PagesController#users']);
});
mydomain.com/users will only be accessible for those users who have roles (id) of 1, 2, or 3 ..
You have 2 separate middleware for checking user level. This forces both the conditions and doesn't work the way you want. You need a single middleware to check the user level and pass parameters to it.
Assume we have this middleware named as access.
public function handle($request, Closure $next, $levels)
{
$userLevels = explode(',', $levels);
if (Auth::check() && in_array(Auth::user()->level, $userLevels)) {
return $next($request);
}
return redirect('/home');
}
In your controller add this
public function __construct()
{
$this->middleware(['auth', 'access:Admin,Student']);
}
I have a role middleware that passes a parameter to the middleware.
public function __construct(UserInterface $user, RoleInterface $role, MaritalStatusInterface $maritalStatus, CityInterface $city){
$this->middleware('auth');
//$this->middleware('role:System Admin'); I changed it to 6 for the id.
$this->middleware('role:6');
}
My RoleMiddleware is this
public function handle($request, Closure $next, ...$params)
{
$roles = $request->user()->roles()->get();
$roles = $roles->map(function($item){
return $item->id;
});
foreach ($params as $value) {
if( ! in_array($value, $roles->toArray())){
return redirect()->action('NavController#home');
}
}
return $next($request);
}
As you can see I it is very tied to 6. What if I want to add 7... I want to make it dynamically. My tables are Users Role_User and Roles. What table do I need and how to execute it.
since you want it dynamically you should have a database for the allowed roles .. then in the same controller you should do
$allowedRoles = AllowedRoles::all();
$loop=1;
foreach($allowedRoles as $role)
{
$roleString = $loop == 1 ? $role->value : $roleString.'|'.$role->value;
// where value is equivalent to the role of your role id in role database
$loop++;
}
$this->middleware('role:'.$roleString);
then in your middleware you should explode the roleString
public function handle($request, Closure $next, $roles)
{
$roles = explode('|',$roles);
$user = User::find($request->user()->id);
foreach($user->intRoles as $myRole)
{
if(in_array($myRole->role_id, $roles))
{
return $next($request);
}
}
// Redirect
return redirect()->back();
}
My roles are dynamic and their User's permission are also dynamic. I have two approaches to validate if the user is authorized to access the particular page.
Approach 1
class BaseController extends Controller
{
public function __construct() {
if(!\Auth::user()->IsPredefined) {
$result = $this->ValidateAuthorization();
if(!$result) {
\Auth::logout();
return redirect()->route("login");
}
}
}
private function ValidateAuthorization() {
$ActionName = \Route::getCurrentRoute()->getPath();
switch ($ActionName) {
case "ChangePassword":
$ModuleID = ModuleEnum::AccountManagemenet;
$ActionID = AccountActionEnum::ChangePassword;
return CheckUsePermissions($ModuleID, $ActionID);
}
}
private function CheckUsePermissions($ModuleID, $ActionID) {
$User = MySession::UserPermissions();
foreach($User->UserRolePermissions as $UserRolePermission) {
$CurrentActionID = $UserRolePermission->RolePermission->Permission->ActionID;
$CurrentModuleID = $UserRolePermission->RolePermission->Permission->ModuleID;
if($CurrentActionID == $ActionID && $CurrentModuleID == $ModuleID &&
$UserRolePermission->IsActive == true) {
return true;
}
}
return false;
}
}
Approach 2
Use Authorize method in Request class
public function authorize()
{
return true;
}
Confusion
If Approach 2 is good, should I create Request class for each Get, Put, Delete and POST?
Is there any better approach to validate authorization?
For Dynamic Roles I do something like this:
Let suppose I have following permissions:
Manage Users
Manage CMS
Manage Hotels
Manage Packages
Manage Roles
Now the Super Admin (who obviously has all the above permissions) can create a role, say, Hotel Manager and assign permission for only Manage Hotels.
Now for a route like:
Route::get('/admin/hotels', 'HotelsController#index')->name('admin.hotels.index');
I will put it in a Route Group with a middleware in it:
Route::group(['prefix' => '/admin/hotels', 'middleware' => ['permission:manage_hotels']], function () {
Route::get('/', 'HotelsController#index')->name('admin.hotels.index');
Route::post('/', 'HotelsController#create')->name('admin.hotels.create');
});
Then I will create a Permission middleware:
class Permission
{
public function handle($request, Closure $next, $permission)
{
//if not super admin (super admin role_id is 1)
if(auth()->user()->role_id != 1) {
//explode extra param passed to this middleware (here manage_hotels)
$permission_array = explode('_', $permission);
foreach ($permission_array as $key => $value) {
$permission_array[$key] = ucfirst($value);
}
$permission_name = implode(' ', $permission_array);
$permitted = auth()->user()->role->permissions()->where('name', $permission_name)->first();
if(! $permitted) {
return redirect()->route('admin.dashboard')->withErrors(['error' => ['message' => 'You are not allowed to perform this action.']]);
}
}
return $next($request);
}
}
Of course you'll need to wrap these routes into Admin Middleware which will ensure that user is logged in and has an admin role.
You should use middleware and use in routes
Route::group(['middleware' => ['auth', 'admin']], function () { });
Or you can use in every controller constructor
public function __construct()
{
$this->middleware('auth');
$this->middleware('admin');
}
Admin middleware like
class AdminAuthenticate
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::check()) {
if (Auth::user()->IsPredefined) {
$result = $this->ValidateAuthorization();
if(!$result) {
return redirect('/logout');
}
}
}
return $next($request);
}
}