mirror of https://github.com/keeweb/keeweb.git
reworked unlocking files for auto-type and browser extension connection
fix #1768
This commit is contained in:
parent
29e5c14009
commit
8fc6f49fb4
|
@ -20,7 +20,6 @@ const AutoType = {
|
||||||
enabled: !!(Launcher && Launcher.autoTypeSupported),
|
enabled: !!(Launcher && Launcher.autoTypeSupported),
|
||||||
supportsEventsWithWindowId: !!(Launcher && Launcher.platform() === 'linux'),
|
supportsEventsWithWindowId: !!(Launcher && Launcher.platform() === 'linux'),
|
||||||
selectEntryView: false,
|
selectEntryView: false,
|
||||||
pendingEvent: null,
|
|
||||||
running: false,
|
running: false,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -28,10 +27,6 @@ const AutoType = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Events.on('auto-type', (e) => this.handleEvent(e));
|
Events.on('auto-type', (e) => this.handleEvent(e));
|
||||||
Events.on('main-window-blur', (e) => this.mainWindowBlur(e));
|
|
||||||
Events.on('main-window-focus', (e) => this.mainWindowFocus(e));
|
|
||||||
Events.on('main-window-will-close', (e) => this.mainWindowWillClose(e));
|
|
||||||
Events.on('closed-open-view', (e) => this.processPendingEvent(e));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleEvent(e) {
|
handleEvent(e) {
|
||||||
|
@ -216,16 +211,24 @@ const AutoType = {
|
||||||
},
|
},
|
||||||
|
|
||||||
selectEntryAndRun() {
|
selectEntryAndRun() {
|
||||||
this.getActiveWindowInfo((e, windowInfo) => {
|
this.getActiveWindowInfo(async (e, windowInfo) => {
|
||||||
const filter = new AutoTypeFilter(windowInfo, AppModel.instance);
|
const filter = new AutoTypeFilter(windowInfo, AppModel.instance);
|
||||||
const evt = { filter, windowInfo };
|
const evt = { filter, windowInfo };
|
||||||
if (!AppModel.instance.files.hasOpenFiles()) {
|
if (!AppModel.instance.files.hasOpenFiles()) {
|
||||||
this.pendingEvent = evt;
|
|
||||||
logger.debug('auto-type event delayed');
|
logger.debug('auto-type event delayed');
|
||||||
this.focusMainWindow();
|
this.focusMainWindow();
|
||||||
} else {
|
try {
|
||||||
this.processEventWithFilter(evt);
|
await AppModel.instance.unlockAnyFile();
|
||||||
|
} catch {
|
||||||
|
logger.debug('auto-type event canceled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.selectEntryView) {
|
||||||
|
this.selectEntryView.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
logger.debug('processing auto-type event');
|
||||||
|
this.processEventWithFilter(evt);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -264,51 +267,6 @@ const AutoType = {
|
||||||
this.selectEntryView.hide();
|
this.selectEntryView.hide();
|
||||||
Events.emit('open-file');
|
Events.emit('open-file');
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
mainWindowBlur() {
|
|
||||||
this.mainWindowBlurTimer = setTimeout(() => {
|
|
||||||
// macOS emits focus-blur-focus event in a row when triggering auto-type from minimized state
|
|
||||||
delete this.mainWindowBlurTimer;
|
|
||||||
this.resetPendingEvent();
|
|
||||||
if (this.selectEntryView) {
|
|
||||||
this.selectEntryView.emit('result', undefined);
|
|
||||||
}
|
|
||||||
}, Timeouts.AutoTypeWindowFocusAfterBlur);
|
|
||||||
},
|
|
||||||
|
|
||||||
mainWindowFocus() {
|
|
||||||
if (this.mainWindowBlurTimer) {
|
|
||||||
clearTimeout(this.mainWindowBlurTimer);
|
|
||||||
this.mainWindowBlurTimer = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mainWindowWillClose() {
|
|
||||||
this.resetPendingEvent();
|
|
||||||
if (this.selectEntryView) {
|
|
||||||
this.selectEntryView.emit('result', undefined);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
resetPendingEvent() {
|
|
||||||
if (this.pendingEvent) {
|
|
||||||
this.pendingEvent = null;
|
|
||||||
logger.debug('auto-type event canceled');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
processPendingEvent() {
|
|
||||||
if (this.selectEntryView) {
|
|
||||||
this.selectEntryView.show();
|
|
||||||
}
|
|
||||||
if (!this.pendingEvent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.debug('processing pending auto-type event');
|
|
||||||
const evt = this.pendingEvent;
|
|
||||||
this.pendingEvent = null;
|
|
||||||
this.processEventWithFilter(evt);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { MenuModel } from 'models/menu/menu-model';
|
||||||
import { PluginManager } from 'plugins/plugin-manager';
|
import { PluginManager } from 'plugins/plugin-manager';
|
||||||
import { Features } from 'util/features';
|
import { Features } from 'util/features';
|
||||||
import { DateFormat } from 'comp/i18n/date-format';
|
import { DateFormat } from 'comp/i18n/date-format';
|
||||||
|
import { Launcher } from 'comp/launcher';
|
||||||
import { UrlFormat } from 'util/formatting/url-format';
|
import { UrlFormat } from 'util/formatting/url-format';
|
||||||
import { IdGenerator } from 'util/generators/id-generator';
|
import { IdGenerator } from 'util/generators/id-generator';
|
||||||
import { Locale } from 'util/locale';
|
import { Locale } from 'util/locale';
|
||||||
|
@ -38,6 +39,8 @@ class AppModel {
|
||||||
advancedSearch = null;
|
advancedSearch = null;
|
||||||
attachedYubiKeysCount = 0;
|
attachedYubiKeysCount = 0;
|
||||||
memoryPasswordStorage = {};
|
memoryPasswordStorage = {};
|
||||||
|
fileUnlockPromise = null;
|
||||||
|
hardwareDecryptInProgress = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
Events.on('refresh', this.refresh.bind(this));
|
Events.on('refresh', this.refresh.bind(this));
|
||||||
|
@ -48,6 +51,9 @@ class AppModel {
|
||||||
Events.on('select-entry', this.selectEntry.bind(this));
|
Events.on('select-entry', this.selectEntry.bind(this));
|
||||||
Events.on('unset-keyfile', this.unsetKeyFile.bind(this));
|
Events.on('unset-keyfile', this.unsetKeyFile.bind(this));
|
||||||
Events.on('usb-devices-changed', this.usbDevicesChanged.bind(this));
|
Events.on('usb-devices-changed', this.usbDevicesChanged.bind(this));
|
||||||
|
Events.on('main-window-blur', this.mainWindowBlur.bind(this));
|
||||||
|
Events.on('hardware-decrypt-started', this.hardwareDecryptStarted.bind(this));
|
||||||
|
Events.on('hardware-decrypt-finished', this.hardwareDecryptFinished.bind(this));
|
||||||
|
|
||||||
this.appLogger = new Logger('app');
|
this.appLogger = new Logger('app');
|
||||||
AppModel.instance = this;
|
AppModel.instance = this;
|
||||||
|
@ -167,11 +173,21 @@ class AppModel {
|
||||||
page: 'file',
|
page: 'file',
|
||||||
file
|
file
|
||||||
});
|
});
|
||||||
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
|
||||||
file.on('reload', this.reloadFile.bind(this));
|
file.on('reload', this.reloadFile.bind(this));
|
||||||
file.on('change', () => Events.emit('file-changed', file));
|
file.on('change', () => Events.emit('file-changed', file));
|
||||||
file.on('ejected', () => this.closeFile(file));
|
file.on('ejected', () => this.closeFile(file));
|
||||||
|
|
||||||
Events.emit('file-opened');
|
Events.emit('file-opened');
|
||||||
|
|
||||||
|
if (this.fileUnlockPromise) {
|
||||||
|
this.appLogger.info('Running pending file unlock operation');
|
||||||
|
this.fileUnlockPromise.resolve(file);
|
||||||
|
this.fileUnlockPromise = null;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1414,6 +1430,39 @@ class AppModel {
|
||||||
this.memoryPasswordStorage = {};
|
this.memoryPasswordStorage = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlockAnyFile() {
|
||||||
|
this.rejectPendingFileUnlockPromise('Replaced with a new operation');
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.fileUnlockPromise = { resolve, reject };
|
||||||
|
this.appLogger.info('Pending file unlock operation is set');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rejectPendingFileUnlockPromise(reason) {
|
||||||
|
if (this.fileUnlockPromise) {
|
||||||
|
this.appLogger.info('Cancel pending file unlock operation', reason);
|
||||||
|
this.fileUnlockPromise.reject(new Error(reason));
|
||||||
|
this.fileUnlockPromise = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindowBlur() {
|
||||||
|
if (!this.hardwareDecryptInProgress) {
|
||||||
|
this.rejectPendingFileUnlockPromise('Main window blur');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hardwareDecryptStarted() {
|
||||||
|
this.hardwareDecryptInProgress = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hardwareDecryptFinished() {
|
||||||
|
this.hardwareDecryptInProgress = false;
|
||||||
|
if (!Launcher.isAppFocused()) {
|
||||||
|
this.rejectPendingFileUnlockPromise('App is not focused after hardware decrypt');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AppModel };
|
export { AppModel };
|
||||||
|
|
|
@ -171,9 +171,6 @@ class AppView extends View {
|
||||||
this.views.open.on('close', () => {
|
this.views.open.on('close', () => {
|
||||||
this.showEntries();
|
this.showEntries();
|
||||||
});
|
});
|
||||||
this.views.open.on('remove', () => {
|
|
||||||
Events.emit('closed-open-view');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showLastOpenFile() {
|
showLastOpenFile() {
|
||||||
|
|
|
@ -680,13 +680,18 @@ class OpenView extends View {
|
||||||
const encryptedPassword = kdbxweb.ProtectedValue.fromBase64(
|
const encryptedPassword = kdbxweb.ProtectedValue.fromBase64(
|
||||||
this.encryptedPassword.value
|
this.encryptedPassword.value
|
||||||
);
|
);
|
||||||
|
Events.emit('hardware-decrypt-started');
|
||||||
NativeModules.hardwareDecrypt(encryptedPassword, touchIdPrompt)
|
NativeModules.hardwareDecrypt(encryptedPassword, touchIdPrompt)
|
||||||
.then((password) => {
|
.then((password) => {
|
||||||
|
Events.emit('hardware-decrypt-finished');
|
||||||
|
|
||||||
this.params.password = password;
|
this.params.password = password;
|
||||||
this.params.encryptedPassword = this.encryptedPassword;
|
this.params.encryptedPassword = this.encryptedPassword;
|
||||||
this.model.openFile(this.params, (err) => this.openDbComplete(err));
|
this.model.openFile(this.params, (err) => this.openDbComplete(err));
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
Events.emit('hardware-decrypt-finished');
|
||||||
|
|
||||||
if (err.message.includes('User refused')) {
|
if (err.message.includes('User refused')) {
|
||||||
err.userCanceled = true;
|
err.userCanceled = true;
|
||||||
} else if (err.message.includes('SecKeyCreateDecryptedData')) {
|
} else if (err.message.includes('SecKeyCreateDecryptedData')) {
|
||||||
|
|
Loading…
Reference in New Issue