fix(database): preserve RDP domain during startup backfill (#1497)

This commit is contained in:
ZacharyZcR authored and GitHub committed 2026-09-30 09:50:32 -05:00
1 parent 1f4b1ff6a4
commit 5b0423c8e0
3 files changed
+110 -1

No files matched your search

@@ -0,0 +1,67 @@
import fs from "fs";
import os from "os";
import path from "path";
import Database from "better-sqlite3";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
describe("RDP domain across database boots", () => {
let dataDir: string;
beforeEach(() => {
dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "termix-rdp-domain-"));
vi.resetModules();
vi.stubEnv("DATA_DIR", dataDir);
vi.stubEnv("DB_FILE_ENCRYPTION", "false");
vi.stubEnv("ALLOW_EMPTY_DATA_DIR", "true");
});
afterEach(() => {
vi.unstubAllEnvs();
fs.rmSync(dataDir, { recursive: true, force: true });
});
it.each([
{ saved: "CORP", legacy: null, expected: "CORP" },
{ saved: "CORP", legacy: "OLD", expected: "CORP" },
{ saved: "", legacy: "OLD", expected: "" },
{ saved: null, legacy: "LEGACY", expected: null },
])("preserves $saved with legacy domain $legacy across boots", async ({
saved,
legacy,
expected,
}) => {
const seed = new Database(":memory:");
seed.exec(
fs.readFileSync(
new URL("../../fixtures/sqlite-2.8-schema.sql", import.meta.url),
"utf8",
),
);
seed.exec(`
INSERT INTO users (id, username, password_hash)
VALUES ('owner', 'alice', 'hash');
`);
seed.prepare(`
INSERT INTO ssh_data
(id, user_id, ip, port, username, auth_type, connection_type,
rdp_auth_type, rdp_user, rdp_password, rdp_domain, domain)
VALUES (1, 'owner', '192.0.2.1', 3389, '', 'password', 'rdp',
'credential', NULL, NULL, ?, ?)
`).run(saved, legacy);
fs.writeFileSync(path.join(dataDir, "db.sqlite"), seed.serialize());
seed.close();
for (let boot = 0; boot < 2; boot++) {
vi.resetModules();
const db = await import("../../../database/db/index.js");
await db.initializeDatabase();
expect(
db.getSqlite()
.prepare("SELECT rdp_domain FROM ssh_data WHERE id = 1")
.pluck()
.get(),
).toBe(expected);
await db.saveMemoryDatabaseToFile();
}
});
});
@@ -35,6 +35,7 @@ vi.mock("../../utils/data-crypto.js", () => ({
import {
copiedMarker,
legacyLogin,
runProtocolAuthMigration,
} from "../../upgrade/protocol-auth-migration.js";
import { createCurrentHostProtocolAuthRepository } from "../../database/repositories/factory.js";
@@ -221,3 +222,43 @@ describe("runProtocolAuthMigration", () => {
expect(await logins()).toHaveProperty("3:telnet");
});
});
describe("legacyLogin rdp domain", () => {
const rdp = {
id: "rdp",
user: "rdp_user",
password: "rdp_password",
passwordField: "rdpPassword",
credential: "rdp_credential_id",
authType: "rdp_auth_type",
domain: "rdp_domain",
};
it.each([
{ saved: "CORP", legacy: null, expected: { domain: "CORP" } },
{ saved: "CORP", legacy: "OLD", expected: { domain: "CORP" } },
{ saved: "", legacy: "OLD", expected: {} },
{ saved: null, legacy: "LEGACY", expected: { domain: "LEGACY" } },
])(
"keeps saved domain $saved over legacy $legacy for a credential host",
({ saved, legacy, expected }) => {
const login = legacyLogin(
{
id: 1,
user_id: "owner",
connection_type: "rdp",
rdp_auth_type: "credential",
rdp_credential_id: 5,
rdp_user: null,
rdp_password: null,
rdp_domain: saved,
domain: legacy,
},
rdp,
ownerKey,
() => true,
);
expect(login?.fields).toEqual(expected);
},
);
});
@@ -139,7 +139,8 @@ export function legacyLogin(
password = openLegacySecret(row.password, dek, recordId, ["password"]);
domain = protocol.domain ? text(row.domain) : "";
if (!username && !password && !domain) return null;
} else if (protocol.domain && !domain) {
} else if (protocol.domain && row[protocol.domain] == null) {
// A saved empty domain was cleared on purpose, so only fill a missing one.
domain = text(row.domain);
}