91 lines
3.2 KiB
PHP
91 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Policies;
|
|
|
|
use App\Models\Operator;
|
|
use App\Models\VpnPeer;
|
|
|
|
/**
|
|
* Who may see and do what with a VPN access.
|
|
*
|
|
* Ownership is record-level and lives here; permissions describe elevated
|
|
* capabilities (vpn.view.all, vpn.manage.all) and are checked through Gate.
|
|
* The two are kept apart on purpose: seeing an access is not managing it, and
|
|
* neither of them is holding its private key.
|
|
*
|
|
* There is no Gate::before super-admin shortcut in this application, so these
|
|
* rules are the whole story — in particular downloadConfig() cannot be
|
|
* out-voted by a role.
|
|
*
|
|
* Every method here takes an Operator, not a User: a VPN access is a route
|
|
* into the management network, and nothing about it is customer-facing — the
|
|
* owner column has only ever held an operator's id (VpnPeerFactory's default
|
|
* is `Operator::factory()->role('Admin')`, and Vpn::create() only ever writes
|
|
* the id of an operator's own account). There is no second, customer-shaped
|
|
* path to branch this into.
|
|
*/
|
|
class VpnPeerPolicy
|
|
{
|
|
public function viewAny(Operator $operator): bool
|
|
{
|
|
// Every operator may at least reach the page to find their own access.
|
|
return $operator->isOperator();
|
|
}
|
|
|
|
public function view(Operator $operator, VpnPeer $peer): bool
|
|
{
|
|
return $this->owns($operator, $peer) || $operator->can('vpn.view.all');
|
|
}
|
|
|
|
public function create(Operator $operator): bool
|
|
{
|
|
// Not self-service: an access reaches the management network, so issuing
|
|
// one is an administrative act even for oneself.
|
|
return $operator->can('vpn.manage.all');
|
|
}
|
|
|
|
/** Rename, reassign, change stored-config settings. */
|
|
public function update(Operator $operator, VpnPeer $peer): bool
|
|
{
|
|
return $operator->can('vpn.manage.all');
|
|
}
|
|
|
|
/** Block / unblock. Owners may switch their own access off and on again. */
|
|
public function block(Operator $operator, VpnPeer $peer): bool
|
|
{
|
|
return $operator->can('vpn.manage.all') || $this->owns($operator, $peer);
|
|
}
|
|
|
|
public function delete(Operator $operator, VpnPeer $peer): bool
|
|
{
|
|
return $operator->can('vpn.manage.all') || $this->owns($operator, $peer);
|
|
}
|
|
|
|
/**
|
|
* The private half of someone's credential. Owner only — explicitly NOT
|
|
* vpn.view.all and NOT vpn.manage.all: an administrator who needs access
|
|
* revokes this one and issues themselves their own, which keeps the record
|
|
* of who holds what intact.
|
|
*/
|
|
public function downloadConfig(Operator $operator, VpnPeer $peer): bool
|
|
{
|
|
return $this->owns($operator, $peer)
|
|
&& $peer->config_secret !== null
|
|
&& ! $peer->trashed();
|
|
}
|
|
|
|
/**
|
|
* isOperator() is part of ownership on purpose: roles can be taken away by
|
|
* other paths than revokeStaff() — a direct role edit, for instance. Losing
|
|
* the console must close these doors by itself, not rely on whoever removed
|
|
* the role also remembering the tunnel.
|
|
*/
|
|
private function owns(Operator $operator, VpnPeer $peer): bool
|
|
{
|
|
return $operator->isOperator()
|
|
&& $peer->kind === VpnPeer::KIND_STAFF
|
|
&& $peer->user_id !== null
|
|
&& $peer->user_id === $operator->id;
|
|
}
|
|
}
|