CluPilotCloud/app/Policies/VpnPeerPolicy.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;
}
}