mirror of https://github.com/keeweb/keeweb
listing YubiKeys on the open view
parent
f215c26a63
commit
93310eb029
|
@ -80,9 +80,13 @@ const YubiKey = {
|
|||
const yubiKeysIncludingEmpty = stdout
|
||||
.trim()
|
||||
.split(/\n/g)
|
||||
.map(line => (line.match(/\d{5,}$/g) || [])[0]);
|
||||
.map(line => {
|
||||
const fullName = line;
|
||||
const serial = (line.match(/\d{5,}$/g) || [])[0];
|
||||
return { fullName, serial };
|
||||
});
|
||||
|
||||
const yubiKeys = yubiKeysIncludingEmpty.filter(s => s);
|
||||
const yubiKeys = yubiKeysIncludingEmpty.filter(s => s.serial);
|
||||
|
||||
if (
|
||||
yubiKeysIncludingEmpty.length === 1 &&
|
||||
|
|
|
@ -233,6 +233,12 @@
|
|||
"openListErrorBody": "There was an error loading file list",
|
||||
"openShowAllFiles": "Show all files",
|
||||
"openFileNoCacheError": "File not found in the cache storage. This can happen because browser storage was cleaned up. To open the file, remove it from KeeWeb and add it again.",
|
||||
"openChalRespHeader": "Challenge-Response",
|
||||
"openChalRespLoading": "Loading the list of YubiKeys",
|
||||
"openChalRespSelectYubiKey": "Select a YubiKey that you would like to use",
|
||||
"openChalRespErrorNotInstalled": "YubiKey integration is not configured, please go to settings to set it up",
|
||||
"openChalRespErrorEmpty": "No YubiKeys found",
|
||||
"openChalRespSlot": "slot",
|
||||
|
||||
"detAttDownload": "Shift-click the attachment button to download it or",
|
||||
"detAttDelToRemove": "Delete to remove",
|
||||
|
|
|
@ -35,7 +35,7 @@ class YubiKeyOtpModel extends ExternalOtpDeviceModel {
|
|||
const openErrors = [];
|
||||
const openNextYubiKey = () => {
|
||||
const yubiKey = yubiKeys.shift();
|
||||
this._addYubiKey(yubiKey, err => {
|
||||
this._addYubiKey(yubiKey.serial, err => {
|
||||
if (YubiKey.aborted) {
|
||||
return callback('Aborted');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import { View } from 'framework/views/view';
|
||||
import { YubiKey } from 'comp/app/yubikey';
|
||||
import { Locale } from 'util/locale';
|
||||
import template from 'templates/open-chal-resp.hbs';
|
||||
|
||||
class OpenChalRespView extends View {
|
||||
template = template;
|
||||
|
||||
events = {
|
||||
'click .open-chal-resp__item': 'itemClick'
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
YubiKey.checkToolStatus().then(status => {
|
||||
if (this.removed) {
|
||||
return;
|
||||
}
|
||||
if (status === 'ok') {
|
||||
YubiKey.list((err, yubiKeys) => {
|
||||
this.error = err;
|
||||
this.yubiKeys = [];
|
||||
for (const { fullName, serial } of yubiKeys) {
|
||||
for (const slot of [1, 2]) {
|
||||
this.yubiKeys.push({
|
||||
fullName,
|
||||
serial,
|
||||
slot
|
||||
});
|
||||
}
|
||||
}
|
||||
this.render();
|
||||
});
|
||||
} else {
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let error = this.error;
|
||||
|
||||
if (YubiKey.ykmanStatus === 'error') {
|
||||
error = Locale.openChalRespErrorNotInstalled.replace('{}', 'ykman');
|
||||
}
|
||||
if (this.yubiKeys && !this.yubiKeys.length) {
|
||||
error = Locale.openChalRespErrorEmpty;
|
||||
}
|
||||
|
||||
super.render({
|
||||
error,
|
||||
yubiKeys: this.yubiKeys,
|
||||
loading: !YubiKey.ykmanStatus || YubiKey.ykmanStatus === 'checking'
|
||||
});
|
||||
}
|
||||
|
||||
itemClick(e) {
|
||||
const el = e.target.closest('[data-serial]');
|
||||
const { serial, slot } = el.dataset;
|
||||
this.emit('select', { serial, slot });
|
||||
}
|
||||
}
|
||||
|
||||
export { OpenChalRespView };
|
|
@ -19,6 +19,7 @@ import { Logger } from 'util/logger';
|
|||
import { InputFx } from 'util/ui/input-fx';
|
||||
import { OpenConfigView } from 'views/open-config-view';
|
||||
import { StorageFileListView } from 'views/storage-file-list-view';
|
||||
import { OpenChalRespView } from 'views/open-chal-resp-view';
|
||||
import { omit } from 'util/fn';
|
||||
import { GeneratorView } from 'views/generator-view';
|
||||
import template from 'templates/open.hbs';
|
||||
|
@ -1043,16 +1044,19 @@ class OpenView extends View {
|
|||
if (this.busy) {
|
||||
return;
|
||||
}
|
||||
this.busy = true;
|
||||
YubiKey.checkToolStatus().then(status => {
|
||||
if (status !== 'ok') {
|
||||
this.busy = false;
|
||||
return Events.emit('toggle-settings', 'devices');
|
||||
}
|
||||
|
||||
this.busy = false;
|
||||
const chalRespView = new OpenChalRespView();
|
||||
chalRespView.on('select', e => {
|
||||
// console.log('e', e.serial, e.slot);
|
||||
});
|
||||
|
||||
Alerts.notImplemented();
|
||||
Alerts.alert({
|
||||
header: Locale.openChalRespHeader,
|
||||
icon: 'exchange',
|
||||
buttons: [{ result: '', title: Locale.alertCancel }],
|
||||
esc: '',
|
||||
click: '',
|
||||
view: chalRespView
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -388,3 +388,19 @@
|
|||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.open-chal-resp {
|
||||
&__head {
|
||||
padding: $base-padding;
|
||||
}
|
||||
&__icon {
|
||||
margin-right: $small-spacing;
|
||||
}
|
||||
&__item {
|
||||
padding: $base-padding;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: var(--action-background-color-focus-tr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<div class="open-chal-resp">
|
||||
{{#if loading}}
|
||||
<div class="open-chal-resp__head">
|
||||
<i class="open-chal-resp__icon fa fa-spinner fa-spin"></i> {{res 'openChalRespLoading'}}
|
||||
</div>
|
||||
{{else if error}}
|
||||
<div class="open-chal-resp__head">
|
||||
<i class="open-chal-resp__icon fa fa-ban"></i> {{error}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="open-chal-resp__head">{{res 'openChalRespSelectYubiKey'}}:</div>
|
||||
<div>
|
||||
{{#each yubiKeys as |yk|}}
|
||||
<div class="open-chal-resp__item" data-serial="{{yk.serial}}" data-slot="{{yk.slot}}">
|
||||
{{yk.fullName}}, {{res 'openChalRespSlot'}} {{yk.slot}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
Loading…
Reference in New Issue