feat: direct TCP URI with SSL toggle and optional password auth (#635)
* feat: direct TCP URI with SSL toggle and optional password auth Replaces the heuristic-driven direct-connection model with a user-controlled one. The Add Host dialog now exposes structured Host, Port, "Use SSL", and masked Password fields that compose the canonical `tcp://host:port?ssl=true&password=xxx` URI used as the storage form. The daemon gains optional shared-secret auth: `Authorization: Bearer <pw>` on HTTP and `Sec-WebSocket-Protocol: paseo.bearer.<pw>` on the WS upgrade (browser WebSocket can't set custom headers). Configured via config.json `auth.password` or `PASEO_PASSWORD` env. Off by default — old clients keep working unchanged. The `port === 443` heuristic for ws/wss is gone; the explicit `useTls` flag drives scheme selection at every call site. * fix: stabilize direct tcp auth ci checks * fix: restore fetch stub in bootstrap smoke test * fix(app): collapse Advanced section in add-host modal * feat(server): hash daemon password in config * fix: update lockfile signatures and Nix hash * Improve direct TCP auth failures * Fix workspace cwd updates after rebase --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
parent
30f7842e8f
commit
088a6c14c0
@@ -1,16 +1,105 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import {
|
||||
buildDaemonWebSocketUrl,
|
||||
buildRelayWebSocketUrl,
|
||||
CURRENT_RELAY_PROTOCOL_VERSION,
|
||||
normalizeRelayProtocolVersion,
|
||||
parseConnectionUri,
|
||||
serializeConnectionUri,
|
||||
serializeConnectionUriForStorage,
|
||||
} from "./daemon-endpoints.js";
|
||||
|
||||
describe("connection URI parsing", () => {
|
||||
test("round-trips a tcp host and port", () => {
|
||||
const parsed = parseConnectionUri("tcp://localhost:6767");
|
||||
|
||||
expect(parsed).toEqual({
|
||||
host: "localhost",
|
||||
port: 6767,
|
||||
isIpv6: false,
|
||||
useTls: false,
|
||||
});
|
||||
expect(serializeConnectionUri(parsed)).toBe("tcp://localhost:6767");
|
||||
});
|
||||
|
||||
test("round-trips an SSL-enabled tcp host and port", () => {
|
||||
const parsed = parseConnectionUri("tcp://example.com:443?ssl=true");
|
||||
|
||||
expect(parsed).toEqual({
|
||||
host: "example.com",
|
||||
port: 443,
|
||||
isIpv6: false,
|
||||
useTls: true,
|
||||
});
|
||||
expect(serializeConnectionUri(parsed)).toBe("tcp://example.com:443?ssl=true");
|
||||
});
|
||||
|
||||
test("round-trips an IPv6 host", () => {
|
||||
const parsed = parseConnectionUri("tcp://[::1]:6767?ssl=true");
|
||||
|
||||
expect(parsed).toEqual({
|
||||
host: "::1",
|
||||
port: 6767,
|
||||
isIpv6: true,
|
||||
useTls: true,
|
||||
});
|
||||
expect(serializeConnectionUri(parsed)).toBe("tcp://[::1]:6767?ssl=true");
|
||||
});
|
||||
|
||||
test("rejects a missing port", () => {
|
||||
expect(() => parseConnectionUri("tcp://localhost")).toThrow("Connection URI port is required");
|
||||
});
|
||||
|
||||
test("rejects an invalid scheme", () => {
|
||||
expect(() => parseConnectionUri("http://localhost:6767")).toThrow(
|
||||
"Connection URI protocol must be tcp:",
|
||||
);
|
||||
});
|
||||
|
||||
test("parses password without including it in the public serializer", () => {
|
||||
const parsed = parseConnectionUri("tcp://localhost:6767?ssl=true&password=secret");
|
||||
|
||||
expect(parsed).toEqual({
|
||||
host: "localhost",
|
||||
port: 6767,
|
||||
isIpv6: false,
|
||||
useTls: true,
|
||||
password: "secret",
|
||||
});
|
||||
expect(serializeConnectionUri(parsed)).toBe("tcp://localhost:6767?ssl=true");
|
||||
expect(serializeConnectionUriForStorage(parsed)).toBe(
|
||||
"tcp://localhost:6767?ssl=true&password=secret",
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects userinfo passwords", () => {
|
||||
expect(() => parseConnectionUri("tcp://:secret@localhost:6767?ssl=true")).toThrow(
|
||||
"Connection URI userinfo is not supported",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("daemon websocket URLs", () => {
|
||||
test("uses ws for port 443 when TLS is disabled", () => {
|
||||
expect(buildDaemonWebSocketUrl("example.com:443", { useTls: false })).toBe(
|
||||
"ws://example.com:443/ws",
|
||||
);
|
||||
});
|
||||
|
||||
test("uses wss for non-443 ports when TLS is enabled", () => {
|
||||
expect(buildDaemonWebSocketUrl("example.com:6767", { useTls: true })).toBe(
|
||||
"wss://example.com:6767/ws",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("relay websocket URL versioning", () => {
|
||||
test("defaults relay URLs to v2", () => {
|
||||
const url = new URL(
|
||||
buildRelayWebSocketUrl({
|
||||
endpoint: "relay.paseo.sh:443",
|
||||
useTls: true,
|
||||
serverId: "srv_test",
|
||||
role: "client",
|
||||
}),
|
||||
@@ -24,6 +113,7 @@ describe("relay websocket URL versioning", () => {
|
||||
const url = new URL(
|
||||
buildRelayWebSocketUrl({
|
||||
endpoint: "relay.paseo.sh:443",
|
||||
useTls: true,
|
||||
serverId: "srv_test",
|
||||
role: "server",
|
||||
connectionId: "conn_abc123",
|
||||
@@ -37,6 +127,7 @@ describe("relay websocket URL versioning", () => {
|
||||
const url = new URL(
|
||||
buildRelayWebSocketUrl({
|
||||
endpoint: "relay.paseo.sh:443",
|
||||
useTls: true,
|
||||
serverId: "srv_test",
|
||||
role: "server",
|
||||
version: "1",
|
||||
@@ -55,3 +146,31 @@ describe("relay websocket URL versioning", () => {
|
||||
expect(() => normalizeRelayProtocolVersion("3")).toThrow('Relay version must be "1" or "2"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("relay websocket URLs", () => {
|
||||
test("uses ws for port 443 when TLS is disabled", () => {
|
||||
const url = new URL(
|
||||
buildRelayWebSocketUrl({
|
||||
endpoint: "relay.paseo.sh:443",
|
||||
useTls: false,
|
||||
serverId: "srv_test",
|
||||
role: "client",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(url.protocol).toBe("ws:");
|
||||
});
|
||||
|
||||
test("uses wss for non-443 ports when TLS is enabled", () => {
|
||||
const url = new URL(
|
||||
buildRelayWebSocketUrl({
|
||||
endpoint: "relay.paseo.sh:6767",
|
||||
useTls: true,
|
||||
serverId: "srv_test",
|
||||
role: "client",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(url.protocol).toBe("wss:");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user