Abozanona
Abozanona

Reputation: 2295

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?

Upvotes: 3

Views: 2084

Answers (2)

freeeon
freeeon

Reputation: 1

Easiest way — create new class that extend Authorize middleware and override in method handle authorize to any

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Auth\Middleware\Authorize;

class CanAny extends Authorize
{
    #[\Override]
    public function handle($request, Closure $next, $ability, ...$models)
    {
        $this->gate->any($ability, $this->getGateArguments($request, $models));

        return $next($request);
    }
}

Add this to Kernel class

protected $routeMiddleware = [
    ...,
    'can.any' => \App\Http\Middleware\CanAny::class,
];

Now you can use it in routes

Route::resource('posts', PostController::class)->middleware('can.any:post,read);

Upvotes: -1

Abozanona
Abozanona

Reputation: 2295

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']);
});

Upvotes: 2

Related Questions