mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
feat: encrypt storages in agent status response when backup requested
This commit is contained in:
@@ -12,12 +12,20 @@ import {logger} from "@/lib/logger";
|
|||||||
import {isUUID} from "@/utils/text";
|
import {isUUID} from "@/utils/text";
|
||||||
import {StorageInput} from "@/features/storages/types";
|
import {StorageInput} from "@/features/storages/types";
|
||||||
import {dispatchStorage} from "@/features/storages/utils/storages.dispatch";
|
import {dispatchStorage} from "@/features/storages/utils/storages.dispatch";
|
||||||
|
import {getMasterServerKeyContent} from "@/features/agents/actions/keys.action";
|
||||||
|
import {encryptStorages, isAgentVersionAtLeast, MIN_AGENT_VERSION_STORAGE_ENC} from "@/utils/status-crypto";
|
||||||
|
|
||||||
const log = logger.child({module: "api/agent/status/helpers"});
|
const log = logger.child({module: "api/agent/status/helpers"});
|
||||||
|
|
||||||
export async function handleDatabases(body: Body, agent: Agent, lastContact: Date, settings: Setting) {
|
export async function handleDatabases(body: Body, agent: Agent, lastContact: Date, settings: Setting) {
|
||||||
const databasesResponse = [];
|
const databasesResponse = [];
|
||||||
|
|
||||||
|
const masterKeyResult = await getMasterServerKeyContent();
|
||||||
|
const masterKey = Buffer.isBuffer(masterKeyResult) ? masterKeyResult : null;
|
||||||
|
if (!masterKey) {
|
||||||
|
log.error({name: "handleDatabases"}, "Master key unavailable; storages will be sent in plaintext");
|
||||||
|
}
|
||||||
|
|
||||||
const formatDatabase = (database: DatabaseWith, backupAction: boolean, restoreAction: boolean, UrlBackup: string | null, storages: PingDatabaseStorageChannels[], urlMeta: string | null, backupSize: number | null) => ({
|
const formatDatabase = (database: DatabaseWith, backupAction: boolean, restoreAction: boolean, UrlBackup: string | null, storages: PingDatabaseStorageChannels[], urlMeta: string | null, backupSize: number | null) => ({
|
||||||
generatedId: database.agentDatabaseId,
|
generatedId: database.agentDatabaseId,
|
||||||
dbms: database.dbms,
|
dbms: database.dbms,
|
||||||
@@ -89,7 +97,9 @@ export async function handleDatabases(body: Body, agent: Agent, lastContact: Dat
|
|||||||
|
|
||||||
const storages = await getDatabaseStorageChannels(databaseCreated.id)
|
const storages = await getDatabaseStorageChannels(databaseCreated.id)
|
||||||
|
|
||||||
databasesResponse.push(formatDatabase(databaseCreated, backupAction, restoreAction, urlBackup, storages, null, null));
|
const entry = formatDatabase(databaseCreated, backupAction, restoreAction, urlBackup, storages, null, null);
|
||||||
|
applyStorageEncryption(entry, backupAction, body.version, masterKey);
|
||||||
|
databasesResponse.push(entry);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -209,7 +219,9 @@ export async function handleDatabases(body: Body, agent: Agent, lastContact: Dat
|
|||||||
.where(eq(drizzleDb.schemas.restoration.id, restoration.id));
|
.where(eq(drizzleDb.schemas.restoration.id, restoration.id));
|
||||||
}
|
}
|
||||||
const storages = await getDatabaseStorageChannels(databaseUpdated.id)
|
const storages = await getDatabaseStorageChannels(databaseUpdated.id)
|
||||||
databasesResponse.push(formatDatabase(databaseUpdated, backupAction, restoreAction, urlBackup, storages, urlMeta, backupSize));
|
const entry = formatDatabase(databaseUpdated, backupAction, restoreAction, urlBackup, storages, urlMeta, backupSize);
|
||||||
|
applyStorageEncryption(entry, backupAction, body.version, masterKey);
|
||||||
|
databasesResponse.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return databasesResponse;
|
return databasesResponse;
|
||||||
@@ -277,3 +289,24 @@ async function getDatabaseStorageChannels(databaseId: string): Promise<PingDatab
|
|||||||
return filteredChannels.length > 0 ? filteredChannels : defaultStorageChannel;
|
return filteredChannels.length > 0 ? filteredChannels : defaultStorageChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyStorageEncryption(
|
||||||
|
entry: Record<string, any>,
|
||||||
|
backupAction: boolean,
|
||||||
|
version: string | undefined,
|
||||||
|
masterKey: Buffer | null,
|
||||||
|
): void {
|
||||||
|
if (!masterKey) return;
|
||||||
|
if (!backupAction) return;
|
||||||
|
if (!Array.isArray(entry.storages) || entry.storages.length === 0) return;
|
||||||
|
if (!isAgentVersionAtLeast(version, MIN_AGENT_VERSION_STORAGE_ENC)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const ciphertext = encryptStorages(entry.storages, masterKey);
|
||||||
|
entry.storages_ciphertext = ciphertext;
|
||||||
|
entry.storages_encrypted = true;
|
||||||
|
entry.storages = [];
|
||||||
|
} catch (err) {
|
||||||
|
log.error({error: err, name: "applyStorageEncryption"}, "Storage encryption failed; sending plaintext");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user