I have a route group with different routes. I want to have different role levels access without changing the URL of the application.
For example I want to have /admin as the route and then I want to allow or disallow users based on their roles. Basically, I want every user to be able to see the same page but with different menu options(I know how to do this) but also secure the links from direct access.
Is there a nice way to achieve that without the need of using different middlewares seperately on each route? Since there doesn't seem to be a way to retrieve the $request variable inside the web.php file but only inside a controller. I'm using the sentinel package for auth.
Some sample code of my web.php:
Route::group(
['prefix' => 'admin', 'middleware' => 'customer', 'as' => 'admin.'],
function () {
// Ad list
Route::get('getMyAnnonsList', 'Admin\BackEndController#getMyAdList')->name('getMyAdList');
}
);
Great answer by #lagbox. This is what I did in the end. Very elegant.
web.php:
Route::group(['prefix' => 'admin', 'as' => 'admin.'], function () {
Route::middleware('admin:admin,user')->group(function(){
Route::get('getMyAnnonsList', 'Admin\BackEndController#getMyAdList')->name('getMyAdList');
});
});
middleware:
public function handle($request, Closure $next, ...$roles)
{
if (!Sentinel::check())
return redirect('admin/signin')->with('info', 'You must be logged in!');
foreach($roles as $role)
if($role == Sentinel::getUser()->roles[0]->slug)
return $next($request);
return redirect()->back();
}
I had already answered something like this before, should be working the same still.
You can create a middleware that can be applied to your group. In that middleware it is asking the route itself for the specific roles to check.
How to assign two middleware to the same group of routes. Laravel
Example of middleware:
class CheckMiddleware
{
public function handle($request, Closure $next)
{
$roles = $request->route()->getAction('roles', []);
foreach ((array) $roles as $role) {
// if the user has this role, let them pass through
if (...) {
return $next($request);
}
}
// user is not one of the matching 'roles'
return redirect('/');
}
}
Example route definition:
Route::middleware('rolescheck')->group(function () {
Route::get('something', ['uses' => 'SomeController#method', 'roles' => [...]])->name(...);
});
You can apply this arbitrary data at the group level, the individual route level or both, as all routes are individually registered; groups just allow for cascading of configuration.
You could also have this middleware take parameters, and just merge them with the arbitrary roles, then it is a dual purpose middleware:
public function handle($request, $next, ...$roles)
{
$roles = array_merge($roles, $request->route()->getAction('roles', []));
...
}
Route::middleware('rolescheck:admin,staff')->group(...);
You can use Laravel Gate And Policies
You can define the gate inside the App > Providers > AuthServiceProvider
and you can also create policies per CRUD. just see info in php artisan help make:policy. This will create a folder in your app called policies you can define the who can access it.
In your controller you can do is this: (this is a gate middleware)
I define the gate first:
Gate::define('check', function ($user, $request) {
return $user->roles->contains('name', $request) || $user->roles->contains('name', 'root');
});
then I initialise it in the controller
abort_if(Gate::denies('check', 'admin only'), 403);
This will throw 403 error if the user don't have access on that role. It will check if the user has admin only role. If it doesn't have it will throw the error
In your view if you want to disable anchor links you can do like this:
#can('check', 'admin only')
dashboard
#endcan
EDIT:
Controller
public function index() {
abort_if(Gate::denies('check', 'admin only'), 403);
// Your Code...
}
Related
I'm trying to create permissions and roles for users in a Laravel API Project. I'm using Passport and Spatie's Permission package. Using that I added a middleware:
Route
Route::middleware('auth:api')->group(function () {
/** Roles Route */
Route::get('roles',[RoleController::class, 'index'])->middleware('permission:role-list');
}
Handler.php:
public function render($request, Throwable $exception)
{
if ($exception instanceof UnauthorizedException) {
return response()->json(['User does not have the right roles.'],403);
}
return parent::render($request, $exception);
}
Controller:
function __construct()
{
$this->middleware('permission:role-list', ['only' => ['index']]);
$this->middleware('permission:role-create', ['only' => ['store']]);
$this->middleware('permission:role-edit', ['only' => ['update']]);
$this->middleware('permission:role-delete', ['only' => ['destroy']]);
}
public function index()
{
$roles = Role::all();
return response(['role' => $roles], 200);
}
I created an admin account and it was given all the existing permissions, but whenever I ask for the route, it shows me that error message:
User does not have the right roles.
I checked the database and the user had all the required permissions for that.
First of all add the package middlewares in the Kernel.php as described here.
protected $routeMiddleware = [
// ... other middleware
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];
Depending on where you want to define the permissions to access a method either define them in the Controller or on the route. Adding them to both the route and Controller is only doing the same thing.
Changing something in the Handler.php should not be required.
Note that permissions can have different guards. For example it can be web or api.
After changing roles or permissions it sometimes helps to reset the cache. Resetting the cache can be done with php artisan permission:cache-reset command.
I have 2 roles, which is admin and user. Now when logging in, the admin goes to the dashboard route while the user goes to home. When user is logged in and changes the url to http://127.0.0.1:8000/dashboard it can access the admin's panel and I don't want that. How can I do achieve this?
PS. I'm new to Laravel
The good practice for this is usage of Middewares.
Create middlewares for admins and users (I'll do that only for admins, you can do that similarly for users):
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminMiddleware
{
public function handle($request, Closure $next)
{
if(Auth::check()){
// check auth user role (I don't know how you can implement this for yourself, this is just for me)
if(Auth::user()->role->name == 'admin'){
return $next($request);
} else {
return redirect()->route('admin.dashboard'); // for admins
}
}
return redirect()->route('main'); // for users
}
}
In "app/Http/Kernel.php" in $routeMiddleware array register that (add to end of that array).
'Admin' => \App\Http\Middleware\AdminMiddleware::class,
Now if you are using all requests in "routes/web.php" (actually I think it does), then you can use routes like this for admins:
// USER ROUTES
Route::get('/', 'FrontController#main')->name('main');
// ADMIN ROUTES
Route::group([
'as' => 'admin.',
'middleware' => [ 'Admin' ],
], function () {
Route::get('dashboard', 'AdminController#dashboard');
});
Refresh caches via "php artisan config:cache".
Try it!
Use middleware to admin route or inside the controller
like this:
Route::put('post/{id}', function ($id) {
//
})->middleware('role:editor');
or
Route::middleware(['auth', 'admin'])->group(function (){
Route::get('dashboard', 'HomeController#index')->name('home.index');
});
or inside the controller like this:
public function __construct()
{
$this->middleware(['auth', 'admin'])->except(['index']);
}
or you can use this for middleware roles.
I have problem to make routing with middleware multi roles
I have tried some in internet but still wont work
I have 3 roles, superadmin, admin and member
I want the superadmin and admin can access the add page
here is my code :
Route::group(['prefix' => 'staff', 'middleware' => 'auth'], function () {
Route::GET('/add', [
'uses' => 'StaffController#page_add',
'middleware' => 'rule:superadmin', ???
]);
});
I have tried to put 'middleware' => 'rule:superadmin|rule:admin'
but wont work
thank you
Create a middleware file eg Role.php
public function handle($request, Closure $next, ... $roles)
{
if (!Auth::check()) // I included this check because you have it, but it really should be part of your 'auth' middleware, most likely added as part of a route group.
return redirect('login');
$user = Auth::user();
if($user->isAdmin())
return $next($request);
foreach($roles as $role) {
// Check if user has the role This check will depend on how your roles are set up
if($user->hasRole($role))
return $next($request);
}
return redirect('login');
}
Finally in your web routes
Route::get('admin/scholen/overzicht', 'SchoolsController#overview')->middleware('role:editor,approver');
Route::get('admin/scholen/{id}/bewerken', 'SchoolsController#edit')->middleware('role:admin');
Check out this best answer for more details
Hey you can put a column named "role" in your users table then check it with a condition.
Route::get('/add', function() {
if (Auth::user()->role == 'superadmin' || Auth::user()->role == 'admin') {
return view('add-page');
}
else {
return view('error-page');
}
});
I have 3 roles roles, admin, tutor and student. i want to place them in 3 different goups, i however want the admin to be in all the groups. I have tried different methods to add the admin to other routes but it's not working. How can i make admin use all routes in tutor's middleware? Here is my code
AdminMiddleware, similar to all the others
class AdminMiddleware
{
public function handle($request, Closure $next)
{
if(Auth::check() && Auth::user()->isRole()=="admin") {
return $next($request);
}
return redirect('login');
}
}
routesmiddleware - in web.php
Route::group(['middleware'=>['auth'=>'admin']], function (){
//admin routes
}
Route::group(['middleware'=>['auth'=>'tutor']], function (){
//tutor routes
}
in the Kernel.php
'admin' => \App\Http\Middleware\AdminMiddleware::class,
'tutor' => \App\Http\Middleware\TutorMiddleware::class,
'student' => \App\Http\Middleware\StudentMiddleware::class,
in user model
public function isRole(){
return $this->role;
}
What you can do is define a middleware that takes the supported roles as argument:
class HasRoleMiddleware
{
public function handle($request, Closure $next, ...$roles)
{
if(Auth::check() && in_array(Auth::user()->getRole(), $roles)) {
return $next($request);
}
return redirect('login');
}
}
This middleware expects that User::getRole() returns the role as string, i.e. admin or tutor. Then you need to define the middleware in Kernel.php as you have already done:
'any_role' => \App\Http\Middleware\HasRoleMiddleware::class,
And finally you can use the middleware like this:
Route::group(['middleware' => 'auth'], function () {
Route::group(['middleware' => 'any_role:admin'], function () {
// admin routes
}
Route::group(['middleware' => 'any_role:admin,tutor'], function () {
// tutor routes
}
}
As you can see, I've also nested the route groups which perform role checks inside another route group which checks for authentication. This makes it more readable and reduces the risk you forget an authentication check in a future extension of your routes.
I created a new Module named Article using laravel-modules. Some backend routes needed authentication and i added auth middleware and an additional permission view_backend. I am using https://github.com/spatie/laravel-permission package for role-permissions.
the issue is when i try to access the route admin/article/posts it prompts me the login as expected. But after login it show null on __construct() method for Auth::user();
I added web middleware as mentioned on #204 but it did not solve the issue. Can you please guide me to resolve this? My project is on Laravel 5.6 and using the latest version of Laravel-Modules
Route::group(['namespace' => 'Modules\Article\Http\Controllers\Backend', 'as' => 'backend.article.', 'middleware' => ['web', 'auth', 'can:view_backend'], 'prefix' => 'admin/article'], function () {
Route::resource("posts", "PostsController");
});
My project is hosted at Github, https://github.com/nasirkhan/laravel-starter/tree/module
First of all, add Spatie Middleware to your kernel:
protected $routeMiddleware = [
// ...
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
];
Then in your controller check for permission or roles:
public function __construct(Request $request)
{
$this->middleware(['permission: order.index']);
}
Now you can access to your authenticated with $request->user() like:
public function create(Request $request)
{
if ($request->user()->hasRole('admin')) {
// return view("carmodel.create", ["manufacturers"=>$manufacturers]);
} else {
return view("admin.error", ['code'=>'001','msg'=>'err']);
}
}
According to the docs:
In previous versions of Laravel, you could access session variables or the authenticated user in your controller's constructor. This was never intended to be an explicit feature of the framework. In Laravel 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
As an alternative, you may define a Closure based middleware directly
in your controller's constructor. Before using this feature, make sure
that your application is running Laravel 5.3.4 or above:
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->projects = Auth::user()->projects;
return $next($request);
});
}
Or you could typehint it:
public function index(Request $request)
{
$projects = $request->user()->projects;
$value = $request->session()->get('key');
}
Docs