From 1fc97ebe23ffdaa449d5166df8b0d543f20cea73 Mon Sep 17 00:00:00 2001 From: Nick Taylor Date: Fri, 13 Feb 2026 14:48:58 +0000 Subject: [PATCH] test(cli): add tests for trusted-proxy auth configuration - Add tests for buildGatewayAuthConfig with trusted-proxy mode - Test all trusted-proxy options (userHeader, requiredHeaders, allowUsers) - Test minimal trusted-proxy config (userHeader only) - Test preserving allowTailscale when switching to trusted-proxy - Test error when trustedProxy config missing - Test dropping token/password when switching to trusted-proxy - Add integration tests for interactive gateway prompting flow - Test trusted-proxy with all options and with minimal options Test coverage: - 5 new tests in configure.gateway-auth.test.ts - 2 new tests in configure.gateway.test.ts All tests verify proper handling of the new trusted-proxy auth mode. --- .../configure.gateway-auth.e2e.test.ts | 90 +++++++++++++++++++ src/commands/configure.gateway.e2e.test.ts | 68 ++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/src/commands/configure.gateway-auth.e2e.test.ts b/src/commands/configure.gateway-auth.e2e.test.ts index ff9e32c31c8..6fd2b329916 100644 --- a/src/commands/configure.gateway-auth.e2e.test.ts +++ b/src/commands/configure.gateway-auth.e2e.test.ts @@ -117,4 +117,94 @@ describe("buildGatewayAuthConfig", () => { expect(typeof result?.token).toBe("string"); expect(result?.token?.length).toBeGreaterThan(0); }); + + it("builds trusted-proxy config with all options", () => { + const result = buildGatewayAuthConfig({ + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-forwarded-user", + requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"], + allowUsers: ["nick@example.com", "admin@company.com"], + }, + }); + + expect(result).toEqual({ + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-forwarded-user", + requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"], + allowUsers: ["nick@example.com", "admin@company.com"], + }, + }); + }); + + it("builds trusted-proxy config with only userHeader", () => { + const result = buildGatewayAuthConfig({ + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-remote-user", + }, + }); + + expect(result).toEqual({ + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-remote-user", + }, + }); + }); + + it("preserves allowTailscale when switching to trusted-proxy", () => { + const result = buildGatewayAuthConfig({ + existing: { + mode: "token", + token: "abc", + allowTailscale: true, + }, + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-forwarded-user", + }, + }); + + expect(result).toEqual({ + mode: "trusted-proxy", + allowTailscale: true, + trustedProxy: { + userHeader: "x-forwarded-user", + }, + }); + }); + + it("throws error when trusted-proxy mode lacks trustedProxy config", () => { + expect(() => { + buildGatewayAuthConfig({ + mode: "trusted-proxy", + // missing trustedProxy + }); + }).toThrow("trustedProxy config is required when mode is trusted-proxy"); + }); + + it("drops token and password when switching to trusted-proxy", () => { + const result = buildGatewayAuthConfig({ + existing: { + mode: "token", + token: "abc", + password: "secret", + }, + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-forwarded-user", + }, + }); + + expect(result).toEqual({ + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-forwarded-user", + }, + }); + expect(result).not.toHaveProperty("token"); + expect(result).not.toHaveProperty("password"); + }); }); diff --git a/src/commands/configure.gateway.e2e.test.ts b/src/commands/configure.gateway.e2e.test.ts index 94388a50975..368be1cfdb1 100644 --- a/src/commands/configure.gateway.e2e.test.ts +++ b/src/commands/configure.gateway.e2e.test.ts @@ -97,4 +97,72 @@ describe("promptGatewayConfig", () => { expect(call?.password).not.toBe("undefined"); expect(call?.password).toBe(""); }); + + it("prompts for trusted-proxy configuration when trusted-proxy mode selected", async () => { + vi.clearAllMocks(); + mocks.resolveGatewayPort.mockReturnValue(18789); + // Flow: loopback bind → trusted-proxy auth → tailscale off + const selectQueue = ["loopback", "trusted-proxy", "off"]; + mocks.select.mockImplementation(async () => selectQueue.shift()); + // Port prompt, userHeader, requiredHeaders, allowUsers, trustedProxies + const textQueue = [ + "18789", + "x-forwarded-user", + "x-forwarded-proto,x-forwarded-host", + "nick@example.com", + "10.0.1.10,192.168.1.5", + ]; + mocks.text.mockImplementation(async () => textQueue.shift()); + mocks.buildGatewayAuthConfig.mockImplementation(({ mode, trustedProxy }) => ({ + mode, + trustedProxy, + })); + + const runtime: RuntimeEnv = { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn(), + }; + + const result = await promptGatewayConfig({}, runtime); + const call = mocks.buildGatewayAuthConfig.mock.calls[0]?.[0]; + + expect(call?.mode).toBe("trusted-proxy"); + expect(call?.trustedProxy).toEqual({ + userHeader: "x-forwarded-user", + requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"], + allowUsers: ["nick@example.com"], + }); + expect(result.config.gateway?.trustedProxies).toEqual(["10.0.1.10", "192.168.1.5"]); + }); + + it("handles trusted-proxy with no optional fields", async () => { + vi.clearAllMocks(); + mocks.resolveGatewayPort.mockReturnValue(18789); + const selectQueue = ["loopback", "trusted-proxy", "off"]; + mocks.select.mockImplementation(async () => selectQueue.shift()); + // Port prompt, userHeader (only required), empty requiredHeaders, empty allowUsers, trustedProxies + const textQueue = ["18789", "x-remote-user", "", "", "10.0.0.1"]; + mocks.text.mockImplementation(async () => textQueue.shift()); + mocks.buildGatewayAuthConfig.mockImplementation(({ mode, trustedProxy }) => ({ + mode, + trustedProxy, + })); + + const runtime: RuntimeEnv = { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn(), + }; + + const result = await promptGatewayConfig({}, runtime); + const call = mocks.buildGatewayAuthConfig.mock.calls[0]?.[0]; + + expect(call?.mode).toBe("trusted-proxy"); + expect(call?.trustedProxy).toEqual({ + userHeader: "x-remote-user", + // requiredHeaders and allowUsers should be undefined when empty + }); + expect(result.config.gateway?.trustedProxies).toEqual(["10.0.0.1"]); + }); });