mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-27 10:46:51 +00:00
fix(nodes): gate node-management actions by role and release pilot tunnels on delete (#1280)
* fix(nodes): gate node-management actions by role and release pilot tunnels on delete The node-management write actions in the Nodes panel (add, edit, delete, generate node token, reset fleet-sync anchor) rendered for every signed-in role, but the API enforces the manage-nodes permission on them, so lower-privilege roles saw buttons that returned 403. The panel now renders each action against the same permission its route enforces; the read-only node table stays visible to every role. Deleting a node now also tears down its live pilot-agent tunnel (and any mesh bridge) immediately, releasing the loopback server, heartbeat timer, and open streams instead of leaving them until the agent next disconnects, matching the cleanup the re-enrollment path already performed. Adds backend route tests for the permission boundaries and tunnel teardown, and a Nodes panel render test covering the viewer, admin, and node-admin views. * fix(nodes): close proxy mesh bridges via the dialer on node delete to skip a redial Deleting a node closed any active mesh bridge through PilotTunnelManager, but a proxy-mode bridge is owned by the mesh dialer, whose close listener then treated the close as unexpected and scheduled a reactive redial against the node being removed. The delete handler now closes a proxy bridge through the dialer's intentional-close path first (which suppresses the redial), then closes a pilot-agent tunnel as before. Adds a backend test that primes a live proxy bridge and asserts deletion closes it without scheduling a redial.
This commit is contained in:
@@ -313,6 +313,15 @@ nodesRouter.delete('/:id', async (req: Request, res: Response) => {
|
||||
if (!requirePermission(req, res, 'node:manage', 'node', nodeIdParam)) return;
|
||||
try {
|
||||
const id = parseInt(nodeIdParam);
|
||||
// Release any live tunnel or mesh bridge before deleting the record so it is
|
||||
// freed immediately rather than lingering until the peer disconnects. Close a
|
||||
// proxy-mode mesh bridge through its dialer FIRST: closeBridge removes the
|
||||
// bridge before closing it, so the dialer does not schedule a reactive redial
|
||||
// against a node that is about to disappear. closeTunnel then closes a
|
||||
// pilot-agent tunnel; both are no-ops when this node has no such bridge (a
|
||||
// local node, or one with no active connection). Mirrors the re-enroll path.
|
||||
MeshProxyTunnelDialer.getInstance().closeBridge(id, 'node deleted');
|
||||
PilotTunnelManager.getInstance().closeTunnel(id, PilotCloseCode.NormalClosure, 'node deleted');
|
||||
DatabaseService.getInstance().deleteNode(id);
|
||||
NodeRegistry.getInstance().evictConnection(id);
|
||||
NodeRegistry.getInstance().notifyNodeRemoved(id);
|
||||
|
||||
Reference in New Issue
Block a user