P2 Items Completed (P2-062 to P2-068): - Switch AgentApiKey from SHA-256 to Argon2id hashing - Add 200+ tests for models, services, and AI providers - Create agent_plans migration with phases and workspace states Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
774 lines
24 KiB
PHP
774 lines
24 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Core\Mod\Agentic\Tests\Feature;
|
|
|
|
use Carbon\Carbon;
|
|
use Core\Mod\Agentic\Models\AgentApiKey;
|
|
use Core\Mod\Agentic\Services\AgentApiKeyService;
|
|
use Core\Tenant\Models\Workspace;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* Tests for the AgentApiKeyService.
|
|
*
|
|
* Covers authentication, IP validation, rate limit tracking, and key management.
|
|
*/
|
|
class AgentApiKeyServiceTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private Workspace $workspace;
|
|
|
|
private AgentApiKeyService $service;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->workspace = Workspace::factory()->create();
|
|
$this->service = app(AgentApiKeyService::class);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Key Creation Tests
|
|
// =========================================================================
|
|
|
|
public function test_create_returns_agent_api_key(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertInstanceOf(AgentApiKey::class, $key);
|
|
$this->assertNotNull($key->plainTextKey);
|
|
}
|
|
|
|
public function test_create_with_workspace_id(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace->id,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertEquals($this->workspace->id, $key->workspace_id);
|
|
}
|
|
|
|
public function test_create_with_permissions(): void
|
|
{
|
|
$permissions = [
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
AgentApiKey::PERM_PLANS_WRITE,
|
|
];
|
|
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
$permissions
|
|
);
|
|
|
|
$this->assertEquals($permissions, $key->permissions);
|
|
}
|
|
|
|
public function test_create_with_custom_rate_limit(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
500
|
|
);
|
|
|
|
$this->assertEquals(500, $key->rate_limit);
|
|
}
|
|
|
|
public function test_create_with_expiry(): void
|
|
{
|
|
$expiresAt = Carbon::now()->addMonth();
|
|
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
$expiresAt
|
|
);
|
|
|
|
$this->assertEquals(
|
|
$expiresAt->toDateTimeString(),
|
|
$key->expires_at->toDateTimeString()
|
|
);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Key Validation Tests
|
|
// =========================================================================
|
|
|
|
public function test_validate_returns_key_for_valid_key(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$result = $this->service->validate($plainKey);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertEquals($key->id, $result->id);
|
|
}
|
|
|
|
public function test_validate_returns_null_for_invalid_key(): void
|
|
{
|
|
$result = $this->service->validate('ak_invalid_key_here');
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function test_validate_returns_null_for_revoked_key(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
$plainKey = $key->plainTextKey;
|
|
$key->revoke();
|
|
|
|
$result = $this->service->validate($plainKey);
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function test_validate_returns_null_for_expired_key(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->subDay()
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$result = $this->service->validate($plainKey);
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Permission Check Tests
|
|
// =========================================================================
|
|
|
|
public function test_check_permission_returns_true_when_granted(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
|
|
$result = $this->service->checkPermission($key, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertTrue($result);
|
|
}
|
|
|
|
public function test_check_permission_returns_false_when_not_granted(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
|
|
$result = $this->service->checkPermission($key, AgentApiKey::PERM_PLANS_WRITE);
|
|
|
|
$this->assertFalse($result);
|
|
}
|
|
|
|
public function test_check_permission_returns_false_for_inactive_key(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
$key->revoke();
|
|
|
|
$result = $this->service->checkPermission($key, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertFalse($result);
|
|
}
|
|
|
|
public function test_check_permissions_returns_true_when_all_granted(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ, AgentApiKey::PERM_PLANS_WRITE]
|
|
);
|
|
|
|
$result = $this->service->checkPermissions($key, [
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
AgentApiKey::PERM_PLANS_WRITE,
|
|
]);
|
|
|
|
$this->assertTrue($result);
|
|
}
|
|
|
|
public function test_check_permissions_returns_false_when_missing_one(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
|
|
$result = $this->service->checkPermissions($key, [
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
AgentApiKey::PERM_PLANS_WRITE,
|
|
]);
|
|
|
|
$this->assertFalse($result);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Rate Limiting Tests
|
|
// =========================================================================
|
|
|
|
public function test_record_usage_increments_cache_counter(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
Cache::forget("agent_api_key_rate:{$key->id}");
|
|
|
|
$this->service->recordUsage($key);
|
|
$this->service->recordUsage($key);
|
|
$this->service->recordUsage($key);
|
|
|
|
$this->assertEquals(3, Cache::get("agent_api_key_rate:{$key->id}"));
|
|
}
|
|
|
|
public function test_record_usage_records_client_ip(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
|
|
$this->service->recordUsage($key, '192.168.1.100');
|
|
|
|
$this->assertEquals('192.168.1.100', $key->fresh()->last_used_ip);
|
|
}
|
|
|
|
public function test_record_usage_updates_last_used_at(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
|
|
$this->service->recordUsage($key);
|
|
|
|
$this->assertNotNull($key->fresh()->last_used_at);
|
|
}
|
|
|
|
public function test_is_rate_limited_returns_false_under_limit(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key', [], 100);
|
|
Cache::put("agent_api_key_rate:{$key->id}", 50, 60);
|
|
|
|
$result = $this->service->isRateLimited($key);
|
|
|
|
$this->assertFalse($result);
|
|
}
|
|
|
|
public function test_is_rate_limited_returns_true_at_limit(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key', [], 100);
|
|
Cache::put("agent_api_key_rate:{$key->id}", 100, 60);
|
|
|
|
$result = $this->service->isRateLimited($key);
|
|
|
|
$this->assertTrue($result);
|
|
}
|
|
|
|
public function test_is_rate_limited_returns_true_over_limit(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key', [], 100);
|
|
Cache::put("agent_api_key_rate:{$key->id}", 150, 60);
|
|
|
|
$result = $this->service->isRateLimited($key);
|
|
|
|
$this->assertTrue($result);
|
|
}
|
|
|
|
public function test_get_rate_limit_status_returns_correct_values(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key', [], 100);
|
|
Cache::put("agent_api_key_rate:{$key->id}", 30, 60);
|
|
|
|
$status = $this->service->getRateLimitStatus($key);
|
|
|
|
$this->assertEquals(100, $status['limit']);
|
|
$this->assertEquals(70, $status['remaining']);
|
|
$this->assertEquals(30, $status['used']);
|
|
$this->assertArrayHasKey('reset_in_seconds', $status);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Key Management Tests
|
|
// =========================================================================
|
|
|
|
public function test_revoke_sets_revoked_at(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
|
|
$this->service->revoke($key);
|
|
|
|
$this->assertNotNull($key->fresh()->revoked_at);
|
|
}
|
|
|
|
public function test_revoke_clears_rate_limit_cache(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
Cache::put("agent_api_key_rate:{$key->id}", 50, 60);
|
|
|
|
$this->service->revoke($key);
|
|
|
|
$this->assertNull(Cache::get("agent_api_key_rate:{$key->id}"));
|
|
}
|
|
|
|
public function test_update_permissions_changes_permissions(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
|
|
$this->service->updatePermissions($key, [AgentApiKey::PERM_SESSIONS_WRITE]);
|
|
|
|
$fresh = $key->fresh();
|
|
$this->assertFalse($fresh->hasPermission(AgentApiKey::PERM_PLANS_READ));
|
|
$this->assertTrue($fresh->hasPermission(AgentApiKey::PERM_SESSIONS_WRITE));
|
|
}
|
|
|
|
public function test_update_rate_limit_changes_limit(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key', [], 100);
|
|
|
|
$this->service->updateRateLimit($key, 500);
|
|
|
|
$this->assertEquals(500, $key->fresh()->rate_limit);
|
|
}
|
|
|
|
public function test_extend_expiry_updates_expiry(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->addDay()
|
|
);
|
|
|
|
$newExpiry = Carbon::now()->addMonth();
|
|
$this->service->extendExpiry($key, $newExpiry);
|
|
|
|
$this->assertEquals(
|
|
$newExpiry->toDateTimeString(),
|
|
$key->fresh()->expires_at->toDateTimeString()
|
|
);
|
|
}
|
|
|
|
public function test_remove_expiry_clears_expiry(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->addDay()
|
|
);
|
|
|
|
$this->service->removeExpiry($key);
|
|
|
|
$this->assertNull($key->fresh()->expires_at);
|
|
}
|
|
|
|
// =========================================================================
|
|
// IP Restriction Tests
|
|
// =========================================================================
|
|
|
|
public function test_update_ip_restrictions_sets_values(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
|
|
$this->service->updateIpRestrictions($key, true, ['192.168.1.1', '10.0.0.0/8']);
|
|
|
|
$fresh = $key->fresh();
|
|
$this->assertTrue($fresh->ip_restriction_enabled);
|
|
$this->assertEquals(['192.168.1.1', '10.0.0.0/8'], $fresh->ip_whitelist);
|
|
}
|
|
|
|
public function test_enable_ip_restrictions_enables_with_whitelist(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
|
|
$this->service->enableIpRestrictions($key, ['192.168.1.1']);
|
|
|
|
$fresh = $key->fresh();
|
|
$this->assertTrue($fresh->ip_restriction_enabled);
|
|
$this->assertEquals(['192.168.1.1'], $fresh->ip_whitelist);
|
|
}
|
|
|
|
public function test_disable_ip_restrictions_disables(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
$this->service->enableIpRestrictions($key, ['192.168.1.1']);
|
|
|
|
$this->service->disableIpRestrictions($key);
|
|
|
|
$this->assertFalse($key->fresh()->ip_restriction_enabled);
|
|
}
|
|
|
|
public function test_is_ip_allowed_returns_true_when_restrictions_disabled(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
|
|
$result = $this->service->isIpAllowed($key, '192.168.1.100');
|
|
|
|
$this->assertTrue($result);
|
|
}
|
|
|
|
public function test_is_ip_allowed_returns_true_when_ip_in_whitelist(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
$this->service->enableIpRestrictions($key, ['192.168.1.100']);
|
|
|
|
$result = $this->service->isIpAllowed($key->fresh(), '192.168.1.100');
|
|
|
|
$this->assertTrue($result);
|
|
}
|
|
|
|
public function test_is_ip_allowed_returns_false_when_ip_not_in_whitelist(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
$this->service->enableIpRestrictions($key, ['192.168.1.100']);
|
|
|
|
$result = $this->service->isIpAllowed($key->fresh(), '10.0.0.1');
|
|
|
|
$this->assertFalse($result);
|
|
}
|
|
|
|
public function test_is_ip_allowed_supports_cidr_ranges(): void
|
|
{
|
|
$key = $this->service->create($this->workspace, 'Test Key');
|
|
$this->service->enableIpRestrictions($key, ['192.168.1.0/24']);
|
|
|
|
$fresh = $key->fresh();
|
|
$this->assertTrue($this->service->isIpAllowed($fresh, '192.168.1.50'));
|
|
$this->assertTrue($this->service->isIpAllowed($fresh, '192.168.1.254'));
|
|
$this->assertFalse($this->service->isIpAllowed($fresh, '192.168.2.1'));
|
|
}
|
|
|
|
public function test_parse_ip_whitelist_input_parses_valid_input(): void
|
|
{
|
|
$input = "192.168.1.1\n192.168.1.2\n10.0.0.0/8";
|
|
|
|
$result = $this->service->parseIpWhitelistInput($input);
|
|
|
|
$this->assertEmpty($result['errors']);
|
|
$this->assertCount(3, $result['entries']);
|
|
$this->assertContains('192.168.1.1', $result['entries']);
|
|
$this->assertContains('192.168.1.2', $result['entries']);
|
|
$this->assertContains('10.0.0.0/8', $result['entries']);
|
|
}
|
|
|
|
public function test_parse_ip_whitelist_input_returns_errors_for_invalid(): void
|
|
{
|
|
$input = "192.168.1.1\ninvalid_ip\n10.0.0.0/8";
|
|
|
|
$result = $this->service->parseIpWhitelistInput($input);
|
|
|
|
$this->assertCount(1, $result['errors']);
|
|
$this->assertCount(2, $result['entries']);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Workspace Query Tests
|
|
// =========================================================================
|
|
|
|
public function test_get_active_keys_for_workspace_returns_active_only(): void
|
|
{
|
|
$active = $this->service->create($this->workspace, 'Active Key');
|
|
$revoked = $this->service->create($this->workspace, 'Revoked Key');
|
|
$revoked->revoke();
|
|
$this->service->create(
|
|
$this->workspace,
|
|
'Expired Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->subDay()
|
|
);
|
|
|
|
$keys = $this->service->getActiveKeysForWorkspace($this->workspace);
|
|
|
|
$this->assertCount(1, $keys);
|
|
$this->assertEquals('Active Key', $keys->first()->name);
|
|
}
|
|
|
|
public function test_get_active_keys_for_workspace_filters_by_workspace(): void
|
|
{
|
|
$otherWorkspace = Workspace::factory()->create();
|
|
|
|
$this->service->create($this->workspace, 'Our Key');
|
|
$this->service->create($otherWorkspace, 'Their Key');
|
|
|
|
$keys = $this->service->getActiveKeysForWorkspace($this->workspace);
|
|
|
|
$this->assertCount(1, $keys);
|
|
$this->assertEquals('Our Key', $keys->first()->name);
|
|
}
|
|
|
|
public function test_get_all_keys_for_workspace_returns_all(): void
|
|
{
|
|
$this->service->create($this->workspace, 'Active Key');
|
|
$revoked = $this->service->create($this->workspace, 'Revoked Key');
|
|
$revoked->revoke();
|
|
$this->service->create(
|
|
$this->workspace,
|
|
'Expired Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->subDay()
|
|
);
|
|
|
|
$keys = $this->service->getAllKeysForWorkspace($this->workspace);
|
|
|
|
$this->assertCount(3, $keys);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Validate With Permission Tests
|
|
// =========================================================================
|
|
|
|
public function test_validate_with_permission_returns_key_when_valid(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$result = $this->service->validateWithPermission($plainKey, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertEquals($key->id, $result->id);
|
|
}
|
|
|
|
public function test_validate_with_permission_returns_null_for_invalid_key(): void
|
|
{
|
|
$result = $this->service->validateWithPermission(
|
|
'ak_invalid_key',
|
|
AgentApiKey::PERM_PLANS_READ
|
|
);
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function test_validate_with_permission_returns_null_without_permission(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_SESSIONS_READ]
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$result = $this->service->validateWithPermission($plainKey, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function test_validate_with_permission_returns_null_when_rate_limited(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ],
|
|
100
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
Cache::put("agent_api_key_rate:{$key->id}", 150, 60);
|
|
|
|
$result = $this->service->validateWithPermission($plainKey, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Full Authentication Flow Tests
|
|
// =========================================================================
|
|
|
|
public function test_authenticate_returns_success_for_valid_key(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$result = $this->service->authenticate($plainKey, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertTrue($result['success']);
|
|
$this->assertInstanceOf(AgentApiKey::class, $result['key']);
|
|
$this->assertEquals($this->workspace->id, $result['workspace_id']);
|
|
$this->assertArrayHasKey('rate_limit', $result);
|
|
}
|
|
|
|
public function test_authenticate_returns_error_for_invalid_key(): void
|
|
{
|
|
$result = $this->service->authenticate(
|
|
'ak_invalid_key',
|
|
AgentApiKey::PERM_PLANS_READ
|
|
);
|
|
|
|
$this->assertFalse($result['success']);
|
|
$this->assertEquals('invalid_key', $result['error']);
|
|
}
|
|
|
|
public function test_authenticate_returns_error_for_revoked_key(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
$key->revoke();
|
|
|
|
$result = $this->service->authenticate($plainKey, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertFalse($result['success']);
|
|
$this->assertEquals('key_revoked', $result['error']);
|
|
}
|
|
|
|
public function test_authenticate_returns_error_for_expired_key(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ],
|
|
100,
|
|
Carbon::now()->subDay()
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$result = $this->service->authenticate($plainKey, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertFalse($result['success']);
|
|
$this->assertEquals('key_expired', $result['error']);
|
|
}
|
|
|
|
public function test_authenticate_returns_error_for_missing_permission(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_SESSIONS_READ]
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$result = $this->service->authenticate($plainKey, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertFalse($result['success']);
|
|
$this->assertEquals('permission_denied', $result['error']);
|
|
}
|
|
|
|
public function test_authenticate_returns_error_when_rate_limited(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ],
|
|
100
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
Cache::put("agent_api_key_rate:{$key->id}", 150, 60);
|
|
|
|
$result = $this->service->authenticate($plainKey, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertFalse($result['success']);
|
|
$this->assertEquals('rate_limited', $result['error']);
|
|
$this->assertArrayHasKey('rate_limit', $result);
|
|
}
|
|
|
|
public function test_authenticate_checks_ip_restrictions(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
$this->service->enableIpRestrictions($key, ['192.168.1.100']);
|
|
|
|
$result = $this->service->authenticate(
|
|
$plainKey,
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
'10.0.0.1'
|
|
);
|
|
|
|
$this->assertFalse($result['success']);
|
|
$this->assertEquals('ip_not_allowed', $result['error']);
|
|
}
|
|
|
|
public function test_authenticate_allows_whitelisted_ip(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
$this->service->enableIpRestrictions($key, ['192.168.1.100']);
|
|
|
|
$result = $this->service->authenticate(
|
|
$plainKey,
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
'192.168.1.100'
|
|
);
|
|
|
|
$this->assertTrue($result['success']);
|
|
$this->assertEquals('192.168.1.100', $result['client_ip']);
|
|
}
|
|
|
|
public function test_authenticate_records_usage_on_success(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
Cache::forget("agent_api_key_rate:{$key->id}");
|
|
|
|
$this->service->authenticate(
|
|
$plainKey,
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
'192.168.1.50'
|
|
);
|
|
|
|
$fresh = $key->fresh();
|
|
$this->assertEquals(1, $fresh->call_count);
|
|
$this->assertNotNull($fresh->last_used_at);
|
|
$this->assertEquals('192.168.1.50', $fresh->last_used_ip);
|
|
}
|
|
|
|
public function test_authenticate_does_not_record_usage_on_failure(): void
|
|
{
|
|
$key = $this->service->create(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[]
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$this->service->authenticate($plainKey, AgentApiKey::PERM_PLANS_READ);
|
|
|
|
$this->assertEquals(0, $key->fresh()->call_count);
|
|
}
|
|
}
|