mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 21:24:32 +00:00
refactor: reuse shared gateway probe auth
This commit is contained in:
@@ -283,6 +283,38 @@ describe("gatherDaemonStatus", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps remote probe auth strict when remote token is missing", async () => {
|
||||||
|
daemonLoadedConfig = {
|
||||||
|
gateway: {
|
||||||
|
mode: "remote",
|
||||||
|
remote: {
|
||||||
|
url: "wss://gateway.example",
|
||||||
|
password: "remote-password", // pragma: allowlist secret
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
mode: "token",
|
||||||
|
token: "local-token",
|
||||||
|
password: "local-password", // pragma: allowlist secret
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
process.env.OPENCLAW_GATEWAY_TOKEN = "env-token";
|
||||||
|
process.env.OPENCLAW_GATEWAY_PASSWORD = "env-password"; // pragma: allowlist secret
|
||||||
|
|
||||||
|
await gatherDaemonStatus({
|
||||||
|
rpc: {},
|
||||||
|
probe: true,
|
||||||
|
deep: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callGatewayStatusProbe).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
token: undefined,
|
||||||
|
password: "env-password", // pragma: allowlist secret
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("skips TLS runtime loading when probe is disabled", async () => {
|
it("skips TLS runtime loading when probe is disabled", async () => {
|
||||||
const status = await gatherDaemonStatus({
|
const status = await gatherDaemonStatus({
|
||||||
rpc: {},
|
rpc: {},
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ import type {
|
|||||||
GatewayBindMode,
|
GatewayBindMode,
|
||||||
GatewayControlUiConfig,
|
GatewayControlUiConfig,
|
||||||
} from "../../config/types.js";
|
} from "../../config/types.js";
|
||||||
import {
|
|
||||||
hasConfiguredSecretInput,
|
|
||||||
normalizeSecretInputString,
|
|
||||||
} from "../../config/types.secrets.js";
|
|
||||||
import { readLastGatewayErrorLine } from "../../daemon/diagnostics.js";
|
import { readLastGatewayErrorLine } from "../../daemon/diagnostics.js";
|
||||||
import type { FindExtraGatewayServicesOptions } from "../../daemon/inspect.js";
|
import type { FindExtraGatewayServicesOptions } from "../../daemon/inspect.js";
|
||||||
import { findExtraGatewayServices } from "../../daemon/inspect.js";
|
import { findExtraGatewayServices } from "../../daemon/inspect.js";
|
||||||
@@ -20,13 +16,9 @@ import type { ServiceConfigAudit } from "../../daemon/service-audit.js";
|
|||||||
import { auditGatewayServiceConfig } from "../../daemon/service-audit.js";
|
import { auditGatewayServiceConfig } from "../../daemon/service-audit.js";
|
||||||
import type { GatewayServiceRuntime } from "../../daemon/service-runtime.js";
|
import type { GatewayServiceRuntime } from "../../daemon/service-runtime.js";
|
||||||
import { resolveGatewayService } from "../../daemon/service.js";
|
import { resolveGatewayService } from "../../daemon/service.js";
|
||||||
import {
|
import { trimToUndefined } from "../../gateway/credentials.js";
|
||||||
readGatewayPasswordEnv,
|
|
||||||
readGatewayTokenEnv,
|
|
||||||
trimToUndefined,
|
|
||||||
} from "../../gateway/credentials.js";
|
|
||||||
import { resolveGatewayBindHost } from "../../gateway/net.js";
|
import { resolveGatewayBindHost } from "../../gateway/net.js";
|
||||||
import { resolveRequiredConfiguredSecretRefInputString } from "../../gateway/resolve-configured-secret-input-string.js";
|
import { resolveGatewayProbeAuthWithSecretInputs } from "../../gateway/probe-auth.js";
|
||||||
import { parseStrictPositiveInteger } from "../../infra/parse-finite-number.js";
|
import { parseStrictPositiveInteger } from "../../infra/parse-finite-number.js";
|
||||||
import {
|
import {
|
||||||
formatPortDiagnostics,
|
formatPortDiagnostics,
|
||||||
@@ -258,92 +250,6 @@ async function inspectDaemonPortStatuses(params: {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveDaemonProbeToken(params: {
|
|
||||||
daemonCfg: OpenClawConfig;
|
|
||||||
mergedDaemonEnv: Record<string, string | undefined>;
|
|
||||||
explicitToken?: string;
|
|
||||||
explicitPassword?: string;
|
|
||||||
}): Promise<string | undefined> {
|
|
||||||
const explicitToken = trimToUndefined(params.explicitToken);
|
|
||||||
if (explicitToken) {
|
|
||||||
return explicitToken;
|
|
||||||
}
|
|
||||||
const envToken = readGatewayTokenEnv(params.mergedDaemonEnv as NodeJS.ProcessEnv);
|
|
||||||
if (envToken) {
|
|
||||||
return envToken;
|
|
||||||
}
|
|
||||||
const defaults = params.daemonCfg.secrets?.defaults;
|
|
||||||
const configured = params.daemonCfg.gateway?.auth?.token;
|
|
||||||
const authMode = params.daemonCfg.gateway?.auth?.mode;
|
|
||||||
if (authMode === "password" || authMode === "none" || authMode === "trusted-proxy") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (authMode !== "token") {
|
|
||||||
const passwordCandidate =
|
|
||||||
trimToUndefined(params.explicitPassword) ||
|
|
||||||
readGatewayPasswordEnv(params.mergedDaemonEnv as NodeJS.ProcessEnv) ||
|
|
||||||
(hasConfiguredSecretInput(params.daemonCfg.gateway?.auth?.password, defaults)
|
|
||||||
? "__configured__"
|
|
||||||
: undefined);
|
|
||||||
if (passwordCandidate) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const resolvedToken = await resolveRequiredConfiguredSecretRefInputString({
|
|
||||||
config: params.daemonCfg,
|
|
||||||
env: params.mergedDaemonEnv as NodeJS.ProcessEnv,
|
|
||||||
value: configured,
|
|
||||||
path: "gateway.auth.token",
|
|
||||||
});
|
|
||||||
if (resolvedToken) {
|
|
||||||
return resolvedToken;
|
|
||||||
}
|
|
||||||
return normalizeSecretInputString(configured);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function resolveDaemonProbePassword(params: {
|
|
||||||
daemonCfg: OpenClawConfig;
|
|
||||||
mergedDaemonEnv: Record<string, string | undefined>;
|
|
||||||
explicitToken?: string;
|
|
||||||
explicitPassword?: string;
|
|
||||||
}): Promise<string | undefined> {
|
|
||||||
const explicitPassword = trimToUndefined(params.explicitPassword);
|
|
||||||
if (explicitPassword) {
|
|
||||||
return explicitPassword;
|
|
||||||
}
|
|
||||||
const envPassword = readGatewayPasswordEnv(params.mergedDaemonEnv as NodeJS.ProcessEnv);
|
|
||||||
if (envPassword) {
|
|
||||||
return envPassword;
|
|
||||||
}
|
|
||||||
const defaults = params.daemonCfg.secrets?.defaults;
|
|
||||||
const configured = params.daemonCfg.gateway?.auth?.password;
|
|
||||||
const authMode = params.daemonCfg.gateway?.auth?.mode;
|
|
||||||
if (authMode === "token" || authMode === "none" || authMode === "trusted-proxy") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (authMode !== "password") {
|
|
||||||
const tokenCandidate =
|
|
||||||
trimToUndefined(params.explicitToken) ||
|
|
||||||
readGatewayTokenEnv(params.mergedDaemonEnv as NodeJS.ProcessEnv) ||
|
|
||||||
(hasConfiguredSecretInput(params.daemonCfg.gateway?.auth?.token, defaults)
|
|
||||||
? "__configured__"
|
|
||||||
: undefined);
|
|
||||||
if (tokenCandidate) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const resolvedPassword = await resolveRequiredConfiguredSecretRefInputString({
|
|
||||||
config: params.daemonCfg,
|
|
||||||
env: params.mergedDaemonEnv as NodeJS.ProcessEnv,
|
|
||||||
value: configured,
|
|
||||||
path: "gateway.auth.password",
|
|
||||||
});
|
|
||||||
if (resolvedPassword) {
|
|
||||||
return resolvedPassword;
|
|
||||||
}
|
|
||||||
return normalizeSecretInputString(configured);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function gatherDaemonStatus(
|
export async function gatherDaemonStatus(
|
||||||
opts: {
|
opts: {
|
||||||
rpc: GatewayRpcOpts;
|
rpc: GatewayRpcOpts;
|
||||||
@@ -395,28 +301,23 @@ export async function gatherDaemonStatus(
|
|||||||
const tlsRuntime = shouldUseLocalTlsRuntime
|
const tlsRuntime = shouldUseLocalTlsRuntime
|
||||||
? await loadGatewayTlsRuntime(daemonCfg.gateway?.tls)
|
? await loadGatewayTlsRuntime(daemonCfg.gateway?.tls)
|
||||||
: undefined;
|
: undefined;
|
||||||
const daemonProbePassword = opts.probe
|
const daemonProbeAuth = opts.probe
|
||||||
? await resolveDaemonProbePassword({
|
? await resolveGatewayProbeAuthWithSecretInputs({
|
||||||
daemonCfg,
|
cfg: daemonCfg,
|
||||||
mergedDaemonEnv,
|
mode: daemonCfg.gateway?.mode === "remote" ? "remote" : "local",
|
||||||
explicitToken: opts.rpc.token,
|
env: mergedDaemonEnv as NodeJS.ProcessEnv,
|
||||||
explicitPassword: opts.rpc.password,
|
explicitAuth: {
|
||||||
})
|
token: opts.rpc.token,
|
||||||
: undefined;
|
password: opts.rpc.password,
|
||||||
const daemonProbeToken = opts.probe
|
},
|
||||||
? await resolveDaemonProbeToken({
|
|
||||||
daemonCfg,
|
|
||||||
mergedDaemonEnv,
|
|
||||||
explicitToken: opts.rpc.token,
|
|
||||||
explicitPassword: opts.rpc.password,
|
|
||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const rpc = opts.probe
|
const rpc = opts.probe
|
||||||
? await probeGatewayStatus({
|
? await probeGatewayStatus({
|
||||||
url: gateway.probeUrl,
|
url: gateway.probeUrl,
|
||||||
token: daemonProbeToken,
|
token: daemonProbeAuth?.token,
|
||||||
password: daemonProbePassword,
|
password: daemonProbeAuth?.password,
|
||||||
tlsFingerprint:
|
tlsFingerprint:
|
||||||
shouldUseLocalTlsRuntime && tlsRuntime?.enabled
|
shouldUseLocalTlsRuntime && tlsRuntime?.enabled
|
||||||
? tlsRuntime.fingerprintSha256
|
? tlsRuntime.fingerprintSha256
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { OpenClawConfig } from "../config/config.js";
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import { resolveGatewayProbeAuthSafe } from "./probe-auth.js";
|
import {
|
||||||
|
resolveGatewayProbeAuthSafe,
|
||||||
|
resolveGatewayProbeAuthWithSecretInputs,
|
||||||
|
} from "./probe-auth.js";
|
||||||
|
|
||||||
describe("resolveGatewayProbeAuthSafe", () => {
|
describe("resolveGatewayProbeAuthSafe", () => {
|
||||||
it("returns probe auth credentials when available", () => {
|
it("returns probe auth credentials when available", () => {
|
||||||
@@ -79,3 +82,32 @@ describe("resolveGatewayProbeAuthSafe", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("resolveGatewayProbeAuthWithSecretInputs", () => {
|
||||||
|
it("resolves local probe SecretRef values before shared credential selection", async () => {
|
||||||
|
const auth = await resolveGatewayProbeAuthWithSecretInputs({
|
||||||
|
cfg: {
|
||||||
|
gateway: {
|
||||||
|
auth: {
|
||||||
|
mode: "token",
|
||||||
|
token: { source: "env", provider: "default", id: "DAEMON_GATEWAY_TOKEN" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
secrets: {
|
||||||
|
providers: {
|
||||||
|
default: { source: "env" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig,
|
||||||
|
mode: "local",
|
||||||
|
env: {
|
||||||
|
DAEMON_GATEWAY_TOKEN: "resolved-daemon-token",
|
||||||
|
} as NodeJS.ProcessEnv,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(auth).toEqual({
|
||||||
|
token: "resolved-daemon-token",
|
||||||
|
password: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { OpenClawConfig } from "../config/config.js";
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
|
import { resolveGatewayCredentialsWithSecretInputs } from "./call.js";
|
||||||
import {
|
import {
|
||||||
|
type ExplicitGatewayAuth,
|
||||||
isGatewaySecretRefUnavailableError,
|
isGatewaySecretRefUnavailableError,
|
||||||
resolveGatewayCredentialsFromConfig,
|
resolveGatewayCredentialsFromConfig,
|
||||||
} from "./credentials.js";
|
} from "./credentials.js";
|
||||||
@@ -18,6 +20,22 @@ export function resolveGatewayProbeAuth(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function resolveGatewayProbeAuthWithSecretInputs(params: {
|
||||||
|
cfg: OpenClawConfig;
|
||||||
|
mode: "local" | "remote";
|
||||||
|
env?: NodeJS.ProcessEnv;
|
||||||
|
explicitAuth?: ExplicitGatewayAuth;
|
||||||
|
}): Promise<{ token?: string; password?: string }> {
|
||||||
|
return await resolveGatewayCredentialsWithSecretInputs({
|
||||||
|
config: params.cfg,
|
||||||
|
env: params.env,
|
||||||
|
explicitAuth: params.explicitAuth,
|
||||||
|
modeOverride: params.mode,
|
||||||
|
includeLegacyEnv: false,
|
||||||
|
remoteTokenFallback: "remote-only",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveGatewayProbeAuthSafe(params: {
|
export function resolveGatewayProbeAuthSafe(params: {
|
||||||
cfg: OpenClawConfig;
|
cfg: OpenClawConfig;
|
||||||
mode: "local" | "remote";
|
mode: "local" | "remote";
|
||||||
|
|||||||
Reference in New Issue
Block a user