Gateway: add SecretRef support for gateway.auth.token with auth-mode guardrails (#35094)

This commit is contained in:
Josh Avant
2026-03-05 12:53:56 -06:00
committed by GitHub
parent bc66a8fa81
commit 72cf9253fc
112 changed files with 5750 additions and 465 deletions

View File

@@ -8,6 +8,7 @@ const detectBrowserOpenSupportMock = vi.hoisted(() => vi.fn());
const openUrlMock = vi.hoisted(() => vi.fn());
const formatControlUiSshHintMock = vi.hoisted(() => vi.fn());
const copyToClipboardMock = vi.hoisted(() => vi.fn());
const resolveSecretRefValuesMock = vi.hoisted(() => vi.fn());
vi.mock("../config/config.js", () => ({
readConfigFileSnapshot: readConfigFileSnapshotMock,
@@ -25,6 +26,10 @@ vi.mock("../infra/clipboard.js", () => ({
copyToClipboard: copyToClipboardMock,
}));
vi.mock("../secrets/resolve.js", () => ({
resolveSecretRefValues: resolveSecretRefValuesMock,
}));
const runtime = {
log: vi.fn(),
error: vi.fn(),
@@ -37,7 +42,7 @@ function resetRuntime() {
runtime.exit.mockClear();
}
function mockSnapshot(token = "abc") {
function mockSnapshot(token: unknown = "abc") {
readConfigFileSnapshotMock.mockResolvedValue({
path: "/tmp/openclaw.json",
exists: true,
@@ -53,6 +58,7 @@ function mockSnapshot(token = "abc") {
httpUrl: "http://127.0.0.1:18789/",
wsUrl: "ws://127.0.0.1:18789",
});
resolveSecretRefValuesMock.mockReset();
}
describe("dashboardCommand", () => {
@@ -65,6 +71,8 @@ describe("dashboardCommand", () => {
openUrlMock.mockClear();
formatControlUiSshHintMock.mockClear();
copyToClipboardMock.mockClear();
delete process.env.OPENCLAW_GATEWAY_TOKEN;
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
});
it("opens and copies the dashboard link by default", async () => {
@@ -115,4 +123,71 @@ describe("dashboardCommand", () => {
"Browser launch disabled (--no-open). Use the URL above.",
);
});
it("prints non-tokenized URL with guidance when token SecretRef is unresolved", async () => {
mockSnapshot({
source: "env",
provider: "default",
id: "MISSING_GATEWAY_TOKEN",
});
copyToClipboardMock.mockResolvedValue(true);
detectBrowserOpenSupportMock.mockResolvedValue({ ok: true });
openUrlMock.mockResolvedValue(true);
resolveSecretRefValuesMock.mockRejectedValue(new Error("missing env var"));
await dashboardCommand(runtime);
expect(copyToClipboardMock).toHaveBeenCalledWith("http://127.0.0.1:18789/");
expect(runtime.log).toHaveBeenCalledWith(
expect.stringContaining("Token auto-auth unavailable"),
);
expect(runtime.log).toHaveBeenCalledWith(
expect.stringContaining(
"gateway.auth.token SecretRef is unresolved (env:default:MISSING_GATEWAY_TOKEN).",
),
);
expect(runtime.log).not.toHaveBeenCalledWith(expect.stringContaining("missing env var"));
});
it("keeps URL non-tokenized when token SecretRef is unresolved but env fallback exists", async () => {
mockSnapshot({
source: "env",
provider: "default",
id: "MISSING_GATEWAY_TOKEN",
});
process.env.OPENCLAW_GATEWAY_TOKEN = "fallback-token";
copyToClipboardMock.mockResolvedValue(true);
detectBrowserOpenSupportMock.mockResolvedValue({ ok: true });
openUrlMock.mockResolvedValue(true);
resolveSecretRefValuesMock.mockRejectedValue(new Error("missing env var"));
await dashboardCommand(runtime);
expect(copyToClipboardMock).toHaveBeenCalledWith("http://127.0.0.1:18789/");
expect(openUrlMock).toHaveBeenCalledWith("http://127.0.0.1:18789/");
expect(runtime.log).toHaveBeenCalledWith(
expect.stringContaining("Token auto-auth is disabled for SecretRef-managed"),
);
expect(runtime.log).not.toHaveBeenCalledWith(
expect.stringContaining("Token auto-auth unavailable"),
);
});
it("resolves env-template gateway.auth.token before building dashboard URL", async () => {
mockSnapshot("${CUSTOM_GATEWAY_TOKEN}");
copyToClipboardMock.mockResolvedValue(true);
detectBrowserOpenSupportMock.mockResolvedValue({ ok: true });
openUrlMock.mockResolvedValue(true);
resolveSecretRefValuesMock.mockResolvedValue(
new Map([["env:default:CUSTOM_GATEWAY_TOKEN", "resolved-secret-token"]]),
);
await dashboardCommand(runtime);
expect(copyToClipboardMock).toHaveBeenCalledWith("http://127.0.0.1:18789/");
expect(openUrlMock).toHaveBeenCalledWith("http://127.0.0.1:18789/");
expect(runtime.log).toHaveBeenCalledWith(
expect.stringContaining("Token auto-auth is disabled for SecretRef-managed"),
);
});
});