mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-30 14:40:20 +00:00
CLI: prune inactive gateway auth credentials on mode set (#50639)
This commit is contained in:
@@ -209,6 +209,94 @@ describe("config cli", () => {
|
||||
apiKey: "ollama-local", // pragma: allowlist secret
|
||||
});
|
||||
});
|
||||
|
||||
it("drops gateway.auth.password when switching mode to token", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: {
|
||||
auth: {
|
||||
mode: "password",
|
||||
token: "token-keep",
|
||||
password: "password-drop", // pragma: allowlist secret
|
||||
allowTailscale: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await runConfigCommand(["config", "set", "gateway.auth.mode", "token"]);
|
||||
|
||||
expect(mockWriteConfigFile).toHaveBeenCalledTimes(1);
|
||||
const written = mockWriteConfigFile.mock.calls[0]?.[0];
|
||||
expect(written.gateway?.auth).toEqual({
|
||||
mode: "token",
|
||||
token: "token-keep",
|
||||
allowTailscale: true,
|
||||
});
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"Removed inactive gateway.auth.password for gateway.auth.mode=token",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("drops gateway.auth.token when switching mode to password", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: {
|
||||
auth: {
|
||||
mode: "token",
|
||||
token: "token-drop",
|
||||
password: "password-keep", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await runConfigCommand(["config", "set", "gateway.auth.mode", "password"]);
|
||||
|
||||
expect(mockWriteConfigFile).toHaveBeenCalledTimes(1);
|
||||
const written = mockWriteConfigFile.mock.calls[0]?.[0];
|
||||
expect(written.gateway?.auth).toEqual({
|
||||
mode: "password",
|
||||
password: "password-keep", // pragma: allowlist secret
|
||||
});
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"Removed inactive gateway.auth.token for gateway.auth.mode=password",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("applies mode-based credential cleanup using the final batch result", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: {
|
||||
auth: {
|
||||
mode: "password",
|
||||
token: "token-keep",
|
||||
password: "password-drop", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await runConfigCommand([
|
||||
"config",
|
||||
"set",
|
||||
"--batch-json",
|
||||
'[{"path":"gateway.auth.password","value":"password-updated"},{"path":"gateway.auth.mode","value":"token"}]',
|
||||
]);
|
||||
|
||||
expect(mockWriteConfigFile).toHaveBeenCalledTimes(1);
|
||||
const written = mockWriteConfigFile.mock.calls[0]?.[0];
|
||||
expect(written.gateway?.auth).toEqual({
|
||||
mode: "token",
|
||||
token: "token-keep",
|
||||
});
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"Removed inactive gateway.auth.password for gateway.auth.mode=token",
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("config get", () => {
|
||||
|
||||
@@ -69,6 +69,7 @@ type ConfigSetOperation = {
|
||||
|
||||
const OLLAMA_API_KEY_PATH: PathSegment[] = ["models", "providers", "ollama", "apiKey"];
|
||||
const OLLAMA_PROVIDER_PATH: PathSegment[] = ["models", "providers", "ollama"];
|
||||
const GATEWAY_AUTH_MODE_PATH: PathSegment[] = ["gateway", "auth", "mode"];
|
||||
const SECRET_PROVIDER_PATH_PREFIX: PathSegment[] = ["secrets", "providers"];
|
||||
const CONFIG_SET_EXAMPLE_VALUE = formatCliCommand(
|
||||
"openclaw config set gateway.port 19001 --strict-json",
|
||||
@@ -352,6 +353,48 @@ function ensureValidOllamaProviderForApiKeySet(
|
||||
});
|
||||
}
|
||||
|
||||
function pruneInactiveGatewayAuthCredentials(params: {
|
||||
root: Record<string, unknown>;
|
||||
operations: ConfigSetOperation[];
|
||||
}): string[] {
|
||||
const touchedGatewayAuthMode = params.operations.some((operation) =>
|
||||
pathEquals(operation.requestedPath, GATEWAY_AUTH_MODE_PATH),
|
||||
);
|
||||
if (!touchedGatewayAuthMode) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const gatewayRaw = params.root.gateway;
|
||||
if (!gatewayRaw || typeof gatewayRaw !== "object" || Array.isArray(gatewayRaw)) {
|
||||
return [];
|
||||
}
|
||||
const gateway = gatewayRaw as Record<string, unknown>;
|
||||
const authRaw = gateway.auth;
|
||||
if (!authRaw || typeof authRaw !== "object" || Array.isArray(authRaw)) {
|
||||
return [];
|
||||
}
|
||||
const auth = authRaw as Record<string, unknown>;
|
||||
const mode = typeof auth.mode === "string" ? auth.mode.trim() : "";
|
||||
|
||||
const removedPaths: string[] = [];
|
||||
const remove = (key: "token" | "password") => {
|
||||
if (Object.hasOwn(auth, key)) {
|
||||
delete auth[key];
|
||||
removedPaths.push(`gateway.auth.${key}`);
|
||||
}
|
||||
};
|
||||
|
||||
if (mode === "token") {
|
||||
remove("password");
|
||||
} else if (mode === "password") {
|
||||
remove("token");
|
||||
} else if (mode === "trusted-proxy") {
|
||||
remove("token");
|
||||
remove("password");
|
||||
}
|
||||
return removedPaths;
|
||||
}
|
||||
|
||||
function toDotPath(path: PathSegment[]): string {
|
||||
return path.join(".");
|
||||
}
|
||||
@@ -964,6 +1007,10 @@ export async function runConfigSet(opts: {
|
||||
ensureValidOllamaProviderForApiKeySet(next, operation.setPath);
|
||||
setAtPath(next, operation.setPath, operation.value);
|
||||
}
|
||||
const removedGatewayAuthPaths = pruneInactiveGatewayAuthCredentials({
|
||||
root: next,
|
||||
operations,
|
||||
});
|
||||
const nextConfig = next as OpenClawConfig;
|
||||
|
||||
if (opts.cliOptions.dryRun) {
|
||||
@@ -1051,6 +1098,13 @@ export async function runConfigSet(opts: {
|
||||
}
|
||||
|
||||
await writeConfigFile(next);
|
||||
if (removedGatewayAuthPaths.length > 0) {
|
||||
runtime.log(
|
||||
info(
|
||||
`Removed inactive ${removedGatewayAuthPaths.join(", ")} for gateway.auth.mode=${String(nextConfig.gateway?.auth?.mode ?? "<unset>")}.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (operations.length === 1) {
|
||||
runtime.log(
|
||||
info(
|
||||
|
||||
Reference in New Issue
Block a user