Files
Termix/plugins/tunnels/src/backend/authorize.ts
T

30 lines
1.2 KiB
TypeScript
Raw Normal View History

2026-10-01 16:20:16 -05:00
import { isReservedTunnelName, parseReservedTunnelName } from "./utils.js";
/**
* Ownership check for an action addressed by tunnel name alone.
*
* On-demand (forward) tunnels are deliberately never registered as configs,
* so a check that only ran for a registered config would never run for them.
* Host ids are small sequential integers and endpoint ids come from the
* client, so anyone could guess `web:{hostId}:{endpointId}` and force-close
* another user's live tunnel. The host id is read back out of the reserved
* name instead, and a reserved name whose host id cannot be parsed fails
* closed rather than falling through to the unchecked path.
*/
export async function authorizeTunnelAction(
canAccessHost: (hostId: number) => Promise<boolean>,
tunnelName: string,
config: { sourceHostId?: number } | undefined,
): Promise<{ allowed: boolean; hostId?: number }> {
const hostId =
config?.sourceHostId ?? parseReservedTunnelName(tunnelName)?.hostId;
if (hostId === undefined) {
// Not reserved and not registered: nothing is running under this name
// that anyone could own, so there is nothing to protect.
return { allowed: !isReservedTunnelName(tunnelName) };
}
return { allowed: await canAccessHost(hostId), hostId };
}