mirror of https://github.com/keeweb/keeweb.git
Added a yubikey workaround
This commit is contained in:
parent
235ceae228
commit
5a535bb07a
|
@ -41,6 +41,7 @@ const DefaultAppSettings = {
|
|||
yubiKeyAutoOpen: true, // auto-load one-time codes when there are open files
|
||||
yubiKeyMatchEntries: true, // show matching one-time codes in entries
|
||||
yubiKeyShowChalResp: true, // show YubiKey challenge-response option
|
||||
yubiKeyOathWorkaround: false, // enable the workaround for YubiKey OATH issues
|
||||
|
||||
canOpen: true, // can select and open new files
|
||||
canOpenDemo: true, // can open a demo file
|
||||
|
|
|
@ -11,7 +11,8 @@ const Timeouts = {
|
|||
PopupWaitTime: 1000,
|
||||
AutoUpdatePluginsAfterStart: 500,
|
||||
LinkDownloadRevoke: 10 * 1000 * 60,
|
||||
DefaultHttpRequest: 60000
|
||||
DefaultHttpRequest: 60000,
|
||||
ExternalDeviceReconnect: 3000
|
||||
};
|
||||
|
||||
export { Timeouts };
|
||||
|
|
|
@ -522,8 +522,8 @@
|
|||
"setFileCloseNoSave": "Close and lose changes",
|
||||
"setFileDontClose": "Don't close",
|
||||
"setFileFormatVersion": "File format",
|
||||
"saveFileExportRaw": "Exporting your passwords",
|
||||
"saveFileExportRawBody": "The exported file will contain your passwords, they will not be encrypted there. Would you like to proceed?",
|
||||
"setFileExportRaw": "Exporting your passwords",
|
||||
"setFileExportRawBody": "The exported file will contain your passwords, they will not be encrypted there. Would you like to proceed?",
|
||||
"setFileDeviceIntro": "One-time codes from this {} will be displayed in the app.",
|
||||
"setFileDeviceSettings": "Settings",
|
||||
|
||||
|
@ -603,6 +603,7 @@
|
|||
"setDevicesYubiKeyChalRespTitle": "Challenge-Response",
|
||||
"setDevicesYubiKeyChalRespDesc": "It's also possible to use a YubiKey in challenge-response mode, so that a piece of private key used to encrypt files resides on a YubiKey.",
|
||||
"setDevicesYubiKeyChalRespShow": "Show an option to use a YubiKey when opening files",
|
||||
"setDevicesYubiKeyOathWorkaround": "Reconnect the YubiKey if it hangs when loading one-time codes",
|
||||
|
||||
"setAboutTitle": "About",
|
||||
"setAboutBuilt": "This app is built with these awesome tools",
|
||||
|
|
|
@ -2,10 +2,15 @@ import { Events } from 'framework/events';
|
|||
import { ExternalOtpDeviceModel } from 'models/external/external-otp-device-model';
|
||||
import { ExternalOtpEntryModel } from 'models/external/external-otp-entry-model';
|
||||
import { Launcher } from 'comp/launcher';
|
||||
import { Logger } from 'util/logger';
|
||||
import { UsbListener } from 'comp/app/usb-listener';
|
||||
import { AppSettingsModel } from 'models/app-settings-model';
|
||||
import { Timeouts } from 'const/timeouts';
|
||||
|
||||
let ykmanStatus;
|
||||
|
||||
const logger = new Logger('yubikey');
|
||||
|
||||
class YubiKeyOtpModel extends ExternalOtpDeviceModel {
|
||||
constructor(props) {
|
||||
super({
|
||||
|
@ -32,27 +37,51 @@ class YubiKeyOtpModel extends ExternalOtpDeviceModel {
|
|||
}
|
||||
|
||||
_open(callback, canRetry) {
|
||||
logger.info('Opening');
|
||||
this.openProcess = Launcher.spawn({
|
||||
cmd: 'ykman',
|
||||
args: ['oath', 'code'],
|
||||
noStdOutLogging: true,
|
||||
complete: (err, stdout, code, stderr) => {
|
||||
logger.info('Open complete with code', code);
|
||||
this.openProcess = null;
|
||||
if (this.openAborted) {
|
||||
return callback('Open aborted');
|
||||
}
|
||||
const isStuck =
|
||||
code === 2 && stderr && stderr.includes('Make sure the application');
|
||||
if (isStuck && canRetry) {
|
||||
this.openProcess = Launcher.spawn({
|
||||
if (isStuck) {
|
||||
logger.info('The YubiKey is probably stuck');
|
||||
}
|
||||
if (isStuck && canRetry && AppSettingsModel.yubiKeyOathWorkaround) {
|
||||
logger.info('Repairing a stuck YubiKey');
|
||||
|
||||
let openTimeout;
|
||||
const countYubiKeys = UsbListener.attachedYubiKeys.length;
|
||||
const onDevicesChangedDuringRepair = () => {
|
||||
if (UsbListener.attachedYubiKeys.length === countYubiKeys) {
|
||||
logger.info('YubiKey was reconnected');
|
||||
Events.off('usb-devices-changed', onDevicesChangedDuringRepair);
|
||||
clearTimeout(openTimeout);
|
||||
this.openAborted = false;
|
||||
this._open(callback, false);
|
||||
}
|
||||
};
|
||||
Events.on('usb-devices-changed', onDevicesChangedDuringRepair);
|
||||
|
||||
Launcher.spawn({
|
||||
cmd: 'ykman',
|
||||
args: ['config', 'usb', '-e', 'oath', '-f'],
|
||||
noStdOutLogging: true,
|
||||
complete: err => {
|
||||
logger.info('Repair complete', err ? 'with error' : 'OK');
|
||||
if (err) {
|
||||
Events.off('usb-devices-changed', onDevicesChangedDuringRepair);
|
||||
return callback(err);
|
||||
}
|
||||
this._open(callback, false);
|
||||
openTimeout = setTimeout(() => {
|
||||
Events.off('usb-devices-changed', onDevicesChangedDuringRepair);
|
||||
}, Timeouts.ExternalDeviceReconnect);
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
@ -87,10 +116,12 @@ class YubiKeyOtpModel extends ExternalOtpDeviceModel {
|
|||
}
|
||||
|
||||
cancelOpen() {
|
||||
logger.info('Cancel open');
|
||||
Events.off('usb-devices-changed', this.onUsbDevicesChanged);
|
||||
this.openAborted = true;
|
||||
if (this.openProcess) {
|
||||
this.openProcess.kill();
|
||||
logger.info('Killed the process');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ class SettingsDevicesView extends View {
|
|||
'change .settings__yubikey-show-icon': 'changeYubiKeyShowIcon',
|
||||
'change .settings__yubikey-auto-open': 'changeYubiKeyAutoOpen',
|
||||
'change .settings__yubikey-match-entries': 'changeYubiKeyMatchEntries',
|
||||
'change .settings__yubikey-chalresp-show': 'changeYubiKeyShowChalResp'
|
||||
'change .settings__yubikey-chalresp-show': 'changeYubiKeyShowChalResp',
|
||||
'change .settings__yubikey-oath-workaround': 'changeYubiKeyOathWorkaround'
|
||||
};
|
||||
|
||||
constructor(...args) {
|
||||
|
@ -36,6 +37,7 @@ class SettingsDevicesView extends View {
|
|||
yubiKeyAutoOpen: AppSettingsModel.yubiKeyAutoOpen,
|
||||
yubiKeyMatchEntries: AppSettingsModel.yubiKeyMatchEntries,
|
||||
yubiKeyShowChalResp: AppSettingsModel.yubiKeyShowChalResp,
|
||||
yubiKeyOathWorkaround: AppSettingsModel.yubiKeyOathWorkaround,
|
||||
yubiKeyManualLink: Links.YubiKeyManual,
|
||||
ykmanInstallLink: Links.YubiKeyManagerInstall
|
||||
});
|
||||
|
@ -65,6 +67,11 @@ class SettingsDevicesView extends View {
|
|||
AppSettingsModel.yubiKeyShowChalResp = e.target.checked;
|
||||
this.render();
|
||||
}
|
||||
|
||||
changeYubiKeyOathWorkaround(e) {
|
||||
AppSettingsModel.yubiKeyOathWorkaround = e.target.checked;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
export { SettingsDevicesView };
|
||||
|
|
|
@ -257,8 +257,8 @@ class SettingsFileView extends View {
|
|||
|
||||
saveToXml() {
|
||||
Alerts.yesno({
|
||||
header: Locale.saveFileExportRaw,
|
||||
body: Locale.saveFileExportRawBody,
|
||||
header: Locale.setFileExportRaw,
|
||||
body: Locale.setFileExportRawBody,
|
||||
success: () => {
|
||||
this.model.getXml(xml => {
|
||||
const blob = new Blob([xml], { type: 'text/xml' });
|
||||
|
@ -270,8 +270,8 @@ class SettingsFileView extends View {
|
|||
|
||||
saveToHtml() {
|
||||
Alerts.yesno({
|
||||
header: Locale.saveFileExportRaw,
|
||||
body: Locale.saveFileExportRawBody,
|
||||
header: Locale.setFileExportRaw,
|
||||
body: Locale.setFileExportRawBody,
|
||||
success: () => {
|
||||
this.model.getHtml(html => {
|
||||
const blob = new Blob([html], { type: 'text/html' });
|
||||
|
|
|
@ -36,6 +36,11 @@
|
|||
{{#if yubiKeyMatchEntries}}checked{{/if}} />
|
||||
<label for="settings__yubikey-match-entries">{{res 'setDevicesYubiKeyOtpMatchEntries'}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__yubikey-oath-workaround" id="settings__yubikey-oath-workaround"
|
||||
{{#if yubiKeyOathWorkaround}}checked{{/if}} />
|
||||
<label for="settings__yubikey-oath-workaround">{{res 'setDevicesYubiKeyOathWorkaround'}}</label>
|
||||
</div>
|
||||
<h3>{{res 'setDevicesYubiKeyChalRespTitle'}}</h3>
|
||||
<p>{{res 'setDevicesYubiKeyChalRespDesc'}}</p>
|
||||
<div>
|
||||
|
|
Loading…
Reference in New Issue