mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-15 04:43:36 +00:00
137 lines
3.9 KiB
TypeScript
137 lines
3.9 KiB
TypeScript
import { db } from "../database/db/index.js";
|
|
import { sshCredentials, sharedCredentials } from "../database/db/schema.js";
|
|
import { eq, and, or, isNull } from "drizzle-orm";
|
|
import { DataCrypto } from "./data-crypto.js";
|
|
import { SystemCrypto } from "./system-crypto.js";
|
|
import { FieldCrypto } from "./field-crypto.js";
|
|
import { databaseLogger } from "./logger.js";
|
|
|
|
export class CredentialSystemEncryptionMigration {
|
|
async migrateUserCredentials(userId: string): Promise<{
|
|
migrated: number;
|
|
failed: number;
|
|
skipped: number;
|
|
}> {
|
|
try {
|
|
const userDEK = DataCrypto.getUserDataKey(userId);
|
|
if (!userDEK) {
|
|
throw new Error("User must be logged in to migrate credentials");
|
|
}
|
|
|
|
const systemCrypto = SystemCrypto.getInstance();
|
|
const CSKEK = await systemCrypto.getCredentialSharingKey();
|
|
|
|
const credentials = await db
|
|
.select()
|
|
.from(sshCredentials)
|
|
.where(
|
|
and(
|
|
eq(sshCredentials.userId, userId),
|
|
or(
|
|
isNull(sshCredentials.systemPassword),
|
|
isNull(sshCredentials.systemKey),
|
|
isNull(sshCredentials.systemKeyPassword),
|
|
),
|
|
),
|
|
);
|
|
|
|
let migrated = 0;
|
|
let failed = 0;
|
|
const skipped = 0;
|
|
|
|
for (const cred of credentials) {
|
|
try {
|
|
const plainPassword = cred.password
|
|
? FieldCrypto.decryptField(
|
|
cred.password,
|
|
userDEK,
|
|
cred.id.toString(),
|
|
"password",
|
|
)
|
|
: null;
|
|
|
|
const plainKey = cred.key
|
|
? FieldCrypto.decryptField(
|
|
cred.key,
|
|
userDEK,
|
|
cred.id.toString(),
|
|
"key",
|
|
)
|
|
: null;
|
|
|
|
const plainKeyPassword = cred.keyPassword
|
|
? FieldCrypto.decryptField(
|
|
cred.keyPassword,
|
|
userDEK,
|
|
cred.id.toString(),
|
|
"keyPassword",
|
|
)
|
|
: null;
|
|
|
|
const systemPassword = plainPassword
|
|
? FieldCrypto.encryptField(
|
|
plainPassword,
|
|
CSKEK,
|
|
cred.id.toString(),
|
|
"password",
|
|
)
|
|
: null;
|
|
|
|
const systemKey = plainKey
|
|
? FieldCrypto.encryptField(
|
|
plainKey,
|
|
CSKEK,
|
|
cred.id.toString(),
|
|
"key",
|
|
)
|
|
: null;
|
|
|
|
const systemKeyPassword = plainKeyPassword
|
|
? FieldCrypto.encryptField(
|
|
plainKeyPassword,
|
|
CSKEK,
|
|
cred.id.toString(),
|
|
"key_password",
|
|
)
|
|
: null;
|
|
|
|
await db
|
|
.update(sshCredentials)
|
|
.set({
|
|
systemPassword,
|
|
systemKey,
|
|
systemKeyPassword,
|
|
updatedAt: new Date().toISOString(),
|
|
})
|
|
.where(eq(sshCredentials.id, cred.id));
|
|
|
|
await db
|
|
.update(sharedCredentials)
|
|
.set({ needsReEncryption: true })
|
|
.where(eq(sharedCredentials.originalCredentialId, cred.id));
|
|
|
|
migrated++;
|
|
} catch (error) {
|
|
databaseLogger.warn(
|
|
`Skipping credential migration for credential ${cred.id}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
{
|
|
operation: "credential_migration_skip",
|
|
credentialId: cred.id,
|
|
userId,
|
|
},
|
|
);
|
|
failed++;
|
|
}
|
|
}
|
|
return { migrated, failed, skipped };
|
|
} catch (error) {
|
|
databaseLogger.warn("Credential system encryption migration incomplete", {
|
|
operation: "credential_migration_incomplete",
|
|
userId,
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
}
|