103 lines
3.4 KiB
PHP
103 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Livewire\Audit\Index;
|
|
use App\Livewire\Settings\Email;
|
|
use App\Models\AuditEvent;
|
|
use App\Models\BannedIp;
|
|
use App\Models\Setting;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Livewire\Livewire;
|
|
use Tests\TestCase;
|
|
|
|
class RbacReviewFixTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_non_admin_cannot_change_audit_retention(): void
|
|
{
|
|
foreach (['operator', 'viewer'] as $role) {
|
|
Livewire::actingAs(User::factory()->{$role}()->create())
|
|
->test(Index::class)
|
|
->set('retentionDays', '1')
|
|
->call('saveRetention')
|
|
->assertForbidden();
|
|
}
|
|
|
|
$this->assertFalse(Setting::where('key', 'audit_retention_days')->exists());
|
|
}
|
|
|
|
public function test_admin_can_change_audit_retention(): void
|
|
{
|
|
Livewire::actingAs(User::factory()->create()) // default factory role = admin
|
|
->test(Index::class)
|
|
->set('retentionDays', '30')
|
|
->call('saveRetention');
|
|
|
|
$this->assertSame('30', Setting::where('key', 'audit_retention_days')->value('value'));
|
|
}
|
|
|
|
public function test_non_admin_cannot_send_test_email(): void
|
|
{
|
|
Mail::fake();
|
|
|
|
// Email::mount() is itself gated, so a non-admin cannot even mount the component; the sendTest
|
|
// guard is the defense-in-depth belt behind it.
|
|
foreach (['operator', 'viewer'] as $role) {
|
|
Livewire::actingAs(User::factory()->{$role}()->create())
|
|
->test(Email::class)
|
|
->assertForbidden();
|
|
}
|
|
}
|
|
|
|
public function test_admin_passes_the_send_test_email_gate(): void
|
|
{
|
|
Mail::fake();
|
|
|
|
// Admin passes the role gate; unconfigured SMTP short-circuits with a notify (proves no 403).
|
|
Livewire::actingAs(User::factory()->create())
|
|
->test(Email::class)
|
|
->call('sendTest')
|
|
->assertDispatched('notify');
|
|
}
|
|
|
|
public function test_honeytoken_trips_on_a_json_body_from_a_non_exempt_ip(): void
|
|
{
|
|
config(['clusev.honeypot.enabled' => true, 'clusev.honeypot.canaries' => ['api_key' => 'C4n4ryJs0nV4lu3XYZ']]);
|
|
|
|
$res = $this->call(
|
|
'POST',
|
|
'/broadcasting/auth',
|
|
[],
|
|
[],
|
|
[],
|
|
['REMOTE_ADDR' => '203.0.113.50', 'CONTENT_TYPE' => 'application/json', 'HTTP_ACCEPT' => 'application/json'],
|
|
(string) json_encode(['api_key' => 'C4n4ryJs0nV4lu3XYZ'])
|
|
);
|
|
|
|
$res->assertStatus(403);
|
|
$this->assertTrue(BannedIp::where('ip', '203.0.113.50')->exists());
|
|
$this->assertTrue(AuditEvent::where('action', 'security.honeytoken_used')->exists());
|
|
}
|
|
|
|
public function test_honeytoken_does_not_trip_without_a_canary(): void
|
|
{
|
|
config(['clusev.honeypot.enabled' => true, 'clusev.honeypot.canaries' => ['api_key' => 'C4n4ryJs0nV4lu3XYZ']]);
|
|
|
|
$this->call(
|
|
'POST',
|
|
'/broadcasting/auth',
|
|
[],
|
|
[],
|
|
[],
|
|
['REMOTE_ADDR' => '203.0.113.51', 'CONTENT_TYPE' => 'application/json', 'HTTP_ACCEPT' => 'application/json'],
|
|
(string) json_encode(['api_key' => 'just-a-normal-value'])
|
|
);
|
|
|
|
$this->assertFalse(BannedIp::where('ip', '203.0.113.51')->exists());
|
|
}
|
|
}
|