create() verknuepft // keinen User (user_id bleibt NULL, siehe CustomerFactory) — jeder bestehende // Test, der einen Kunden anmeldet, ruft dafuer ensureUser() (Muster: // tests/Feature/CustomerTwoFactorTest.php::portalUser()). Der RUECKGABEWERT // wird gebraucht, nicht $customer->user danach: ensureUser() selbst liest // $this->user als ersten Schritt ("if ($this->user) return $this->user;"), // bevor user_id gesetzt ist — das cacht die Beziehung als null, und ein // spaeteres update() raeumt einen bereits geladenen Beziehungs-Cache nicht // weg. $customer->user bliebe also null, obwohl user_id laengst gesetzt ist. it('zeigt dem Inhaber die Sperren seiner eigenen Instanz', function () { $customer = Customer::factory()->create(); $user = $customer->ensureUser(); $instance = Instance::factory()->for($customer)->create(); $block = SecurityBlock::factory()->for($instance)->create(['ip' => '203.0.113.7']); Livewire::actingAs($user)->test(Security::class)->assertSee('203.0.113.7'); }); it('zeigt einem Inhaber die Sperren eines FREMDEN Kunden nicht', function () { $meine = Customer::factory()->create(); $user = $meine->ensureUser(); $fremde = Instance::factory()->create(); SecurityBlock::factory()->for($fremde)->create(['ip' => '198.51.100.9']); Livewire::actingAs($user)->test(Security::class)->assertDontSee('198.51.100.9'); }); it('laesst einen Inhaber eine fremde Sperre nicht aufheben', function () { $meine = Customer::factory()->create(); $user = $meine->ensureUser(); $fremd = SecurityBlock::factory()->for(Instance::factory()->create())->create(); Livewire::actingAs($user)->test(Security::class) ->call('onReleaseConfirmed', $fremd->uuid) ->assertForbidden(); expect($fremd->fresh()->released_at)->toBeNull(); }); // Der Weg, den ein Mensch nimmt: das Modal mutiert nichts, es schickt nur // 'block-release-confirmed' (R23). Die bestehenden Pruefungen rufen // onReleaseConfirmed() direkt auf — damit steht das Ereignis selbst nie auf // dem Pruefstand, und die Zeile danach auch nicht. Genau das war der Verdacht: // dass die Zeile nach dem Aufheben weiter auf "Aktiv" steht, bis jemand neu // laedt. Diese Pruefung nimmt den ganzen Weg. it('raeumt die Zeile nach dem Aufheben aus der aktiven Liste in den Verlauf', function () { $customer = Customer::factory()->create(); $user = $customer->ensureUser(); $instance = Instance::factory()->for($customer)->create(); $block = SecurityBlock::factory()->for($instance)->create(['ip' => '203.0.113.7']); $seite = Livewire::actingAs($user)->test(Security::class) ->assertSee('active-'.$block->uuid, escape: false) ->assertDontSee('history-'.$block->uuid, escape: false); $seite->dispatch('block-release-confirmed', uuid: $block->uuid) ->assertDontSee('active-'.$block->uuid, escape: false) ->assertSee('history-'.$block->uuid, escape: false) ->assertSee(__('security.status_released')); }); it('sagt es, wenn die Firewall beim Aufheben nicht mitkam', function () { // HostFirewall schluckt jeden Fehlschlag und meldet ihn nur ins Log. Ohne // diesen Unterschied faende der Kunde „Sperre aufgehoben." vor, versuchte // sich gleich wieder anzumelden — und wuerde wortlos erneut abgewiesen, // ohne dass ihm irgendjemand sagen kann, warum. $shell = new \App\Services\Ssh\FakeRemoteShell; $shell->failConnect = true; app()->instance(\App\Services\Ssh\RemoteShell::class, $shell); $customer = Customer::factory()->create(); $user = $customer->ensureUser(); $host = \App\Models\Host::factory()->active()->create(['ssh_host_key' => 'SHA256:abc']); $instance = Instance::factory()->for($customer)->create(['host_id' => $host->id]); $block = SecurityBlock::factory()->for($instance)->create(); Livewire::actingAs($user)->test(Security::class) ->call('onReleaseConfirmed', $block->uuid) ->assertDispatched('notify', message: __('security.blocks_released_pending')); // Der Datensatz steht trotzdem — er IST die Sperre, die Firewall-Menge nur // ihr Abdruck. ScanForIntrusions traegt das Loeschen nach. expect($block->fresh()->released_at)->not->toBeNull(); }); it('hebt eine eigene Sperre auf und traegt ein, wer es war', function () { $customer = Customer::factory()->create(); $user = $customer->ensureUser(); $instance = Instance::factory()->for($customer)->create(); $block = SecurityBlock::factory()->for($instance)->create(); Livewire::actingAs($user)->test(Security::class) ->call('onReleaseConfirmed', $block->uuid); expect($block->fresh()->released_at)->not->toBeNull() ->and($block->fresh()->released_by_id)->toBe($user->id); });