renamed external model to otp-device

This commit is contained in:
antelle 2021-03-28 19:05:20 +02:00
parent 92da783e82
commit 68010076f9
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C
19 changed files with 174 additions and 145 deletions

View File

@ -1,8 +0,0 @@
import { Collection } from 'framework/collection';
import { ExternalEntryModel } from 'models/external/external-entry-model';
class ExternalEntryCollection extends Collection {
static model = ExternalEntryModel;
}
export { ExternalEntryCollection };

View File

@ -12,7 +12,7 @@ import { EntryModel } from 'models/entry-model';
import { FileInfoModel } from 'models/file-info-model';
import { FileModel } from 'models/file-model';
import { GroupModel } from 'models/group-model';
import { YubiKeyOtpModel } from 'models/external/yubikey-otp-model';
import { YubiKeyOtpModel } from 'models/otp-device/yubikey-otp-model';
import { MenuModel } from 'models/menu/menu-model';
import { PluginManager } from 'plugins/plugin-manager';
import { Features } from 'util/features';
@ -307,12 +307,12 @@ class AppModel {
const preparedFilter = this.prepareFilter(filter);
const entries = new SearchResultCollection();
const devicesToMatchOtpEntries = this.files.filter((file) => file.external);
const devicesToMatchOtpEntries = this.files.filter((file) => file.backend === 'otp-device');
const matchedOtpEntrySet = this.settings.yubiKeyMatchEntries ? new Set() : undefined;
this.files
.filter((file) => !file.external)
.filter((file) => file.backend !== 'otp-device')
.forEach((file) => {
file.forEachEntry(preparedFilter, (entry) => {
if (matchedOtpEntrySet) {
@ -827,7 +827,10 @@ class AppModel {
this.scheduleBackupFile(file, data);
}
if (this.settings.yubiKeyAutoOpen) {
if (this.attachedYubiKeysCount > 0 && !this.files.some((f) => f.external)) {
if (
this.attachedYubiKeysCount > 0 &&
!this.files.some((f) => f.backend === 'otp-device')
) {
this.tryOpenOtpDeviceInBackground();
}
}
@ -1271,7 +1274,9 @@ class AppModel {
}
const isNewYubiKey = UsbListener.attachedYubiKeys > attachedYubiKeysCount;
const hasOpenFiles = this.files.some((file) => file.active && !file.external);
const hasOpenFiles = this.files.some(
(file) => file.active && file.backend !== 'otp-device'
);
if (isNewYubiKey && hasOpenFiles && !this.openingOtpDevice) {
this.tryOpenOtpDeviceInBackground();
@ -1303,7 +1308,7 @@ class AppModel {
return null;
}
for (const file of this.files) {
if (file.external) {
if (file.backend === 'otp-device') {
const matchingEntry = file.getMatchingEntry(entry);
if (matchingEntry) {
return matchingEntry;

View File

@ -505,7 +505,7 @@ class EntryModel extends Model {
}
try {
this.otpGenerator = Otp.parseUrl(otpUrl);
} catch (e) {
} catch {
this.otpGenerator = null;
}
} else {

View File

@ -1,21 +0,0 @@
import { ExternalDeviceModel } from 'models/external/external-device-model';
class ExternalOtpDeviceModel extends ExternalDeviceModel {
open(callback) {
throw 'Not implemented';
}
cancelOpen() {
throw 'Not implemented';
}
close(callback) {
throw 'Not implemented';
}
getOtp(callback) {
throw 'Not implemented';
}
}
export { ExternalOtpDeviceModel };

View File

@ -766,7 +766,11 @@ FileModel.defineModelProperties({
oldKeyFileHash: null,
oldKeyChangeDate: null,
encryptedPassword: null,
encryptedPasswordDate: null
encryptedPasswordDate: null,
supportsTags: true,
supportsColors: true,
supportsIcons: true,
supportsExpiration: true
});
export { FileModel };

View File

@ -0,0 +1,8 @@
import { Collection } from 'framework/collection';
import { OtpDeviceEntryModel } from './otp-device-entry-model';
class OtpDeviceEntryCollection extends Collection {
static model = OtpDeviceEntryModel;
}
export { OtpDeviceEntryCollection };

View File

@ -1,7 +1,7 @@
import { Model } from 'framework/model';
import { EntrySearch } from 'util/entry-search';
class ExternalEntryModel extends Model {
class OtpDeviceEntryModel extends Model {
tags = [];
fields = {};
@ -44,9 +44,10 @@ class ExternalEntryModel extends Model {
}
}
ExternalEntryModel.defineModelProperties({
OtpDeviceEntryModel.defineModelProperties({
id: '',
external: true,
file: null,
entry: true,
readOnly: true,
device: undefined,
deviceSubId: null,
@ -59,4 +60,4 @@ ExternalEntryModel.defineModelProperties({
_search: undefined
});
export { ExternalEntryModel };
export { OtpDeviceEntryModel };

View File

@ -1,13 +1,11 @@
import { Model } from 'framework/model';
import { ExternalEntryCollection } from 'collections/external-entry-collection';
import { OtpDeviceEntryCollection } from './otp-device-entry-collection';
class ExternalDeviceModel extends Model {
entries = new ExternalEntryCollection();
class OtpDeviceModel extends Model {
entries = new OtpDeviceEntryCollection();
groups = [];
entryMap = {};
close() {}
forEachEntry(filter, callback) {
if (filter.trash || filter.group || filter.tag) {
return;
@ -32,11 +30,28 @@ class ExternalDeviceModel extends Model {
this.entryMap[entry.id.toLowerCase()] = entry;
}
}
open(callback) {
throw 'Not implemented';
}
cancelOpen() {
throw 'Not implemented';
}
close(callback) {
throw 'Not implemented';
}
getOtp(callback) {
throw 'Not implemented';
}
}
ExternalDeviceModel.defineModelProperties({
OtpDeviceModel.defineModelProperties({
id: '',
external: true,
backend: 'otp-device',
skipOpenList: true,
readOnly: true,
active: false,
entries: undefined,
@ -47,4 +62,4 @@ ExternalDeviceModel.defineModelProperties({
entryMap: undefined
});
export { ExternalDeviceModel };
export { OtpDeviceModel };

View File

@ -1,6 +1,6 @@
import { ExternalEntryModel } from 'models/external/external-entry-model';
import { OtpDeviceEntryModel } from './otp-device-entry-model';
class ExternalOtpEntryModel extends ExternalEntryModel {
class OtpEntryModel extends OtpDeviceEntryModel {
constructor(props) {
super({
...props,
@ -59,10 +59,11 @@ class ExternalOtpEntryModel extends ExternalEntryModel {
}
}
ExternalOtpEntryModel.defineModelProperties({
OtpEntryModel.defineModelProperties({
user: undefined,
backend: 'otp-device',
otpGenerator: undefined,
needsTouch: false
});
export { ExternalOtpEntryModel };
export { OtpEntryModel };

View File

@ -1,13 +1,13 @@
import { Events } from 'framework/events';
import { ExternalOtpDeviceModel } from 'models/external/external-otp-device-model';
import { ExternalOtpEntryModel } from 'models/external/external-otp-entry-model';
import { OtpDeviceModel } from './otp-device-model';
import { OtpEntryModel } from './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 {
class YubiKeyOtpModel extends OtpDeviceModel {
constructor(props) {
super({
id: 'yubikey',
@ -67,8 +67,9 @@ class YubiKeyOtpModel extends ExternalOtpDeviceModel {
for (const code of codes) {
this.entries.push(
new ExternalOtpEntryModel({
new OtpEntryModel({
id: this.entryId(code.title, code.user),
file: this,
device: this,
deviceSubId: serial,
icon: 'clock',

View File

@ -14,9 +14,6 @@ EntryPresenter.prototype = {
this.entry = item;
} else if (item.group) {
this.group = item;
} else if (item.external) {
this.entry = item;
this.external = true;
}
return this;
},
@ -71,7 +68,7 @@ EntryPresenter.prototype = {
if (!this.entry) {
return '[' + Locale.listGroup + ']';
}
if (this.external) {
if (this.entry.backend === 'otp-device') {
return this.entry.description;
}
switch (this.descField) {

View File

@ -616,7 +616,7 @@ class AppView extends View {
closeAllFilesAndShowFirst() {
let fileToShow = this.model.files.find(
(file) => !file.demo && !file.created && !file.external
(file) => !file.demo && !file.created && !file.skipOpenList
);
this.model.closeAllFiles();
if (!fileToShow) {

View File

@ -21,7 +21,7 @@ function createDetailsFields(detailsView) {
const fieldViews = [];
const fieldViewsAside = [];
if (model.external) {
if (model.backend === 'otp-device') {
fieldViewsAside.push(
new FieldViewReadOnly({
name: 'Device',
@ -73,15 +73,27 @@ function createDetailsFields(detailsView) {
})
);
} else {
fieldViewsAside.push(
new FieldViewReadOnly({
name: 'File',
title: StringFormat.capFirst(Locale.file),
value() {
return model.fileName;
}
})
);
if (model.backend) {
fieldViewsAside.push(
new FieldViewReadOnly({
name: 'Storage',
title: StringFormat.capFirst(Locale.storage),
value() {
return model.fileName;
}
})
);
} else {
fieldViewsAside.push(
new FieldViewReadOnly({
name: 'File',
title: StringFormat.capFirst(Locale.file),
value() {
return model.fileName;
}
})
);
}
}
fieldViews.push(
new FieldViewAutocomplete({
@ -127,26 +139,30 @@ function createDetailsFields(detailsView) {
sequence: '{NOTES}'
})
);
fieldViews.push(
new FieldViewTags({
name: 'Tags',
title: StringFormat.capFirst(Locale.tags),
tags: AppModel.instance.tags,
value() {
return model.tags;
}
})
);
fieldViews.push(
new FieldViewDate({
name: 'Expires',
title: Locale.detExpires,
lessThanNow: '(' + Locale.detExpired + ')',
value() {
return model.expires;
}
})
);
if (model.file.supportsTags) {
fieldViews.push(
new FieldViewTags({
name: 'Tags',
title: StringFormat.capFirst(Locale.tags),
tags: AppModel.instance.tags,
value() {
return model.tags;
}
})
);
}
if (model.file.supportsExpiration) {
fieldViews.push(
new FieldViewDate({
name: 'Expires',
title: Locale.detExpires,
lessThanNow: '(' + Locale.detExpired + ')',
value() {
return model.expires;
}
})
);
}
fieldViewsAside.push(
new FieldViewReadOnly({
name: 'Group',
@ -159,33 +175,39 @@ function createDetailsFields(detailsView) {
}
})
);
fieldViewsAside.push(
new FieldViewReadOnly({
name: 'Created',
title: Locale.detCreated,
value() {
return DateFormat.dtStr(model.created);
}
})
);
fieldViewsAside.push(
new FieldViewReadOnly({
name: 'Updated',
title: Locale.detUpdated,
value() {
return DateFormat.dtStr(model.updated);
}
})
);
fieldViewsAside.push(
new FieldViewHistory({
name: 'History',
title: StringFormat.capFirst(Locale.history),
value() {
return { length: model.historyLength, unsaved: model.unsaved };
}
})
);
if (model.created) {
fieldViewsAside.push(
new FieldViewReadOnly({
name: 'Created',
title: Locale.detCreated,
value() {
return DateFormat.dtStr(model.created);
}
})
);
}
if (model.updated) {
fieldViewsAside.push(
new FieldViewReadOnly({
name: 'Updated',
title: Locale.detUpdated,
value() {
return DateFormat.dtStr(model.updated);
}
})
);
}
if (!model.backend) {
fieldViewsAside.push(
new FieldViewHistory({
name: 'History',
title: StringFormat.capFirst(Locale.history),
value() {
return { length: model.historyLength, unsaved: model.unsaved };
}
})
);
}
if (otpEntry) {
fieldViews.push(
new FieldViewOtp({
@ -207,7 +229,7 @@ function createDetailsFields(detailsView) {
fieldViews.push(
new FieldViewOtp({
name: '$' + field,
title: field,
title: Locale.detOtpField,
value() {
return model.otpGenerator;
},

View File

@ -121,6 +121,9 @@ class DetailsView extends View {
}
const model = {
deleted: this.appModel.filter.trash,
canEditColor: this.model.file.supportsColors && !this.model.readOnly,
canEditIcon: this.model.file.supportsIcons && !this.model.readOnly,
showButtons: !this.model.backend && !this.model.readOnly,
...this.model
};
this.template = template;
@ -185,7 +188,7 @@ class DetailsView extends View {
this.fieldViews = fieldViews.concat(fieldViewsAside);
if (!this.model.external) {
if (!this.model.backend) {
this.moreView = new DetailsAddFieldView();
this.moreView.render();
this.moreView.on('add-field', this.addNewField.bind(this));
@ -363,7 +366,7 @@ class DetailsView extends View {
}
toggleIcons() {
if (this.model.external) {
if (this.model.backend) {
return;
}
if (this.views.sub && this.views.sub instanceof IconSelectView) {
@ -464,7 +467,7 @@ class DetailsView extends View {
}
this.model.initOtpGenerator?.();
if (this.model.external) {
if (this.model.backend === 'otp-device') {
return;
}
@ -496,7 +499,7 @@ class DetailsView extends View {
if (!this.model) {
return;
}
if (this.model.external) {
if (this.model.backend === 'otp-device') {
this.copyOtp();
e.preventDefault();
}
@ -520,7 +523,7 @@ class DetailsView extends View {
copyOtp() {
const otpField = this.getFieldView('$otp');
if (this.model.external) {
if (this.model.backend === 'otp-device') {
if (!otpField) {
return false;
}
@ -715,7 +718,7 @@ class DetailsView extends View {
}
editTitle() {
if (this.model.external) {
if (this.model.backend === 'otp-device') {
return;
}
const input = $('<input/>')
@ -875,7 +878,7 @@ class DetailsView extends View {
const canCopy = document.queryCommandSupported('copy');
const options = [];
if (canCopy) {
if (this.model.external) {
if (this.model.backend === 'otp-device') {
options.push({
value: 'det-copy-otp',
icon: 'copy',
@ -894,7 +897,7 @@ class DetailsView extends View {
text: Locale.detMenuCopyUser
});
}
if (!this.model.external) {
if (!this.model.backend) {
options.push({ value: 'det-add-new', icon: 'plus', text: Locale.detMenuAddNewField });
options.push({ value: 'det-clone', icon: 'clone', text: Locale.detClone });
if (canCopy) {
@ -985,7 +988,8 @@ class DetailsView extends View {
autoType(sequence) {
const entry = this.model;
const hasOtp = sequence?.includes('{TOTP}') || (entry.external && !sequence);
const hasOtp =
sequence?.includes('{TOTP}') || (entry.backend === 'otp-device' && !sequence);
if (hasOtp) {
const otpField = this.getFieldView('$otp');
otpField.refreshOtp((err) => {

View File

@ -5,7 +5,7 @@ class FieldViewReadOnly extends FieldView {
readonly = true;
renderValue(value) {
value = value.isProtected ? new Array(value.textLength + 1).join('•') : escape(value);
value = value?.isProtected ? new Array(value.textLength + 1).join('•') : escape(value);
value = value.replace(/\n/g, '<br/>');
return value;
}

View File

@ -1,8 +1,8 @@
import { View } from 'framework/views/view';
import { Events } from 'framework/events';
import template from 'templates/settings/settings-file-external.hbs';
import template from 'templates/settings/settings-file-otp-device.hbs';
class SettingsFileExternalView extends View {
class SettingsFileOtpDeviceView extends View {
template = template;
events = {
@ -26,4 +26,4 @@ class SettingsFileExternalView extends View {
}
}
export { SettingsFileExternalView };
export { SettingsFileOtpDeviceView };

View File

@ -33,8 +33,8 @@ class SettingsView extends View {
setPage(e) {
let { page, section, file } = e;
if (page === 'file' && file && file.external) {
page = 'file-external';
if (page === 'file' && file && file.backend === 'otp-device') {
page = 'file-otp-device';
}
const module = require('./settings-' + page + '-view');
const viewName = StringFormat.pascalCase(page);

View File

@ -4,11 +4,11 @@
</div>
<div class="details__header">
<i class="details__header-color fa fa-bookmark-o"
{{#unless readOnly}}
{{#if canEditColor}}
title="{{res 'detSetIconColor'}}" tip-placement="left"
{{/unless}}
{{/if}}
>
{{#unless readOnly}}
{{#if canEditColor}}
<span class="details__colors-popup">
<span class="details__colors-popup-item yellow-color fa fa-bookmark-o" data-color="yellow"></span>
<span class="details__colors-popup-item green-color fa fa-bookmark-o" data-color="green"></span>
@ -17,18 +17,18 @@
<span class="details__colors-popup-item blue-color fa fa-bookmark-o" data-color="blue"></span>
<span class="details__colors-popup-item violet-color fa fa-bookmark-o" data-color="violet"></span>
</span>
{{/unless}}
{{/if}}
</i>
<h1 class="details__header-title">{{#if title}}{{title}}{{else}}(no title){{/if}}</h1>
{{#if customIcon}}
<div class="details__header-icon details__header-icon--icon"
{{#unless readOnly}}title="{{res 'detSetIcon'}}"{{/unless}}
{{#if canEditIcon}}title="{{res 'detSetIcon'}}"{{/if}}
>
<img class="details__header-icon-img" src="{{customIcon}}" />
</div>
{{else}}
<i class="details__header-icon fa fa-{{icon}}"
{{#unless readOnly}}title="{{res 'detSetIcon'}}"{{/unless}}
{{#if canEditIcon}}title="{{res 'detSetIcon'}}"{{/if}}
></i>
{{/if}}
</div>
@ -42,9 +42,9 @@
</div>
<div class="scroller__bar-wrapper"><div class="scroller__bar"></div></div>
</div>
{{#unless readOnly}}
<div class="details__issues-container">
</div>
<div class="details__issues-container">
</div>
{{#if showButtons}}
<div class="details__buttons">
{{#if deleted~}}
<i class="details__buttons-trash-del fa fa-minus-circle" title="{{res 'detDelEntryPerm'}}" tip-placement="top"></i>
@ -67,7 +67,7 @@
{{/each}}
</div>
</div>
{{/unless}}
{{/if}}
<div class="details__dropzone">
<i class="fa fa-paperclip muted-color details__dropzone-icon"></i>
<h1 class="muted-color details__dropzone-header">{{res 'detDropAttachments'}}</h1>