CluPilotCloud/app/Models/SecurityBlock.php

83 lines
2.5 KiB
PHP

<?php
namespace App\Models;
use App\Models\Concerns\HasUuid;
use App\Services\Security\HostFirewall;
use Database\Factories\SecurityBlockFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* Eine Sperre gegen eine Adresse — an genau einem Subjekt, einer Instanz oder
* einem Host (nie beiden). Entsteht in `BlockAddress` auch dann, wenn
* `HostFirewall::block()` selbst scheitert: der Datensatz IST die Sperre, die
* Firewall-Menge ist nur ihr aktueller Abdruck (siehe HostFirewall).
*/
class SecurityBlock extends Model
{
/** @use HasFactory<SecurityBlockFactory> */
use HasFactory, HasUuid;
protected $fillable = [
'host_id', 'instance_id', 'ip', 'reason', 'attempts', 'strikes',
'blocked_at', 'expires_at', 'released_at', 'released_by_type', 'released_by_id',
];
protected function casts(): array
{
return [
'attempts' => 'integer',
'strikes' => 'integer',
'blocked_at' => 'datetime',
'expires_at' => 'datetime',
'released_at' => 'datetime',
];
}
public function host(): BelongsTo
{
return $this->belongsTo(Host::class);
}
public function instance(): BelongsTo
{
return $this->belongsTo(Instance::class);
}
/** Wer vorzeitig aufgehoben hat — Operator oder Customer, wenn jemand. */
public function releasedBy(): MorphTo
{
return $this->morphTo();
}
/** Was gerade gilt: noch nicht aufgehoben und noch nicht abgelaufen. */
public function scopeActive(Builder $query): Builder
{
return $query->whereNull('released_at')->where('expires_at', '>', now());
}
/**
* Hebt die Sperre vorzeitig auf — am Datensatz UND, wenn das Subjekt einen
* erreichbaren Host hat, in dessen Firewall. `$by` ist null, wenn der
* Kernel sie ohnehin schon fallen ließ (Ablauf) und niemand sie manuell
* aufgehoben hat; sonst der Operator oder Customer, der den Knopf gedrückt
* hat (R23-Modal auf der aufrufenden Seite, nicht hier).
*/
public function release(?Model $by): void
{
$this->releasedBy()->associate($by);
$this->released_at = now();
$this->save();
$host = $this->host ?? $this->instance?->host;
if ($host !== null) {
app(HostFirewall::class)->release($host, $this->ip);
}
}
}