2021-06-17 14:08:30 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
2024-09-15 12:00:40 +00:00
|
|
|
use Carbon\Carbon;
|
2021-06-17 14:08:30 +00:00
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
2022-01-01 14:52:21 +00:00
|
|
|
use Illuminate\Support\Collection;
|
|
|
|
use Illuminate\Support\Facades\Auth;
|
2021-06-17 14:08:30 +00:00
|
|
|
|
2024-04-21 10:40:19 +00:00
|
|
|
use App\Classes\FTN\Packet;
|
2023-07-26 09:44:07 +00:00
|
|
|
use App\Jobs\AddressPoll;
|
|
|
|
|
2021-06-17 14:08:30 +00:00
|
|
|
class System extends Model
|
|
|
|
{
|
2022-01-05 13:19:57 +00:00
|
|
|
use HasFactory;
|
2021-06-17 14:08:30 +00:00
|
|
|
|
2023-09-15 12:57:32 +00:00
|
|
|
public const default = 'Discovered System';
|
|
|
|
|
2023-06-26 00:32:38 +00:00
|
|
|
protected $casts = [
|
|
|
|
'last_session' => 'datetime:Y-m-d H:i:s'
|
|
|
|
];
|
2021-07-04 13:24:38 +00:00
|
|
|
|
2023-09-15 12:57:32 +00:00
|
|
|
/* STATIC */
|
|
|
|
|
|
|
|
public static function createUnknownSystem(): self
|
|
|
|
{
|
|
|
|
self::unguard();
|
|
|
|
$so = self::firstOrCreate([
|
|
|
|
'name' => self::default,
|
|
|
|
'sysop' => 'Unknown',
|
2023-09-19 12:16:25 +00:00
|
|
|
'location' => 'Unknown',
|
2023-09-15 12:57:32 +00:00
|
|
|
'active' => TRUE,
|
|
|
|
]);
|
|
|
|
self::reguard();
|
|
|
|
|
|
|
|
return $so;
|
|
|
|
}
|
|
|
|
|
2022-01-01 14:52:21 +00:00
|
|
|
/* SCOPES */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Only query active records
|
2023-04-22 11:31:09 +00:00
|
|
|
* @todo test false action
|
2022-01-01 14:52:21 +00:00
|
|
|
*/
|
|
|
|
public function scopeActive($query)
|
|
|
|
{
|
|
|
|
$uo = Auth::user();
|
|
|
|
|
|
|
|
return $query
|
2024-11-03 22:05:27 +00:00
|
|
|
->when(
|
|
|
|
$uo && ! $uo->isAdmin(),
|
|
|
|
fn($query)=>
|
|
|
|
$query
|
|
|
|
->whereIn('id',$uo->systems->pluck('id'))
|
|
|
|
->orWhere($this->getTable().'.active',TRUE),
|
|
|
|
fn($query)=>$query->where($this->getTable().'.active',TRUE))
|
2022-01-01 14:52:21 +00:00
|
|
|
->orderBy('name');
|
|
|
|
}
|
|
|
|
|
2021-06-17 14:08:30 +00:00
|
|
|
/* RELATIONS */
|
|
|
|
|
2024-11-03 22:05:27 +00:00
|
|
|
/**
|
|
|
|
* All addresses assigned to a system, including addresses pending deletion
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2021-06-20 13:03:20 +00:00
|
|
|
public function addresses()
|
|
|
|
{
|
|
|
|
return $this->hasMany(Address::class)
|
2024-11-03 22:05:27 +00:00
|
|
|
->FTN()
|
|
|
|
->withTrashed();
|
2021-06-20 13:03:20 +00:00
|
|
|
}
|
2021-06-24 13:09:09 +00:00
|
|
|
|
2024-11-03 22:05:27 +00:00
|
|
|
/**
|
|
|
|
* System addresses that are active
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2023-06-23 11:28:29 +00:00
|
|
|
public function akas()
|
|
|
|
{
|
|
|
|
return $this->hasMany(Address::class)
|
2024-11-03 22:05:27 +00:00
|
|
|
->FTN()
|
|
|
|
->ActiveFTN();
|
2023-06-23 11:28:29 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 13:59:04 +00:00
|
|
|
public function mailers()
|
|
|
|
{
|
|
|
|
return $this->belongsToMany(Mailer::class)
|
|
|
|
->withPivot(['last_poll','port','attempts','active']);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function mailer_preferred()
|
|
|
|
{
|
|
|
|
return $this->mailers()
|
|
|
|
->preferred();
|
|
|
|
}
|
|
|
|
|
2022-12-02 11:54:02 +00:00
|
|
|
public function logs()
|
|
|
|
{
|
|
|
|
return $this->hasMany(SystemLog::class);
|
|
|
|
}
|
|
|
|
|
2023-04-15 03:34:08 +00:00
|
|
|
public function logs_recent()
|
|
|
|
{
|
|
|
|
return $this->hasMany(SystemLog::class)
|
|
|
|
->orderby('created_at','DESC')
|
|
|
|
->limit(10);
|
|
|
|
}
|
|
|
|
|
2021-07-04 11:47:23 +00:00
|
|
|
/**
|
|
|
|
* Session Passwords for system
|
|
|
|
*
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
|
|
*/
|
|
|
|
public function sessions()
|
|
|
|
{
|
|
|
|
return $this->belongsToMany(Zone::class)
|
2024-11-04 13:28:34 +00:00
|
|
|
->select(['zones.id','zones.zone_id','domain_id','zones.active'])
|
|
|
|
->join('domains',['domains.id'=>'zones.domain_id'])
|
2024-05-17 22:27:17 +00:00
|
|
|
->withPivot(['sespass','pktpass','ticpass','fixpass','zt_ipv4','zt_ipv6','default'])
|
2024-12-06 03:44:19 +00:00
|
|
|
->orderBy('domains.name')
|
|
|
|
->dontCache();
|
2021-07-04 11:47:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If this system is configured as this host
|
|
|
|
*
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
|
|
|
*/
|
|
|
|
public function setup()
|
|
|
|
{
|
|
|
|
return $this->hasOne(Setup::class);
|
|
|
|
}
|
|
|
|
|
2021-08-21 11:15:22 +00:00
|
|
|
public function users()
|
|
|
|
{
|
|
|
|
return $this->belongsToMany(User::class);
|
|
|
|
}
|
|
|
|
|
2021-08-15 01:42:38 +00:00
|
|
|
/**
|
|
|
|
* This system is the ZC for the following zones
|
|
|
|
*/
|
|
|
|
public function zcs()
|
|
|
|
{
|
2024-11-03 22:05:27 +00:00
|
|
|
return $this->hasMany(Zone::class)
|
|
|
|
->select(['zones.id','zone_id','domain_id','system_id','zones.active'])
|
|
|
|
->join('domains',['domains.id'=>'zones.domain_id'])
|
|
|
|
->orderBy('domains.name')
|
|
|
|
->orderBy('zone_id')
|
|
|
|
->with([
|
|
|
|
'domain:id,name,active',
|
|
|
|
]);
|
2021-08-15 01:42:38 +00:00
|
|
|
}
|
|
|
|
|
2021-07-04 11:47:23 +00:00
|
|
|
/**
|
|
|
|
* Zones a system has addresses for
|
|
|
|
*
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
|
|
|
|
*/
|
|
|
|
public function zones()
|
|
|
|
{
|
|
|
|
return $this->hasManyThrough(Zone::class,Address::class,'system_id','id','id','zone_id');
|
|
|
|
}
|
|
|
|
|
2022-11-25 10:44:03 +00:00
|
|
|
/* ATTRIBUTES */
|
|
|
|
|
|
|
|
public function getAccessMethodAttribute(): string
|
|
|
|
{
|
|
|
|
switch ($this->method) {
|
|
|
|
case 23: return sprintf('telnet://%s:%s',$this->address,$this->port);
|
|
|
|
case 22: return sprintf('ssh://%s:%s',$this->address,$this->port);
|
|
|
|
case 513: return sprintf('rlogin://%s:%s',$this->address,$this->port);
|
|
|
|
default:
|
|
|
|
return $this->method ? sprintf('%s:%s',$this->address,$this->port) : 'No access method available.';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAccessMailerAttribute(): string
|
|
|
|
{
|
2023-07-07 13:59:04 +00:00
|
|
|
switch (($x=$this->mailer_preferred()->first())?->name) {
|
|
|
|
case 'BINKP': return sprintf('binkp://%s:%s',$this->address,$x->pivot->port);
|
|
|
|
case 'EMSI': return sprintf('emsi://%s:%s',$this->address,$x->pivot->port);
|
2022-11-25 10:44:03 +00:00
|
|
|
default:
|
2023-07-07 13:59:04 +00:00
|
|
|
return $x?->name ? sprintf('%s:%s',$this->address,$x->pivot->port) : 'No mailer available.';
|
2022-11-25 10:44:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-26 11:07:07 +00:00
|
|
|
public function getBatchFilesAttribute(?int $val): int
|
2024-06-15 04:23:05 +00:00
|
|
|
{
|
2024-11-26 11:07:07 +00:00
|
|
|
return $val ?: Setup::findOrFail(config('app.id'))->batch_files;
|
2024-06-15 04:23:05 +00:00
|
|
|
}
|
|
|
|
|
2024-11-26 11:07:07 +00:00
|
|
|
public function getIsOwnedAttribute(): bool
|
2024-06-01 06:47:13 +00:00
|
|
|
{
|
2024-11-26 11:07:07 +00:00
|
|
|
return $this->users->count();
|
2024-06-01 06:47:13 +00:00
|
|
|
}
|
|
|
|
|
2024-09-15 12:00:40 +00:00
|
|
|
public function getLastSeenAttribute(): ?Carbon
|
|
|
|
{
|
|
|
|
return $this->logs_recent->first()?->created_at;
|
|
|
|
}
|
|
|
|
|
2024-11-26 11:07:07 +00:00
|
|
|
public function getPktMsgsAttribute(?int $val): int
|
|
|
|
{
|
|
|
|
return $val ?: Setup::findOrFail(config('app.id'))->msgs_pkt;
|
|
|
|
}
|
|
|
|
|
2021-07-15 14:54:23 +00:00
|
|
|
/* METHODS */
|
|
|
|
|
2024-11-04 04:58:20 +00:00
|
|
|
public function addresses_common(): Collection
|
|
|
|
{
|
|
|
|
$our = our_address()->pluck('zone.domain_id')->unique();
|
|
|
|
|
|
|
|
// Return our akas, filter with our_addresses()
|
|
|
|
return $this->addresses->filter(fn($item)=>$our->contains($item->zone->domain_id));
|
|
|
|
}
|
|
|
|
|
2024-11-03 22:05:27 +00:00
|
|
|
/**
|
|
|
|
* Return the ACTIVE addresses that are common with our addresses
|
|
|
|
*
|
|
|
|
* @return Collection
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
|
|
|
public function aka_common(): Collection
|
|
|
|
{
|
|
|
|
$our = our_address()->pluck('zone.domain_id')->unique();
|
|
|
|
|
|
|
|
// Return our akas, filter with our_addresses()
|
|
|
|
return $this->akas->filter(fn($item)=>$our->contains($item->zone->domain_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the ACTIVE addresses that we auth with
|
|
|
|
*
|
|
|
|
* @return Collection
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
|
|
|
public function aka_known(): Collection
|
|
|
|
{
|
|
|
|
return $this->aka_common()
|
|
|
|
->filter(fn($item)=>$this->sessions->contains($item->zone_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the ACTIVE addresses in the same networks as us, but dont auth here
|
|
|
|
*
|
|
|
|
* @return Collection
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
|
|
|
public function aka_unknown(): Collection
|
|
|
|
{
|
|
|
|
return $this->aka_common()
|
|
|
|
->filter(fn($item)=>! $this->sessions->contains($item->zone_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the AKAs that are in networks not common with us
|
|
|
|
*
|
|
|
|
* @return Collection
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
|
|
|
public function aka_uncommon(): Collection
|
|
|
|
{
|
|
|
|
$our = our_address()->pluck('zone.domain_id')->unique();
|
|
|
|
|
|
|
|
// Return our akas, filter with our_addresses()
|
|
|
|
return $this->akas->filter(fn($item)=>! $our->contains($item->zone->domain_id));
|
|
|
|
}
|
|
|
|
|
2021-08-25 12:13:49 +00:00
|
|
|
public function echoareas()
|
|
|
|
{
|
|
|
|
return Echoarea::select('echoareas.*')
|
|
|
|
->join('address_echoarea',['address_echoarea.echoarea_id'=>'echoareas.id'])
|
|
|
|
->join('addresses',['addresses.id'=>'address_echoarea.address_id'])
|
|
|
|
->where('addresses.system_id',$this->id);
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:24:36 +00:00
|
|
|
public function fileareas()
|
|
|
|
{
|
|
|
|
return Filearea::select('fileareas.*')
|
|
|
|
->join('address_filearea',['address_filearea.filearea_id'=>'fileareas.id'])
|
|
|
|
->join('addresses',['addresses.id'=>'address_filearea.address_id'])
|
|
|
|
->where('addresses.system_id',$this->id);
|
|
|
|
}
|
|
|
|
|
2021-06-24 13:09:09 +00:00
|
|
|
/**
|
|
|
|
* Return the system name, or role name for the zone
|
|
|
|
*
|
|
|
|
* @param Address $o
|
|
|
|
* @return string
|
|
|
|
*/
|
2021-06-25 11:31:57 +00:00
|
|
|
public function full_name(Address $o): string
|
2021-06-24 13:09:09 +00:00
|
|
|
{
|
2024-05-09 11:22:30 +00:00
|
|
|
switch ($o->role_id) {
|
2022-01-24 11:56:13 +00:00
|
|
|
case Address::NODE_ZC;
|
2021-06-24 13:09:09 +00:00
|
|
|
return sprintf('ZC-%s-%05d',$o->zone->domain->name,$o->zone->zone_id);
|
|
|
|
|
2022-01-24 11:56:13 +00:00
|
|
|
case Address::NODE_RC;
|
2021-06-24 13:09:09 +00:00
|
|
|
return sprintf('RC-%s-%05d',$o->zone->domain->name,$o->region_id);
|
|
|
|
|
2022-01-24 11:56:13 +00:00
|
|
|
case Address::NODE_NC;
|
2021-07-26 11:21:58 +00:00
|
|
|
return sprintf('NC-%s-%05d',$o->zone->domain->name,$o->host_id);
|
|
|
|
|
2022-01-24 11:56:13 +00:00
|
|
|
case Address::NODE_HC;
|
2024-05-09 11:22:30 +00:00
|
|
|
case Address::NODE_NN;
|
2021-06-24 13:09:09 +00:00
|
|
|
default:
|
|
|
|
return $this->name;
|
|
|
|
}
|
|
|
|
}
|
2021-09-06 13:39:32 +00:00
|
|
|
|
2024-11-04 13:28:34 +00:00
|
|
|
public function inDomain(Domain $o): bool
|
|
|
|
{
|
|
|
|
return $this->addresses
|
|
|
|
->filter(fn($item)=>$item->zone->domain_id === $o->id)
|
|
|
|
->count() > 0;
|
|
|
|
}
|
|
|
|
|
2021-09-06 13:39:32 +00:00
|
|
|
/**
|
|
|
|
* Return the system's address in the same zone
|
2022-01-24 11:56:13 +00:00
|
|
|
* This function can filter based on the address type needed.
|
2021-09-06 13:39:32 +00:00
|
|
|
*
|
|
|
|
* @param Zone $o
|
2022-01-24 11:56:13 +00:00
|
|
|
* @param int $type
|
2021-09-06 13:39:32 +00:00
|
|
|
* @return Collection
|
|
|
|
*/
|
2024-05-09 11:22:30 +00:00
|
|
|
public function match(Zone $o,int $type=(Address::NODE_NC|Address::NODE_HC|Address::NODE_NN|Address::NODE_POINT)): Collection
|
2021-09-06 13:39:32 +00:00
|
|
|
{
|
2023-11-22 02:41:37 +00:00
|
|
|
$akas = $this->akas
|
2023-07-30 02:11:08 +00:00
|
|
|
->where(function($item) use($o) {
|
|
|
|
return ($item->zone_id === $o->id) || ($item->zone->domain_id === $o->domain_id);
|
2022-01-24 11:56:13 +00:00
|
|
|
});
|
2023-11-22 02:41:37 +00:00
|
|
|
|
|
|
|
return ($akas->count() > 1)
|
2024-05-09 11:22:30 +00:00
|
|
|
? $akas->filter(function($item) use ($type) { return $item->role_id & $type;})
|
2023-11-22 02:41:37 +00:00
|
|
|
: $akas;
|
2021-09-06 13:39:32 +00:00
|
|
|
}
|
2022-03-14 11:28:54 +00:00
|
|
|
|
2024-04-21 10:40:19 +00:00
|
|
|
/**
|
|
|
|
* Return the packet that this system uses
|
|
|
|
*
|
2024-05-17 12:10:54 +00:00
|
|
|
* @param Address $ao
|
2024-05-19 13:28:45 +00:00
|
|
|
* @param string|null $password
|
2024-04-21 10:40:19 +00:00
|
|
|
* @return Packet
|
2024-11-03 22:05:27 +00:00
|
|
|
* @throws \Exception
|
2024-04-21 10:40:19 +00:00
|
|
|
*/
|
2024-05-19 13:28:45 +00:00
|
|
|
public function packet(Address $ao,string $password=NULL): Packet
|
2024-04-21 10:40:19 +00:00
|
|
|
{
|
2024-06-28 13:27:06 +00:00
|
|
|
if ($ao->system_id !== $this->id)
|
|
|
|
throw new \Exception('Packet for [%s] is not for system [%d]',$ao->ftn,$this->id);
|
2024-05-17 12:10:54 +00:00
|
|
|
|
2024-05-19 13:28:45 +00:00
|
|
|
return
|
|
|
|
(new (collect(Packet::PACKET_TYPES)
|
|
|
|
->get($this->pkt_type ?: config('fido.packet_default'))))
|
|
|
|
->for($ao)
|
|
|
|
->password($password);
|
2024-04-21 10:40:19 +00:00
|
|
|
}
|
|
|
|
|
2023-07-26 09:44:07 +00:00
|
|
|
public function poll(): ?Job
|
|
|
|
{
|
|
|
|
return Job::where('queue',AddressPoll::QUEUE)
|
|
|
|
->get()
|
2024-11-03 22:05:27 +00:00
|
|
|
->where(fn($item)=>$this->akas->pluck('id')->contains($item->command->address->id))
|
2023-07-26 09:44:07 +00:00
|
|
|
->last();
|
|
|
|
}
|
2021-06-17 14:08:30 +00:00
|
|
|
}
|