diff --git a/resources/views/livewire/servers/show.blade.php b/resources/views/livewire/servers/show.blade.php index 394b400..bce8f19 100644 --- a/resources/views/livewire/servers/show.blade.php +++ b/resources/views/livewire/servers/show.blade.php @@ -231,7 +231,7 @@ - {{-- Firewall-Regeln --}} + {{-- Firewall + fail2ban: only the tools actually installed, side-by-side on lg --}} @php $fw = $firewall ?? []; $fwTool = $fw['tool'] ?? 'ufw'; @@ -245,105 +245,7 @@ $fwRules = $fw['rules'] ?? []; $fwDefaults = $fw['defaults'] ?? []; $actTone = ['ALLOW' => 'online', 'LIMIT' => 'warning', 'DENY' => 'offline', 'REJECT' => 'offline']; - @endphp - - @if ($fwSupported && $fwManageable) - - - {{ __('servers.firewall_add_rule') }} - - - @endif - @if (! $fwSupported) -
-

{{ $fw['reason'] ?? __('servers.firewall_not_supported') }}

-
- @elseif ($fwReadError) -
- -

{{ __('servers.firewall_read_error') }}

-
- @elseif (! $fwInstalled) -
-

{{ __('servers.firewall_tool_not_installed', ['tool' => $fwToolLabel]) }}

- - {{ __('common.enable') }} - -
- @elseif ($fwTool === 'firewalld' && ! $fwActive) -
-

{{ __('servers.firewalld_inactive') }}

- - {{ __('common.enable') }} - -
- @else -
- @if ($fwTool === 'ufw') - {{ __('servers.firewall_default') }} - {{ __('servers.firewall_incoming', ['value' => $fwDefaults['incoming'] ?? '—']) }} - {{ __('servers.firewall_outgoing', ['value' => $fwDefaults['outgoing'] ?? '—']) }} - @else - {{ __('servers.firewall_zone') }} - {{ $fwDefaults['zone'] ?? '—' }} - @endif -
- - @if ($fwReadOnly) -
- -

{{ __('servers.firewall_readonly') }}

-
- @endif - -
- @forelse ($fwRules as $rule) - {{-- $ruleTone, NOT $tone: the page-level $tone is a closure used by the - gauges/Volumes panel; reusing the name here would clobber it. --}} - @php $ruleTone = $actTone[$rule['action'] ?? 'ALLOW'] ?? 'online'; @endphp -
- $ruleTone === 'online', - 'bg-warning' => $ruleTone === 'warning', - 'bg-offline' => $ruleTone === 'offline', - ])> -
-

{{ $rule['label'] ?? ($rule['to'] ?? $rule['raw']) }}

- @if ($fwTool === 'ufw' && ($rule['from'] ?? '') !== '' && ! \Illuminate\Support\Str::contains($rule['from'], 'Anywhere')) -

{{ __('servers.firewall_rule_from', ['value' => $rule['from']]) }}

- @endif -
- $ruleTone === 'online', - 'text-warning' => $ruleTone === 'warning', - 'text-offline' => $ruleTone === 'offline', - ])>{{ strtolower($rule['action'] ?? 'allow') }} - @unless ($fwReadOnly) - - - - @endunless -
- @empty -
-

{{ __('servers.firewall_no_rules') }}

-
- @endforelse -
- @endif -
- - {{-- fail2ban-Status --}} - @php $f2b = $fail2ban ?? []; $f2bSupported = $f2b['supported'] ?? false; $f2bReadError = $f2b['readError'] ?? false; @@ -352,90 +254,188 @@ $f2bJails = $f2b['jails'] ?? []; $f2bIgnore = $f2b['ignoreip'] ?? []; $f2bBanned = array_sum(array_map(fn ($j) => $j['currentlyBanned'] ?? 0, $f2bJails)); - @endphp - - @if ($f2bSupported && $f2bInstalled && $f2bActive) - - - - - - {{ __('servers.fail2ban_ban_ip') }} - - - @endif - @if (! $f2bSupported) -
-

{{ $f2b['reason'] ?? __('servers.fail2ban_not_supported') }}

-
- @elseif ($f2bReadError) -
- -

{{ __('servers.fail2ban_read_error') }}

-
- @elseif (! $f2bInstalled || ! $f2bActive) -
-

{{ $f2bInstalled ? __('servers.fail2ban_state_installed_inactive') : __('servers.fail2ban_state_not_installed') }}

- - {{ __('common.enable') }} - -
- @else -
- @foreach ($f2bJails as $jail) -
-
-

{{ $jail['name'] }}

-

- ($jail['currentlyBanned'] ?? 0) > 0])>{{ __('servers.fail2ban_banned', ['count' => $jail['currentlyBanned'] ?? 0]) }} - · {{ __('servers.fail2ban_failed', ['count' => $jail['currentlyFailed'] ?? 0]) }} -

+ // Show a panel ONLY when its tool is actually installed (active or inactive). + // When absent, the Sicherheit checklist row carries the "Aktivieren" install + // path — so the redundant "nicht installiert" box is gone. + $showFw = $fwSupported && $fwInstalled; + $showF2b = $f2bSupported && $f2bInstalled; + $panelCount = ($showFw ? 1 : 0) + ($showF2b ? 1 : 0); + @endphp + + @if ($panelCount > 0) +
$panelCount === 2])> + @if ($showFw) + {{-- Firewall-Regeln --}} + + @if ($fwManageable) + + + {{ __('servers.firewall_add_rule') }} + + + @endif + + @if ($fwReadError) +
+ +

{{ __('servers.firewall_read_error') }}

- @if (count($jail['bannedIps'] ?? [])) -
- @foreach ($jail['bannedIps'] as $ip) -
- {{ $ip }} - {{ __('common.unlock') }} + @elseif ($fwTool === 'firewalld' && ! $fwActive) +
+

{{ __('servers.firewalld_inactive') }}

+ + {{ __('common.enable') }} + +
+ @else +
+ @if ($fwTool === 'ufw') + {{ __('servers.firewall_default') }} + {{ __('servers.firewall_incoming', ['value' => $fwDefaults['incoming'] ?? '—']) }} + {{ __('servers.firewall_outgoing', ['value' => $fwDefaults['outgoing'] ?? '—']) }} + @else + {{ __('servers.firewall_zone') }} + {{ $fwDefaults['zone'] ?? '—' }} + @endif +
+ + @if ($fwReadOnly) +
+ +

{{ __('servers.firewall_readonly') }}

+
+ @endif + +
+ @forelse ($fwRules as $rule) + {{-- $ruleTone, NOT $tone: the page-level $tone is a closure used by the + gauges/Volumes panel; reusing the name here would clobber it. --}} + @php $ruleTone = $actTone[$rule['action'] ?? 'ALLOW'] ?? 'online'; @endphp +
+ $ruleTone === 'online', + 'bg-warning' => $ruleTone === 'warning', + 'bg-offline' => $ruleTone === 'offline', + ])> +
+

{{ $rule['label'] ?? ($rule['to'] ?? $rule['raw']) }}

+ @if ($fwTool === 'ufw' && ($rule['from'] ?? '') !== '' && ! \Illuminate\Support\Str::contains($rule['from'], 'Anywhere')) +

{{ __('servers.firewall_rule_from', ['value' => $rule['from']]) }}

+ @endif
+ $ruleTone === 'online', + 'text-warning' => $ruleTone === 'warning', + 'text-offline' => $ruleTone === 'offline', + ])>{{ strtolower($rule['action'] ?? 'allow') }} + @unless ($fwReadOnly) + + + + @endunless +
+ @empty +
+

{{ __('servers.firewall_no_rules') }}

+
+ @endforelse +
+ @endif + + @endif + + @if ($showF2b) + {{-- fail2ban-Status --}} + + @if ($f2bActive) + + + + + + {{ __('servers.fail2ban_ban_ip') }} + + + @endif + + @if ($f2bReadError) +
+ +

{{ __('servers.fail2ban_read_error') }}

+
+ @elseif (! $f2bActive) +
+

{{ __('servers.fail2ban_state_installed_inactive') }}

+ + {{ __('common.enable') }} + +
+ @else +
+ @foreach ($f2bJails as $jail) +
+
+

{{ $jail['name'] }}

+

+ ($jail['currentlyBanned'] ?? 0) > 0])>{{ __('servers.fail2ban_banned', ['count' => $jail['currentlyBanned'] ?? 0]) }} + · {{ __('servers.fail2ban_failed', ['count' => $jail['currentlyFailed'] ?? 0]) }} +

+
+ @if (count($jail['bannedIps'] ?? [])) +
+ @foreach ($jail['bannedIps'] as $ip) +
+ {{ $ip }} + {{ __('common.unlock') }} +
+ @endforeach +
+ @else +

{{ __('servers.fail2ban_no_banned') }}

+ @endif +
+ @endforeach +
+ + {{-- Whitelist (ignoreip) --}} +
+

{{ __('servers.whitelist_label') }}

+
+ @foreach ($f2bIgnore as $ip) + + {{ $ip }} + @unless (in_array($ip, ['127.0.0.1/8', '::1'], true)) + + @endunless + @endforeach
- @else -

{{ __('servers.fail2ban_no_banned') }}

- @endif -
- @endforeach -
- - {{-- Whitelist (ignoreip) --}} -
-

{{ __('servers.whitelist_label') }}

-
- @foreach ($f2bIgnore as $ip) - - {{ $ip }} - @unless (in_array($ip, ['127.0.0.1/8', '::1'], true)) - - @endunless - - @endforeach -
-
- - {{ __('common.add') }} -
-
- @endif - +
+ + {{ __('common.add') }} +
+
+ @endif +
+ @endif +
+ @endif {{-- Volumes + Netzwerk-Interfaces --}}
diff --git a/tests/Feature/ServerShowPanelsTest.php b/tests/Feature/ServerShowPanelsTest.php new file mode 100644 index 0000000..e0af616 --- /dev/null +++ b/tests/Feature/ServerShowPanelsTest.php @@ -0,0 +1,44 @@ + 's', 'ip' => '10.0.0.9', 'ssh_port' => 22, 'status' => 'online']); + } + + public function test_firewall_and_fail2ban_panels_hidden_when_not_installed(): void + { + $this->actingAs(User::factory()->create()); + + Livewire::test(Show::class, ['server' => $this->server()]) + ->set('ready', true) + ->set('firewall', ['supported' => true, 'installed' => false]) + ->set('fail2ban', ['supported' => true, 'installed' => false]) + ->assertDontSee(__('servers.firewall_title')) + ->assertDontSee(__('servers.fail2ban_title')); + } + + public function test_both_panels_shown_when_installed(): void + { + $this->actingAs(User::factory()->create()); + + Livewire::test(Show::class, ['server' => $this->server()]) + ->set('ready', true) + ->set('firewall', ['supported' => true, 'installed' => true, 'active' => true, 'tool' => 'ufw', 'manageable' => true, 'rules' => [], 'defaults' => []]) + ->set('fail2ban', ['supported' => true, 'installed' => true, 'active' => true, 'jails' => [], 'ignoreip' => []]) + ->assertSee(__('servers.firewall_title')) + ->assertSee(__('servers.fail2ban_title')); + } +}