Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Password prompt rebuild #1255

Merged
merged 4 commits into from
Nov 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion source/main/services/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { applyCurrentTheme } from "./theme";
import { updateTrayIcon } from "../actions/tray";
import { updateAppMenu } from "../actions/appMenu";
import { getConfigValue, initialise as initialiseConfig } from "./config";
import { getConfigPath, getVaultStoragePath } from "./storage";
import { getConfigPath, getVaultSettingsPath, getVaultStoragePath } from "./storage";
import { getOSLocale } from "./locale";
import { startFileHost } from "./fileHost";
import { isPortable } from "../library/portability";
Expand All @@ -28,6 +28,7 @@ export async function initialise() {
logInfo(`Logs location: ${getLogPath()}`);
logInfo(`Config location: ${getConfigPath()}`);
logInfo(`Vault config storage location: ${getVaultStoragePath()}`);
logInfo(`Vault-specific settings path: ${getVaultSettingsPath("<ID>")}`);
await initialiseConfig();
const preferences = await getConfigValue("preferences");
const locale = await getOSLocale();
Expand Down
6 changes: 3 additions & 3 deletions source/renderer/actions/facade.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ipcRenderer } from "electron";
import { VaultFacade, VaultSourceID } from "buttercup";
import { setCurrentVault, setCurrentVaultSupportsAttachments } from "../state/vaults";
import { VAULTS_STATE } from "../state/vaults";
import { setCurrentFacade } from "../services/facade";

export async function fetchUpdatedFacade(sourceID: VaultSourceID) {
Expand All @@ -9,8 +9,8 @@ export async function fetchUpdatedFacade(sourceID: VaultSourceID) {
sourceID
);
const facade: VaultFacade = JSON.parse(rawFacade);
setCurrentVaultSupportsAttachments(attachments);
setCurrentVault(sourceID);
VAULTS_STATE.currentVaultAttachments = !!attachments;
VAULTS_STATE.currentVault = sourceID;
if (!facade) {
setCurrentFacade(null);
return;
Expand Down
22 changes: 13 additions & 9 deletions source/renderer/actions/password.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { VaultSourceID } from "buttercup";
import { getPasswordEmitter } from "../services/password";
import { sourceHasBiometricAvailability } from "../services/biometrics";
import { setBiometricSourceID, showPasswordPrompt } from "../state/password";
import { PASSWORD_STATE } from "../state/password";

export async function getPrimaryPassword(sourceID?: VaultSourceID): Promise<string | null> {
export async function getPrimaryPassword(
sourceID?: VaultSourceID
): Promise<[password: string | null, biometricsEnabled: boolean, usedBiometrics: boolean]> {
let biometricsEnabled: boolean = false;
if (sourceID) {
const supportsBiometrics = await sourceHasBiometricAvailability(sourceID);
if (supportsBiometrics) {
setBiometricSourceID(sourceID);
PASSWORD_STATE.passwordViaBiometricSource = sourceID;
biometricsEnabled = true;
}
}
showPasswordPrompt(true);
PASSWORD_STATE.showPrompt = true;
const emitter = getPasswordEmitter();
const password = await new Promise<string | null>((resolve) => {
const callback = (password: string | null) => {
resolve(password);
const [password, usedBiometrics] = await new Promise<[string | null, boolean]>((resolve) => {
const callback = (password: string | null, usedBiometrics: boolean) => {
resolve([password, usedBiometrics]);
emitter.removeListener("password", callback);
};
emitter.once("password", callback);
});
setBiometricSourceID(null);
return password;
PASSWORD_STATE.passwordViaBiometricSource = null;
return [password, biometricsEnabled, usedBiometrics];
}
6 changes: 3 additions & 3 deletions source/renderer/actions/removeVault.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ipcRenderer } from "electron";
import { VaultSourceID } from "buttercup";
import { getCurrentSourceID, setCurrentVault } from "../state/vaults";
import { VAULTS_STATE } from "../state/vaults";

export async function removeVaultSource(sourceID: VaultSourceID) {
if (sourceID === getCurrentSourceID()) {
setCurrentVault(null);
if (sourceID === VAULTS_STATE.currentVault) {
VAULTS_STATE.currentVault = null;
}
ipcRenderer.send(
"remove-source",
Expand Down
44 changes: 43 additions & 1 deletion source/renderer/actions/unlockVault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { getPrimaryPassword } from "./password";
import { setBusy } from "../state/app";
import { showError } from "../services/notifications";
import { logInfo } from "../library/log";
import { getVaultSettings, saveVaultSettings } from "../services/vaultSettings";
import { t } from "../../shared/i18n/trans";

export async function unlockVaultSource(sourceID: VaultSourceID): Promise<boolean> {
const password = await getPrimaryPassword(sourceID);
const [password, biometricsEnabled, usedBiometrics] = await getPrimaryPassword(sourceID);
if (!password) return false;
setBusy(true);
logInfo(`Unlocking source: ${sourceID}`);
Expand All @@ -23,6 +24,47 @@ export async function unlockVaultSource(sourceID: VaultSourceID): Promise<boolea
return await unlockVaultSource(sourceID);
}
setBusy(false);
// Update config
if (biometricsEnabled) {
const vaultSettings = await getVaultSettings(sourceID);
const { biometricForcePasswordMaxInterval, biometricForcePasswordCount } = vaultSettings;
const maxPasswordCount = parseInt(biometricForcePasswordCount, 10);
const maxInterval = parseInt(biometricForcePasswordMaxInterval, 10);
if (!isNaN(maxPasswordCount) && maxPasswordCount > 0 && usedBiometrics) {
// Max password count enabled, increment count
vaultSettings.biometricUnlockCount += 1;
logInfo(`biometric unlock count increased: ${vaultSettings.biometricUnlockCount}`);
} else {
// Not enabled, ensure 0
vaultSettings.biometricUnlockCount = 0;
}
if (!isNaN(maxInterval) && maxInterval > 0 && usedBiometrics) {
// Interval enabled, set to now
if (
typeof vaultSettings.biometricLastManualUnlock === "number" &&
vaultSettings.biometricLastManualUnlock > 0
) {
logInfo(
`biometric unlock date ignored as already set: ${vaultSettings.biometricLastManualUnlock}`
);
} else {
vaultSettings.biometricLastManualUnlock = Date.now();
logInfo(`biometric unlock date set: ${vaultSettings.biometricLastManualUnlock}`);
}
} else if (
typeof vaultSettings.biometricLastManualUnlock === "number" &&
vaultSettings.biometricLastManualUnlock > 0
) {
// Exceeded: new date
vaultSettings.biometricLastManualUnlock = Date.now();
logInfo(`biometric unlock date reset: ${vaultSettings.biometricLastManualUnlock}`);
} else {
// Not enabled: back to null
vaultSettings.biometricLastManualUnlock = null;
}
await saveVaultSettings(sourceID, vaultSettings);
}
// Return result
logInfo(`Unlocked source: ${sourceID}`);
return true;
}
Loading
Loading