I've two types for user and I've created multiple middlewares.
Some routes need to allow for both type of user.
I've trying following code:
Route::group(['namespace' => 'Common', 'middleware' => ['Auth1', 'Auth2']], function() {
Route::get('viewdetail', array('as' => 'viewdetail', 'uses' => 'DashboardController#viewdetail'));
});
But its not working :(
Middleware is supposed to either return a response or pass the request down the pipeline. Middlewares are independent of each other and shouldn't be aware of other middlewares run.
You'll need to implement a separate middleware that allows 2 roles or single middleware that takes allowed roles as parameters.
Option 1: just create a middleware is a combined version of Auth1 and Auth2 that checks for 2 user types. This is the simplest option, although not really flexible.
Option 2: since version 5.1 middlewares can take parameters - see more details here: https://laravel.com/docs/5.1/middleware#middleware-parameters. You could implement a single middleware that would take list of user roles to check against and just define the allowed roles in your routes file. The following code should do the trick:
// define allowed roles in your routes.php
Route::group(['namespace' => 'Common', 'middleware' => 'checkUserRoles:role1,role2', function() {
//routes that should be allowed for users with role1 OR role2 go here
});
// PHP < 5.6
// create a parametrized middleware that takes allowed roles as parameters
public function handle($request, Closure $next) {
// will contain ['role1', 'role2']
$allowedRoles = array_slice(func_get_args(), 2);
// do whatever role check logic you need
}
// PHP >= 5.6
// create a parametrized middleware that takes allowed roles as parameters
public function handle($request, Closure $next, ...$roles) {
// $roles will contain ['role1', 'role2']
// do whatever role check logic you need
}
This example
How to pass multiple parameters to middleware with OR condition in Laravel 5.2
Instead of adding multiple arguments to your handle method and having to update it every time you add a new role to your application, you can make it dynamic.
Middleware
/**
* Handle an incoming request.
*
* #param $request
* #param Closure $next
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function handle($request, Closure $next) {
$roles = array_slice(func_get_args(), 2); // [default, admin, manager]
foreach ($roles as $role) {
try {
Role::whereName($role)->firstOrFail(); // make sure we got a "real" role
if (Auth::user()->hasRole($role)) {
return $next($request);
}
} catch (ModelNotFoundException $exception) {
dd('Could not find role ' . $role);
}
}
Flash::warning('Access Denied', 'You are not authorized to view that content.'); // custom flash class
return redirect('/');
}
Route
Route::group(['middleware' => ['role_check:default,admin,manager']], function() {
Route::get('/user/{user_id}', array('uses' => 'UserController#showUserDashboard', 'as' => 'showUserDashboard'));
});
This will check if the authenticated user has at least one of the roles provided and if so, passes the request to the next middleware stack. Of course the hasRole() method and the roles themselves will need to be implemented by you.
You can use php 5.6
public function handle($request, Closure $next, ...$roles)
{
foreach ($roles as $role) {
try {
if ($request->user()->can($role)) {
return $next($request);
}
} catch (ModelNotFoundException $exception) {
abort(403);
}
}
}
Route::group(['middleware' => 'role:manager,admin'], function () {}
In middleware named 'role' you can destruct arguments into array
public function handle($request, Closure $next, ...$roles)
{
$userRole = $request->user()->role;
if (! $userRole || ! in_array($userRole->name, $roles)) {
abort(403);
}
return $next($request);
}
simply You can use like this syntax that means if has one of them
Route::group(['middleware' => ['role:first|second|third|....']], function () {});
Related
I already have authentication with api_token working correctly, but I want my consumer to make an API request and return the info respecting only to his user_id.
I know I can do this by passing info in the URL, as I have it right now in Route::get('/getOrder/{user_id}','OrderController#indexByUser'); but I'm afraid that any user could just enter another user_id and get the orders from another User.
api.php
<?php
use Illuminate\Http\Request;
Route::post('login', 'Auth\LoginController#login');
Route::post('logout', 'Auth\LoginController#logout');
Route::group(['middleware' => 'auth:api'], function() {
Route::get('/user', function (Request $request) {
return $request->user();
});
Route::get('/getOrder/{user_id}','OrderController#indexByUser');
});
Login and Logout gives/clears api_token respectively.
Thanks in advance.
You can create own midlleware, were set this logic.
public function handle($request, Closure $next, ...$guards)
{
if (Auth::user()->getId() !== $request->getPathInfo()) { // if userId contains in path
return redirect(back(413));
}
return parent::handle($request, $next, $guards);
}
(this is only example code )
Or you can use policies
https://laravel.com/docs/8.x/authorization#writing-policies
I am working on a project in which I have three type of users Admin and user1 and user2. I want user1 and user2 to able to use certain features in application only if the admin has assigned an invoice to them. I have tried using helper function given below.
$invoice = Invoice::pluck('user_id')->toArray();
if (Auth::user()->admin == 1 || in_array(Auth::user()->id, $invoice)) {
return 1;
} else {
return 0;
}
but this does not work fine. I'll have to place it before every method of a controller in order to restrains users to use that feature. Is there any thing else I can do?
Any Better Approach for this?
You can use middlewares.
Create your middleware with
php artisan make:middleware UserWithInvoiceMiddleware
Then open your file in app/Http/Middleware/UserWithInvoiceMiddleware.php, and add this to the handle method:
public function handle($request, Closure $next, ...$guards)
{
$user = auth()->user();
$invoice = Invoice::pluck('user_id')->toArray();
if ($user->admin || in_array($user->id, $invoice)) {
return $next($request);
}
return response()->json(['message' => 'Request not authorized.'], 401);
}
Also, you can create a relation in your user model with the Invoice model:
public function invoice()
{
return $this->hasOne(Invoice::class);
}
Then, you can simplify your middleware using this relation:
public function handle($request, Closure $next, ...$guards)
{
if (auth()->user()->admin || auth()->user()->has('invoice')) {
return $next($request);
}
return response()->json(['message' => 'Request not authorized.'], 401);
}
You have to register your middleware in app/Http/Kernel.php, under the $routeMiddleware array:
protected $routeMiddleware = [
...
'user-with-invoice' => App\Http\Middleware\UserWithInvoiceMiddleware::class,
];
Then, you can protect your routes with this middleware, adding a ->middleware('user-with-invoice') to the routes where the user has to be an admin or have an invoice:
Route::get('/example', ExampleController::class)->middleware('user-with-invoice');
you can use make a middleware and pass requests throw it to check if the user is authorized to do that or not.
class SomeMidllewareName
{
/**
* Handle an incoming request.
*
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
$invoice = Invoice::pluck('user_id')->toArray();
if (1 == Auth::user()->admin || in_array(Auth::user()->id, $invoice)) {
return $next($request);
}
return \response()->json(['message' => 'you are not authorized'], 401);
}
}
then, you can validate on the routes and you can use also policies and validate every feature alone
I'm trying to add multiple roles to a route group in my web file in Laravel.
I would like protect certain routes based on the users Role like the admin section.
Some Routes need multiple roles for access.
Example Roles:
SuperAdmin
Admin
Moderator
etc..
This works and I can go to the admin panel if loged in and have One Role only in the route group.
Route::middleware(['auth','role:SuperAdmin'])->group(function () {
Secret routes..
});
This does not work if I try and add more roles to to the route group
like this: Route::middleware(['auth','role:SuperAdmin|Admin|Moderator'])->group(function () {
Note the 'role:SuperAdmin|Admin|Moderator'
This is the RoleMiddleware file:
public function handle($request, Closure $next, $role, $permission = null)
{
if (!$request->user()->hasRole($role)) {
abort(404);
}
if ($permission !== null && !$request->user()->can($permission)) {
abort(404);
}
return $next($request);
}
The User Class has a trait called use:HasPermissionTrait
class User extends Authenticatable
{
use Notifiable, HasPermissionsTrait, Billable;
In that HasPermissionsTrait I have the following:
I have permission setup fine, just focusing on the Roles in this file. I moved the Role logic to the top.
use App\{Role, Permission};
trait HasPermissionsTrait
{
public function hasRole(...$roles)
{
foreach ($roles as $role) {
if ($this->roles->contains('name', $role)) {
return true;
}
}
return false;
}
public function roles()
{
return $this->belongsToMany(Role::class, 'users_roles');
}
... // Permission logic start here...
}
Worth Mentioning:
The tables for roles are:
users
roles
users_roles (I know the correct naming convention should be roles_users, but not relevant here)
Just need to know how to get this working in the route group:
'role:SuperAdmin|Admin|Moderator'
Solution:
RoleMiddleware file:
public function handle($request, Closure $next, $role, $permission = null)
{
$role = strtolower( $request->user()->hasRole($role));
$allowed_roles = array_slice(func_get_args(), 2);
if (!$request->user()->hasRole(in_array($role, $allowed_roles))) {
abort(404);
}
if ($permission !== null && !$request->user()->can($permission)) {
abort(404);
}
return $next($request);
}
Can do route group like this.
Route::middleware(['auth','role:SuperAdmin|Admin'])->group(function () {
This is what I did in my CheckRole Middleware
public function handle($request, Closure $next) {
// I'm using the api guard
$role = strtolower( request()->user()->type );
$allowed_roles = array_slice(func_get_args(), 2);
if( in_array($role, $allowed_roles) ) {
return $next($request);
}
throw new AuthenticationException();
}
And in my router file
Route::group(["middleware" => "role:admin,worker"], function() {
});
This might not be the perfect solution, at least it works for me.
Route::middleware(['auth'])->group(function () {
//Routes available to super admin
Route::middleware(['role:SuperAdmin'])->group(function () {
//write route hear
});
//Routes available to SuperAdmin, Admin and Moderator
Route::middleware(['role:SuperAdmin|Admin|Moderator'])->group(function () {
//write route hear
})
});
try this way to define route group of routing. I already use this syntax and it's work.
define auth in parent middleware group and roles defined in the child middleware groupe.
explode the roles in your middleware and check against the available roles
public function handle($request, Closure $next, $role, $permission = null)
{
$roles = is_array($role)
? $role
: explode('|', $role);
if (!$request->user()->hasRole($roles)) {
abort(404);
}
if ($permission !== null && !$request->user()->can($permission)) {
abort(404);
}
return $next($request);
}
I have a deadline attribute (date type) in User model by which would I like to disable login for users who's deadline has passed.
For example: if the deadline was 10.06 and it is 17.06, that specific user isn't supposed to be able to log in.
I tried making a middleware, and putting a complete Route::auth() within it, but that didn't work:
public function handle($request, Closure $next)
{
if (Auth::check() && Auth::user()->deadline < Carbon::now()) {
return redirect('some_not_authorized_route');
}
return $next($request);
}
Routes:
Route::middleware('web')->group(function () {
Route::middleware('deadline')->group(function () {
Route::auth();
});
});
Kernel.php:
protected $routeMiddleware = [
...
'deadline' => \App\Http\Middleware\IsOverDeadline::class,
...
];
This is my route group,
Route::group(['middleware' => 'checkUserLevel'], function () {
// my routes
});
And this is my middleware checkUserLevel,
public function handle($request, Closure $next, $level)
{
$user = Auth::user();
if ($user->level > $level) {
return redirect('testUrl');
}
return $next($request);
}
I want to pass the $level variale to middleware from route group.
Thanks.
You can simply pass multiple arguments into the middleware using a colon. Use it like:
Route::group(['middleware' => 'checkUserLevel:some_value_of_level'], function () {
// my routes
});
Now, you can have this value inside your $level variable.
public function handle($request, Closure $next, $level)
{
$user = Auth::user();
if ($user->level > $level) {
return redirect('testUrl');
}
return $next($request);
}
This would help.
Edit: 14 Dec 2018
You can also send multiple variables to middleware. You just need to seperate the values using a comma (,).
Route::group(['middleware' => 'checkUserLevel:some_value_of_level, one_more_value_to_send'], function () {
// my routes
});
And you will get the value one_more_value_to_send in the variable after $level in the middleware handler.
public function handle($request, Closure $next, $level, $another_value)
{
$user = Auth::user();
if ($user->level > $level) {
return redirect('testUrl');
}
return $next($request);
}
For more details you can refer to: Passing parameters to Middleware in Laravel 5.1
In Laravel 6.x you have to do this
add code like in your middleware
public function handle($request, Closure $next,$module=null,$right=null)
{
dd($module,$right);
return $next($request);
}
your route code like this
Route::get('/department/add', 'DepartmentController#addNew')->middleware('ManualSec:abc,xyz');
In Kernel.php register your middleware in the section of $routeMiddleware like
'ManualSec' => \App\Http\Middleware\ManualSec::class,
by calling the rout using url in my case
http://local.pms.com:8080/department/add
it will result is like:
now you can code in your middleware