Matrix: scope legacy credential migration

This commit is contained in:
Gustavo Madeira Santana 2026-03-12 10:05:27 +00:00
parent 82cd6097fc
commit efc07dc6ca
No known key found for this signature in database
2 changed files with 78 additions and 5 deletions

View File

@ -21,10 +21,19 @@ describe("matrix credentials storage", () => {
}
});
function setupStateDir(): string {
function setupStateDir(
cfg: Record<string, unknown> = {
channels: {
matrix: {},
},
},
): string {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-matrix-creds-"));
tempDirs.push(dir);
setMatrixRuntime({
config: {
loadConfig: () => cfg,
},
state: {
resolveStateDir: () => dir,
},
@ -82,7 +91,15 @@ describe("matrix credentials storage", () => {
});
it("migrates legacy matrix credential files on read", async () => {
const stateDir = setupStateDir();
const stateDir = setupStateDir({
channels: {
matrix: {
accounts: {
ops: {},
},
},
},
});
const legacyPath = path.join(stateDir, "credentials", "matrix", "credentials.json");
const currentPath = resolveMatrixCredentialsPath({}, "ops");
fs.mkdirSync(path.dirname(legacyPath), { recursive: true });
@ -103,8 +120,51 @@ describe("matrix credentials storage", () => {
expect(fs.existsSync(currentPath)).toBe(true);
});
it("does not migrate legacy default credentials during a non-selected account read", () => {
const stateDir = setupStateDir({
channels: {
matrix: {
defaultAccount: "default",
accounts: {
default: {
homeserver: "https://matrix.default.example.org",
accessToken: "default-token",
},
ops: {},
},
},
},
});
const legacyPath = path.join(stateDir, "credentials", "matrix", "credentials.json");
const currentPath = resolveMatrixCredentialsPath({}, "ops");
fs.mkdirSync(path.dirname(legacyPath), { recursive: true });
fs.writeFileSync(
legacyPath,
JSON.stringify({
homeserver: "https://matrix.default.example.org",
userId: "@default:example.org",
accessToken: "default-token",
createdAt: "2026-03-01T10:00:00.000Z",
}),
);
const loaded = loadMatrixCredentials({}, "ops");
expect(loaded).toBeNull();
expect(fs.existsSync(legacyPath)).toBe(true);
expect(fs.existsSync(currentPath)).toBe(false);
});
it("clears both current and legacy credential paths", () => {
const stateDir = setupStateDir();
const stateDir = setupStateDir({
channels: {
matrix: {
accounts: {
ops: {},
},
},
},
});
const currentPath = resolveMatrixCredentialsPath({}, "ops");
const legacyPath = path.join(stateDir, "credentials", "matrix", "credentials.json");
fs.mkdirSync(path.dirname(currentPath), { recursive: true });

View File

@ -3,6 +3,8 @@ import os from "node:os";
import path from "node:path";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import {
requiresExplicitMatrixDefaultAccount,
resolveMatrixDefaultOrOnlyAccountId,
resolveMatrixCredentialsDir as resolveSharedMatrixCredentialsDir,
resolveMatrixCredentialsPath as resolveSharedMatrixCredentialsPath,
writeJsonFileAtomically,
@ -26,12 +28,23 @@ function resolveLegacyMatrixCredentialsPath(env: NodeJS.ProcessEnv): string | nu
return path.join(resolveMatrixCredentialsDir(env), "credentials.json");
}
function shouldReadLegacyCredentialsForAccount(accountId?: string | null): boolean {
const normalizedAccountId = normalizeAccountId(accountId);
const cfg = getMatrixRuntime().config.loadConfig();
if (!cfg.channels?.matrix || typeof cfg.channels.matrix !== "object") {
return normalizedAccountId === DEFAULT_ACCOUNT_ID;
}
if (requiresExplicitMatrixDefaultAccount(cfg)) {
return false;
}
return normalizeAccountId(resolveMatrixDefaultOrOnlyAccountId(cfg)) === normalizedAccountId;
}
function resolveLegacyMigrationSourcePath(
env: NodeJS.ProcessEnv,
accountId?: string | null,
): string | null {
const normalized = normalizeAccountId(accountId);
if (normalized === DEFAULT_ACCOUNT_ID) {
if (!shouldReadLegacyCredentialsForAccount(accountId)) {
return null;
}
const legacyPath = resolveLegacyMatrixCredentialsPath(env);