Prevent role-specific users from accessing route - laravel

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.

Related

How can I add OR relationship in laravel middleware for a Route

I'm using spatie/laravel-permission to add permissions for my routes.
One of the routes I have can be accessed by two permissions (earnings, financial_fund). The user doesn't need to have both permissions to access the controller. He can access the controller by having one of the permissions or both of them.
I've tried to write something like this.
Route::group(['middleware' => ['can:earnings']], function () {
Route::get('/payment', [PaymentsController::class, 'getAll']);
Route::post('/payment/cash', [PaymentsController::class, 'addCashPayment']);
});
Route::group(['middleware' => ['can:financial_fund']], function () {
Route::get('/payment', [PaymentsController::class, 'getAll']);
Route::post('/payment/cash', [PaymentsController::class, 'addCashPayment']);
});
But the above code allows only users with can:earnings permission to access the routes, it doesn't allow users with can:financial_fund permission.
I've also tried to write something like this
Route::group(['middleware' => ['can:earnings,financial_fund']], function () {
Route::get('/payment', [PaymentsController::class, 'getAll']);
Route::post('/payment/cash', [PaymentsController::class, 'addCashPayment']);
});
But this requires both of permissions to exist with the current user.
How can I tell that I want only one of the permissions at least to exist?
I have found that Laravel has introduced the canAny for use in blade templates. Is there a way I can use it in my api.php file while defining the Routes?
I fixed it by creating a new middleware
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class AuthorizeCanAny
{
public function handle(Request $request, Closure $next, ...$permissions)
{
if (!$request->user()) {
abort(403);
}
$userPermissions = array_map(function ($e) {
return $e['name'];
}, $request->user()->permissions->toArray());
$userPermissionsIntersect = array_intersect($userPermissions, $permissions);
if (!sizeof($userPermissionsIntersect)) {
abort(403);
}
return $next($request);
}
}
Adding the middleware to kernal.php file
protected $routeMiddleware = [
...,
'canAny' => AuthorizeCanAny::class,
];
Then use it in the router
Route::group(['middleware' => ['canAny:earnings,financial_fund']], function () {
Route::get('/payment', [PaymentsController::class, 'getAll']);
Route::post('/payment/cash', [PaymentsController::class, 'addCashPayment']);
});

Laravel 6 perform role based routing

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...
}

How can i register multiple middleware permissions in routegroup?-Laravel

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.

Using cookies in laravel to get referral code id?

I have generated a referral code for each user on my platform, and would to use cookies to track if a new user has signed up through someone else(aka. they got referred).
I can generate a url fine, i.e., http://localhost:8888/Test/public/?ref=111222333444
But the cookie doesn't appear to be storing and translating back to my database when I use the code to sign up as a new user
What am I missing?
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Response;
use Closure;
class CheckReferral
{
public function handle($request, Closure $next)
{
if( $request->hasCookie('referral')) {
return $next($request);
}
else {
if( $request->query('ref') ) {
print "yes cookie detected";
return redirect($request->fullUrl())->withCookie(cookie()->forever('referral', $request->query('ref')));
}
}
return $next($request);
}
}
Okay this was my dumb mistake - I overlooked adding the Middleware to my route. Solved it simply by doing this:
use App\Http\Middleware\CheckReferral;
Route::get('/', function () {
return view('welcome');
})->middleware(CheckReferral::class);
Best way to use middleware is to add it in Kernal class by setting it against routeMiddleware array e.g
$routeMiddleware = [
'auth' => \Laravel\Http\Middleware\Authenticate::class,
'guest' => \Laravel\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
...
'check-referral' => \Laravel\Http\Middleware\CheckReferral::class,
];
and then assign the route e.g
Route::get('/', function () {
//
})->middleware('check-referral');
Hope this will help

Removing a route from Auth::routes()

I have these routes:
Auth::routes();
Route::get('/home', 'LibraryController#home');
Route::get('/', 'LibraryController#index');
Auth::routes() is generated by the command php artisan make::auth. But, i don't want the index page to be under auth middleware group, the third route in the above list.
Here is the controller methods. index() is for everyone and home() for authenticated users.
public function index()
{
return view('index');
}
public function home()
{
return view('home')->with('message','Logged in!');
}
the login users is redirected to home url:
protected $redirectTo = '/home';
But whenever i run the third route the login page appears. so, how can i remove that route from auth middleware group.
In your LibraryController before index where your controllers start you need to write
public function __construct()
{
$this->middleware('auth', ['except' => ['index']]);
}
Now every user will be able to go to index method without authentication
Documentation Reference https://laravel.com/docs/5.0/controllers#controller-middleware
Since Laravel 7.7 you can use excluded_middleware property eg:
Route::group([
'excluded_middleware' => ['auth'],
], function () {
Route::get('/home', 'LibraryController#home');
Route::get('/', 'LibraryController#index');
});

Resources