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; } }