mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
refactor: unify gateway credential planning
This commit is contained in:
@@ -1,16 +1,10 @@
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { resolveGatewayCredentialsFromConfig } from "../../gateway/credentials.js";
|
||||
import { resolveGatewayDriftCheckCredentialsFromConfig } from "../../gateway/credentials.js";
|
||||
|
||||
export function resolveGatewayTokenForDriftCheck(params: {
|
||||
cfg: OpenClawConfig;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}) {
|
||||
return resolveGatewayCredentialsFromConfig({
|
||||
cfg: params.cfg,
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
modeOverride: "local",
|
||||
// Drift checks should compare the configured local token source against the
|
||||
// persisted service token, not let exported shell env hide stale service state.
|
||||
localTokenPrecedence: "config-first",
|
||||
}).token;
|
||||
void params.env;
|
||||
return resolveGatewayDriftCheckCredentialsFromConfig({ cfg: params.cfg }).token;
|
||||
}
|
||||
|
||||
220
src/gateway/credential-planner.ts
Normal file
220
src/gateway/credential-planner.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { containsEnvVarReference } from "../config/env-substitution.js";
|
||||
import { hasConfiguredSecretInput, resolveSecretInputRef } from "../config/types.secrets.js";
|
||||
|
||||
export type GatewayCredentialInputPath =
|
||||
| "gateway.auth.token"
|
||||
| "gateway.auth.password"
|
||||
| "gateway.remote.token"
|
||||
| "gateway.remote.password";
|
||||
|
||||
export type GatewayConfiguredCredentialInput = {
|
||||
path: GatewayCredentialInputPath;
|
||||
configured: boolean;
|
||||
value?: string;
|
||||
refPath?: GatewayCredentialInputPath;
|
||||
hasSecretRef: boolean;
|
||||
};
|
||||
|
||||
export type GatewayCredentialPlan = {
|
||||
configuredMode: "local" | "remote";
|
||||
authMode?: string;
|
||||
envToken?: string;
|
||||
envPassword?: string;
|
||||
localToken: GatewayConfiguredCredentialInput;
|
||||
localPassword: GatewayConfiguredCredentialInput;
|
||||
remoteToken: GatewayConfiguredCredentialInput;
|
||||
remotePassword: GatewayConfiguredCredentialInput;
|
||||
localTokenCanWin: boolean;
|
||||
localPasswordCanWin: boolean;
|
||||
localTokenSurfaceActive: boolean;
|
||||
tokenCanWin: boolean;
|
||||
passwordCanWin: boolean;
|
||||
remoteMode: boolean;
|
||||
remoteUrlConfigured: boolean;
|
||||
tailscaleRemoteExposure: boolean;
|
||||
remoteEnabled: boolean;
|
||||
remoteConfiguredSurface: boolean;
|
||||
remoteTokenFallbackActive: boolean;
|
||||
remoteTokenActive: boolean;
|
||||
remotePasswordFallbackActive: boolean;
|
||||
remotePasswordActive: boolean;
|
||||
};
|
||||
|
||||
type GatewaySecretDefaults = NonNullable<OpenClawConfig["secrets"]>["defaults"];
|
||||
|
||||
function readGatewayEnv(
|
||||
env: NodeJS.ProcessEnv,
|
||||
names: readonly string[],
|
||||
includeLegacyEnv: boolean,
|
||||
): string | undefined {
|
||||
const keys = includeLegacyEnv ? names : names.slice(0, 1);
|
||||
for (const name of keys) {
|
||||
const value = trimToUndefined(env[name]);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function trimToUndefined(value: unknown): string | undefined {
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like trimToUndefined but also rejects unresolved env var placeholders (e.g. `${VAR}`).
|
||||
* This prevents literal placeholder strings like `${OPENCLAW_GATEWAY_TOKEN}` from being
|
||||
* accepted as valid credentials when the referenced env var is missing.
|
||||
* Note: legitimate credential values containing literal `${UPPER_CASE}` patterns will
|
||||
* also be rejected, but this is an extremely unlikely edge case.
|
||||
*/
|
||||
export function trimCredentialToUndefined(value: unknown): string | undefined {
|
||||
const trimmed = trimToUndefined(value);
|
||||
if (trimmed && containsEnvVarReference(trimmed)) {
|
||||
return undefined;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
export function readGatewayTokenEnv(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
includeLegacyEnv = true,
|
||||
): string | undefined {
|
||||
return readGatewayEnv(
|
||||
env,
|
||||
["OPENCLAW_GATEWAY_TOKEN", "CLAWDBOT_GATEWAY_TOKEN"],
|
||||
includeLegacyEnv,
|
||||
);
|
||||
}
|
||||
|
||||
export function readGatewayPasswordEnv(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
includeLegacyEnv = true,
|
||||
): string | undefined {
|
||||
return readGatewayEnv(
|
||||
env,
|
||||
["OPENCLAW_GATEWAY_PASSWORD", "CLAWDBOT_GATEWAY_PASSWORD"],
|
||||
includeLegacyEnv,
|
||||
);
|
||||
}
|
||||
|
||||
export function hasGatewayTokenEnvCandidate(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
includeLegacyEnv = true,
|
||||
): boolean {
|
||||
return Boolean(readGatewayTokenEnv(env, includeLegacyEnv));
|
||||
}
|
||||
|
||||
export function hasGatewayPasswordEnvCandidate(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
includeLegacyEnv = true,
|
||||
): boolean {
|
||||
return Boolean(readGatewayPasswordEnv(env, includeLegacyEnv));
|
||||
}
|
||||
|
||||
function resolveConfiguredGatewayCredentialInput(params: {
|
||||
value: unknown;
|
||||
defaults?: GatewaySecretDefaults;
|
||||
path: GatewayCredentialInputPath;
|
||||
}): GatewayConfiguredCredentialInput {
|
||||
const ref = resolveSecretInputRef({
|
||||
value: params.value,
|
||||
defaults: params.defaults,
|
||||
}).ref;
|
||||
return {
|
||||
path: params.path,
|
||||
configured: hasConfiguredSecretInput(params.value, params.defaults),
|
||||
value: ref ? undefined : trimToUndefined(params.value),
|
||||
refPath: ref ? params.path : undefined,
|
||||
hasSecretRef: ref !== null,
|
||||
};
|
||||
}
|
||||
|
||||
export function createGatewayCredentialPlan(params: {
|
||||
config: OpenClawConfig;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
includeLegacyEnv?: boolean;
|
||||
defaults?: GatewaySecretDefaults;
|
||||
}): GatewayCredentialPlan {
|
||||
const env = params.env ?? process.env;
|
||||
const includeLegacyEnv = params.includeLegacyEnv ?? true;
|
||||
const gateway = params.config.gateway;
|
||||
const remote = gateway?.remote;
|
||||
const defaults = params.defaults ?? params.config.secrets?.defaults;
|
||||
const authMode = gateway?.auth?.mode;
|
||||
const envToken = readGatewayTokenEnv(env, includeLegacyEnv);
|
||||
const envPassword = readGatewayPasswordEnv(env, includeLegacyEnv);
|
||||
|
||||
const localToken = resolveConfiguredGatewayCredentialInput({
|
||||
value: gateway?.auth?.token,
|
||||
defaults,
|
||||
path: "gateway.auth.token",
|
||||
});
|
||||
const localPassword = resolveConfiguredGatewayCredentialInput({
|
||||
value: gateway?.auth?.password,
|
||||
defaults,
|
||||
path: "gateway.auth.password",
|
||||
});
|
||||
const remoteToken = resolveConfiguredGatewayCredentialInput({
|
||||
value: remote?.token,
|
||||
defaults,
|
||||
path: "gateway.remote.token",
|
||||
});
|
||||
const remotePassword = resolveConfiguredGatewayCredentialInput({
|
||||
value: remote?.password,
|
||||
defaults,
|
||||
path: "gateway.remote.password",
|
||||
});
|
||||
|
||||
const localTokenCanWin =
|
||||
authMode !== "password" && authMode !== "none" && authMode !== "trusted-proxy";
|
||||
const tokenCanWin = Boolean(envToken || localToken.configured || remoteToken.configured);
|
||||
const passwordCanWin =
|
||||
authMode === "password" ||
|
||||
(authMode !== "token" && authMode !== "none" && authMode !== "trusted-proxy" && !tokenCanWin);
|
||||
const localTokenSurfaceActive =
|
||||
localTokenCanWin &&
|
||||
!envToken &&
|
||||
(authMode === "token" ||
|
||||
(authMode === undefined && !(envPassword || localPassword.configured)));
|
||||
|
||||
const remoteMode = gateway?.mode === "remote";
|
||||
const remoteUrlConfigured = Boolean(trimToUndefined(remote?.url));
|
||||
const tailscaleRemoteExposure =
|
||||
gateway?.tailscale?.mode === "serve" || gateway?.tailscale?.mode === "funnel";
|
||||
const remoteEnabled = remote?.enabled !== false;
|
||||
const remoteConfiguredSurface = remoteMode || remoteUrlConfigured || tailscaleRemoteExposure;
|
||||
const remoteTokenFallbackActive = localTokenCanWin && !envToken && !localToken.configured;
|
||||
const remotePasswordFallbackActive = !envPassword && !localPassword.configured && passwordCanWin;
|
||||
|
||||
return {
|
||||
configuredMode: gateway?.mode === "remote" ? "remote" : "local",
|
||||
authMode,
|
||||
envToken,
|
||||
envPassword,
|
||||
localToken,
|
||||
localPassword,
|
||||
remoteToken,
|
||||
remotePassword,
|
||||
localTokenCanWin,
|
||||
localPasswordCanWin: passwordCanWin,
|
||||
localTokenSurfaceActive,
|
||||
tokenCanWin,
|
||||
passwordCanWin,
|
||||
remoteMode,
|
||||
remoteUrlConfigured,
|
||||
tailscaleRemoteExposure,
|
||||
remoteEnabled,
|
||||
remoteConfiguredSurface,
|
||||
remoteTokenFallbackActive,
|
||||
remoteTokenActive: remoteEnabled && (remoteConfiguredSurface || remoteTokenFallbackActive),
|
||||
remotePasswordFallbackActive,
|
||||
remotePasswordActive:
|
||||
remoteEnabled && (remoteConfiguredSurface || remotePasswordFallbackActive),
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,20 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { containsEnvVarReference } from "../config/env-substitution.js";
|
||||
import { hasConfiguredSecretInput, resolveSecretInputRef } from "../config/types.secrets.js";
|
||||
import {
|
||||
createGatewayCredentialPlan,
|
||||
type GatewayCredentialPlan,
|
||||
readGatewayPasswordEnv,
|
||||
readGatewayTokenEnv,
|
||||
trimCredentialToUndefined,
|
||||
trimToUndefined,
|
||||
} from "./credential-planner.js";
|
||||
export {
|
||||
hasGatewayPasswordEnvCandidate,
|
||||
hasGatewayTokenEnvCandidate,
|
||||
readGatewayPasswordEnv,
|
||||
readGatewayTokenEnv,
|
||||
trimCredentialToUndefined,
|
||||
trimToUndefined,
|
||||
} from "./credential-planner.js";
|
||||
|
||||
export type ExplicitGatewayAuth = {
|
||||
token?: string;
|
||||
@@ -16,13 +30,6 @@ export type GatewayCredentialMode = "local" | "remote";
|
||||
export type GatewayCredentialPrecedence = "env-first" | "config-first";
|
||||
export type GatewayRemoteCredentialPrecedence = "remote-first" | "env-first";
|
||||
export type GatewayRemoteCredentialFallback = "remote-env-local" | "remote-only";
|
||||
type GatewaySecretDefaults = NonNullable<OpenClawConfig["secrets"]>["defaults"];
|
||||
|
||||
type GatewayConfiguredCredentialInput = {
|
||||
configured: boolean;
|
||||
value?: string;
|
||||
refPath?: string;
|
||||
};
|
||||
|
||||
const GATEWAY_SECRET_REF_UNAVAILABLE_ERROR_CODE = "GATEWAY_SECRET_REF_UNAVAILABLE"; // pragma: allowlist secret
|
||||
|
||||
@@ -56,29 +63,6 @@ export function isGatewaySecretRefUnavailableError(
|
||||
return error.path === expectedPath;
|
||||
}
|
||||
|
||||
export function trimToUndefined(value: unknown): string | undefined {
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like trimToUndefined but also rejects unresolved env var placeholders (e.g. `${VAR}`).
|
||||
* This prevents literal placeholder strings like `${OPENCLAW_GATEWAY_TOKEN}` from being
|
||||
* accepted as valid credentials when the referenced env var is missing.
|
||||
* Note: legitimate credential values containing literal `${UPPER_CASE}` patterns will
|
||||
* also be rejected, but this is an extremely unlikely edge case.
|
||||
*/
|
||||
export function trimCredentialToUndefined(value: unknown): string | undefined {
|
||||
const trimmed = trimToUndefined(value);
|
||||
if (trimmed && containsEnvVarReference(trimmed)) {
|
||||
return undefined;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function firstDefined(values: Array<string | undefined>): string | undefined {
|
||||
for (const value of values) {
|
||||
if (value) {
|
||||
@@ -92,64 +76,6 @@ function throwUnresolvedGatewaySecretInput(path: string): never {
|
||||
throw new GatewaySecretRefUnavailableError(path);
|
||||
}
|
||||
|
||||
function resolveConfiguredGatewayCredentialInput(params: {
|
||||
value: unknown;
|
||||
defaults?: GatewaySecretDefaults;
|
||||
path: string;
|
||||
}): GatewayConfiguredCredentialInput {
|
||||
const ref = resolveSecretInputRef({
|
||||
value: params.value,
|
||||
defaults: params.defaults,
|
||||
}).ref;
|
||||
return {
|
||||
configured: hasConfiguredSecretInput(params.value, params.defaults),
|
||||
value: ref ? undefined : trimToUndefined(params.value),
|
||||
refPath: ref ? params.path : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function readGatewayTokenEnv(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
includeLegacyEnv = true,
|
||||
): string | undefined {
|
||||
const primary = trimToUndefined(env.OPENCLAW_GATEWAY_TOKEN);
|
||||
if (primary) {
|
||||
return primary;
|
||||
}
|
||||
if (!includeLegacyEnv) {
|
||||
return undefined;
|
||||
}
|
||||
return trimToUndefined(env.CLAWDBOT_GATEWAY_TOKEN);
|
||||
}
|
||||
|
||||
export function readGatewayPasswordEnv(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
includeLegacyEnv = true,
|
||||
): string | undefined {
|
||||
const primary = trimToUndefined(env.OPENCLAW_GATEWAY_PASSWORD);
|
||||
if (primary) {
|
||||
return primary;
|
||||
}
|
||||
if (!includeLegacyEnv) {
|
||||
return undefined;
|
||||
}
|
||||
return trimToUndefined(env.CLAWDBOT_GATEWAY_PASSWORD);
|
||||
}
|
||||
|
||||
export function hasGatewayTokenEnvCandidate(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
includeLegacyEnv = true,
|
||||
): boolean {
|
||||
return Boolean(readGatewayTokenEnv(env, includeLegacyEnv));
|
||||
}
|
||||
|
||||
export function hasGatewayPasswordEnvCandidate(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
includeLegacyEnv = true,
|
||||
): boolean {
|
||||
return Boolean(readGatewayPasswordEnv(env, includeLegacyEnv));
|
||||
}
|
||||
|
||||
export function resolveGatewayCredentialsFromValues(params: {
|
||||
configToken?: unknown;
|
||||
configPassword?: unknown;
|
||||
@@ -179,6 +105,151 @@ export function resolveGatewayCredentialsFromValues(params: {
|
||||
return { token, password };
|
||||
}
|
||||
|
||||
function resolveLocalGatewayCredentials(params: {
|
||||
plan: GatewayCredentialPlan;
|
||||
env: NodeJS.ProcessEnv;
|
||||
includeLegacyEnv: boolean;
|
||||
localTokenPrecedence: GatewayCredentialPrecedence;
|
||||
localPasswordPrecedence: GatewayCredentialPrecedence;
|
||||
}): ResolvedGatewayCredentials {
|
||||
const fallbackToken = params.plan.localToken.configured
|
||||
? params.plan.localToken.value
|
||||
: params.plan.remoteToken.value;
|
||||
const fallbackPassword = params.plan.localPassword.configured
|
||||
? params.plan.localPassword.value
|
||||
: params.plan.remotePassword.value;
|
||||
const localResolved = resolveGatewayCredentialsFromValues({
|
||||
configToken: fallbackToken,
|
||||
configPassword: fallbackPassword,
|
||||
env: params.env,
|
||||
includeLegacyEnv: params.includeLegacyEnv,
|
||||
tokenPrecedence: params.localTokenPrecedence,
|
||||
passwordPrecedence: params.localPasswordPrecedence,
|
||||
});
|
||||
const localPasswordCanWin =
|
||||
params.plan.authMode === "password" ||
|
||||
(params.plan.authMode !== "token" &&
|
||||
params.plan.authMode !== "none" &&
|
||||
params.plan.authMode !== "trusted-proxy" &&
|
||||
!localResolved.token);
|
||||
const localTokenCanWin =
|
||||
params.plan.authMode === "token" ||
|
||||
(params.plan.authMode !== "password" &&
|
||||
params.plan.authMode !== "none" &&
|
||||
params.plan.authMode !== "trusted-proxy" &&
|
||||
!localResolved.password);
|
||||
|
||||
if (
|
||||
params.plan.localToken.refPath &&
|
||||
params.localTokenPrecedence === "config-first" &&
|
||||
!params.plan.localToken.value &&
|
||||
Boolean(params.plan.envToken) &&
|
||||
localTokenCanWin
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput(params.plan.localToken.refPath);
|
||||
}
|
||||
if (
|
||||
params.plan.localPassword.refPath &&
|
||||
params.localPasswordPrecedence === "config-first" && // pragma: allowlist secret
|
||||
!params.plan.localPassword.value &&
|
||||
Boolean(params.plan.envPassword) &&
|
||||
localPasswordCanWin
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput(params.plan.localPassword.refPath);
|
||||
}
|
||||
if (
|
||||
params.plan.localToken.refPath &&
|
||||
!localResolved.token &&
|
||||
!params.plan.envToken &&
|
||||
localTokenCanWin
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput(params.plan.localToken.refPath);
|
||||
}
|
||||
if (
|
||||
params.plan.localPassword.refPath &&
|
||||
!localResolved.password &&
|
||||
!params.plan.envPassword &&
|
||||
localPasswordCanWin
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput(params.plan.localPassword.refPath);
|
||||
}
|
||||
return localResolved;
|
||||
}
|
||||
|
||||
function resolveRemoteGatewayCredentials(params: {
|
||||
plan: GatewayCredentialPlan;
|
||||
remoteTokenPrecedence: GatewayRemoteCredentialPrecedence;
|
||||
remotePasswordPrecedence: GatewayRemoteCredentialPrecedence;
|
||||
remoteTokenFallback: GatewayRemoteCredentialFallback;
|
||||
remotePasswordFallback: GatewayRemoteCredentialFallback;
|
||||
}): ResolvedGatewayCredentials {
|
||||
const token =
|
||||
params.remoteTokenFallback === "remote-only"
|
||||
? params.plan.remoteToken.value
|
||||
: params.remoteTokenPrecedence === "env-first"
|
||||
? firstDefined([
|
||||
params.plan.envToken,
|
||||
params.plan.remoteToken.value,
|
||||
params.plan.localToken.value,
|
||||
])
|
||||
: firstDefined([
|
||||
params.plan.remoteToken.value,
|
||||
params.plan.envToken,
|
||||
params.plan.localToken.value,
|
||||
]);
|
||||
const password =
|
||||
params.remotePasswordFallback === "remote-only" // pragma: allowlist secret
|
||||
? params.plan.remotePassword.value
|
||||
: params.remotePasswordPrecedence === "env-first" // pragma: allowlist secret
|
||||
? firstDefined([
|
||||
params.plan.envPassword,
|
||||
params.plan.remotePassword.value,
|
||||
params.plan.localPassword.value,
|
||||
])
|
||||
: firstDefined([
|
||||
params.plan.remotePassword.value,
|
||||
params.plan.envPassword,
|
||||
params.plan.localPassword.value,
|
||||
]);
|
||||
const localTokenFallbackEnabled = params.remoteTokenFallback !== "remote-only";
|
||||
const localTokenFallback =
|
||||
params.remoteTokenFallback === "remote-only" ? undefined : params.plan.localToken.value;
|
||||
const localPasswordFallback =
|
||||
params.remotePasswordFallback === "remote-only" ? undefined : params.plan.localPassword.value; // pragma: allowlist secret
|
||||
|
||||
if (
|
||||
params.plan.remoteToken.refPath &&
|
||||
!token &&
|
||||
!params.plan.envToken &&
|
||||
!localTokenFallback &&
|
||||
!password
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput(params.plan.remoteToken.refPath);
|
||||
}
|
||||
if (
|
||||
params.plan.remotePassword.refPath &&
|
||||
!password &&
|
||||
!params.plan.envPassword &&
|
||||
!localPasswordFallback &&
|
||||
!token
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput(params.plan.remotePassword.refPath);
|
||||
}
|
||||
if (
|
||||
params.plan.localToken.refPath &&
|
||||
localTokenFallbackEnabled &&
|
||||
!token &&
|
||||
!password &&
|
||||
!params.plan.envToken &&
|
||||
!params.plan.remoteToken.value &&
|
||||
params.plan.localTokenCanWin
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput(params.plan.localToken.refPath);
|
||||
}
|
||||
|
||||
return { token, password };
|
||||
}
|
||||
|
||||
export function resolveGatewayCredentialsFromConfig(params: {
|
||||
cfg: OpenClawConfig;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
@@ -215,42 +286,12 @@ export function resolveGatewayCredentialsFromConfig(params: {
|
||||
});
|
||||
}
|
||||
|
||||
const mode: GatewayCredentialMode =
|
||||
params.modeOverride ?? (params.cfg.gateway?.mode === "remote" ? "remote" : "local");
|
||||
const remote = params.cfg.gateway?.remote;
|
||||
const defaults = params.cfg.secrets?.defaults;
|
||||
const authMode = params.cfg.gateway?.auth?.mode;
|
||||
const envToken = readGatewayTokenEnv(env, includeLegacyEnv);
|
||||
const envPassword = readGatewayPasswordEnv(env, includeLegacyEnv);
|
||||
|
||||
const localTokenInput = resolveConfiguredGatewayCredentialInput({
|
||||
value: params.cfg.gateway?.auth?.token,
|
||||
defaults,
|
||||
path: "gateway.auth.token",
|
||||
const plan = createGatewayCredentialPlan({
|
||||
config: params.cfg,
|
||||
env,
|
||||
includeLegacyEnv,
|
||||
});
|
||||
const localPasswordInput = resolveConfiguredGatewayCredentialInput({
|
||||
value: params.cfg.gateway?.auth?.password,
|
||||
defaults,
|
||||
path: "gateway.auth.password",
|
||||
});
|
||||
const remoteTokenInput = resolveConfiguredGatewayCredentialInput({
|
||||
value: remote?.token,
|
||||
defaults,
|
||||
path: "gateway.remote.token",
|
||||
});
|
||||
const remotePasswordInput = resolveConfiguredGatewayCredentialInput({
|
||||
value: remote?.password,
|
||||
defaults,
|
||||
path: "gateway.remote.password",
|
||||
});
|
||||
const localTokenRef = localTokenInput.refPath;
|
||||
const localPasswordRef = localPasswordInput.refPath;
|
||||
const remoteTokenRef = remoteTokenInput.refPath;
|
||||
const remotePasswordRef = remotePasswordInput.refPath;
|
||||
const remoteToken = remoteTokenInput.value;
|
||||
const remotePassword = remotePasswordInput.value;
|
||||
const localToken = localTokenInput.value;
|
||||
const localPassword = localPasswordInput.value;
|
||||
const mode: GatewayCredentialMode = params.modeOverride ?? plan.configuredMode;
|
||||
|
||||
const localTokenPrecedence =
|
||||
params.localTokenPrecedence ??
|
||||
@@ -258,56 +299,13 @@ export function resolveGatewayCredentialsFromConfig(params: {
|
||||
const localPasswordPrecedence = params.localPasswordPrecedence ?? "env-first";
|
||||
|
||||
if (mode === "local") {
|
||||
// In local mode, prefer gateway.auth.token, but also accept gateway.remote.token
|
||||
// as a fallback for cron commands and other local gateway clients.
|
||||
// This allows users in remote mode to use a single token for all operations.
|
||||
const fallbackToken = localTokenInput.configured ? localToken : remoteToken;
|
||||
const fallbackPassword = localPasswordInput.configured ? localPassword : remotePassword;
|
||||
const localResolved = resolveGatewayCredentialsFromValues({
|
||||
configToken: fallbackToken,
|
||||
configPassword: fallbackPassword,
|
||||
return resolveLocalGatewayCredentials({
|
||||
plan,
|
||||
env,
|
||||
includeLegacyEnv,
|
||||
tokenPrecedence: localTokenPrecedence,
|
||||
passwordPrecedence: localPasswordPrecedence,
|
||||
localTokenPrecedence,
|
||||
localPasswordPrecedence,
|
||||
});
|
||||
const localPasswordCanWin =
|
||||
authMode === "password" ||
|
||||
(authMode !== "token" &&
|
||||
authMode !== "none" &&
|
||||
authMode !== "trusted-proxy" &&
|
||||
!localResolved.token);
|
||||
const localTokenCanWin =
|
||||
authMode === "token" ||
|
||||
(authMode !== "password" &&
|
||||
authMode !== "none" &&
|
||||
authMode !== "trusted-proxy" &&
|
||||
!localResolved.password);
|
||||
if (
|
||||
localTokenRef &&
|
||||
localTokenPrecedence === "config-first" &&
|
||||
!localToken &&
|
||||
Boolean(envToken) &&
|
||||
localTokenCanWin
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput("gateway.auth.token");
|
||||
}
|
||||
if (
|
||||
localPasswordRef &&
|
||||
localPasswordPrecedence === "config-first" && // pragma: allowlist secret
|
||||
!localPassword &&
|
||||
Boolean(envPassword) &&
|
||||
localPasswordCanWin
|
||||
) {
|
||||
throwUnresolvedGatewaySecretInput("gateway.auth.password");
|
||||
}
|
||||
if (localTokenRef && !localResolved.token && !envToken && localTokenCanWin) {
|
||||
throwUnresolvedGatewaySecretInput("gateway.auth.token");
|
||||
}
|
||||
if (localPasswordRef && !localResolved.password && !envPassword && localPasswordCanWin) {
|
||||
throwUnresolvedGatewaySecretInput("gateway.auth.password");
|
||||
}
|
||||
return localResolved;
|
||||
}
|
||||
|
||||
const remoteTokenFallback = params.remoteTokenFallback ?? "remote-env-local";
|
||||
@@ -315,43 +313,38 @@ export function resolveGatewayCredentialsFromConfig(params: {
|
||||
const remoteTokenPrecedence = params.remoteTokenPrecedence ?? "remote-first";
|
||||
const remotePasswordPrecedence = params.remotePasswordPrecedence ?? "env-first";
|
||||
|
||||
const token =
|
||||
remoteTokenFallback === "remote-only"
|
||||
? remoteToken
|
||||
: remoteTokenPrecedence === "env-first"
|
||||
? firstDefined([envToken, remoteToken, localToken])
|
||||
: firstDefined([remoteToken, envToken, localToken]);
|
||||
const password =
|
||||
remotePasswordFallback === "remote-only" // pragma: allowlist secret
|
||||
? remotePassword
|
||||
: remotePasswordPrecedence === "env-first" // pragma: allowlist secret
|
||||
? firstDefined([envPassword, remotePassword, localPassword])
|
||||
: firstDefined([remotePassword, envPassword, localPassword]);
|
||||
|
||||
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; // pragma: allowlist secret
|
||||
if (remoteTokenRef && !token && !envToken && !localTokenFallback && !password) {
|
||||
throwUnresolvedGatewaySecretInput("gateway.remote.token");
|
||||
}
|
||||
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 };
|
||||
return resolveRemoteGatewayCredentials({
|
||||
plan,
|
||||
remoteTokenPrecedence,
|
||||
remotePasswordPrecedence,
|
||||
remoteTokenFallback,
|
||||
remotePasswordFallback,
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveGatewayProbeCredentialsFromConfig(params: {
|
||||
cfg: OpenClawConfig;
|
||||
mode: GatewayCredentialMode;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
explicitAuth?: ExplicitGatewayAuth;
|
||||
}): ResolvedGatewayCredentials {
|
||||
return resolveGatewayCredentialsFromConfig({
|
||||
cfg: params.cfg,
|
||||
env: params.env,
|
||||
explicitAuth: params.explicitAuth,
|
||||
modeOverride: params.mode,
|
||||
includeLegacyEnv: false,
|
||||
remoteTokenFallback: "remote-only",
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveGatewayDriftCheckCredentialsFromConfig(params: {
|
||||
cfg: OpenClawConfig;
|
||||
}): ResolvedGatewayCredentials {
|
||||
return resolveGatewayCredentialsFromConfig({
|
||||
cfg: params.cfg,
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
modeOverride: "local",
|
||||
localTokenPrecedence: "config-first",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,21 +3,34 @@ import { resolveGatewayCredentialsWithSecretInputs } from "./call.js";
|
||||
import {
|
||||
type ExplicitGatewayAuth,
|
||||
isGatewaySecretRefUnavailableError,
|
||||
resolveGatewayCredentialsFromConfig,
|
||||
resolveGatewayProbeCredentialsFromConfig,
|
||||
} from "./credentials.js";
|
||||
|
||||
function buildGatewayProbeCredentialPolicy(params: {
|
||||
cfg: OpenClawConfig;
|
||||
mode: "local" | "remote";
|
||||
env?: NodeJS.ProcessEnv;
|
||||
explicitAuth?: ExplicitGatewayAuth;
|
||||
}) {
|
||||
return {
|
||||
config: params.cfg,
|
||||
cfg: params.cfg,
|
||||
env: params.env,
|
||||
explicitAuth: params.explicitAuth,
|
||||
modeOverride: params.mode,
|
||||
mode: params.mode,
|
||||
includeLegacyEnv: false,
|
||||
remoteTokenFallback: "remote-only" as const,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveGatewayProbeAuth(params: {
|
||||
cfg: OpenClawConfig;
|
||||
mode: "local" | "remote";
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { token?: string; password?: string } {
|
||||
return resolveGatewayCredentialsFromConfig({
|
||||
cfg: params.cfg,
|
||||
env: params.env,
|
||||
modeOverride: params.mode,
|
||||
includeLegacyEnv: false,
|
||||
remoteTokenFallback: "remote-only",
|
||||
});
|
||||
const policy = buildGatewayProbeCredentialPolicy(params);
|
||||
return resolveGatewayProbeCredentialsFromConfig(policy);
|
||||
}
|
||||
|
||||
export async function resolveGatewayProbeAuthWithSecretInputs(params: {
|
||||
@@ -26,13 +39,14 @@ export async function resolveGatewayProbeAuthWithSecretInputs(params: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
explicitAuth?: ExplicitGatewayAuth;
|
||||
}): Promise<{ token?: string; password?: string }> {
|
||||
const policy = buildGatewayProbeCredentialPolicy(params);
|
||||
return await resolveGatewayCredentialsWithSecretInputs({
|
||||
config: params.cfg,
|
||||
env: params.env,
|
||||
explicitAuth: params.explicitAuth,
|
||||
modeOverride: params.mode,
|
||||
includeLegacyEnv: false,
|
||||
remoteTokenFallback: "remote-only",
|
||||
config: policy.config,
|
||||
env: policy.env,
|
||||
explicitAuth: policy.explicitAuth,
|
||||
modeOverride: policy.modeOverride,
|
||||
includeLegacyEnv: policy.includeLegacyEnv,
|
||||
remoteTokenFallback: policy.remoteTokenFallback,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,28 @@ describe("evaluateGatewayAuthSurfaceStates", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("marks gateway.remote.token inactive when local token SecretRef is configured", () => {
|
||||
const states = evaluate({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: {
|
||||
mode: "token",
|
||||
token: envRef("GW_AUTH_TOKEN"),
|
||||
},
|
||||
remote: {
|
||||
enabled: true,
|
||||
token: envRef("GW_REMOTE_TOKEN"),
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig);
|
||||
|
||||
expect(states["gateway.remote.token"]).toMatchObject({
|
||||
hasSecretRef: true,
|
||||
active: false,
|
||||
reason: "gateway.auth.token is configured.",
|
||||
});
|
||||
});
|
||||
|
||||
it("marks gateway.remote.password active when remote url is configured", () => {
|
||||
const states = evaluate({
|
||||
gateway: {
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { coerceSecretRef, hasConfiguredSecretInput } from "../config/types.secrets.js";
|
||||
import { createGatewayCredentialPlan } from "../gateway/credential-planner.js";
|
||||
import type { SecretDefaults } from "./runtime-shared.js";
|
||||
import { isRecord } from "./shared.js";
|
||||
|
||||
const GATEWAY_TOKEN_ENV_KEYS = ["OPENCLAW_GATEWAY_TOKEN", "CLAWDBOT_GATEWAY_TOKEN"] as const;
|
||||
const GATEWAY_PASSWORD_ENV_KEYS = [
|
||||
"OPENCLAW_GATEWAY_PASSWORD",
|
||||
"CLAWDBOT_GATEWAY_PASSWORD",
|
||||
] as const;
|
||||
|
||||
export const GATEWAY_AUTH_SURFACE_PATHS = [
|
||||
"gateway.auth.token",
|
||||
"gateway.auth.password",
|
||||
@@ -27,20 +21,6 @@ export type GatewayAuthSurfaceState = {
|
||||
|
||||
export type GatewayAuthSurfaceStateMap = Record<GatewayAuthSurfacePath, GatewayAuthSurfaceState>;
|
||||
|
||||
function readNonEmptyEnv(env: NodeJS.ProcessEnv, names: readonly string[]): string | undefined {
|
||||
for (const name of names) {
|
||||
const raw = env[name];
|
||||
if (typeof raw !== "string") {
|
||||
continue;
|
||||
}
|
||||
const trimmed = raw.trim();
|
||||
if (trimmed.length > 0) {
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function formatAuthMode(mode: string | undefined): string {
|
||||
return mode ?? "unset";
|
||||
}
|
||||
@@ -82,7 +62,6 @@ export function evaluateGatewayAuthSurfaceStates(params: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
defaults?: SecretDefaults;
|
||||
}): GatewayAuthSurfaceStateMap {
|
||||
const defaults = params.defaults ?? params.config.secrets?.defaults;
|
||||
const gateway = params.config.gateway as Record<string, unknown> | undefined;
|
||||
if (!isRecord(gateway)) {
|
||||
return {
|
||||
@@ -114,65 +93,36 @@ export function evaluateGatewayAuthSurfaceStates(params: {
|
||||
}
|
||||
const auth = isRecord(gateway?.auth) ? gateway.auth : undefined;
|
||||
const remote = isRecord(gateway?.remote) ? gateway.remote : undefined;
|
||||
const authMode = auth && typeof auth.mode === "string" ? auth.mode : undefined;
|
||||
|
||||
const hasAuthTokenRef = coerceSecretRef(auth?.token, defaults) !== null;
|
||||
const hasAuthPasswordRef = coerceSecretRef(auth?.password, defaults) !== null;
|
||||
const hasRemoteTokenRef = coerceSecretRef(remote?.token, defaults) !== null;
|
||||
const hasRemotePasswordRef = coerceSecretRef(remote?.password, defaults) !== null;
|
||||
|
||||
const envToken = readNonEmptyEnv(params.env, GATEWAY_TOKEN_ENV_KEYS);
|
||||
const envPassword = readNonEmptyEnv(params.env, GATEWAY_PASSWORD_ENV_KEYS);
|
||||
const localTokenConfigured = hasConfiguredSecretInput(auth?.token, defaults);
|
||||
const localPasswordConfigured = hasConfiguredSecretInput(auth?.password, defaults);
|
||||
const remoteTokenConfigured = hasConfiguredSecretInput(remote?.token, defaults);
|
||||
const passwordSourceConfigured = Boolean(envPassword || localPasswordConfigured);
|
||||
|
||||
const localTokenCanWin =
|
||||
authMode !== "password" && authMode !== "none" && authMode !== "trusted-proxy";
|
||||
const localTokenSurfaceActive =
|
||||
localTokenCanWin &&
|
||||
!envToken &&
|
||||
(authMode === "token" || (authMode === undefined && !passwordSourceConfigured));
|
||||
const tokenCanWin = Boolean(envToken || localTokenConfigured || remoteTokenConfigured);
|
||||
const passwordCanWin =
|
||||
authMode === "password" ||
|
||||
(authMode !== "token" && authMode !== "none" && authMode !== "trusted-proxy" && !tokenCanWin);
|
||||
|
||||
const remoteMode = gateway?.mode === "remote";
|
||||
const remoteUrlConfigured = typeof remote?.url === "string" && remote.url.trim().length > 0;
|
||||
const tailscale =
|
||||
isRecord(gateway?.tailscale) && typeof gateway.tailscale.mode === "string"
|
||||
? gateway.tailscale
|
||||
: undefined;
|
||||
const tailscaleRemoteExposure = tailscale?.mode === "serve" || tailscale?.mode === "funnel";
|
||||
const remoteEnabled = remote?.enabled !== false;
|
||||
const remoteConfiguredSurface = remoteMode || remoteUrlConfigured || tailscaleRemoteExposure;
|
||||
const remoteTokenFallbackActive = localTokenCanWin && !envToken && !localTokenConfigured;
|
||||
const remoteTokenActive = remoteEnabled && (remoteConfiguredSurface || remoteTokenFallbackActive);
|
||||
const remotePasswordFallbackActive = !envPassword && !localPasswordConfigured && passwordCanWin;
|
||||
const remotePasswordActive =
|
||||
remoteEnabled && (remoteConfiguredSurface || remotePasswordFallbackActive);
|
||||
const plan = createGatewayCredentialPlan({
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
includeLegacyEnv: true,
|
||||
defaults: params.defaults,
|
||||
});
|
||||
|
||||
const authPasswordReason = (() => {
|
||||
if (!auth) {
|
||||
return "gateway.auth is not configured.";
|
||||
}
|
||||
if (passwordCanWin) {
|
||||
return authMode === "password"
|
||||
if (plan.passwordCanWin) {
|
||||
return plan.authMode === "password"
|
||||
? 'gateway.auth.mode is "password".'
|
||||
: "no token source can win, so password auth can win.";
|
||||
}
|
||||
if (authMode === "token" || authMode === "none" || authMode === "trusted-proxy") {
|
||||
return `gateway.auth.mode is "${authMode}".`;
|
||||
if (
|
||||
plan.authMode === "token" ||
|
||||
plan.authMode === "none" ||
|
||||
plan.authMode === "trusted-proxy"
|
||||
) {
|
||||
return `gateway.auth.mode is "${plan.authMode}".`;
|
||||
}
|
||||
if (envToken) {
|
||||
if (plan.envToken) {
|
||||
return "gateway token env var is configured.";
|
||||
}
|
||||
if (localTokenConfigured) {
|
||||
if (plan.localToken.configured) {
|
||||
return "gateway.auth.token is configured.";
|
||||
}
|
||||
if (remoteTokenConfigured) {
|
||||
if (plan.remoteToken.configured) {
|
||||
return "gateway.remote.token is configured.";
|
||||
}
|
||||
return "token auth can win.";
|
||||
@@ -182,50 +132,56 @@ export function evaluateGatewayAuthSurfaceStates(params: {
|
||||
if (!auth) {
|
||||
return "gateway.auth is not configured.";
|
||||
}
|
||||
if (authMode === "token") {
|
||||
return envToken ? "gateway token env var is configured." : 'gateway.auth.mode is "token".';
|
||||
if (plan.authMode === "token") {
|
||||
return plan.envToken
|
||||
? "gateway token env var is configured."
|
||||
: 'gateway.auth.mode is "token".';
|
||||
}
|
||||
if (authMode === "password" || authMode === "none" || authMode === "trusted-proxy") {
|
||||
return `gateway.auth.mode is "${authMode}".`;
|
||||
if (
|
||||
plan.authMode === "password" ||
|
||||
plan.authMode === "none" ||
|
||||
plan.authMode === "trusted-proxy"
|
||||
) {
|
||||
return `gateway.auth.mode is "${plan.authMode}".`;
|
||||
}
|
||||
if (envToken) {
|
||||
if (plan.envToken) {
|
||||
return "gateway token env var is configured.";
|
||||
}
|
||||
if (envPassword) {
|
||||
if (plan.envPassword) {
|
||||
return "gateway password env var is configured.";
|
||||
}
|
||||
if (localPasswordConfigured) {
|
||||
if (plan.localPassword.configured) {
|
||||
return "gateway.auth.password is configured.";
|
||||
}
|
||||
return "token auth can win (mode is unset and no password source is configured).";
|
||||
})();
|
||||
|
||||
const remoteSurfaceReason = describeRemoteConfiguredSurface({
|
||||
remoteMode,
|
||||
remoteUrlConfigured,
|
||||
tailscaleRemoteExposure,
|
||||
remoteMode: plan.remoteMode,
|
||||
remoteUrlConfigured: plan.remoteUrlConfigured,
|
||||
tailscaleRemoteExposure: plan.tailscaleRemoteExposure,
|
||||
});
|
||||
|
||||
const remoteTokenReason = (() => {
|
||||
if (!remote) {
|
||||
return "gateway.remote is not configured.";
|
||||
}
|
||||
if (!remoteEnabled) {
|
||||
if (!plan.remoteEnabled) {
|
||||
return "gateway.remote.enabled is false.";
|
||||
}
|
||||
if (remoteConfiguredSurface) {
|
||||
if (plan.remoteConfiguredSurface) {
|
||||
return `remote surface is active: ${remoteSurfaceReason}.`;
|
||||
}
|
||||
if (remoteTokenFallbackActive) {
|
||||
if (plan.remoteTokenFallbackActive) {
|
||||
return "local token auth can win and no env/auth token is configured.";
|
||||
}
|
||||
if (!localTokenCanWin) {
|
||||
return `token auth cannot win with gateway.auth.mode="${formatAuthMode(authMode)}".`;
|
||||
if (!plan.localTokenCanWin) {
|
||||
return `token auth cannot win with gateway.auth.mode="${formatAuthMode(plan.authMode)}".`;
|
||||
}
|
||||
if (envToken) {
|
||||
if (plan.envToken) {
|
||||
return "gateway token env var is configured.";
|
||||
}
|
||||
if (localTokenConfigured) {
|
||||
if (plan.localToken.configured) {
|
||||
return "gateway.auth.token is configured.";
|
||||
}
|
||||
return "remote token fallback is not active.";
|
||||
@@ -235,25 +191,29 @@ export function evaluateGatewayAuthSurfaceStates(params: {
|
||||
if (!remote) {
|
||||
return "gateway.remote is not configured.";
|
||||
}
|
||||
if (!remoteEnabled) {
|
||||
if (!plan.remoteEnabled) {
|
||||
return "gateway.remote.enabled is false.";
|
||||
}
|
||||
if (remoteConfiguredSurface) {
|
||||
if (plan.remoteConfiguredSurface) {
|
||||
return `remote surface is active: ${remoteSurfaceReason}.`;
|
||||
}
|
||||
if (remotePasswordFallbackActive) {
|
||||
if (plan.remotePasswordFallbackActive) {
|
||||
return "password auth can win and no env/auth password is configured.";
|
||||
}
|
||||
if (!passwordCanWin) {
|
||||
if (authMode === "token" || authMode === "none" || authMode === "trusted-proxy") {
|
||||
return `password auth cannot win with gateway.auth.mode="${authMode}".`;
|
||||
if (!plan.passwordCanWin) {
|
||||
if (
|
||||
plan.authMode === "token" ||
|
||||
plan.authMode === "none" ||
|
||||
plan.authMode === "trusted-proxy"
|
||||
) {
|
||||
return `password auth cannot win with gateway.auth.mode="${plan.authMode}".`;
|
||||
}
|
||||
return "a token source can win, so password auth cannot win.";
|
||||
}
|
||||
if (envPassword) {
|
||||
if (plan.envPassword) {
|
||||
return "gateway password env var is configured.";
|
||||
}
|
||||
if (localPasswordConfigured) {
|
||||
if (plan.localPassword.configured) {
|
||||
return "gateway.auth.password is configured.";
|
||||
}
|
||||
return "remote password fallback is not active.";
|
||||
@@ -262,27 +222,27 @@ export function evaluateGatewayAuthSurfaceStates(params: {
|
||||
return {
|
||||
"gateway.auth.token": createState({
|
||||
path: "gateway.auth.token",
|
||||
active: localTokenSurfaceActive,
|
||||
active: plan.localTokenSurfaceActive,
|
||||
reason: authTokenReason,
|
||||
hasSecretRef: hasAuthTokenRef,
|
||||
hasSecretRef: plan.localToken.hasSecretRef,
|
||||
}),
|
||||
"gateway.auth.password": createState({
|
||||
path: "gateway.auth.password",
|
||||
active: passwordCanWin,
|
||||
active: plan.passwordCanWin,
|
||||
reason: authPasswordReason,
|
||||
hasSecretRef: hasAuthPasswordRef,
|
||||
hasSecretRef: plan.localPassword.hasSecretRef,
|
||||
}),
|
||||
"gateway.remote.token": createState({
|
||||
path: "gateway.remote.token",
|
||||
active: remoteTokenActive,
|
||||
active: plan.remoteTokenActive,
|
||||
reason: remoteTokenReason,
|
||||
hasSecretRef: hasRemoteTokenRef,
|
||||
hasSecretRef: plan.remoteToken.hasSecretRef,
|
||||
}),
|
||||
"gateway.remote.password": createState({
|
||||
path: "gateway.remote.password",
|
||||
active: remotePasswordActive,
|
||||
active: plan.remotePasswordActive,
|
||||
reason: remotePasswordReason,
|
||||
hasSecretRef: hasRemotePasswordRef,
|
||||
hasSecretRef: plan.remotePassword.hasSecretRef,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user