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';
|
|
|
|
import { Launcher } from 'comp/launcher';
|
2020-05-05 21:26:41 +02:00
|
|
|
import { UsbListener } from 'comp/app/usb-listener';
|
2020-04-15 16:50:01 +02:00
|
|
|
|
2020-04-19 20:55:38 +02:00
|
|
|
let ykmanStatus;
|
2020-04-19 20:42:55 +02:00
|
|
|
|
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-04-19 20:55:38 +02:00
|
|
|
static get ykmanStatus() {
|
|
|
|
return ykmanStatus;
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
this.openProcess = Launcher.spawn({
|
|
|
|
cmd: 'ykman',
|
|
|
|
args: ['oath', 'code'],
|
|
|
|
noStdOutLogging: true,
|
|
|
|
complete: (err, stdout, code) => {
|
|
|
|
this.openProcess = null;
|
2020-05-05 21:26:41 +02:00
|
|
|
if (this.openAborted) {
|
|
|
|
err = 'Open aborted';
|
|
|
|
}
|
2020-04-15 16:50:01 +02:00
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
|
|
|
for (const line of stdout.split('\n')) {
|
|
|
|
const match = line.match(/^(.*?):(.*?)\s+(.*)$/);
|
|
|
|
if (!match) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const [, title, user, code] = match;
|
|
|
|
const needsTouch = !code.match(/^\d+$/);
|
|
|
|
|
|
|
|
this.entries.push(
|
|
|
|
new ExternalOtpEntryModel({
|
|
|
|
id: title + ':' + user,
|
|
|
|
device: this,
|
|
|
|
icon: 'clock-o',
|
|
|
|
title,
|
|
|
|
user,
|
|
|
|
needsTouch
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
2020-05-05 20:03:38 +02:00
|
|
|
this.active = true;
|
2020-05-05 21:26:41 +02:00
|
|
|
Events.on('usb-devices-changed', this.onUsbDevicesChanged);
|
2020-04-15 16:50:01 +02:00
|
|
|
callback();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
cancelOpen() {
|
2020-05-05 21:26:41 +02:00
|
|
|
Events.off('usb-devices-changed', this.onUsbDevicesChanged);
|
2020-04-15 16:50:01 +02:00
|
|
|
this.openAborted = true;
|
|
|
|
if (this.openProcess) {
|
|
|
|
this.openProcess.kill();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getOtp(entry, callback) {
|
|
|
|
const msPeriod = 30000;
|
|
|
|
const timeLeft = msPeriod - (Date.now() % msPeriod) + 500;
|
|
|
|
return Launcher.spawn({
|
|
|
|
cmd: 'ykman',
|
|
|
|
args: ['oath', 'code', '--single', `${entry.title}:${entry.user}`],
|
|
|
|
noStdOutLogging: true,
|
|
|
|
complete: (err, stdout) => {
|
|
|
|
if (err) {
|
|
|
|
return callback(err, null, timeLeft);
|
|
|
|
}
|
|
|
|
const otp = stdout.trim();
|
|
|
|
callback(null, otp, timeLeft);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
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-19 20:42:55 +02:00
|
|
|
static checkToolStatus() {
|
2020-04-19 20:55:38 +02:00
|
|
|
if (ykmanStatus === 'ok') {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
2020-04-19 20:42:55 +02:00
|
|
|
return new Promise(resolve => {
|
2020-04-19 20:55:38 +02:00
|
|
|
ykmanStatus = 'checking';
|
2020-04-19 20:42:55 +02:00
|
|
|
Launcher.spawn({
|
|
|
|
cmd: 'ykman',
|
|
|
|
args: ['-v'],
|
|
|
|
noStdOutLogging: true,
|
|
|
|
complete: (err, stdout, code) => {
|
|
|
|
if (err || code !== 0) {
|
2020-04-19 20:55:38 +02:00
|
|
|
ykmanStatus = 'error';
|
2020-04-19 20:42:55 +02:00
|
|
|
} else {
|
2020-04-19 20:55:38 +02:00
|
|
|
ykmanStatus = 'ok';
|
2020-04-19 20:42:55 +02:00
|
|
|
}
|
|
|
|
resolve();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2020-04-15 16:50:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
YubiKeyOtpModel.defineModelProperties({
|
2020-05-05 21:26:41 +02:00
|
|
|
onUsbDevicesChanged: null,
|
2020-04-15 16:50:01 +02:00
|
|
|
openProcess: null,
|
|
|
|
openAborted: false
|
|
|
|
});
|
|
|
|
|
|
|
|
export { YubiKeyOtpModel };
|