diff --git a/database/factories/ClientFactory.php b/database/factories/ClientFactory.php index ac345080..6791b911 100644 --- a/database/factories/ClientFactory.php +++ b/database/factories/ClientFactory.php @@ -32,9 +32,8 @@ public function definition() 'user_id' => null, 'name' => $this->faker->company(), 'secret' => Str::random(40), - 'redirect' => $this->faker->url(), - 'personal_access_client' => false, - 'password_client' => false, + 'redirect_uris' => [$this->faker->url()], + 'grant_types' => ['authorization_code', 'refresh_token'], 'revoked' => false, ]; } @@ -47,8 +46,7 @@ public function definition() public function asPasswordClient() { return $this->state([ - 'personal_access_client' => false, - 'password_client' => true, + 'grant_types' => ['password', 'refresh_token'], ]); } @@ -60,8 +58,7 @@ public function asPasswordClient() public function asPersonalAccessTokenClient() { return $this->state([ - 'personal_access_client' => true, - 'password_client' => false, + 'grant_types' => ['personal_access'], ]); } @@ -73,8 +70,7 @@ public function asPersonalAccessTokenClient() public function asClientCredentials() { return $this->state([ - 'personal_access_client' => false, - 'password_client' => false, + 'grant_types' => ['client_credentials'], ]); } } diff --git a/database/migrations/2016_06_01_000004_create_oauth_clients_table.php b/database/migrations/2016_06_01_000004_create_oauth_clients_table.php index 547fecac..21ec7462 100644 --- a/database/migrations/2016_06_01_000004_create_oauth_clients_table.php +++ b/database/migrations/2016_06_01_000004_create_oauth_clients_table.php @@ -17,9 +17,8 @@ public function up(): void $table->string('name'); $table->string('secret')->nullable(); $table->string('provider')->nullable(); - $table->text('redirect'); - $table->boolean('personal_access_client'); - $table->boolean('password_client'); + $table->text('redirect_uris'); + $table->text('grant_types'); $table->boolean('revoked'); $table->timestamps(); }); diff --git a/src/Bridge/Client.php b/src/Bridge/Client.php index 50b129c9..df425595 100644 --- a/src/Bridge/Client.php +++ b/src/Bridge/Client.php @@ -21,7 +21,7 @@ class Client implements ClientEntityInterface public function __construct( string $identifier, string $name, - string $redirectUri, + array $redirectUri, bool $isConfidential = false, ?string $provider = null ) { @@ -29,7 +29,7 @@ public function __construct( $this->name = $name; $this->isConfidential = $isConfidential; - $this->redirectUri = explode(',', $redirectUri); + $this->redirectUri = $redirectUri; $this->provider = $provider; } } diff --git a/src/Bridge/ClientRepository.php b/src/Bridge/ClientRepository.php index 0420808e..2ed2b45d 100644 --- a/src/Bridge/ClientRepository.php +++ b/src/Bridge/ClientRepository.php @@ -43,7 +43,7 @@ public function getClientEntity(string $clientIdentifier): ?ClientEntityInterfac return new Client( $clientIdentifier, $record->name, - $record->redirect, + $record->redirect_uris, $record->confidential(), $record->provider ); @@ -71,17 +71,7 @@ public function validateClient(string $clientIdentifier, ?string $clientSecret, */ protected function handlesGrant(ClientModel $record, string $grantType): bool { - if (! $record->hasGrantType($grantType)) { - return false; - } - - return match ($grantType) { - 'authorization_code' => ! $record->firstParty(), - 'personal_access' => $record->personal_access_client && $record->confidential(), - 'password' => $record->password_client, - 'client_credentials' => $record->confidential(), - default => true, - }; + return $record->hasGrantType($grantType); } /** diff --git a/src/Client.php b/src/Client.php index 69b79a8f..ab2bc27d 100644 --- a/src/Client.php +++ b/src/Client.php @@ -2,6 +2,7 @@ namespace Laravel\Passport; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Hash; @@ -44,6 +45,7 @@ class Client extends Model protected $casts = [ 'grant_types' => 'array', 'scopes' => 'array', + 'redirect_uris' => 'array', 'personal_access_client' => 'bool', 'password_client' => 'bool', 'revoked' => 'bool', @@ -132,6 +134,22 @@ public function setSecretAttribute($value) $this->attributes['secret'] = is_null($value) ? $value : Hash::make($value); } + /** + * Get the client's redirect URIs. + */ + protected function redirectUris(): Attribute + { + return Attribute::make( + get: function (?string $value, array $attributes) { + if (isset($value)) { + return $this->fromJson($value); + } + + return empty($attributes['redirect']) ? [] : explode(',', $attributes['redirect']); + }, + ); + } + /** * Determine if the client is a "first party" client. * @@ -139,7 +157,7 @@ public function setSecretAttribute($value) */ public function firstParty() { - return $this->personal_access_client || $this->password_client; + return $this->hasGrantType('personal_access') || $this->hasGrantType('password'); } /** @@ -160,11 +178,17 @@ public function skipsAuthorization() */ public function hasGrantType($grantType) { - if (! isset($this->attributes['grant_types']) || ! is_array($this->grant_types)) { - return true; + if (isset($this->attributes['grant_types']) && is_array($this->grant_types)) { + return in_array($grantType, $this->grant_types); } - return in_array($grantType, $this->grant_types); + return match ($grantType) { + 'authorization_code' => ! $this->personal_access_client && ! $this->password_client, + 'personal_access' => $this->personal_access_client && $this->confidential(), + 'password' => $this->password_client, + 'client_credentials' => $this->confidential(), + default => true, + }; } /** diff --git a/src/ClientRepository.php b/src/ClientRepository.php index 5b443ded..61ecab47 100644 --- a/src/ClientRepository.php +++ b/src/ClientRepository.php @@ -2,6 +2,7 @@ namespace Laravel\Passport; +use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Str; class ClientRepository @@ -78,75 +79,110 @@ public function activeForUser($userId) /** * Store a new client. * - * @param int|null $userId - * @param string $name - * @param string $redirect - * @param string|null $provider - * @param bool $personalAccess - * @param bool $password - * @param bool $confidential - * @return \Laravel\Passport\Client + * @param string[] $redirectUris + * @param string[] $grantTypes */ - public function create($userId, $name, $redirect, $provider = null, $personalAccess = false, $password = false, $confidential = true) - { - $client = Passport::client()->forceFill([ - 'user_id' => $userId, + protected function create( + string $name, + array $grantTypes, + array $redirectUris = [], + ?string $provider = null, + bool $confidential = true, + ?Authenticatable $user = null + ): Client { + $client = Passport::client(); + $columns = $client->getConnection()->getSchemaBuilder()->getColumnListing($client->getTable()); + + $attributes = [ 'name' => $name, - 'secret' => ($confidential || $personalAccess) ? Str::random(40) : null, + 'secret' => $confidential ? Str::random(40) : null, 'provider' => $provider, - 'redirect' => $redirect, - 'personal_access_client' => $personalAccess, - 'password_client' => $password, 'revoked' => false, - ]); - - $client->save(); - - return $client; + ...(in_array('redirect_uris', $columns) ? [ + 'redirect_uris' => $redirectUris, + ] : [ + 'redirect' => implode(',', $redirectUris), + ]), + ...(in_array('grant_types', $columns) ? [ + 'grant_types' => $grantTypes, + ] : [ + 'personal_access_client' => in_array('personal_access', $grantTypes), + 'password_client' => in_array('password', $grantTypes), + ]), + ]; + + return $user + ? $user->clients()->forceCreate($attributes) + : $client->forceCreate($attributes); } /** * Store a new personal access token client. - * - * @param int|null $userId - * @param string $name - * @param string $redirect - * @return \Laravel\Passport\Client */ - public function createPersonalAccessClient($userId, $name, $redirect) + public function createPersonalAccessGrantClient(string $name, ?string $provider = null): Client { - return $this->create($userId, $name, $redirect, null, true); + return $this->create($name, ['personal_access'], [], $provider); } /** * Store a new password grant client. + */ + public function createPasswordGrantClient(string $name, ?string $provider = null): Client + { + return $this->create($name, ['password', 'refresh_token'], [], $provider); + } + + /** + * Store a new client credentials grant client. + */ + public function createClientCredentialsGrantClient(string $name): Client + { + return $this->create($name, ['client_credentials']); + } + + /** + * Store a new implicit grant client. * - * @param int|null $userId - * @param string $name - * @param string $redirect - * @param string|null $provider - * @return \Laravel\Passport\Client + * @param string[] $redirectUris */ - public function createPasswordGrantClient($userId, $name, $redirect, $provider = null) + public function createImplicitGrantClient(string $name, array $redirectUris): Client { - return $this->create($userId, $name, $redirect, $provider, false, true); + return $this->create($name, ['implicit'], $redirectUris); + } + + /** + * Store a new authorization code grant client. + * + * @param string[] $redirectUris + */ + public function createAuthorizationCodeGrantClient( + string $name, + array $redirectUris, + bool $confidential = true, + ?Authenticatable $user = null + ): Client { + return $this->create( + $name, ['authorization_code', 'refresh_token'], $redirectUris, null, $confidential, $user + ); } /** * Update the given client. * - * @param \Laravel\Passport\Client $client - * @param string $name - * @param string $redirect - * @return \Laravel\Passport\Client + * @param string[] $redirectUris */ - public function update(Client $client, $name, $redirect) + public function update(Client $client, string $name, array $redirectUris): bool { - $client->forceFill([ - 'name' => $name, 'redirect' => $redirect, - ])->save(); + $columns = $client->getConnection()->getSchemaBuilder()->getColumnListing($client->getTable()); - return $client; + return $client->forceFill([ + 'name' => $name, + ...(in_array('redirect_uris', $columns) ? [ + 'redirect_uris' => $redirectUris, + ] : [ + 'redirect' => implode(',', $redirectUris), + ]), + ])->save(); } /** diff --git a/src/Console/ClientCommand.php b/src/Console/ClientCommand.php index c9358e97..a783dedb 100644 --- a/src/Console/ClientCommand.php +++ b/src/Console/ClientCommand.php @@ -19,10 +19,10 @@ class ClientCommand extends Command {--personal : Create a personal access token client} {--password : Create a password grant client} {--client : Create a client credentials grant client} + {--implicit : Create an implicit grant client} {--name= : The name of the client} {--provider= : The name of the user provider} {--redirect_uri= : The URI to redirect to after authorization } - {--user_id= : The user ID the client should be assigned to } {--public : Create a public client (Auth code grant type only) }'; /** @@ -41,11 +41,13 @@ class ClientCommand extends Command public function handle(ClientRepository $clients) { if ($this->option('personal')) { - $this->createPersonalClient($clients); + $this->createPersonalAccessClient($clients); } elseif ($this->option('password')) { $this->createPasswordClient($clients); } elseif ($this->option('client')) { $this->createClientCredentialsClient($clients); + } elseif ($this->option('implicit')) { + $this->createImplicitClient($clients); } else { $this->createAuthCodeClient($clients); } @@ -57,17 +59,21 @@ public function handle(ClientRepository $clients) * @param \Laravel\Passport\ClientRepository $clients * @return void */ - protected function createPersonalClient(ClientRepository $clients) + protected function createPersonalAccessClient(ClientRepository $clients) { $name = $this->option('name') ?: $this->ask( - 'What should we name the personal access client?', - config('app.name').' Personal Access Client' + 'What should we name the client?', + config('app.name').' Personal Access Grant Client' ); - $client = $clients->createPersonalAccessClient( - null, $name, 'http://localhost' + $provider = $this->option('provider') ?: $this->choice( + 'Which user provider should this client use to retrieve users?', + array_keys(config('auth.providers')), + config('auth.guards.api.provider') ); + $client = $clients->createPersonalAccessGrantClient($name, $provider); + $this->components->info('Personal access client created successfully.'); if (! config('passport.personal_access_client')) { @@ -86,21 +92,17 @@ protected function createPersonalClient(ClientRepository $clients) protected function createPasswordClient(ClientRepository $clients) { $name = $this->option('name') ?: $this->ask( - 'What should we name the password grant client?', + 'What should we name the client?', config('app.name').' Password Grant Client' ); - $providers = array_keys(config('auth.providers')); - $provider = $this->option('provider') ?: $this->choice( 'Which user provider should this client use to retrieve users?', - $providers, - in_array('users', $providers) ? 'users' : null + array_keys(config('auth.providers')), + config('auth.guards.api.provider') ); - $client = $clients->createPasswordGrantClient( - null, $name, 'http://localhost', $provider - ); + $client = $clients->createPasswordGrantClient($name, $provider); $this->components->info('Password grant client created successfully.'); @@ -117,13 +119,36 @@ protected function createClientCredentialsClient(ClientRepository $clients) { $name = $this->option('name') ?: $this->ask( 'What should we name the client?', - config('app.name').' ClientCredentials Grant Client' + config('app.name').' Client Credentials Grant Client' + ); + + $client = $clients->createClientCredentialsGrantClient($name); + + $this->components->info('New client created successfully.'); + + $this->outputClientDetails($client); + } + + /** + * Create an implicit grant client. + * + * @param \Laravel\Passport\ClientRepository $clients + * @return void + */ + protected function createImplicitClient(ClientRepository $clients) + { + $name = $this->option('name') ?: $this->ask( + 'What should we name the client?', + config('app.name').' Implicit Grant Client' ); - $client = $clients->create( - null, $name, '' + $redirect = $this->option('redirect_uri') ?: $this->ask( + 'Where should we redirect the request after authorization?', + url('/auth/callback') ); + $client = $clients->createImplicitGrantClient($name, explode(',', $redirect)); + $this->components->info('New client created successfully.'); $this->outputClientDetails($client); @@ -137,12 +162,9 @@ protected function createClientCredentialsClient(ClientRepository $clients) */ protected function createAuthCodeClient(ClientRepository $clients) { - $userId = $this->option('user_id') ?: $this->ask( - 'Which user ID should the client be assigned to? (Optional)' - ); - $name = $this->option('name') ?: $this->ask( - 'What should we name the client?' + 'What should we name the client?', + config('app.name') ); $redirect = $this->option('redirect_uri') ?: $this->ask( @@ -150,8 +172,8 @@ protected function createAuthCodeClient(ClientRepository $clients) url('/auth/callback') ); - $client = $clients->create( - $userId, $name, $redirect, null, false, false, ! $this->option('public') + $client = $clients->createAuthorizationCodeGrantClient( + $name, explode(',', $redirect), ! $this->option('public'), ); $this->components->info('New client created successfully.'); diff --git a/src/Http/Controllers/ClientController.php b/src/Http/Controllers/ClientController.php index acff352f..ca9117f2 100644 --- a/src/Http/Controllers/ClientController.php +++ b/src/Http/Controllers/ClientController.php @@ -76,9 +76,11 @@ public function store(Request $request) 'confidential' => 'boolean', ])->validate(); - $client = $this->clients->create( - $request->user()->getAuthIdentifier(), $request->name, $request->redirect, - null, false, false, (bool) $request->input('confidential', true) + $client = $this->clients->createAuthorizationCodeGrantClient( + $request->name, + explode(',', $request->redirect), + (bool) $request->input('confidential', true), + $request->user(), ); $client->secret = $client->plainSecret; @@ -106,9 +108,11 @@ public function update(Request $request, $clientId) 'redirect' => ['required', $this->redirectRule], ])->validate(); - return $this->clients->update( - $client, $request->name, $request->redirect + $this->clients->update( + $client, $request->name, explode(',', $request->redirect) ); + + return $client; } /** diff --git a/src/Http/Controllers/PersonalAccessTokenController.php b/src/Http/Controllers/PersonalAccessTokenController.php index 2fde4248..565fd4fa 100644 --- a/src/Http/Controllers/PersonalAccessTokenController.php +++ b/src/Http/Controllers/PersonalAccessTokenController.php @@ -48,7 +48,7 @@ public function forUser(Request $request) $tokens = $this->tokenRepository->forUser($request->user()->getAuthIdentifier()); return $tokens->load('client')->filter(function ($token) { - return $token->client->personal_access_client && ! $token->revoked; + return $token->client->hasGrantType('personal_access') && ! $token->revoked; })->values(); } diff --git a/tests/Feature/ClientTest.php b/tests/Feature/ClientTest.php index 38fb93e1..fbaac916 100644 --- a/tests/Feature/ClientTest.php +++ b/tests/Feature/ClientTest.php @@ -76,13 +76,44 @@ public function testGrantTypesWhenColumnDoesNotExist(): void $client->exists = true; $this->assertTrue($client->hasGrantType('foo')); + + $client->personal_access_client = false; + $client->password_client = false; + + $this->assertTrue($client->hasGrantType('authorization_code')); + + $client->personal_access_client = false; + $client->password_client = true; + + $this->assertTrue($client->hasGrantType('password')); + + $client->personal_access_client = true; + $client->password_client = false; + $client->secret = 'secret'; + + $this->assertTrue($client->hasGrantType('personal_access')); + $this->assertTrue($client->hasGrantType('client_credentials')); } public function testGrantTypesWhenColumnIsNull(): void { - $client = new Client(['scopes' => null]); + $client = new Client(['grant_types' => null]); $client->exists = true; $this->assertTrue($client->hasGrantType('foo')); + + $client->personal_access_client = false; + $client->password_client = false; + $this->assertTrue($client->hasGrantType('authorization_code')); + + $client->personal_access_client = false; + $client->password_client = true; + $this->assertTrue($client->hasGrantType('password')); + + $client->personal_access_client = true; + $client->password_client = false; + $client->secret = 'secret'; + $this->assertTrue($client->hasGrantType('personal_access')); + $this->assertTrue($client->hasGrantType('client_credentials')); } } diff --git a/tests/Unit/AuthorizedAccessTokenControllerTest.php b/tests/Unit/AuthorizedAccessTokenControllerTest.php index fdf04fa5..2846918b 100644 --- a/tests/Unit/AuthorizedAccessTokenControllerTest.php +++ b/tests/Unit/AuthorizedAccessTokenControllerTest.php @@ -52,9 +52,9 @@ public function test_tokens_can_be_retrieved_for_users() $userTokens = m::mock(); $client1 = new Client; - $client1->personal_access_client = true; + $client1->grant_types = ['personal_access']; $client2 = new Client; - $client2->personal_access_client = false; + $client2->grant_types = []; $token1->client = $client1; $token2->client = $client2; $userTokens->shouldReceive('load')->with('client')->andReturn(collect([ diff --git a/tests/Unit/BridgeAccessTokenRepositoryTest.php b/tests/Unit/BridgeAccessTokenRepositoryTest.php index f8049d18..6f64b658 100644 --- a/tests/Unit/BridgeAccessTokenRepositoryTest.php +++ b/tests/Unit/BridgeAccessTokenRepositoryTest.php @@ -40,7 +40,7 @@ public function test_access_tokens_can_be_persisted() $events->shouldReceive('dispatch')->once(); - $accessToken = new AccessToken(2, [new Scope('scopes')], new Client('client-id', 'name', 'redirect')); + $accessToken = new AccessToken(2, [new Scope('scopes')], new Client('client-id', 'name', ['redirect'])); $accessToken->setIdentifier(1); $accessToken->setExpiryDateTime($expiration); @@ -54,7 +54,7 @@ public function test_can_get_new_access_token() $tokenRepository = m::mock(TokenRepository::class); $events = m::mock(Dispatcher::class); $repository = new AccessTokenRepository($tokenRepository, $events); - $client = new Client('client-id', 'name', 'redirect'); + $client = new Client('client-id', 'name', ['redirect']); $scopes = [new Scope('place-orders'), new Scope('check-status')]; $userIdentifier = 123; diff --git a/tests/Unit/BridgeClientRepositoryTest.php b/tests/Unit/BridgeClientRepositoryTest.php index 2f440b92..12a0477c 100644 --- a/tests/Unit/BridgeClientRepositoryTest.php +++ b/tests/Unit/BridgeClientRepositoryTest.php @@ -64,7 +64,7 @@ public function test_can_validate_client_for_auth_code_grant() public function test_can_validate_client_for_client_credentials_grant() { $client = $this->clientModelRepository->findActive(1); - $client->personal_access_client = true; + $client->grant_types = ['client_credentials']; $this->assertTrue($this->repository->validateClient(1, 'secret', 'client_credentials')); $this->assertFalse($this->repository->validateClient(1, 'wrong-secret', 'client_credentials')); @@ -74,7 +74,7 @@ public function test_can_validate_client_for_client_credentials_grant() public function test_password_grant_is_permitted() { $client = $this->clientModelRepository->findActive(1); - $client->password_client = true; + $client->grant_types = ['password']; $this->assertTrue($this->repository->validateClient(1, 'secret', 'password')); } @@ -82,7 +82,7 @@ public function test_password_grant_is_permitted() public function test_public_client_password_grant_is_permitted() { $client = $this->clientModelRepository->findActive(1); - $client->password_client = true; + $client->grant_types = ['password']; $client->secret = null; $this->assertTrue($this->repository->validateClient(1, null, 'password')); @@ -109,7 +109,7 @@ public function test_public_client_authorization_code_grant_is_permitted() public function test_authorization_code_grant_is_prevented() { $client = $this->clientModelRepository->findActive(1); - $client->password_client = true; + $client->grant_types = ['password']; $this->assertFalse($this->repository->validateClient(1, 'secret', 'authorization_code')); } @@ -117,7 +117,7 @@ public function test_authorization_code_grant_is_prevented() public function test_personal_access_grant_is_permitted() { $client = $this->clientModelRepository->findActive(1); - $client->personal_access_client = true; + $client->grant_types = ['personal_access']; $this->assertTrue($this->repository->validateClient(1, 'secret', 'personal_access')); } @@ -127,25 +127,8 @@ public function test_personal_access_grant_is_prevented() $this->assertFalse($this->repository->validateClient(1, 'secret', 'personal_access')); } - public function test_public_client_personal_access_grant_is_prevented() - { - $client = $this->clientModelRepository->findActive(1); - $client->personal_access_client = true; - $client->secret = null; - - $this->assertFalse($this->repository->validateClient(1, null, 'personal_access')); - } - - public function test_client_credentials_grant_is_permitted() - { - $this->assertTrue($this->repository->validateClient(1, 'secret', 'client_credentials')); - } - public function test_client_credentials_grant_is_prevented() { - $client = $this->clientModelRepository->findActive(1); - $client->secret = null; - $this->assertFalse($this->repository->validateClient(1, 'secret', 'client_credentials')); } @@ -182,19 +165,54 @@ public function test_refresh_grant_is_prevented() { $this->assertFalse($this->repository->validateClient(1, 'wrong-secret', 'refresh_token')); } -} -class BridgeClientRepositoryTestClientStub extends \Laravel\Passport\Client -{ - public $name = 'Client'; + public function test_without_grant_types() + { + $client = $this->clientModelRepository->findActive(1); + $client->grant_types = null; - public $redirect = 'http://localhost'; + $this->assertTrue($this->repository->validateClient(1, 'secret', 'client_credentials')); + $this->assertFalse($this->repository->validateClient(1, 'wrong-secret', 'client_credentials')); + + $client->personal_access_client = true; + $client->password_client = false; + + $this->assertTrue($this->repository->validateClient(1, 'secret', 'client_credentials')); + $this->assertFalse($this->repository->validateClient(1, 'secret', 'authorization_code')); + $this->assertTrue($this->repository->validateClient(1, 'secret', 'personal_access')); + $this->assertFalse($this->repository->validateClient(1, 'wrong-secret', 'personal_access')); + $this->assertFalse($this->repository->validateClient(1, 'secret', 'password')); + + $client->personal_access_client = false; + $client->password_client = true; + + $this->assertTrue($this->repository->validateClient(1, 'secret', 'client_credentials')); + $this->assertFalse($this->repository->validateClient(1, 'secret', 'authorization_code')); + $this->assertTrue($this->repository->validateClient(1, 'secret', 'password')); + $this->assertFalse($this->repository->validateClient(1, 'wrong-secret', 'password')); + $this->assertFalse($this->repository->validateClient(1, 'secret', 'personal_access')); - public $secret = '$2y$10$WgqU4wQpfsARCIQk.nPSOOiNkrMpPVxQiLCFUt8comvQwh1z6WFMG'; + $client->personal_access_client = false; + $client->password_client = true; + $client->secret = null; - public $personal_access_client = false; + $this->assertFalse($this->repository->validateClient(1, null, 'client_credentials')); + $this->assertTrue($this->repository->validateClient(1, null, 'password')); - public $password_client = false; + $client->personal_access_client = true; + $client->password_client = false; + $client->secret = null; - public $provider = null; + $this->assertFalse($this->repository->validateClient(1, null, 'personal_access')); + } +} + +class BridgeClientRepositoryTestClientStub extends \Laravel\Passport\Client +{ + protected $attributes = [ + 'name' => 'Client', + 'redirect_uris' => '["http://localhost"]', + 'secret' => '$2y$10$WgqU4wQpfsARCIQk.nPSOOiNkrMpPVxQiLCFUt8comvQwh1z6WFMG', + 'grant_types' => '["authorization_code","refresh_token"]', + ]; } diff --git a/tests/Unit/BridgeScopeRepositoryTest.php b/tests/Unit/BridgeScopeRepositoryTest.php index fb204ff5..8b204935 100644 --- a/tests/Unit/BridgeScopeRepositoryTest.php +++ b/tests/Unit/BridgeScopeRepositoryTest.php @@ -32,7 +32,7 @@ public function test_invalid_scopes_are_removed() $repository = new ScopeRepository($clients); $scopes = $repository->finalizeScopes( - [$scope1 = new Scope('scope-1'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', 'http://localhost'), 1 + [$scope1 = new Scope('scope-1'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', ['http://localhost']), 1 ); $this->assertEquals([$scope1], $scopes); @@ -52,7 +52,7 @@ public function test_invalid_scopes_are_removed_without_a_client_repository() $repository = new ScopeRepository($clients); $scopes = $repository->finalizeScopes( - [$scope1 = new Scope('scope-1'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', 'http://localhost'), 1 + [$scope1 = new Scope('scope-1'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', ['http://localhost']), 1 ); $this->assertEquals([$scope1], $scopes); @@ -74,7 +74,7 @@ public function test_clients_do_not_restrict_scopes_by_default() $repository = new ScopeRepository($clients); $scopes = $repository->finalizeScopes( - [$scope1 = new Scope('scope-1'), $scope2 = new Scope('scope-2')], 'client_credentials', new Client('id', 'name', 'http://localhost'), 1 + [$scope1 = new Scope('scope-1'), $scope2 = new Scope('scope-2')], 'client_credentials', new Client('id', 'name', ['http://localhost']), 1 ); $this->assertEquals([$scope1, $scope2], $scopes); @@ -96,7 +96,7 @@ public function test_scopes_disallowed_for_client_are_removed() $repository = new ScopeRepository($clients); $scopes = $repository->finalizeScopes( - [$scope1 = new Scope('scope-1'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', 'http://localhost'), 1 + [$scope1 = new Scope('scope-1'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', ['http://localhost']), 1 ); $this->assertEquals([$scope1], $scopes); @@ -121,7 +121,7 @@ public function test_scopes_disallowed_for_client_are_removed_but_inherited_scop $repository = new ScopeRepository($clients); $scopes = $repository->finalizeScopes( - [$scope1 = new Scope('scope-1:limited-access'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', 'http://localhost'), 1 + [$scope1 = new Scope('scope-1:limited-access'), new Scope('scope-2')], 'client_credentials', new Client('id', 'name', ['http://localhost']), 1 ); $this->assertEquals([$scope1], $scopes); @@ -141,7 +141,7 @@ public function test_superuser_scope_cant_be_applied_if_wrong_grant() $repository = new ScopeRepository($clients); $scopes = $repository->finalizeScopes( - [$scope1 = new Scope('*')], 'refresh_token', new Client('id', 'name', 'http://localhost'), 1 + [$scope1 = new Scope('*')], 'refresh_token', new Client('id', 'name', ['http://localhost']), 1 ); $this->assertEquals([], $scopes); @@ -161,7 +161,7 @@ public function test_superuser_scope_cant_be_applied_if_wrong_grant_without_a_cl $repository = new ScopeRepository($clients); $scopes = $repository->finalizeScopes( - [$scope1 = new Scope('*')], 'refresh_token', new Client('id', 'name', 'http://localhost'), 1 + [$scope1 = new Scope('*')], 'refresh_token', new Client('id', 'name', ['http://localhost']), 1 ); $this->assertEquals([], $scopes); diff --git a/tests/Unit/ClientControllerTest.php b/tests/Unit/ClientControllerTest.php index 7da48404..66eba0c3 100644 --- a/tests/Unit/ClientControllerTest.php +++ b/tests/Unit/ClientControllerTest.php @@ -40,15 +40,14 @@ public function test_all_the_clients_for_the_current_user_can_be_retrieved() public function test_clients_can_be_stored() { $clients = m::mock(ClientRepository::class); + $user = new ClientControllerFakeUser; $request = Request::create('/', 'GET', ['name' => 'client name', 'redirect' => 'http://localhost']); - $request->setUserResolver(function () { - return new ClientControllerFakeUser; - }); + $request->setUserResolver(fn () => $user); - $clients->shouldReceive('create') + $clients->shouldReceive('createAuthorizationCodeGrantClient') ->once() - ->with(1, 'client name', 'http://localhost', null, false, false, true) + ->with('client name', ['http://localhost'], true, $user) ->andReturn($client = new Client); $redirectRule = m::mock(RedirectRule::class); @@ -74,19 +73,18 @@ public function test_clients_can_be_stored() public function test_public_clients_can_be_stored() { $clients = m::mock(ClientRepository::class); + $user = new ClientControllerFakeUser; $request = Request::create( '/', 'GET', ['name' => 'client name', 'redirect' => 'http://localhost', 'confidential' => false] ); - $request->setUserResolver(function () { - return new ClientControllerFakeUser; - }); + $request->setUserResolver(fn () => $user); - $clients->shouldReceive('create') + $clients->shouldReceive('createAuthorizationCodeGrantClient') ->once() - ->with(1, 'client name', 'http://localhost', null, false, false, false) + ->with('client name', ['http://localhost'], false, $user) ->andReturn($client = new Client); $redirectRule = m::mock(RedirectRule::class); @@ -126,8 +124,8 @@ public function test_clients_can_be_updated() }); $clients->shouldReceive('update')->once()->with( - m::type(Client::class), 'client name', 'http://localhost' - )->andReturn('response'); + $client, 'client name', ['http://localhost'] + )->andReturn(true); $redirectRule = m::mock(RedirectRule::class); @@ -145,7 +143,7 @@ public function test_clients_can_be_updated() $clients, $validator, $redirectRule ); - $this->assertSame('response', $controller->update($request, 1)); + $this->assertSame($client, $controller->update($request, 1)); } public function test_404_response_if_client_doesnt_belong_to_user() @@ -229,7 +227,7 @@ public function test_404_response_if_client_doesnt_belong_to_user_on_delete() } } -class ClientControllerFakeUser +class ClientControllerFakeUser extends \Illuminate\Foundation\Auth\User { public $id = 1; diff --git a/tests/Unit/PersonalAccessTokenControllerTest.php b/tests/Unit/PersonalAccessTokenControllerTest.php index 9e8b1ad5..0302c7d7 100644 --- a/tests/Unit/PersonalAccessTokenControllerTest.php +++ b/tests/Unit/PersonalAccessTokenControllerTest.php @@ -4,6 +4,7 @@ use Illuminate\Contracts\Validation\Factory; use Illuminate\Http\Request; +use Laravel\Passport\Client; use Laravel\Passport\Http\Controllers\PersonalAccessTokenController; use Laravel\Passport\Passport; use Laravel\Passport\Token; @@ -27,8 +28,8 @@ public function test_tokens_can_be_retrieved_for_users() $token2 = new Token; $userTokens = m::mock(); - $token1->client = (object) ['personal_access_client' => true]; - $token2->client = (object) ['personal_access_client' => false]; + $token1->client = new Client(['grant_types' => ['personal_access']]); + $token2->client = new Client(['grant_types' => []]); $userTokens->shouldReceive('load')->with('client')->andReturn(collect([ $token1, $token2, ]));