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

124 lines
3.5 KiB
JavaScript

import { Events } from 'framework/events';
import { ExternalOtpDeviceModel } from 'models/external/external-otp-device-model';
import { ExternalOtpEntryModel } from 'models/external/external-otp-entry-model';
import { Logger } from 'util/logger';
import { UsbListener } from 'comp/app/usb-listener';
import { YubiKey } from 'comp/app/yubikey';
const logger = new Logger('yubikey');
class YubiKeyOtpModel extends ExternalOtpDeviceModel {
constructor(props) {
super({
id: 'yubikey',
name: 'YubiKey',
shortName: 'YubiKey',
deviceClassName: 'YubiKey',
...props
});
}
onUsbDevicesChanged = () => {
if (UsbListener.attachedYubiKeys.length === 0) {
this.emit('ejected');
}
};
open(callback) {
YubiKey.listWithYkman((err, yubiKeys) => {
if (err) {
return callback(err);
}
let openSuccess = 0;
const openErrors = [];
const openNextYubiKey = () => {
const yubiKey = yubiKeys.shift();
this._addYubiKey(yubiKey.serial, (err) => {
if (YubiKey.aborted) {
return callback('Aborted');
}
if (err) {
openErrors.push(err);
} else {
openSuccess++;
}
if (yubiKeys && yubiKeys.length) {
openNextYubiKey();
} else {
if (openSuccess) {
this._openComplete();
}
callback(openSuccess ? null : openErrors[0]);
}
});
};
openNextYubiKey();
});
}
_addYubiKey(serial, callback) {
logger.info('Adding YubiKey', serial);
YubiKey.getOtpCodes(serial, (err, codes) => {
if (err) {
return callback(err);
}
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
})
);
}
callback();
});
}
_openComplete() {
this.active = true;
this._buildEntryMap();
Events.on('usb-devices-changed', this.onUsbDevicesChanged);
}
cancelOpen() {
YubiKey.abort();
Events.off('usb-devices-changed', this.onUsbDevicesChanged);
}
getOtp(entry, callback) {
const msPeriod = 30000;
const timeLeft = msPeriod - (Date.now() % msPeriod) + 500;
YubiKey.getOtp(entry.deviceSubId, `${entry.title}:${entry.user}`, (err, otp) => {
callback(err, otp, timeLeft);
});
}
cancelGetOtp(entry, ps) {
if (ps) {
ps.kill();
}
}
close(callback) {
Events.off('usb-devices-changed', this.onUsbDevicesChanged);
this.set({
active: false
});
}
}
YubiKeyOtpModel.defineModelProperties({
onUsbDevicesChanged: null
});
export { YubiKeyOtpModel };