Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2025-04-16 14:54:58 +01:00
parent 0cdf4dfbec
commit 8e70347330
No known key found for this signature in database
GPG Key ID: A2B008A5F49F5D0D
2 changed files with 48 additions and 11 deletions

View File

@ -64,9 +64,12 @@
}, },
"store": { "store": {
"error": { "error": {
"title": "Failed to load configuration", "title": "Failed to load database",
"unsupported_backend_override": "TODO", "unsupported_backend_override": "The encrypted database is corrupted, unable to access your system keyring.",
"unknown_backend_override": "TODO" "unsupported_backend_override_details": "Please check the logs for more details.",
"unknown_backend_override": "Your system has an unsupported keyring meaning the database cannot be opened.",
"unknown_backend_override_details": "Please check the logs for more details.",
"clear_data_cta": "Clear data"
} }
} }
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
import ElectronStore from "electron-store"; import ElectronStore from "electron-store";
import keytar from "keytar-forked"; import keytar from "keytar-forked";
import { app, safeStorage, dialog, type SafeStorage } from "electron"; import { app, safeStorage, dialog, type SafeStorage } from "electron";
import { clearAllUserData, clearSensitiveDirectories, relaunchApp } from "@standardnotes/electron-clear-data";
import { _t } from "./language-helper.js"; import { _t } from "./language-helper.js";
@ -26,20 +27,29 @@ import { _t } from "./language-helper.js";
const KEYTAR_SERVICE = "element.io"; const KEYTAR_SERVICE = "element.io";
const LEGACY_KEYTAR_SERVICE = "riot.im"; const LEGACY_KEYTAR_SERVICE = "riot.im";
type SafeStorageBackend = ReturnType<SafeStorage["getSelectedStorageBackend"]>; type SafeStorageBackend = ReturnType<SafeStorage["getSelectedStorageBackend"]> | "system";
/** /**
* Map of safeStorage backends to their command line arguments. * Map of safeStorage backends to their command line arguments.
* kwallet6 cannot be specified via command line * kwallet6 cannot be specified via command line
* https://www.electronjs.org/docs/latest/api/safe-storage#safestoragegetselectedstoragebackend-linux * https://www.electronjs.org/docs/latest/api/safe-storage#safestoragegetselectedstoragebackend-linux
*/ */
const safeStorageBackendMap: Omit<Record<SafeStorageBackend, string>, "unknown" | "kwallet6"> = { const safeStorageBackendMap: Omit<Record<SafeStorageBackend, string>, "unknown" | "kwallet6" | "system"> = {
basic_text: "basic", basic_text: "basic",
gnome_libsecret: "gnome-libsecret", gnome_libsecret: "gnome-libsecret",
kwallet: "kwallet", kwallet: "kwallet",
kwallet5: "kwallet5", kwallet5: "kwallet5",
}; };
async function clearDataAndRelaunch(): Promise<void> {
global.store?.clear();
global.mainWindow?.webContents.session.flushStorageData();
await global.mainWindow?.webContents.session.clearStorageData();
clearAllUserData();
clearSensitiveDirectories();
relaunchApp();
}
/** /**
* JSON-backed store for settings which need to be accessible by the main process. * JSON-backed store for settings which need to be accessible by the main process.
* Secrets are stored within the `safeStorage` object, encrypted with safeStorage. * Secrets are stored within the `safeStorage` object, encrypted with safeStorage.
@ -115,7 +125,19 @@ class Store extends ElectronStore<{
} else { } else {
// This case should never happen, but could due to a downgrade or a modified store. // This case should never happen, but could due to a downgrade or a modified store.
dialog.showErrorBox(_t("store|error|title"), _t("store|error|unsupported_backend_override")); dialog.showErrorBox(_t("store|error|title"), _t("store|error|unsupported_backend_override"));
throw new Error("safeStorage backend override is not supported"); const response = dialog.showMessageBoxSync({
title: _t("store|error|title"),
message: _t("store|error|unsupported_backend_override"),
detail: _t("store|error|unsupported_backend_override_details"),
type: "question",
buttons: [_t("common|no"), _t("store|error|clear_data_cta")],
defaultId: 0,
cancelId: 0,
});
if (response === 0) {
throw new Error("safeStorage backend override is not supported");
}
void clearDataAndRelaunch();
} }
} }
} }
@ -129,7 +151,7 @@ class Store extends ElectronStore<{
await this.safeStorageReadyPromise; await this.safeStorageReadyPromise;
} }
private getSecretStorageKey = (key: string) => `safeStorage.${key}` as const; private getSecretStorageKey = (key: string) => `safeStorage.${key.replaceAll(".", "-")}` as const;
private async prepareSafeStorage(): Promise<void> { private async prepareSafeStorage(): Promise<void> {
await app.whenReady(); await app.whenReady();
@ -142,7 +164,12 @@ class Store extends ElectronStore<{
if (selectedSafeStorageBackend === "unknown") { if (selectedSafeStorageBackend === "unknown") {
// This should never happen but good to be safe // This should never happen but good to be safe
dialog.showErrorBox(_t("store|error|title"), _t("store|error|unknown_backend_override")); await dialog.showMessageBox({
title: _t("store|error|title"),
message: _t("store|error|unknown_backend_override"),
detail: _t("store|error|unknown_backend_override_details"),
type: "error",
});
throw new Error("safeStorage backend unknown"); throw new Error("safeStorage backend unknown");
} }
@ -166,7 +193,7 @@ class Store extends ElectronStore<{
} }
// Store the backend used for the safeStorage data so we can detect if it changes // Store the backend used for the safeStorage data so we can detect if it changes
this.set("safeStorageBackend", selectedSafeStorageBackend); this.recordSafeStorageBackend(selectedSafeStorageBackend);
safeStorageBackend = selectedSafeStorageBackend; safeStorageBackend = selectedSafeStorageBackend;
} else if (safeStorageBackend !== selectedSafeStorageBackend) { } else if (safeStorageBackend !== selectedSafeStorageBackend) {
console.warn(`safeStorage backend changed from ${safeStorageBackend} to ${selectedSafeStorageBackend}`); console.warn(`safeStorage backend changed from ${safeStorageBackend} to ${selectedSafeStorageBackend}`);
@ -219,16 +246,23 @@ class Store extends ElectronStore<{
await this.migrateSecrets(); await this.migrateSecrets();
} }
private recordSafeStorageBackend(backend: SafeStorageBackend): void {
this.set("safeStorageBackend", backend);
}
private get isPlaintext(): boolean {
return this.get("safeStorageBackend") === "basic_text";
}
/** /**
* Migrates keytar data to safeStorage, * Migrates keytar data to safeStorage,
* deletes data from legacy keytar but keeps it in the new keytar for downgrade compatibility. * deletes data from legacy keytar but keeps it in the new keytar for downgrade compatibility.
* @throws if safeStorage is not available. TODO
*/ */
private async migrateSecrets(): Promise<void> { private async migrateSecrets(): Promise<void> {
if (this.has("safeStorage")) return; // already migrated if (this.has("safeStorage")) return; // already migrated
console.info("Store migration: started"); console.info("Store migration: started");
if (process.platform === "linux" && safeStorage.getSelectedStorageBackend() === "basic_text") { if (process.platform === "linux" && this.isPlaintext) {
console.warn("Store migration: safeStorage is using basic text encryption"); console.warn("Store migration: safeStorage is using basic text encryption");
} }