diff --git a/.github/workflows/run-tests-L8.yml b/.github/workflows/run-tests-L8.yml index dd9d0c446..4d8f8e774 100644 --- a/.github/workflows/run-tests-L8.yml +++ b/.github/workflows/run-tests-L8.yml @@ -40,6 +40,10 @@ jobs: extensions: curl, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, iconv coverage: none + - name: Install dependencies (remove passport) + run: composer remove --dev laravel/passport --no-interaction --no-update + if: matrix.laravel == '8.*' + - name: Install dependencies run: | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "symfony/console:>=4.3.4" "mockery/mockery:^1.3.2" "nesbot/carbon:>=2.62.1" --no-interaction --no-update diff --git a/composer.json b/composer.json index 9ea458849..4e31db50e 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "illuminate/database": "^8.0|^9.0|^10.0" }, "require-dev": { + "laravel/passport": "^11.0", "orchestra/testbench": "^6.0|^7.0|^8.0", "phpunit/phpunit": "^9.4" }, diff --git a/config/permission.php b/config/permission.php index b4407316b..b98b24148 100644 --- a/config/permission.php +++ b/config/permission.php @@ -115,6 +115,13 @@ 'teams' => false, + /* + * Passport Client Credentials Grant + * When set to true the package will use Passports Client to check permissions + */ + + 'use_passport_client_credentials' => false, + /* * When set to true, the required permission names are added to exception messages. * This could be considered an information leak in some contexts, so the default diff --git a/docs/basic-usage/passport.md b/docs/basic-usage/passport.md new file mode 100644 index 000000000..a5a007009 --- /dev/null +++ b/docs/basic-usage/passport.md @@ -0,0 +1,53 @@ +--- +title: Passport Client Credentials Grant usage +weight: 12 +--- + +**NOTE** currently this only works for Laravel 9 and Passport 11 and newer. + +## Install Passport +First of all make sure to have Passport installed as described in the [Laravel documentation](https://laravel.com/docs/master/passport). + +## Extend the Client model +After installing the Passport package we need to extend Passports Client model. +The extended Client model should look like something as shown below. + +```php +use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; +use Illuminate\Foundation\Auth\Access\Authorizable; +use Laravel\Passport\Client as BaseClient; +use Spatie\Permission\Traits\HasRoles; + +class Client extends BaseClient implements AuthorizableContract +{ + use HasRoles; + use Authorizable; + + public $guard_name = 'api'; + + // or + + public function guardName() + { + return 'api' + } +} +``` + +You need to extend the Client model to make it possible to add the required traits and properties/ methods. +The extended Client should either provide a `$guard_name` property or a `guardName()` method. +They should return a string that matches the [configured](https://laravel.com/docs/master/passport#installation) guard name for the passport driver. + +## Middleware +All middlewares provided by this package work with the Client. + +Do make sure that you only wrap your routes in the [`client`](https://laravel.com/docs/master/passport#via-middleware) middleware and not the `auth:api` middleware as well. +Wrapping routes in the `auth:api` middleware currently does not work for the Client Credentials Grant. + +## Config +Finally, update the config file as well. Setting `use_passport_client_credentials` to `true` will make sure that the right checks are performed. + +```php +// config/permission.php +'use_passport_client_credentials' => true, +``` diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6cd82acfb..1e08d1014 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,5 +12,7 @@ + + diff --git a/src/Guard.php b/src/Guard.php index b5904cc4d..dd5023148 100644 --- a/src/Guard.php +++ b/src/Guard.php @@ -2,8 +2,10 @@ namespace Spatie\Permission; +use Illuminate\Contracts\Auth\Access\Authorizable; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Auth; class Guard { @@ -72,4 +74,34 @@ public static function getDefaultName($class): string return $possible_guards->first() ?: $default; } + + /** + * Lookup a passport guard + */ + public static function getPassportClient($guard): ?Authorizable + { + $guards = collect(config('auth.guards'))->where('driver', 'passport'); + + if (! $guards->count()) { + return null; + } + + $authGuard = Auth::guard($guards->keys()[0]); + + if (! \method_exists($authGuard, 'client')) { + return null; + } + + $client = $authGuard->client(); + + if (! $guard || ! $client) { + return $client; + } + + if (self::getNames($client)->contains($guard)) { + return $client; + } + + return null; + } } diff --git a/src/Middlewares/PermissionMiddleware.php b/src/Middlewares/PermissionMiddleware.php index ca37cb672..7d303261c 100644 --- a/src/Middlewares/PermissionMiddleware.php +++ b/src/Middlewares/PermissionMiddleware.php @@ -5,6 +5,7 @@ use Closure; use Illuminate\Support\Facades\Auth; use Spatie\Permission\Exceptions\UnauthorizedException; +use Spatie\Permission\Guard; class PermissionMiddleware { @@ -12,11 +13,16 @@ public function handle($request, Closure $next, $permission, $guard = null) { $authGuard = Auth::guard($guard); - if ($authGuard->guest()) { - throw UnauthorizedException::notLoggedIn(); + $user = $authGuard->user(); + + // For machine-to-machine Passport clients + if (! $user && $request->bearerToken() && config('permission.use_passport_client_credentials')) { + $user = Guard::getPassportClient($guard); } - $user = $authGuard->user(); + if (! $user) { + throw UnauthorizedException::notLoggedIn(); + } if (! method_exists($user, 'hasAnyPermission')) { throw UnauthorizedException::missingTraitHasRoles($user); diff --git a/src/Middlewares/RoleMiddleware.php b/src/Middlewares/RoleMiddleware.php index ae9232cd5..c98ab9d30 100644 --- a/src/Middlewares/RoleMiddleware.php +++ b/src/Middlewares/RoleMiddleware.php @@ -5,6 +5,7 @@ use Closure; use Illuminate\Support\Facades\Auth; use Spatie\Permission\Exceptions\UnauthorizedException; +use Spatie\Permission\Guard; class RoleMiddleware { @@ -12,11 +13,16 @@ public function handle($request, Closure $next, $role, $guard = null) { $authGuard = Auth::guard($guard); - if ($authGuard->guest()) { - throw UnauthorizedException::notLoggedIn(); + $user = $authGuard->user(); + + // For machine-to-machine Passport clients + if (! $user && $request->bearerToken() && config('permission.use_passport_client_credentials')) { + $user = Guard::getPassportClient($guard); } - $user = $authGuard->user(); + if (! $user) { + throw UnauthorizedException::notLoggedIn(); + } if (! method_exists($user, 'hasAnyRole')) { throw UnauthorizedException::missingTraitHasRoles($user); diff --git a/src/Middlewares/RoleOrPermissionMiddleware.php b/src/Middlewares/RoleOrPermissionMiddleware.php index 52675a43f..4a4abb562 100644 --- a/src/Middlewares/RoleOrPermissionMiddleware.php +++ b/src/Middlewares/RoleOrPermissionMiddleware.php @@ -5,18 +5,25 @@ use Closure; use Illuminate\Support\Facades\Auth; use Spatie\Permission\Exceptions\UnauthorizedException; +use Spatie\Permission\Guard; class RoleOrPermissionMiddleware { public function handle($request, Closure $next, $roleOrPermission, $guard = null) { $authGuard = Auth::guard($guard); - if ($authGuard->guest()) { - throw UnauthorizedException::notLoggedIn(); - } $user = $authGuard->user(); + // For machine-to-machine Passport clients + if (! $user && $request->bearerToken() && config('permission.use_passport_client_credentials')) { + $user = Guard::getPassportClient($guard); + } + + if (! $user) { + throw UnauthorizedException::notLoggedIn(); + } + if (! method_exists($user, 'hasAnyRole') || ! method_exists($user, 'hasAnyPermission')) { throw UnauthorizedException::missingTraitHasRoles($user); } diff --git a/tests/PermissionMiddlewareTest.php b/tests/PermissionMiddlewareTest.php index d8831c309..7059bd3e9 100644 --- a/tests/PermissionMiddlewareTest.php +++ b/tests/PermissionMiddlewareTest.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Gate; use InvalidArgumentException; +use Laravel\Passport\Passport; use Spatie\Permission\Contracts\Permission; use Spatie\Permission\Exceptions\UnauthorizedException; use Spatie\Permission\Middlewares\PermissionMiddleware; @@ -17,6 +18,8 @@ class PermissionMiddlewareTest extends TestCase { protected $permissionMiddleware; + protected $usePassport = true; + protected function setUp(): void { parent::setUp(); @@ -71,6 +74,32 @@ public function a_user_cannot_access_a_route_protected_by_the_permission_middlew ); } + /** @test */ + public function a_client_cannot_access_a_route_protected_by_the_permission_middleware_of_a_different_guard(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + // These permissions are created fresh here in reverse order of guard being applied, so they are not "found first" in the db lookup when matching + app(Permission::class)->create(['name' => 'admin-permission2', 'guard_name' => 'web']); + $p1 = app(Permission::class)->create(['name' => 'admin-permission2', 'guard_name' => 'api']); + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->givePermissionTo($p1); + + $this->assertEquals( + 200, + $this->runMiddleware($this->permissionMiddleware, 'admin-permission2', 'api', true) + ); + + $this->assertEquals( + 403, + $this->runMiddleware($this->permissionMiddleware, 'edit-articles2', 'web', true) + ); + } + /** @test */ public function a_super_admin_user_can_access_a_route_protected_by_permission_middleware() { @@ -99,6 +128,23 @@ public function a_user_can_access_a_route_protected_by_permission_middleware_if_ ); } + /** @test */ + public function a_client_can_access_a_route_protected_by_permission_middleware_if_have_this_permission(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*'], 'api'); + + $this->testClient->givePermissionTo('edit-posts'); + + $this->assertEquals( + 200, + $this->runMiddleware($this->permissionMiddleware, 'edit-posts', null, true) + ); + } + /** @test */ public function a_user_can_access_a_route_protected_by_this_permission_middleware_if_have_one_of_the_permissions() { @@ -117,6 +163,28 @@ public function a_user_can_access_a_route_protected_by_this_permission_middlewar ); } + /** @test */ + public function a_client_can_access_a_route_protected_by_this_permission_middleware_if_have_one_of_the_permissions(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->givePermissionTo('edit-posts'); + + $this->assertEquals( + 200, + $this->runMiddleware($this->permissionMiddleware, 'edit-news|edit-posts', null, true) + ); + + $this->assertEquals( + 200, + $this->runMiddleware($this->permissionMiddleware, ['edit-news', 'edit-posts'], null, true) + ); + } + /** @test */ public function a_user_cannot_access_a_route_protected_by_the_permission_middleware_if_have_not_has_roles_trait() { @@ -143,6 +211,23 @@ public function a_user_cannot_access_a_route_protected_by_the_permission_middlew ); } + /** @test */ + public function a_client_cannot_access_a_route_protected_by_the_permission_middleware_if_have_a_different_permission(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->givePermissionTo('edit-posts'); + + $this->assertEquals( + 403, + $this->runMiddleware($this->permissionMiddleware, 'edit-news', null, true) + ); + } + /** @test */ public function a_user_cannot_access_a_route_protected_by_permission_middleware_if_have_not_permissions() { @@ -154,6 +239,21 @@ public function a_user_cannot_access_a_route_protected_by_permission_middleware_ ); } + /** @test */ + public function a_client_cannot_access_a_route_protected_by_permission_middleware_if_have_not_permissions(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->assertEquals( + 403, + $this->runMiddleware($this->permissionMiddleware, 'edit-articles|edit-posts', null, true) + ); + } + /** @test */ public function a_user_can_access_a_route_protected_by_permission_middleware_if_has_permission_via_role() { @@ -173,6 +273,29 @@ public function a_user_can_access_a_route_protected_by_permission_middleware_if_ ); } + /** @test */ + public function a_client_can_access_a_route_protected_by_permission_middleware_if_has_permission_via_role(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->assertEquals( + 403, + $this->runMiddleware($this->permissionMiddleware, 'edit-articles', null, true) + ); + + $this->testClientRole->givePermissionTo('edit-posts'); + $this->testClient->assignRole('clientRole'); + + $this->assertEquals( + 200, + $this->runMiddleware($this->permissionMiddleware, 'edit-posts', null, true) + ); + } + /** @test */ public function the_required_permissions_can_be_fetched_from_the_exception() { @@ -242,6 +365,23 @@ public function user_can_not_access_permission_with_guard_admin_while_login_usin ); } + /** @test */ + public function client_can_not_access_permission_with_guard_admin_while_login_using_default_guard(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->givePermissionTo('edit-posts'); + + $this->assertEquals( + 403, + $this->runMiddleware($this->permissionMiddleware, 'edit-posts', 'admin', true) + ); + } + /** @test */ public function user_can_access_permission_with_guard_admin_while_login_using_admin_guard() { diff --git a/tests/RoleMiddlewareTest.php b/tests/RoleMiddlewareTest.php index a28c2de07..25f411ee1 100644 --- a/tests/RoleMiddlewareTest.php +++ b/tests/RoleMiddlewareTest.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Config; use InvalidArgumentException; +use Laravel\Passport\Passport; use Spatie\Permission\Exceptions\UnauthorizedException; use Spatie\Permission\Middlewares\RoleMiddleware; use Spatie\Permission\Tests\TestModels\UserWithoutHasRoles; @@ -15,6 +16,8 @@ class RoleMiddlewareTest extends TestCase { protected $roleMiddleware; + protected $usePassport = true; + protected function setUp(): void { parent::setUp(); @@ -44,6 +47,23 @@ public function a_user_cannot_access_a_route_protected_by_role_middleware_of_ano ); } + /** @test */ + public function a_client_cannot_access_a_route_protected_by_role_middleware_of_another_guard(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->assignRole('clientRole'); + + $this->assertEquals( + 403, + $this->runMiddleware($this->roleMiddleware, 'testAdminRole', null, true) + ); + } + /** @test */ public function a_user_can_access_a_route_protected_by_role_middleware_if_have_this_role() { @@ -57,6 +77,23 @@ public function a_user_can_access_a_route_protected_by_role_middleware_if_have_t ); } + /** @test */ + public function a_client_can_access_a_route_protected_by_role_middleware_if_have_this_role(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->assignRole('clientRole'); + + $this->assertEquals( + 200, + $this->runMiddleware($this->roleMiddleware, 'clientRole', null, true) + ); + } + /** @test */ public function a_user_can_access_a_route_protected_by_this_role_middleware_if_have_one_of_the_roles() { @@ -75,6 +112,28 @@ public function a_user_can_access_a_route_protected_by_this_role_middleware_if_h ); } + /** @test */ + public function a_client_can_access_a_route_protected_by_this_role_middleware_if_have_one_of_the_roles(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->assignRole('clientRole'); + + $this->assertEquals( + 200, + $this->runMiddleware($this->roleMiddleware, 'clientRole|testRole2', null, true) + ); + + $this->assertEquals( + 200, + $this->runMiddleware($this->roleMiddleware, ['testRole2', 'clientRole'], null, true) + ); + } + /** @test */ public function a_user_cannot_access_a_route_protected_by_the_role_middleware_if_have_not_has_roles_trait() { @@ -101,6 +160,23 @@ public function a_user_cannot_access_a_route_protected_by_the_role_middleware_if ); } + /** @test */ + public function a_client_cannot_access_a_route_protected_by_the_role_middleware_if_have_a_different_role(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->assignRole(['clientRole']); + + $this->assertEquals( + 403, + $this->runMiddleware($this->roleMiddleware, 'clientRole2', null, true) + ); + } + /** @test */ public function a_user_cannot_access_a_route_protected_by_role_middleware_if_have_not_roles() { @@ -112,6 +188,21 @@ public function a_user_cannot_access_a_route_protected_by_role_middleware_if_hav ); } + /** @test */ + public function a_client_cannot_access_a_route_protected_by_role_middleware_if_have_not_roles(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->assertEquals( + 403, + $this->runMiddleware($this->roleMiddleware, 'testRole|testRole2', null, true) + ); + } + /** @test */ public function a_user_cannot_access_a_route_protected_by_role_middleware_if_role_is_undefined() { @@ -123,6 +214,21 @@ public function a_user_cannot_access_a_route_protected_by_role_middleware_if_rol ); } + /** @test */ + public function a_client_cannot_access_a_route_protected_by_role_middleware_if_role_is_undefined(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->assertEquals( + 403, + $this->runMiddleware($this->roleMiddleware, '', null, true) + ); + } + /** @test */ public function the_required_roles_can_be_fetched_from_the_exception() { @@ -192,6 +298,23 @@ public function user_can_not_access_role_with_guard_admin_while_login_using_defa ); } + /** @test */ + public function client_can_not_access_role_with_guard_admin_while_login_using_default_guard(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->assignRole('clientRole'); + + $this->assertEquals( + 403, + $this->runMiddleware($this->roleMiddleware, 'clientRole', 'admin', true) + ); + } + /** @test */ public function user_can_access_role_with_guard_admin_while_login_using_admin_guard() { diff --git a/tests/RoleOrPermissionMiddlewareTest.php b/tests/RoleOrPermissionMiddlewareTest.php index 24367e51f..a3de1054d 100644 --- a/tests/RoleOrPermissionMiddlewareTest.php +++ b/tests/RoleOrPermissionMiddlewareTest.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Gate; use InvalidArgumentException; +use Laravel\Passport\Passport; use Spatie\Permission\Exceptions\UnauthorizedException; use Spatie\Permission\Middlewares\RoleOrPermissionMiddleware; use Spatie\Permission\Tests\TestModels\UserWithoutHasRoles; @@ -16,6 +17,8 @@ class RoleOrPermissionMiddlewareTest extends TestCase { protected $roleOrPermissionMiddleware; + protected $usePassport = true; + protected function setUp(): void { parent::setUp(); @@ -66,6 +69,44 @@ public function a_user_can_access_a_route_protected_by_permission_or_role_middle ); } + /** @test */ + public function a_client_can_access_a_route_protected_by_permission_or_role_middleware_if_has_this_permission_or_role(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->assignRole('clientRole'); + $this->testClient->givePermissionTo('edit-posts'); + + $this->assertEquals( + 200, + $this->runMiddleware($this->roleOrPermissionMiddleware, 'clientRole|edit-news|edit-posts', null, true) + ); + + $this->testClient->removeRole('clientRole'); + + $this->assertEquals( + 200, + $this->runMiddleware($this->roleOrPermissionMiddleware, 'clientRole|edit-posts', null, true) + ); + + $this->testClient->revokePermissionTo('edit-posts'); + $this->testClient->assignRole('clientRole'); + + $this->assertEquals( + 200, + $this->runMiddleware($this->roleOrPermissionMiddleware, 'clientRole|edit-posts', null, true) + ); + + $this->assertEquals( + 200, + $this->runMiddleware($this->roleOrPermissionMiddleware, ['clientRole', 'edit-posts'], null, true) + ); + } + /** @test */ public function a_super_admin_user_can_access_a_route_protected_by_permission_or_role_middleware() { @@ -110,6 +151,26 @@ public function a_user_can_not_access_a_route_protected_by_permission_or_role_mi ); } + /** @test */ + public function a_client_can_not_access_a_route_protected_by_permission_or_role_middleware_if_have_not_this_permission_and_role(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->assertEquals( + 403, + $this->runMiddleware($this->roleOrPermissionMiddleware, 'clientRole|edit-posts', null, true) + ); + + $this->assertEquals( + 403, + $this->runMiddleware($this->roleOrPermissionMiddleware, 'missingRole|missingPermission', null, true) + ); + } + /** @test */ public function use_not_existing_custom_guard_in_role_or_permission() { @@ -140,6 +201,24 @@ public function user_can_not_access_permission_or_role_with_guard_admin_while_lo ); } + /** @test */ + public function client_can_not_access_permission_or_role_with_guard_admin_while_login_using_default_guard(): void + { + if ($this->getLaravelVersion() < 9) { + $this->markTestSkipped('requires laravel >= 9'); + } + + Passport::actingAsClient($this->testClient, ['*']); + + $this->testClient->assignRole('clientRole'); + $this->testClient->givePermissionTo('edit-posts'); + + $this->assertEquals( + 403, + $this->runMiddleware($this->roleOrPermissionMiddleware, 'edit-posts|clientRole', 'admin', true) + ); + } + /** @test */ public function user_can_access_permission_or_role_with_guard_admin_while_login_using_admin_guard() { diff --git a/tests/TestCase.php b/tests/TestCase.php index 3fe775b08..302169f65 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Schema; +use Laravel\Passport\PassportServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; use Spatie\Permission\Contracts\Permission; use Spatie\Permission\Contracts\Role; @@ -15,6 +16,7 @@ use Spatie\Permission\PermissionRegistrar; use Spatie\Permission\PermissionServiceProvider; use Spatie\Permission\Tests\TestModels\Admin; +use Spatie\Permission\Tests\TestModels\Client; use Spatie\Permission\Tests\TestModels\User; abstract class TestCase extends Orchestra @@ -47,6 +49,15 @@ abstract class TestCase extends Orchestra protected static $customMigration; + /** @var bool */ + protected $usePassport = false; + + protected Client $testClient; + + protected \Spatie\Permission\Models\Permission $testClientPermission; + + protected \Spatie\Permission\Models\Role $testClientRole; + protected function setUp(): void { parent::setUp(); @@ -61,6 +72,10 @@ protected function setUp(): void setPermissionsTeamId(1); } + if ($this->usePassport) { + $this->setUpPassport($this->app); + } + $this->setUpRoutes(); } @@ -70,8 +85,11 @@ protected function setUp(): void */ protected function getPackageProviders($app) { - return [ + return $this->getLaravelVersion() < 9 ? [ + PermissionServiceProvider::class, + ] : [ PermissionServiceProvider::class, + PassportServiceProvider::class, ]; } @@ -169,6 +187,23 @@ protected function setUpDatabase($app) $app[Permission::class]->create(['name' => 'Edit News']); } + protected function setUpPassport($app): void + { + if ($this->getLaravelVersion() < 9) { + return; + } + + $app['config']->set('permission.use_passport_client_credentials', true); + $app['config']->set('auth.guards.api', ['driver' => 'passport', 'provider' => 'users']); + + $this->artisan('migrate'); + $this->artisan('passport:install'); + + $this->testClient = Client::create(['name' => 'Test', 'redirect' => 'https://example.com', 'personal_access_client' => 0, 'password_client' => 0, 'revoked' => 0]); + $this->testClientRole = $app[Role::class]->create(['name' => 'clientRole', 'guard_name' => 'api']); + $this->testClientPermission = $app[Permission::class]->create(['name' => 'edit-posts', 'guard_name' => 'api']); + } + private function prepareMigration() { $migration = str_replace( @@ -227,10 +262,15 @@ public function setUpRoutes(): void } ////// TEST HELPERS - public function runMiddleware($middleware, $permission, $guard = null) + public function runMiddleware($middleware, $permission, $guard = null, bool $client = false) { + $request = new Request; + if ($client) { + $request->headers->set('Authorization', 'Bearer '.str()->random(30)); + } + try { - return $middleware->handle(new Request(), function () { + return $middleware->handle($request, function () { return (new Response())->setContent(''); }, $permission, $guard)->status(); } catch (UnauthorizedException $e) { @@ -254,4 +294,9 @@ public function getRouteResponse() return (new Response())->setContent(''); }; } + + protected function getLaravelVersion() + { + return (float) app()->version(); + } } diff --git a/tests/TestModels/Client.php b/tests/TestModels/Client.php new file mode 100644 index 000000000..a920bddd0 --- /dev/null +++ b/tests/TestModels/Client.php @@ -0,0 +1,21 @@ +