CluPilotCloud/app/Models/VpnPeer.php

112 lines
3.1 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class VpnPeer extends Model
{
use HasFactory;
use HasUuids;
use SoftDeletes;
/** A tunnel is considered live if the last handshake is recent. WireGuard
* re-handshakes about every two minutes, so three is the first value that
* does not flap for an idle-but-connected peer. */
public const ONLINE_AFTER_MINUTES = 3;
/** A person's access. Requires an owner. */
public const KIND_STAFF = 'staff';
/** Registered by the host pipeline for a Proxmox host. Requires host_id. */
public const KIND_HOST = 'host';
/** Found on the interface without a match — adopted so it stays visible. */
public const KIND_SYSTEM = 'system';
protected $guarded = [];
protected function casts(): array
{
return [
'enabled' => 'boolean',
'present' => 'boolean',
'last_handshake_at' => 'datetime',
'secret_purged_at' => 'datetime',
'last_downloaded_at' => 'datetime',
'observed_at' => 'datetime',
'rx_bytes' => 'integer',
'tx_bytes' => 'integer',
];
}
public function uniqueIds(): array
{
return ['uuid'];
}
public function host(): BelongsTo
{
return $this->belongsTo(Host::class);
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
/** The person this access belongs to (staff peers only). */
public function owner(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function hasStoredConfig(): bool
{
return $this->config_secret !== null;
}
/**
* Overwrite the stored credential. Called on revocation: a tombstone that
* still carries a usable private key is a liability, not a record.
*/
public function purgeSecret(): void
{
if ($this->config_secret === null) {
return;
}
$this->forceFill(['config_secret' => null, 'secret_purged_at' => now()])->saveQuietly();
}
public function isOnline(): bool
{
return $this->enabled
&& $this->present
&& $this->last_handshake_at !== null
&& $this->last_handshake_at->gt(now()->subMinutes(self::ONLINE_AFTER_MINUTES));
}
/**
* blocked — operator revoked it; it must not be on the hub
* pending — desired and observed state disagree; a job is still applying it
* online — configured and handshaking
* idle — configured, but no recent handshake
*/
public function status(): string
{
if (! $this->enabled) {
return $this->present ? 'pending' : 'blocked';
}
if (! $this->present) {
return 'pending';
}
return $this->isOnline() ? 'online' : 'idle';
}
}