Work on registration of existing systems to users

This commit is contained in:
Deon George
2022-03-14 22:28:54 +11:00
parent d68307461e
commit 8072f7c5a9
19 changed files with 553 additions and 56 deletions

View File

@@ -36,6 +36,7 @@ class Page
$this->logo = new ANSI;
$this->left_box = new Font;
$this->crlf = $crlf;
$this->text = '';
}
public function __get($key)
@@ -114,7 +115,8 @@ class Page
*/
public function addText(string $text,bool $right=FALSE)
{
$this->text = $text;
$this->text .= $text;
$this->text_right = $right;
}

View File

@@ -7,11 +7,13 @@ use Illuminate\Support\Collection;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\ViewErrorBag;
use App\Http\Requests\SystemRegister;
use App\Models\{Address,Echoarea,System,SystemZone,Zone};
use App\Models\{Address,Echoarea,Setup,System,SystemZone,Zone};
use App\Notifications\AddressLink;
use App\Rules\{FidoInteger,TwoByteInteger};
class SystemController extends Controller
@@ -258,6 +260,8 @@ class SystemController extends Controller
*/
public function add_edit(SystemRegister $request,System $o)
{
$this->authorize('update',$o);
if ($request->post()) {
foreach (['name','location','sysop','hold','phone','address','port','active','method','notes','mailer_type','mailer_address','mailer_port','zt_id'] as $key)
$o->{$key} = $request->post($key);
@@ -269,11 +273,9 @@ class SystemController extends Controller
$o->load(['addresses.zone.domain']);
return Gate::check('update',$o)
? view('system.addedit')
return view('system.addedit')
->with('action',$o->exists ? 'update' : 'create')
->with('o',$o)
: redirect()->to('user/system/register');
->with('o',$o);
}
public function api_address(Request $request,System $o): Collection
@@ -300,6 +302,42 @@ class SystemController extends Controller
->get();
}
/**
* Identify all the addresses from systems that are not owned by a user
*
* @param Request $request
* @return Collection
*/
public function api_orphan_address(Request $request): Collection
{
$result = collect();
list($zone_id,$host_id,$node_id,$point_id,$domain) = sscanf($request->query('term'),'%d:%d/%d.%d@%s');
# Look for Systems
foreach (Address::select(['addresses.id','systems.name',DB::raw('systems.id AS system_id'),'zones.zone_id','region_id','host_id','node_id','point_id','addresses.zone_id'])
->join('zones',['zones.id'=>'addresses.zone_id'])
->rightjoin('systems',['systems.id'=>'addresses.system_id'])
->when($zone_id || $host_id || $node_id,function($query) use ($zone_id,$host_id,$node_id) {
return $query
->when($zone_id,function($q,$zone_id) { return $q->where('zones.zone_id',$zone_id); })
->where(function($q) use ($host_id) {
return $q
->when($host_id,function($q,$host_id) { return $q->where('region_id',$host_id); })
->when($host_id,function($q,$host_id) { return $q->orWhere('host_id',$host_id); });
})
->when($node_id,function($q,$node_id) { return $q->where('node_id',$node_id); });
})
->orWhere('systems.name','ilike','%'.$request->query('term').'%')
->orderBy('systems.name')
->get() as $o)
{
$result->push(['id'=>$o->id,'name'=>sprintf('%s (%s)',$o->ftn3d,$o->name),'category'=>'Systems']);
}
return $result;
}
/**
* Delete address assigned to a host
*
@@ -450,8 +488,26 @@ class SystemController extends Controller
*/
public function system_register(SystemRegister $request)
{
// Step 1, show the user a form to select an existing defined system
if ($request->isMethod('GET'))
return view('user.system.register');
$o = System::findOrNew($request->system_id);
// If the system exists, and we are 'register', we'll start the address claim process
if ($o->exists && $request->action == 'register') {
$validate = Setup::findOrFail(config('app.id'))->system->inMyZones($o->addresses);
// If we have addresses, we'll trigger the routed netmail
if ($validate->count())
Notification::route('netmail',$x=$validate->first())->notify(new AddressLink($x,Auth::user()));
return view('user.system.widget.register_confirm')
->with('validate',$validate)
->with('o',$o);
}
// If the system doesnt exist, we'll create it
if (! $o->exist) {
$o->sysop = Auth::user()->name;
@@ -479,4 +535,4 @@ class SystemController extends Controller
->with('o',$o)
->with('errors',new ViewErrorBag);
}
}
}

View File

@@ -5,8 +5,9 @@ namespace App\Http\Controllers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use App\Models\User;
use App\Models\{Address,User};
class UserController extends Controller
{
@@ -63,6 +64,36 @@ class UserController extends Controller
return view('user.home');
}
public function link(Request $request)
{
if ($request->post()) {
$request->validate([
'address_id'=>'required|exists:addresses,id',
'code'=>'required:string',
]);
$ao = Address::findOrFail($request->address_id);
if ($ao->check_activation(Auth::user(),$request->code)) {
$ao->validated = TRUE;
$ao->save();
$ao->system->users()->save(Auth::user());
return redirect()->to('/');
} else {
$validator = Validator::make([],[]);
$validator->errors()->add(
'code', 'Invalid Code!'
);
return back()->withErrors($validator);
}
}
return view('user.link');
}
public function register()
{
return view('user/system/register');

View File

@@ -21,7 +21,7 @@ class SystemRegister extends FormRequest
{
$this->so = System::findOrNew($request->system_id);
return Gate::allows($this->so->exists ? 'update' : 'create',$this->so);
return Gate::allows($this->so->users->count() ? 'update' : 'register',$this->so);
}
/**
@@ -31,7 +31,7 @@ class SystemRegister extends FormRequest
*/
public function rules(Request $request)
{
if (! $request->isMethod('post'))
if ((! $request->isMethod('post')) || ($request->action == 'register'))
return [];
if ((! $this->so->exists) && ($request->action == 'create')) {

View File

@@ -7,6 +7,7 @@ use Exception;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
@@ -369,6 +370,41 @@ class Address extends Model
return ($o && $o->system->active) ? $o : NULL;
}
/**
* Create an activation code for this address
*
* @param User $uo
* @return string
*/
public function set_activation(User $uo): string
{
return sprintf('%x:%s',
$this->id,
substr(md5(sprintf('%d:%x',$uo->id,timew($this->updated_at))),0,10)
);
}
/**
* Check the user's activation code for this address is correct
*
* @param User $uo
* @param string $code
* @return bool
*/
public function check_activation(User $uo,string $code): bool
{
try {
Log::info(sprintf('%s:Checking Activation code [%s] invalid for user [%d]',self::LOGKEY,$code,$uo->id));
return ($code == $this->set_activation($uo));
} catch (\Exception $e) {
Log::error(sprintf('%s:! Activation code [%s] invalid for user [%d]',self::LOGKEY,$code,$uo->id));
return FALSE;
}
}
/**
* Netmail waiting to be sent to this system
*

View File

@@ -9,13 +9,13 @@ use Illuminate\Support\Facades\Log;
use App\Classes\FTN\Message;
use App\Interfaces\Packet;
use App\Traits\EncodeUTF8;
use App\Traits\{EncodeUTF8,MsgID};
final class Netmail extends Model implements Packet
{
private const LOGKEY = 'MN-';
use SoftDeletes,EncodeUTF8;
use SoftDeletes,EncodeUTF8,MsgID;
private const cast_utf8 = [
'to',

View File

@@ -8,7 +8,9 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\File;
/**
* Class Setup
* This class represents our configuration.
*
* Our 'System' is defined by system_id, and from it we can find out our BBS name and addresses.
*
* @package App\Models
* @property Collection nodes
@@ -47,37 +49,6 @@ class Setup extends Model
// Our non model attributes and values
private array $internal = [];
public static function product_id(int $c=self::PRODUCT_ID): string
{
return hexstr($c);
}
/* RELATIONS */
public function system()
{
return $this->belongsTo(System::class);
}
/* ATTRIBUTES */
public function getLocationAttribute()
{
return $this->system->location;
}
public function getSysopAttribute()
{
return $this->system->sysop;
}
public function getSystemNameAttribute()
{
return $this->system->name;
}
/* METHODS */
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
@@ -143,6 +114,49 @@ class Setup extends Model
}
}
/**
* The Mailer Product ID in hex.
*
* @param int $c
* @return string
* @throws Exception
*/
public static function product_id(int $c=self::PRODUCT_ID): string
{
return hexstr($c);
}
/* RELATIONS */
/**
* The defined system that this setup is valid for
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function system()
{
return $this->belongsTo(System::class);
}
/* ATTRIBUTES */
public function getLocationAttribute()
{
return $this->system->location;
}
public function getSysopAttribute()
{
return $this->system->sysop;
}
public function getSystemNameAttribute()
{
return $this->system->name;
}
/* METHODS */
/* BINKP OPTIONS: BINKP_OPT_* */
public function binkpOptionClear(int $key): void

View File

@@ -133,4 +133,20 @@ class System extends Model
return $item->role & $type;
});
}
/**
* Parse the addresses and return which ones are in my zones
*
* @param \Illuminate\Database\Eloquent\Collection $addresses
* @param int $type
* @return Collection
*/
public function inMyZones(Collection $addresses,int $type=(Address::NODE_HC|Address::NODE_ACTIVE|Address::NODE_PVT|Address::NODE_POINT)): Collection
{
$myzones = $this->addresses->pluck('zone_id')->unique();
return $addresses->filter(function($item) use ($myzones,$type) {
return ($item->role & $type) && ($myzones->search($item->zone_id) !== FALSE);
});
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace App\Notifications;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use App\Classes\{ANSI,Fonts\Thin,Page};
use App\Classes\Fonts\Thick;
use App\Classes\FTN\Message;
use App\Models\{Address,Netmail,Setup,User};
class AddressLink extends Notification //implements ShouldQueue
{
private const LOGKEY = 'NAL';
use Queueable;
private Address $ao;
private User $uo;
/**
* Create a netmail to enable a sysop to activate an address.
*
* @param Address $ao
* @param User $uo
*/
public function __construct(Address $ao,User $uo)
{
$this->queue = 'netmail';
$this->ao = $ao;
$this->uo = $uo;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['netmail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return Netmail
* @throws \Exception
*/
public function toNetmail($notifiable): Netmail
{
Log::info(sprintf('%s:Sending a link code for address [%s]',self::LOGKEY,$this->ao->ftn));
$so = Setup::findOrFail(config('app.id'))->system;
$o = new Netmail;
$o->to = $this->ao->system->sysop;
$o->from = Setup::PRODUCT_NAME;
$o->subject = 'Address Link Code';
$o->datetime = Carbon::now();
$o->tzoffset = $o->datetime->utcOffset();
$o->fftn_id = $so->match($this->ao->zone)->first()->id;
$o->tftn_id = $this->ao->id;
$o->flags = Message::FLAG_LOCAL;
$o->cost = 0;
$o->tagline = 'Address Linking...';
$o->tearline = sprintf('%s (%04X)',Setup::PRODUCT_NAME,Setup::PRODUCT_ID);
// Message
$msg = new Page;
$msg->addLogo(new ANSI(base_path('public/logo/netmail.bin')));
$header = new Thick;
$header->addText(ANSI::ansi_code([1,37]).'Clearing Houz');
$msg->addHeader($header,'FTN Mailer and Tosser',TRUE,0xc4);
$lbc = new Thin;
$lbc->addText('#link');
$msg->addLeftBoxContent($lbc);
$msg->addText(sprintf(
"Hi %s,\r\r".
"This message is to link your address [%s] to your user ID in the Clearing Houz web site.\r\r".
"If you didnt start this process, then you can safely ignore this netmail. But if you wanted to link this address, please head over to [%s] and paste in the following:\r\r%s\r",
$this->ao->system->sysop,
$this->ao->ftn3d,
url('/link'),
$this->ao->set_activation($this->uo)
));
$o->msg = $msg->render();
$o->save();
return $o;
}
}

View File

@@ -34,7 +34,7 @@ class NetmailChannel
*
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @return \Psr\Http\Message\ResponseInterface|null
* @return \Psr\Http\Message\ResponseInterface|void
*/
public function send($notifiable,Notification $notification)
{
@@ -42,9 +42,8 @@ class NetmailChannel
return;
$o = $notification->toNetmail($notifiable);
Log::info(sprintf('%s:Test Netmail created [%s]',self::LOGKEY,$o->id));
Job::dispatch($ao);
Log::info(sprintf('%s:Dispatched job to pool address [%s]',self::LOGKEY,$ao->ftn));
Log::info(sprintf('%s:Sent netmail [%s] via [%s]',self::LOGKEY,$o->msgid,$ao->ftn));
}
}

View File

@@ -64,7 +64,7 @@ class NetmailTest extends Notification //implements ShouldQueue
$o->datetime = Carbon::now();
$o->tzoffset = $o->datetime->utcOffset();
$o->fftn_id = $so->match($this->ao->zone)->first();
$o->fftn_id = $so->match($this->ao->zone)->first()->id;
$o->tftn_id = $this->ao->id;
$o->flags = Message::FLAG_LOCAL;
$o->cost = 0;

View File

@@ -26,6 +26,18 @@ class SystemPolicy
return ($user->isAdmin() || (! $system->exists));
}
/**
* Can the user register this system
*
* @param User $user
* @param System $system
* @return bool
*/
public function register(User $user,System $system): bool
{
return ! $system->users->count() || $system->users->has($user);
}
/**
* Determine whether the user can update the model.
*