seed(RolePermissionSeeder::class); config(['pireon.webhook_secrets' => ['password-secret', 'redirect-secret']]); } /** * @param array $payload */ protected function deliver(array $payload, ?string $secret = 'password-secret'): TestResponse { $body = (string) json_encode($payload); $headers = ['CONTENT_TYPE' => 'application/json', 'HTTP_ACCEPT' => 'application/json']; if ($secret !== null) { $headers['HTTP_X_WEBHOOK_SIGNATURE'] = hash_hmac('sha256', $body, $secret); } return $this->call('POST', route('api.v1.webhooks.pireon'), [], [], [], $headers, $body); } #[Test] public function it_refuses_everything_without_a_secret(): void { config(['pireon.webhook_secrets' => []]); $this->deliver(['event' => 'token.revoked', 'data' => ['user_id' => 1]])->assertUnauthorized(); } #[Test] public function it_refuses_a_delivery_that_is_not_signed_with_either_secret(): void { $user = $this->userWithRoles('editor', ['oauth_id' => 77]); $this->deliver(['event' => 'user.deactivated', 'data' => ['user_id' => 77]], 'someone-else')->assertUnauthorized(); $this->deliver(['event' => 'user.deactivated', 'data' => ['user_id' => 77]], null)->assertUnauthorized(); $this->assertTrue($user->fresh()->is_active); } #[Test] public function a_deactivated_account_goes_inactive_and_loses_its_sessions(): void { $user = $this->userWithRoles('editor', ['oauth_id' => 77]); config(['session.driver' => 'database']); DB::table('sessions')->insert([ 'id' => 'a-session', 'user_id' => $user->id, 'payload' => '', 'last_activity' => time(), ]); // Signed by the redirect client: either secret is good. $this->deliver(['event' => 'user.deactivated', 'data' => ['user_id' => 77]], 'redirect-secret') ->assertNoContent(); $this->assertFalse($user->fresh()->is_active); $this->assertDatabaseMissing('sessions', ['id' => 'a-session']); } #[Test] public function a_revoked_token_stops_working_at_once_instead_of_after_the_cache(): void { $user = $this->userWithRoles('editor', ['oauth_id' => 77]); $headers = $this->apiHeadersFor($user); $this->getJson(route('api.v1.me'), $headers)->assertOk(); // Revoked upstream: the cached answer would still let it through… $this->pireonIdentities = array_fill_keys(['nobody'], []); $this->app['auth']->forgetGuards(); $this->getJson(route('api.v1.me'), $headers)->assertOk(); // …until Pireon says so. $this->deliver(['event' => 'token.revoked', 'data' => ['user_id' => 77]])->assertNoContent(); $this->app['auth']->forgetGuards(); $this->getJson(route('api.v1.me'), $headers)->assertUnauthorized(); $this->assertTrue($user->fresh()->is_active); } #[Test] public function other_events_and_unknown_people_change_nothing(): void { $user = $this->userWithRoles('editor', ['oauth_id' => 77]); $this->deliver(['event' => 'user.updated', 'data' => ['user_id' => 77]])->assertNoContent(); $this->deliver(['event' => 'user.deactivated', 'data' => ['user_id' => 999]])->assertNoContent(); $this->deliver(['data' => ['user_id' => 77]])->assertStatus(400); $this->assertTrue($user->fresh()->is_active); } }