keeweb/app/scripts/models/external/yubikey-otp-model.js

124 lines
3.5 KiB
JavaScript
Raw Normal View History

2020-05-05 21:26:41 +02:00
import { Events } from 'framework/events';
2020-04-15 16:50:01 +02:00
import { ExternalOtpDeviceModel } from 'models/external/external-otp-device-model';
import { ExternalOtpEntryModel } from 'models/external/external-otp-entry-model';
2020-05-06 18:37:37 +02:00
import { Logger } from 'util/logger';
2020-05-05 21:26:41 +02:00
import { UsbListener } from 'comp/app/usb-listener';
2020-05-24 18:39:56 +02:00
import { YubiKey } from 'comp/app/yubikey';
2020-04-19 20:42:55 +02:00
2020-05-06 18:37:37 +02:00
const logger = new Logger('yubikey');
2020-04-19 20:55:38 +02:00
class YubiKeyOtpModel extends ExternalOtpDeviceModel {
2020-04-15 16:50:01 +02:00
constructor(props) {
super({
2020-05-05 20:03:38 +02:00
id: 'yubikey',
name: 'YubiKey',
2020-04-15 16:50:01 +02:00
shortName: 'YubiKey',
2020-05-05 20:50:11 +02:00
deviceClassName: 'YubiKey',
2020-04-15 16:50:01 +02:00
...props
});
}
2020-05-05 21:26:41 +02:00
onUsbDevicesChanged = () => {
if (UsbListener.attachedYubiKeys.length === 0) {
this.emit('ejected');
}
};
2020-04-15 16:50:01 +02:00
open(callback) {
2020-05-29 23:15:10 +02:00
YubiKey.listWithYkman((err, yubiKeys) => {
2020-05-24 18:39:56 +02:00
if (err) {
return callback(err);
}
2020-05-07 18:58:17 +02:00
2020-05-24 18:39:56 +02:00
let openSuccess = 0;
const openErrors = [];
const openNextYubiKey = () => {
const yubiKey = yubiKeys.shift();
2020-06-01 16:53:51 +02:00
this._addYubiKey(yubiKey.serial, (err) => {
2020-05-24 18:39:56 +02:00
if (YubiKey.aborted) {
return callback('Aborted');
}
2020-05-24 18:39:56 +02:00
if (err) {
openErrors.push(err);
} else {
openSuccess++;
2020-05-07 18:58:17 +02:00
}
2020-05-24 18:39:56 +02:00
if (yubiKeys && yubiKeys.length) {
openNextYubiKey();
} else {
if (openSuccess) {
this._openComplete();
2020-05-06 19:30:30 +02:00
}
2020-05-24 18:39:56 +02:00
callback(openSuccess ? null : openErrors[0]);
}
});
};
openNextYubiKey();
2020-05-06 19:30:30 +02:00
});
}
2020-05-07 18:58:17 +02:00
_addYubiKey(serial, callback) {
logger.info('Adding YubiKey', serial);
2020-05-24 18:39:56 +02:00
YubiKey.getOtpCodes(serial, (err, codes) => {
if (err) {
return callback(err);
2020-04-15 16:50:01 +02:00
}
2020-05-24 18:39:56 +02:00
for (const code of codes) {
this.entries.push(
new ExternalOtpEntryModel({
id: this.entryId(code.title, code.user),
device: this,
deviceSubId: serial,
icon: 'clock-o',
title: code.title,
user: code.user,
needsTouch: code.needsTouch
})
);
2020-05-06 23:12:49 +02:00
}
2020-05-24 18:39:56 +02:00
callback();
2020-05-06 23:12:49 +02:00
});
}
_openComplete() {
this.active = true;
this._buildEntryMap();
Events.on('usb-devices-changed', this.onUsbDevicesChanged);
}
2020-04-15 16:50:01 +02:00
cancelOpen() {
2020-05-24 18:39:56 +02:00
YubiKey.abort();
2020-05-05 21:26:41 +02:00
Events.off('usb-devices-changed', this.onUsbDevicesChanged);
2020-04-15 16:50:01 +02:00
}
getOtp(entry, callback) {
const msPeriod = 30000;
const timeLeft = msPeriod - (Date.now() % msPeriod) + 500;
2020-05-24 18:39:56 +02:00
YubiKey.getOtp(entry.deviceSubId, `${entry.title}:${entry.user}`, (err, otp) => {
callback(err, otp, timeLeft);
2020-04-15 16:50:01 +02:00
});
}
cancelGetOtp(entry, ps) {
if (ps) {
ps.kill();
}
}
2020-04-19 20:42:55 +02:00
2020-05-05 20:47:15 +02:00
close(callback) {
2020-05-05 21:26:41 +02:00
Events.off('usb-devices-changed', this.onUsbDevicesChanged);
2020-05-05 20:47:15 +02:00
this.set({
active: false
});
}
2020-04-15 16:50:01 +02:00
}
YubiKeyOtpModel.defineModelProperties({
2020-05-24 18:39:56 +02:00
onUsbDevicesChanged: null
2020-04-15 16:50:01 +02:00
});
export { YubiKeyOtpModel };