diff --git a/app/Http/Controllers/DomainController.php b/app/Http/Controllers/DomainController.php index 3dca424..34d8928 100644 --- a/app/Http/Controllers/DomainController.php +++ b/app/Http/Controllers/DomainController.php @@ -96,6 +96,7 @@ class DomainController extends Controller ->where('node_id',0) ->where('point_id',0) ->orderBy('region_id') + ->active() ->with(['system']) ->get(); diff --git a/app/Http/Controllers/EchoareaController.php b/app/Http/Controllers/EchoareaController.php index b392162..5b9f395 100644 --- a/app/Http/Controllers/EchoareaController.php +++ b/app/Http/Controllers/EchoareaController.php @@ -22,11 +22,16 @@ class EchoareaController extends Controller 'description' => 'required', 'active' => 'required|boolean', 'show' => 'required|boolean', + 'sec_read' => 'required|integer|min:0|max:7', + 'sec_write' => 'required|integer|min:0|max:7', ]); foreach (['name','description','active','show','notes','domain_id'] as $key) $o->{$key} = $request->post($key); + $o->setRead($request->post('sec_read')); + $o->setWrite($request->post('sec_write')); + $o->save(); return redirect()->action([self::class,'home']); diff --git a/app/Http/Controllers/FileareaController.php b/app/Http/Controllers/FileareaController.php index 72079ec..8667a20 100644 --- a/app/Http/Controllers/FileareaController.php +++ b/app/Http/Controllers/FileareaController.php @@ -21,12 +21,17 @@ class FileareaController extends Controller 'description' => 'required', 'active' => 'required|boolean', 'show' => 'required|boolean', - 'nodelist' => 'nullable|string|max:12' + 'nodelist' => 'nullable|string|max:12', + 'sec_read' => 'required|integer|min:0|max:7', + 'sec_write' => 'required|integer|min:0|max:7', ]); foreach (['name','description','active','show','notes','domain_id'] as $key) $o->{$key} = $request->post($key); + $o->setRead($request->post('sec_read')); + $o->setWrite($request->post('sec_write')); + $o->save(); if ($request->post('nodelist')) { diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php index a1ac308..59641be 100644 --- a/app/Http/Controllers/SystemController.php +++ b/app/Http/Controllers/SystemController.php @@ -35,7 +35,7 @@ class SystemController extends Controller session()->flash('accordion','address'); $request->validate([ - 'action' => 'required|in:region,host,node', + 'action' => 'required|in:region,host,node,update', 'zone_id' => 'required|exists:zones,id', ]); @@ -166,6 +166,7 @@ class SystemController extends Controller $o->addresses()->save($oo); break; + case 'update': case 'node': $request->validate([ 'region_id' => ['required',new FidoInteger], @@ -181,7 +182,8 @@ class SystemController extends Controller ->where('zone_id',$request->post('zone_id')) ->where('host_id',$request->post('host_id')) ->where('node_id',$value) - ->where('point_id',0); + ->where('point_id',0) + ->where('id','<>',$request->post('submit')); }); if ($o->count()) { @@ -202,7 +204,8 @@ class SystemController extends Controller ->where('zone_id',$request->post('zone_id')) ->where('host_id',$request->post('host_id')) ->where('node_id',$request->post('node_id')) - ->where('point_id',$value); + ->where('point_id',$value) + ->where('id','<>',$request->post('submit')); }); if ($o->count()) { @@ -212,16 +215,19 @@ class SystemController extends Controller ], 'hub' => 'required|boolean', 'hub_id' => 'nullable|exists:addresses,id', + 'security' => 'required|integer|min:0|max:7', ]); - $oo = new Address; + $oo = Address::findOrNew($request->post('submit')); $oo->zone_id = $request->post('zone_id'); $oo->region_id = $request->post('region_id'); $oo->host_id = $request->post('host_id'); $oo->node_id = $request->post('node_id'); $oo->point_id = $request->post('point_id'); $oo->hub_id = $request->post('hub_id') > 0 ? $request->post('hub_id') : NULL; - $oo->role = ((! $oo->point_id) && $request->post('hub')) ? Address::NODE_HC : ($request->post('point_id') ? Address::NODE_POINT : Address::NODE_ACTIVE); + if (is_null($oo->role)) + $oo->role = ((! $oo->point_id) && $request->post('hub')) ? Address::NODE_HC : ($request->post('point_id') ? Address::NODE_POINT : Address::NODE_ACTIVE); + $oo->security = $request->post('security'); $oo->active = TRUE; $o->addresses()->save($oo); @@ -317,6 +323,11 @@ class SystemController extends Controller ->map(function($item) { return ['id'=>(string)$item->id,'value'=>$item->ftn4d]; }); } + public function api_address_get(Address $o) + { + return $o; + } + /** * Systems with no owners */ diff --git a/app/Jobs/MessageProcess.php b/app/Jobs/MessageProcess.php index 958d89b..9f5e8f1 100644 --- a/app/Jobs/MessageProcess.php +++ b/app/Jobs/MessageProcess.php @@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Notification; use App\Classes\FTN\Message; use App\Models\{Address,Echoarea,Echomail,Netmail,Setup}; -use App\Notifications\Netmails\Reject; +use App\Notifications\Netmails\{EchoareaNotExist,EchoareaNotSubscribed,EchoareaNoWrite,Reject}; class MessageProcess implements ShouldQueue { @@ -195,6 +195,8 @@ class MessageProcess implements ShouldQueue if (! $ea) { Log::alert(sprintf('%s:! Echoarea [%s] doesnt exist for zone [%d]',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss_o->zone->zone_id)); + + Notification::route('netmail',$this->msg->fftn_o)->notify(new EchoareaNotExist($this->msg)); return; } @@ -251,9 +253,17 @@ class MessageProcess implements ShouldQueue } // @todo Can the sender create it if it doesnt exist? - // @todo Can the sender send messages to this area? - // - Create it, or - // - Else record in bad area + + // Can the system send messages to this area? + if (! $ea->sec_write || ($this->msg->fftn_o->security < $ea->sec_write)) { + Notification::route('netmail',$this->msg->fftn_o)->notify(new EchoareaNoWrite($this->msg)); + return; + } + + // If the node is not subsccribed + if ($this->msg->fftn_o->echoareas->search(function($item) use ($ea) { return $item->id === $ea->id; }) === FALSE) { + Notification::route('netmail',$this->msg->fftn_o)->notify(new EchoareaNotSubscribed($this->msg)); + } // We know about this area, store it $o = new Echomail; diff --git a/app/Models/Address.php b/app/Models/Address.php index 3fe281e..6874b7a 100644 --- a/app/Models/Address.php +++ b/app/Models/Address.php @@ -47,6 +47,8 @@ class Address extends Model }); } + protected $visible = ['zone_id','region_id','host_id','node_id','point_id','security']; + /* SCOPES */ public function scopeActiveFTN($query) @@ -308,7 +310,12 @@ class Address extends Model */ public function getActiveAttribute(bool $value): bool { - return $value && $this->zone->active && $this->zone->domain->active; + return $value && $this->getActiveDomainAttribute(); + } + + public function getActiveDomainAttribute(): bool + { + return $this->zone->active && $this->zone->domain->active; } /** diff --git a/app/Models/Echoarea.php b/app/Models/Echoarea.php index 238f630..840f387 100644 --- a/app/Models/Echoarea.php +++ b/app/Models/Echoarea.php @@ -8,11 +8,41 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Rennokki\QueryCache\Traits\QueryCacheable; -use App\Traits\ScopeActive; +use App\Traits\{AreaSecurity,ScopeActive}; +/** + * Echomail echoareas + * + * Security thoughts: + * + ZCs/RCs/NCs/HUBs carry all echos + * + echos for HUBs only (not NODES/POINTs, thus Hub/NC/RC/ZC) + * + echos for NCs only (NC/RC/ZC) + * + echos for RCs only (RC/ZC) + * YYRRRWWW + * + * Thus YY: + * + 0 - not exported + * + 1 - Sent to RCs (RW) + * + 2 - Sent to NCs as well (RW) + * + 3 - Sent to Hubs as well (RW) + * + * Thus RRR: (Read) + * + 0-7 + * = 0 no read access + * = 1-7 minimum access required to perform + * Thus WWW: (Write) + * + 0-7 + * = 0 no write access + * = 1-7 minimum access required to perform + * + * - If a node has 0, or an echoarea has 0, then no access to the function + * - So if node has 1, and echoarea has 2, no access to function + * + * @note change "public" to "bot posts"? + */ class Echoarea extends Model { - use SoftDeletes,ScopeActive,QueryCacheable; + use SoftDeletes,ScopeActive,QueryCacheable,AreaSecurity; private const CACHE_TIME = 3600; diff --git a/app/Models/Echomail.php b/app/Models/Echomail.php index 6200b0c..a6933b8 100644 --- a/app/Models/Echomail.php +++ b/app/Models/Echomail.php @@ -96,18 +96,25 @@ final class Echomail extends Model implements Packet } // See if we need to export this message. - $exportto = $model->echoarea->addresses->pluck('id')->diff($model->set_seenby); + if ($model->echoarea->sec_read) { + $exportto = ($x=$model + ->echoarea + ->addresses + ->filter(function($item) use ($model) { return $item->security >= $model->echoarea->sec_read; })) + ->pluck('id') + ->diff($model->set_seenby); - if ($exportto->count()) { - if ($model->no_export) { - Log::debug(sprintf('%s:- NOT processing exporting of message by configuration [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); - return; + if ($exportto->count()) { + if ($model->no_export) { + Log::debug(sprintf('%s:- NOT processing exporting of message by configuration [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); + return; + } + + Log::debug(sprintf('%s:- Exporting message [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); + + // Save the seenby for the exported systems + $model->seenby()->syncWithPivotValues($exportto,['export_at'=>Carbon::now()],FALSE); } - - Log::debug(sprintf('%s:- Exporting message [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); - - // Save the seenby for the exported systems - $model->seenby()->syncWithPivotValues($exportto,['export_at'=>Carbon::now()],FALSE); } }); } diff --git a/app/Models/File.php b/app/Models/File.php index b6dea5c..5a3942f 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -112,18 +112,25 @@ class File extends Model } // See if we need to export this message. - $exportto = $model->filearea->addresses->pluck('id')->diff($model->set_seenby); + if ($model->filearea->sec_read) { + $exportto = $model + ->filearea + ->addresses + ->filter(function($item) use ($model) { return $item->security >= $model->echoarea->sec_read; }) + ->pluck('id') + ->diff($model->set_seenby); - if ($exportto->count()) { - if ($model->no_export) { - Log::debug(sprintf('%s:- NOT processing exporting of message by configuration [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); - return; + if ($exportto->count()) { + if ($model->no_export) { + Log::debug(sprintf('%s:- NOT processing exporting of message by configuration [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); + return; + } + + Log::debug(sprintf('%s:- Exporting file [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); + + // Save the seenby for the exported systems + $model->seenby()->syncWithPivotValues($exportto,['export_at'=>Carbon::now()],FALSE); } - - Log::debug(sprintf('%s:- Exporting file [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); - - // Save the seenby for the exported systems - $model->seenby()->syncWithPivotValues($exportto,['export_at'=>Carbon::now()],FALSE); } }); } diff --git a/app/Models/Filearea.php b/app/Models/Filearea.php index 1f1edf6..1736a1d 100644 --- a/app/Models/Filearea.php +++ b/app/Models/Filearea.php @@ -5,11 +5,11 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use App\Traits\ScopeActive; +use App\Traits\{AreaSecurity,ScopeActive}; class Filearea extends Model { - use SoftDeletes,ScopeActive; + use SoftDeletes,ScopeActive,AreaSecurity; protected $fillable = [ 'name', diff --git a/app/Notifications/Netmails/EchoareaNoWrite.php b/app/Notifications/Netmails/EchoareaNoWrite.php new file mode 100644 index 0000000..be8ed9c --- /dev/null +++ b/app/Notifications/Netmails/EchoareaNoWrite.php @@ -0,0 +1,74 @@ +mo = $mo; + } + + /** + * Get the mail representation of the notification. + * + * @param System $so + * @param mixed $notifiable + * @return Netmail + * @throws \Exception + */ + public function toNetmail(System $so,object $notifiable): Netmail + { + $o = $this->setupNetmail($so,$notifiable); + $ao = $notifiable->routeNotificationFor(static::via); + + Log::info(sprintf('%s:+ Creating ECHOMAIL NO WRITE netmail to [%s]',self::LOGKEY,$ao->ftn)); + + $o->subject = 'Echomail rejected - '.$this->mo->msgid; + + // Message + $msg = $this->page(FALSE,'nowrite'); + + $msg->addText( + sprintf("Your echomail with ID [%s] to [%s] here was received here on [%s] and it looks like you sent it on [%s].\r\r", + $this->mo->msgid, + $this->mo->user_to, + Carbon::now()->utc()->toDateTimeString(), + $this->mo->date->utc()->toDateTimeString(), + ) + ); + + $msg->addText("It appears that you do not have permission to post in this echoarea, so the message from your system was rejected.\r\r"); + $msg->addText("Please contact the ZC if you think this is a mistake.\r\r"); + + $msg->addText($this->message_path($this->mo)); + + $o->msg = $msg->render(); + $o->tagline = 'See something wrong? Do something right.'; + + $o->save(); + + return $o; + } +} \ No newline at end of file diff --git a/app/Notifications/Netmails/EchoareaNotExist.php b/app/Notifications/Netmails/EchoareaNotExist.php new file mode 100644 index 0000000..26c1053 --- /dev/null +++ b/app/Notifications/Netmails/EchoareaNotExist.php @@ -0,0 +1,74 @@ +mo = $mo; + } + + /** + * Get the mail representation of the notification. + * + * @param System $so + * @param mixed $notifiable + * @return Netmail + * @throws \Exception + */ + public function toNetmail(System $so,object $notifiable): Netmail + { + $o = $this->setupNetmail($so,$notifiable); + $ao = $notifiable->routeNotificationFor(static::via); + + Log::info(sprintf('%s:+ Creating ECHOMAIL NOT EXIST netmail to [%s]',self::LOGKEY,$ao->ftn)); + + $o->subject = 'Echoarea doesnt exist - '.$this->mo->echoarea; + + // Message + $msg = $this->page(FALSE,'nothere'); + + $msg->addText( + sprintf("Your echomail with ID [%s] to [%s] here was received here on [%s] and it looks like you sent it on [%s].\r\r", + $this->mo->msgid, + $this->mo->user_to, + Carbon::now()->utc()->toDateTimeString(), + $this->mo->date->utc()->toDateTimeString(), + ) + ); + + $msg->addText("It appears that the echoarea that this message is for doesnt exist, so the message from your system was rejected.\r\r"); + $msg->addText("Please contact the ZC if you think this is a mistake.\r\r"); + + $msg->addText($this->message_path($this->mo)); + + $o->msg = $msg->render(); + $o->tagline = 'Don\'t let your trash become someone else\'s treasure. Feed your shredder often.'; + + $o->save(); + + return $o; + } +} \ No newline at end of file diff --git a/app/Notifications/Netmails/EchoareaNotSubscribed.php b/app/Notifications/Netmails/EchoareaNotSubscribed.php new file mode 100644 index 0000000..8fc3e19 --- /dev/null +++ b/app/Notifications/Netmails/EchoareaNotSubscribed.php @@ -0,0 +1,75 @@ +mo = $mo; + } + + /** + * Get the mail representation of the notification. + * + * @param System $so + * @param mixed $notifiable + * @return Netmail + * @throws \Exception + */ + public function toNetmail(System $so,object $notifiable): Netmail + { + $o = $this->setupNetmail($so,$notifiable); + $ao = $notifiable->routeNotificationFor(static::via); + + Log::info(sprintf('%s:+ Creating ECHOMAIL NOT SUBSCRIBED netmail to [%s]',self::LOGKEY,$ao->ftn)); + + $o->subject = 'Echoarea not subscribed - '.$this->mo->echoarea; + + // Message + $msg = $this->page(FALSE,'nothere'); + + $msg->addText( + sprintf("Your echomail with ID [%s] to [%s] here was received here on [%s] and it looks like you sent it on [%s].\r\r", + $this->mo->msgid, + $this->mo->user_to, + Carbon::now()->utc()->toDateTimeString(), + $this->mo->date->utc()->toDateTimeString(), + ) + ); + + $msg->addText("It appears that you havent been previously subscribed to this echoarea.\r\r"); + + $msg->addText("Even though your post was accepted, I'm not configured to export any posts to you when others respond, you might like to log in to the web interface and do that.\r\r"); + + $msg->addText($this->message_path($this->mo)); + + $o->msg = $msg->render(); + $o->tagline = 'Don\'t let your trash become someone else\'s treasure. Feed your shredder.'; + + $o->save(); + + return $o; + } +} \ No newline at end of file diff --git a/app/Traits/AreaSecurity.php b/app/Traits/AreaSecurity.php new file mode 100644 index 0000000..dd64e22 --- /dev/null +++ b/app/Traits/AreaSecurity.php @@ -0,0 +1,29 @@ +security>>3) & 0x7; + } + + public function getSecWriteAttribute(): int + { + return $this->security & 0x7; + } + + public function setRead(int $security): void + { + $this->security = ($this->security & ~(0x7<<3)) | (($security & 0x7) << 3); + } + + public function setWrite(int $security): void + { + $this->security = ($this->security & ~(0x7)) | ($security & 0x7); + } +} \ No newline at end of file diff --git a/database/migrations/2023_07_28_071914_echoarea_security.php b/database/migrations/2023_07_28_071914_echoarea_security.php new file mode 100644 index 0000000..56d13c6 --- /dev/null +++ b/database/migrations/2023_07_28_071914_echoarea_security.php @@ -0,0 +1,46 @@ +smallInteger('security')->unsigned()->nullable(); + }); + Schema::table('fileareas',function (Blueprint $table) { + $table->smallInteger('security')->unsigned()->nullable(); + }); + Schema::table('addresses',function (Blueprint $table) { + $table->smallInteger('security')->unsigned()->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::statement('ALTER TABLE echoareas RENAME COLUMN show TO public'); + DB::statement('ALTER TABLE fileareas RENAME COLUMN show TO public'); + + Schema::table('echoareas',function (Blueprint $table) { + $table->dropColumn(['security']); + }); + Schema::table('fileareas',function (Blueprint $table) { + $table->dropColumn(['security']); + }); + Schema::table('addresses',function (Blueprint $table) { + $table->dropColumn(['security']); + }); + } +}; diff --git a/resources/views/echoarea/addedit.blade.php b/resources/views/echoarea/addedit.blade.php index a907524..572562e 100644 --- a/resources/views/echoarea/addedit.blade.php +++ b/resources/views/echoarea/addedit.blade.php @@ -91,6 +91,31 @@ +