mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-07 22:09:57 +00:00
Gateway: add SecretRef support for gateway.auth.token with auth-mode guardrails (#35094)
This commit is contained in:
@@ -16,6 +16,38 @@ export type GatewayCredentialPrecedence = "env-first" | "config-first";
|
||||
export type GatewayRemoteCredentialPrecedence = "remote-first" | "env-first";
|
||||
export type GatewayRemoteCredentialFallback = "remote-env-local" | "remote-only";
|
||||
|
||||
const GATEWAY_SECRET_REF_UNAVAILABLE_ERROR_CODE = "GATEWAY_SECRET_REF_UNAVAILABLE";
|
||||
|
||||
export class GatewaySecretRefUnavailableError extends Error {
|
||||
readonly code = GATEWAY_SECRET_REF_UNAVAILABLE_ERROR_CODE;
|
||||
readonly path: string;
|
||||
|
||||
constructor(path: string) {
|
||||
super(
|
||||
[
|
||||
`${path} is configured as a secret reference but is unavailable in this command path.`,
|
||||
"Fix: set OPENCLAW_GATEWAY_TOKEN/OPENCLAW_GATEWAY_PASSWORD, pass explicit --token/--password,",
|
||||
"or run a gateway command path that resolves secret references before credential selection.",
|
||||
].join("\n"),
|
||||
);
|
||||
this.name = "GatewaySecretRefUnavailableError";
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
|
||||
export function isGatewaySecretRefUnavailableError(
|
||||
error: unknown,
|
||||
expectedPath?: string,
|
||||
): error is GatewaySecretRefUnavailableError {
|
||||
if (!(error instanceof GatewaySecretRefUnavailableError)) {
|
||||
return false;
|
||||
}
|
||||
if (!expectedPath) {
|
||||
return true;
|
||||
}
|
||||
return error.path === expectedPath;
|
||||
}
|
||||
|
||||
export function trimToUndefined(value: unknown): string | undefined {
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
@@ -34,13 +66,7 @@ function firstDefined(values: Array<string | undefined>): string | undefined {
|
||||
}
|
||||
|
||||
function throwUnresolvedGatewaySecretInput(path: string): never {
|
||||
throw new Error(
|
||||
[
|
||||
`${path} is configured as a secret reference but is unavailable in this command path.`,
|
||||
"Fix: set OPENCLAW_GATEWAY_TOKEN/OPENCLAW_GATEWAY_PASSWORD, pass explicit --token/--password,",
|
||||
"or run a gateway command path that resolves secret references before credential selection.",
|
||||
].join("\n"),
|
||||
);
|
||||
throw new GatewaySecretRefUnavailableError(path);
|
||||
}
|
||||
|
||||
function readGatewayTokenEnv(
|
||||
@@ -144,10 +170,28 @@ export function resolveGatewayCredentialsFromConfig(params: {
|
||||
const envToken = readGatewayTokenEnv(env, includeLegacyEnv);
|
||||
const envPassword = readGatewayPasswordEnv(env, includeLegacyEnv);
|
||||
|
||||
const remoteToken = trimToUndefined(remote?.token);
|
||||
const remotePassword = trimToUndefined(remote?.password);
|
||||
const localToken = trimToUndefined(params.cfg.gateway?.auth?.token);
|
||||
const localPassword = trimToUndefined(params.cfg.gateway?.auth?.password);
|
||||
const localTokenRef = resolveSecretInputRef({
|
||||
value: params.cfg.gateway?.auth?.token,
|
||||
defaults,
|
||||
}).ref;
|
||||
const localPasswordRef = resolveSecretInputRef({
|
||||
value: params.cfg.gateway?.auth?.password,
|
||||
defaults,
|
||||
}).ref;
|
||||
const remoteTokenRef = resolveSecretInputRef({
|
||||
value: remote?.token,
|
||||
defaults,
|
||||
}).ref;
|
||||
const remotePasswordRef = resolveSecretInputRef({
|
||||
value: remote?.password,
|
||||
defaults,
|
||||
}).ref;
|
||||
const remoteToken = remoteTokenRef ? undefined : trimToUndefined(remote?.token);
|
||||
const remotePassword = remotePasswordRef ? undefined : trimToUndefined(remote?.password);
|
||||
const localToken = localTokenRef ? undefined : trimToUndefined(params.cfg.gateway?.auth?.token);
|
||||
const localPassword = localPasswordRef
|
||||
? undefined
|
||||
: trimToUndefined(params.cfg.gateway?.auth?.password);
|
||||
|
||||
const localTokenPrecedence = params.localTokenPrecedence ?? "env-first";
|
||||
const localPasswordPrecedence = params.localPasswordPrecedence ?? "env-first";
|
||||
@@ -172,10 +216,15 @@ export function resolveGatewayCredentialsFromConfig(params: {
|
||||
authMode !== "none" &&
|
||||
authMode !== "trusted-proxy" &&
|
||||
!localResolved.token);
|
||||
const localPasswordRef = resolveSecretInputRef({
|
||||
value: params.cfg.gateway?.auth?.password,
|
||||
defaults,
|
||||
}).ref;
|
||||
const localTokenCanWin =
|
||||
authMode === "token" ||
|
||||
(authMode !== "password" &&
|
||||
authMode !== "none" &&
|
||||
authMode !== "trusted-proxy" &&
|
||||
!localResolved.password);
|
||||
if (localTokenRef && !localResolved.token && !envToken && localTokenCanWin) {
|
||||
throwUnresolvedGatewaySecretInput("gateway.auth.token");
|
||||
}
|
||||
if (localPasswordRef && !localResolved.password && !envPassword && localPasswordCanWin) {
|
||||
throwUnresolvedGatewaySecretInput("gateway.auth.password");
|
||||
}
|
||||
@@ -200,14 +249,10 @@ export function resolveGatewayCredentialsFromConfig(params: {
|
||||
? firstDefined([envPassword, remotePassword, localPassword])
|
||||
: firstDefined([remotePassword, envPassword, localPassword]);
|
||||
|
||||
const remoteTokenRef = resolveSecretInputRef({
|
||||
value: remote?.token,
|
||||
defaults,
|
||||
}).ref;
|
||||
const remotePasswordRef = resolveSecretInputRef({
|
||||
value: remote?.password,
|
||||
defaults,
|
||||
}).ref;
|
||||
const localTokenCanWin =
|
||||
authMode === "token" ||
|
||||
(authMode !== "password" && authMode !== "none" && authMode !== "trusted-proxy");
|
||||
const localTokenFallbackEnabled = remoteTokenFallback !== "remote-only";
|
||||
const localTokenFallback = remoteTokenFallback === "remote-only" ? undefined : localToken;
|
||||
const localPasswordFallback =
|
||||
remotePasswordFallback === "remote-only" ? undefined : localPassword;
|
||||
@@ -217,6 +262,17 @@ export function resolveGatewayCredentialsFromConfig(params: {
|
||||
if (remotePasswordRef && !password && !envPassword && !localPasswordFallback && !token) {
|
||||
throwUnresolvedGatewaySecretInput("gateway.remote.password");
|
||||
}
|
||||
if (
|
||||
localTokenRef &&
|
||||
localTokenFallbackEnabled &&
|
||||
!token &&
|
||||
!password &&
|
||||
!envToken &&
|
||||
!remoteToken &&
|
||||
localTokenCanWin
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput("gateway.auth.token");
|
||||
}
|
||||
|
||||
return { token, password };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user