TokensControllerTest.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Tests\Integration\Admin;
  4. use App\Domain\Auth\Role;
  5. use App\Domain\Auth\TokenKind;
  6. use App\Tests\Integration\Support\AppTestCase;
  7. final class TokensControllerTest extends AppTestCase
  8. {
  9. public function testCreateReporterTokenReturnsRawOnce(): void
  10. {
  11. $admin = $this->createToken(TokenKind::Admin, role: Role::Admin);
  12. $reporterId = $this->createReporter('web-tokens');
  13. $resp = $this->request(
  14. 'POST',
  15. '/api/v1/admin/tokens',
  16. ['Authorization' => 'Bearer ' . $admin, 'Content-Type' => 'application/json'],
  17. json_encode(['kind' => 'reporter', 'reporter_id' => $reporterId]) ?: null,
  18. );
  19. self::assertSame(201, $resp->getStatusCode());
  20. $body = $this->decode($resp);
  21. self::assertSame('reporter', $body['kind']);
  22. self::assertSame($reporterId, $body['reporter_id']);
  23. self::assertArrayHasKey('raw_token', $body);
  24. self::assertStringStartsWith('irdb_rep_', (string) $body['raw_token']);
  25. // Second fetch via list must not include raw_token.
  26. $list = $this->request('GET', '/api/v1/admin/tokens', [
  27. 'Authorization' => 'Bearer ' . $admin,
  28. ]);
  29. $listBody = $this->decode($list);
  30. foreach ($listBody['data'] as $row) {
  31. self::assertArrayNotHasKey('raw_token', $row);
  32. }
  33. }
  34. public function testCreateAdminTokenRequiresRole(): void
  35. {
  36. $admin = $this->createToken(TokenKind::Admin, role: Role::Admin);
  37. $resp = $this->request(
  38. 'POST',
  39. '/api/v1/admin/tokens',
  40. ['Authorization' => 'Bearer ' . $admin, 'Content-Type' => 'application/json'],
  41. json_encode(['kind' => 'admin']) ?: null,
  42. );
  43. self::assertSame(400, $resp->getStatusCode());
  44. self::assertArrayHasKey('role', $this->decode($resp)['details']);
  45. }
  46. public function testServiceKindRefused(): void
  47. {
  48. $admin = $this->createToken(TokenKind::Admin, role: Role::Admin);
  49. $resp = $this->request(
  50. 'POST',
  51. '/api/v1/admin/tokens',
  52. ['Authorization' => 'Bearer ' . $admin, 'Content-Type' => 'application/json'],
  53. json_encode(['kind' => 'service']) ?: null,
  54. );
  55. self::assertSame(400, $resp->getStatusCode());
  56. }
  57. public function testListExcludesServiceTokens(): void
  58. {
  59. $admin = $this->createToken(TokenKind::Admin, role: Role::Admin);
  60. $this->createToken(TokenKind::Service);
  61. $resp = $this->request('GET', '/api/v1/admin/tokens', [
  62. 'Authorization' => 'Bearer ' . $admin,
  63. ]);
  64. $body = $this->decode($resp);
  65. foreach ($body['data'] as $row) {
  66. self::assertNotSame('service', $row['kind']);
  67. }
  68. }
  69. public function testRevokeMarksRevokedAt(): void
  70. {
  71. $admin = $this->createToken(TokenKind::Admin, role: Role::Admin);
  72. $reporterId = $this->createReporter('web-revoke');
  73. $created = $this->request(
  74. 'POST',
  75. '/api/v1/admin/tokens',
  76. ['Authorization' => 'Bearer ' . $admin, 'Content-Type' => 'application/json'],
  77. json_encode(['kind' => 'reporter', 'reporter_id' => $reporterId]) ?: null,
  78. );
  79. $tokenId = (int) $this->decode($created)['id'];
  80. $delete = $this->request('DELETE', "/api/v1/admin/tokens/{$tokenId}", [
  81. 'Authorization' => 'Bearer ' . $admin,
  82. ]);
  83. self::assertSame(204, $delete->getStatusCode());
  84. $row = $this->db->fetchAssociative('SELECT revoked_at FROM api_tokens WHERE id = :id', ['id' => $tokenId]);
  85. self::assertIsArray($row);
  86. self::assertNotNull($row['revoked_at']);
  87. }
  88. public function testPurgeDeletesRevokedToken(): void
  89. {
  90. $admin = $this->createToken(TokenKind::Admin, role: Role::Admin);
  91. $reporterId = $this->createReporter('web-purge');
  92. $created = $this->request(
  93. 'POST',
  94. '/api/v1/admin/tokens',
  95. ['Authorization' => 'Bearer ' . $admin, 'Content-Type' => 'application/json'],
  96. json_encode(['kind' => 'reporter', 'reporter_id' => $reporterId]) ?: null,
  97. );
  98. $tokenId = (int) $this->decode($created)['id'];
  99. // Revoke first.
  100. self::assertSame(204, $this->request('DELETE', "/api/v1/admin/tokens/{$tokenId}", [
  101. 'Authorization' => 'Bearer ' . $admin,
  102. ])->getStatusCode());
  103. $purge = $this->request('DELETE', "/api/v1/admin/tokens/{$tokenId}/purge", [
  104. 'Authorization' => 'Bearer ' . $admin,
  105. ]);
  106. self::assertSame(204, $purge->getStatusCode());
  107. $count = (int) $this->db->fetchOne('SELECT COUNT(*) FROM api_tokens WHERE id = :id', ['id' => $tokenId]);
  108. self::assertSame(0, $count);
  109. $audit = $this->db->fetchAssociative(
  110. "SELECT action, target_id FROM audit_log WHERE action = 'token.deleted' ORDER BY id DESC LIMIT 1"
  111. );
  112. self::assertIsArray($audit);
  113. self::assertSame((string) $tokenId, $audit['target_id']);
  114. }
  115. public function testPurgeRefusesActiveToken(): void
  116. {
  117. $admin = $this->createToken(TokenKind::Admin, role: Role::Admin);
  118. $reporterId = $this->createReporter('web-still-active');
  119. $created = $this->request(
  120. 'POST',
  121. '/api/v1/admin/tokens',
  122. ['Authorization' => 'Bearer ' . $admin, 'Content-Type' => 'application/json'],
  123. json_encode(['kind' => 'reporter', 'reporter_id' => $reporterId]) ?: null,
  124. );
  125. $tokenId = (int) $this->decode($created)['id'];
  126. $purge = $this->request('DELETE', "/api/v1/admin/tokens/{$tokenId}/purge", [
  127. 'Authorization' => 'Bearer ' . $admin,
  128. ]);
  129. self::assertSame(409, $purge->getStatusCode());
  130. self::assertSame('not_revoked', $this->decode($purge)['error']);
  131. $count = (int) $this->db->fetchOne('SELECT COUNT(*) FROM api_tokens WHERE id = :id', ['id' => $tokenId]);
  132. self::assertSame(1, $count);
  133. }
  134. }