From 7f11f7d29c9cad8444e62c767be0f8793320e1fd Mon Sep 17 00:00:00 2001 From: Utsav Date: Sun, 13 Apr 2025 00:22:33 +0530 Subject: [PATCH 1/2] Introduce `excludeCan` method to remove middleware --- src/Illuminate/Routing/Route.php | 16 ++++ src/Illuminate/Routing/RouteRegistrar.php | 2 + tests/Auth/AuthorizeMiddlewareTest.php | 94 +++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/src/Illuminate/Routing/Route.php b/src/Illuminate/Routing/Route.php index 355d18b0e058..5c2c2a0aad3b 100755 --- a/src/Illuminate/Routing/Route.php +++ b/src/Illuminate/Routing/Route.php @@ -1100,6 +1100,22 @@ public function can($ability, $models = []) : $this->middleware(['can:'.$ability.','.implode(',', Arr::wrap($models))]); } + /** + * Specify that the "can" middleware for a specific ability should be removed from the route. + * + * @param \UnitEnum|string $ability + * @param array|string $models + * @return $this + */ + public function excludeCan($ability, $models = []) + { + $ability = enum_value($ability); + + return empty($models) + ? $this->withoutMiddleware(['can:'.$ability]) + : $this->withoutMiddleware(['can:'.$ability.','.implode(',', Arr::wrap($models))]); + } + /** * Get the middleware for the route's controller. * diff --git a/src/Illuminate/Routing/RouteRegistrar.php b/src/Illuminate/Routing/RouteRegistrar.php index b3e543b777b1..85f830b8c65a 100644 --- a/src/Illuminate/Routing/RouteRegistrar.php +++ b/src/Illuminate/Routing/RouteRegistrar.php @@ -19,6 +19,7 @@ * @method \Illuminate\Routing\Route put(string $uri, \Closure|array|string|null $action = null) * @method \Illuminate\Routing\RouteRegistrar as(string $value) * @method \Illuminate\Routing\RouteRegistrar can(\UnitEnum|string $ability, array|string $models = []) + * @method \Illuminate\Routing\RouteRegistrar excludeCan(\UnitEnum|string $ability, array|string $models = []) * @method \Illuminate\Routing\RouteRegistrar controller(string $controller) * @method \Illuminate\Routing\RouteRegistrar domain(\BackedEnum|string $value) * @method \Illuminate\Routing\RouteRegistrar middleware(array|string|null $middleware) @@ -66,6 +67,7 @@ class RouteRegistrar protected $allowedAttributes = [ 'as', 'can', + 'excludeCan', 'controller', 'domain', 'middleware', diff --git a/tests/Auth/AuthorizeMiddlewareTest.php b/tests/Auth/AuthorizeMiddlewareTest.php index d9ca83e78b5d..2f0a25cc8115 100644 --- a/tests/Auth/AuthorizeMiddlewareTest.php +++ b/tests/Auth/AuthorizeMiddlewareTest.php @@ -106,6 +106,100 @@ public function testSimpleAbilityAuthorized() $this->assertSame('success', $response->content()); } + public function testRouteCanHandleMultiplePermissionsInGroup() + { + $this->gate()->define('manage-company', function ($user) { + return true; + }); + + $this->gate()->define('manage-users', function ($user) { + return true; + }); + + $this->router->can('manage-company')->group(function () { + $this->router->get('company', [ + 'uses' => function () { + return 'Company Access Granted'; + }, + ]); + + $this->router->can('manage-users')->group(function () { + $this->router->get('users', [ + 'uses' => function () { + if (! app(GateContract::class)->allows('manage-company')) { + return 'Forbidden'; + } + + return 'Users Access Granted'; + }, + ]); + }); + }); + + $response = $this->router->dispatch(Request::create('company', 'GET')); + $this->assertSame('Company Access Granted', $response->content()); + + $response = $this->router->dispatch(Request::create('users', 'GET')); + $this->assertSame('Users Access Granted', $response->content()); + } + + public function testRouteCanOverrideMiddlewareInsideGroup() + { + $this->gate()->define('manage-company', function ($user) { + return true; + }); + + $this->gate()->define('manage-users', function ($user) { + return true; + }); + + $this->router->can('manage-company')->group(function () { + $this->router->can('manage-users')->get('overridden-resource', [ + 'uses' => function () { + return 'Overridden Access Granted'; + }, + ]); + }); + + $response = $this->router->dispatch(Request::create('overridden-resource', 'GET')); + $this->assertSame('Overridden Access Granted', $response->content()); + } + + public function testRouteCanHandleMultiplePermissionsInGroupWithExcludeCan() + { + $this->gate()->define('manage-company', function ($user) { + return false; + }); + + $this->gate()->define('manage-users', function ($user) { + return true; + }); + + $this->router->can('manage-company')->group(function () { + $this->router->get('company', [ + 'uses' => function () { + return 'Company Access Denied'; + }, + ]); + + $this->router->excludeCan('manage-company')->can('manage-users')->get('users', [ + 'uses' => function () { + if (app(GateContract::class)->allows('manage-company')) { + return 'Forbidden'; + } + + return 'Users Access Granted Without Manage-Company'; + }, + ]); + }); + + $response = $this->router->dispatch(Request::create('company', 'GET')); + $this->assertSame('Company Access Denied', $response->content()); + + $response = $this->router->dispatch(Request::create('users', 'GET')); + $this->assertSame('Users Access Granted Without Manage-Company', $response->content()); + } + public function testSimpleAbilityWithStringParameter() { $this->gate()->define('view-dashboard', function ($user, $param) { From 8c1ec7c84e5b390aa4860567725fd7612a154060 Mon Sep 17 00:00:00 2001 From: utsavsomaiya <96036522+utsavsomaiya@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:53:08 +0000 Subject: [PATCH 2/2] Update facade docblocks --- src/Illuminate/Support/Facades/Route.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Illuminate/Support/Facades/Route.php b/src/Illuminate/Support/Facades/Route.php index 187bd3883422..7baaa9aea1d3 100755 --- a/src/Illuminate/Support/Facades/Route.php +++ b/src/Illuminate/Support/Facades/Route.php @@ -90,6 +90,7 @@ * @method static \Illuminate\Routing\RouteRegistrar whereIn(array|string $parameters, array $values) * @method static \Illuminate\Routing\RouteRegistrar as(string $value) * @method static \Illuminate\Routing\RouteRegistrar can(\UnitEnum|string $ability, array|string $models = []) + * @method static \Illuminate\Routing\RouteRegistrar excludeCan(\UnitEnum|string $ability, array|string $models = []) * @method static \Illuminate\Routing\RouteRegistrar controller(string $controller) * @method static \Illuminate\Routing\RouteRegistrar domain(\BackedEnum|string $value) * @method static \Illuminate\Routing\RouteRegistrar middleware(array|string|null $middleware)