Work on registration of existing systems to users
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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');
|
||||
|
@@ -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')) {
|
||||
|
@@ -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
|
||||
*
|
||||
|
@@ -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',
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
104
app/Notifications/AddressLink.php
Normal file
104
app/Notifications/AddressLink.php
Normal 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;
|
||||
}
|
||||
}
|
@@ -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));
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
@@ -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.
|
||||
*
|
||||
|
Reference in New Issue
Block a user