151 lines
5.3 KiB
PHP
151 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Laravel\Passport\Token;
|
|
use Tests\TestCase;
|
|
|
|
class ClientCrudTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private User $owner;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->owner = User::factory()->approved()->create();
|
|
}
|
|
|
|
public function test_owner_can_list_only_their_own_non_revoked_clients(): void
|
|
{
|
|
$this->actingAs($this->owner)->postJson('/web-api/clients', [
|
|
'name' => 'My Client',
|
|
'grant_type' => 'client_credentials',
|
|
])->assertCreated();
|
|
|
|
$stranger = User::factory()->approved()->create();
|
|
$this->actingAs($stranger)->postJson('/web-api/clients', [
|
|
'name' => 'Someone Elses Client',
|
|
'grant_type' => 'client_credentials',
|
|
])->assertCreated();
|
|
|
|
$response = $this->actingAs($this->owner)->getJson('/web-api/clients');
|
|
|
|
$response->assertOk();
|
|
$names = collect($response->json())->pluck('name');
|
|
$this->assertTrue($names->contains('My Client'));
|
|
$this->assertFalse($names->contains('Someone Elses Client'));
|
|
}
|
|
|
|
public function test_creating_a_client_credentials_client_is_always_confidential(): void
|
|
{
|
|
$response = $this->actingAs($this->owner)->postJson('/web-api/clients', [
|
|
'name' => 'CC Client',
|
|
'grant_type' => 'client_credentials',
|
|
'confidential' => false,
|
|
]);
|
|
|
|
$response->assertCreated();
|
|
$this->assertNotNull($response->json('plain_secret'));
|
|
$this->assertDatabaseHas('audit_logs', ['action' => 'client.created']);
|
|
}
|
|
|
|
public function test_creating_a_public_authorization_code_client_has_no_secret(): void
|
|
{
|
|
$response = $this->actingAs($this->owner)->postJson('/web-api/clients', [
|
|
'name' => 'Public SPA',
|
|
'grant_type' => 'authorization_code',
|
|
'redirect_uris' => ['https://example.test/callback'],
|
|
'confidential' => false,
|
|
]);
|
|
|
|
$response->assertCreated();
|
|
$this->assertNull($response->json('plain_secret'));
|
|
}
|
|
|
|
public function test_authorization_code_requires_redirect_uris(): void
|
|
{
|
|
$this->actingAs($this->owner)->postJson('/web-api/clients', [
|
|
'name' => 'No Redirect',
|
|
'grant_type' => 'authorization_code',
|
|
])->assertUnprocessable()->assertJsonValidationErrors('redirect_uris');
|
|
}
|
|
|
|
public function test_invalid_grant_type_is_rejected(): void
|
|
{
|
|
$this->actingAs($this->owner)->postJson('/web-api/clients', [
|
|
'name' => 'Bad Grant',
|
|
'grant_type' => 'not-a-real-grant',
|
|
])->assertUnprocessable()->assertJsonValidationErrors('grant_type');
|
|
}
|
|
|
|
public function test_owner_can_update_name_and_redirect_uris(): void
|
|
{
|
|
$created = $this->actingAs($this->owner)->postJson('/web-api/clients', [
|
|
'name' => 'Original Name',
|
|
'grant_type' => 'authorization_code',
|
|
'redirect_uris' => ['https://example.test/callback'],
|
|
])->json('client');
|
|
|
|
$response = $this->actingAs($this->owner)->putJson("/web-api/clients/{$created['id']}", [
|
|
'name' => 'Updated Name',
|
|
'redirect_uris' => ['https://example.test/new-callback'],
|
|
]);
|
|
|
|
$response->assertOk();
|
|
$response->assertJsonPath('name', 'Updated Name');
|
|
$response->assertJsonPath('redirect_uris.0', 'https://example.test/new-callback');
|
|
}
|
|
|
|
public function test_non_owner_cannot_update_or_revoke_another_users_client(): void
|
|
{
|
|
$created = $this->actingAs($this->owner)->postJson('/web-api/clients', [
|
|
'name' => 'Owned Client',
|
|
'grant_type' => 'client_credentials',
|
|
])->json('client');
|
|
|
|
$stranger = User::factory()->approved()->create();
|
|
|
|
$this->actingAs($stranger)
|
|
->putJson("/web-api/clients/{$created['id']}", ['name' => 'Hijacked'])
|
|
->assertNotFound();
|
|
|
|
$this->actingAs($stranger)
|
|
->deleteJson("/web-api/clients/{$created['id']}")
|
|
->assertNotFound();
|
|
}
|
|
|
|
public function test_revoking_a_client_also_revokes_its_active_tokens(): void
|
|
{
|
|
$response = $this->actingAs($this->owner)->postJson('/web-api/clients', [
|
|
'name' => 'To Revoke',
|
|
'grant_type' => 'client_credentials',
|
|
]);
|
|
$clientId = $response->json('client.id');
|
|
|
|
$token = Token::create([
|
|
'id' => \Illuminate\Support\Str::random(80),
|
|
'user_id' => $this->owner->id,
|
|
'client_id' => $clientId,
|
|
'name' => null,
|
|
'scopes' => ['users:read'],
|
|
'revoked' => false,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
'expires_at' => now()->addDays(15),
|
|
]);
|
|
|
|
$this->actingAs($this->owner)
|
|
->deleteJson("/web-api/clients/{$clientId}")
|
|
->assertNoContent();
|
|
|
|
$this->assertDatabaseHas('oauth_clients', ['id' => $clientId, 'revoked' => true]);
|
|
$this->assertDatabaseHas('oauth_access_tokens', ['id' => $token->id, 'revoked' => true]);
|
|
$this->assertDatabaseHas('audit_logs', ['action' => 'client.revoked', 'subject_id' => $clientId]);
|
|
}
|
|
}
|