mirror of https://github.com/keeweb/keeweb.git
otp moved to model
This commit is contained in:
parent
6217c97441
commit
b3ee43e519
|
@ -5,7 +5,7 @@ var Backbone = require('backbone'),
|
|||
Locale = require('../util/locale'),
|
||||
Logger = require('../util/logger'),
|
||||
FeatureDetector = require('../util/feature-detector'),
|
||||
Otp = require('../comp/otp'),
|
||||
Otp = require('../util/otp'),
|
||||
QrCode = require('qrcode');
|
||||
|
||||
var logger = new Logger('otp-qr-reader');
|
||||
|
|
|
@ -5,6 +5,7 @@ var Backbone = require('backbone'),
|
|||
IconMap = require('../const/icon-map'),
|
||||
Color = require('../util/color'),
|
||||
IconUrl = require('../util/icon-url'),
|
||||
Otp = require('../util/otp'),
|
||||
kdbxweb = require('kdbxweb');
|
||||
|
||||
var EntryModel = Backbone.Model.extend({
|
||||
|
@ -367,6 +368,66 @@ var EntryModel = Backbone.Model.extend({
|
|||
this.group.group.entries.splice(ix, 1);
|
||||
}
|
||||
this.file.reload();
|
||||
},
|
||||
|
||||
initOtpGenerator: function() {
|
||||
this.otpGenerator = null;
|
||||
var otpUrl;
|
||||
if (this.fields.otp) {
|
||||
otpUrl = this.fields.otp;
|
||||
if (otpUrl.isProtected) {
|
||||
otpUrl = otpUrl.getText();
|
||||
}
|
||||
if (otpUrl.toLowerCase().lastIndexOf('otpauth:', 0) !== 0) {
|
||||
// KeeOTP plugin format
|
||||
var args = {};
|
||||
otpUrl.split('&').forEach(function(part) {
|
||||
var parts = part.split('=', 2);
|
||||
args[parts[0]] = decodeURIComponent(parts[1]).replace(/=/g, '');
|
||||
});
|
||||
if (args.key) {
|
||||
otpUrl = 'otpauth://totp/null?secret=' + args.key +
|
||||
(args.step ? '&period=' + args.step : '') +
|
||||
(args.size ? '&digits=' + args.size : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.fields['TOTP Seed']) {
|
||||
// TrayTOTP plugin format
|
||||
var key = this.fields['TOTP Seed'];
|
||||
if (key.isProtected) {
|
||||
key = key.getText();
|
||||
}
|
||||
if (key) {
|
||||
otpUrl = 'otpauth://totp/null?secret=' + key;
|
||||
}
|
||||
var settings = this.fields['TOTP Settings'];
|
||||
if (settings && settings.isProtected) {
|
||||
settings = settings.getText();
|
||||
}
|
||||
if (settings) {
|
||||
settings = settings.split(';');
|
||||
if (settings.length > 0 && settings[0] > 0) {
|
||||
otpUrl += '&period=' + settings[0];
|
||||
}
|
||||
if (settings.length > 1 && settings[1] > 0) {
|
||||
otpUrl += '&digits=' + settings[1];
|
||||
}
|
||||
}
|
||||
this.fields.otp = kdbxweb.ProtectedValue.fromString(otpUrl);
|
||||
}
|
||||
if (otpUrl) {
|
||||
try {
|
||||
this.otpGenerator = Otp.parseUrl(otpUrl);
|
||||
} catch (e) {
|
||||
this.otpGenerator = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setOtp: function(otp) {
|
||||
this.otpGenerator = otp;
|
||||
this.setField('otp', kdbxweb.ProtectedValue.fromString(otp.url));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
var Logger = require('../util/logger');
|
||||
var Logger = require('./logger');
|
||||
|
||||
var logger = new Logger('otp');
|
||||
|
|
@ -24,7 +24,6 @@ var Backbone = require('backbone'),
|
|||
Alerts = require('../../comp/alerts'),
|
||||
CopyPaste = require('../../comp/copy-paste'),
|
||||
OtpQrReqder = require('../../comp/otp-qr-reader'),
|
||||
Otp = require('../../comp/otp'),
|
||||
Format = require('../../util/format'),
|
||||
Locale = require('../../util/locale'),
|
||||
Tip = require('../../util/tip'),
|
||||
|
@ -42,10 +41,6 @@ var DetailsView = Backbone.View.extend({
|
|||
userEditView: null,
|
||||
urlEditView: null,
|
||||
fieldCopyTip: null,
|
||||
otpTimer: null,
|
||||
otp: null,
|
||||
otpValue: null,
|
||||
otpView: null,
|
||||
|
||||
events: {
|
||||
'click .details__colors-popup-item': 'selectColor',
|
||||
|
@ -84,7 +79,6 @@ var DetailsView = Backbone.View.extend({
|
|||
KeyHandler.offKey(Keys.DOM_VK_DELETE, this.deleteKeyPress, this, KeyHandler.SHORTCUT_ACTION);
|
||||
KeyHandler.offKey(Keys.DOM_VK_BACK_SPACE, this.deleteKeyPress, this, KeyHandler.SHORTCUT_ACTION);
|
||||
this.removeFieldViews();
|
||||
this.stopOtpTimer();
|
||||
Backbone.View.prototype.remove.call(this);
|
||||
},
|
||||
|
||||
|
@ -97,19 +91,9 @@ var DetailsView = Backbone.View.extend({
|
|||
}
|
||||
},
|
||||
|
||||
stopOtpTimer: function() {
|
||||
if (this.otpTimer) {
|
||||
clearTimeout(this.otpTimer);
|
||||
this.otpTimer = null;
|
||||
}
|
||||
this.otp = null;
|
||||
this.otpValue = null;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.removeScroll();
|
||||
this.removeFieldViews();
|
||||
this.stopOtpTimer();
|
||||
if (this.views.sub) {
|
||||
this.views.sub.remove();
|
||||
delete this.views.sub;
|
||||
|
@ -131,7 +115,7 @@ var DetailsView = Backbone.View.extend({
|
|||
this.$el.html(this.template(model));
|
||||
Tip.createTips(this.$el);
|
||||
this.setSelectedColor(this.model.color);
|
||||
this.setOtpByEntry();
|
||||
this.model.initOtpGenerator();
|
||||
this.addFieldViews();
|
||||
this.createScroll({
|
||||
root: this.$el.find('.details__body')[0],
|
||||
|
@ -174,11 +158,9 @@ var DetailsView = Backbone.View.extend({
|
|||
this.fieldViews.push(new FieldViewHistory({ model: { name: 'History', title: Locale.detHistory,
|
||||
value: function() { return { length: model.historyLength, unsaved: model.unsaved }; } } }));
|
||||
_.forEach(model.fields, function(value, field) {
|
||||
if (field.toLowerCase() === 'otp') {
|
||||
var that = this;
|
||||
this.otpView = new FieldViewOtp({ model: { name: '$' + field, title: field,
|
||||
value: function() { return that.otpValue; } } });
|
||||
this.fieldViews.push(this.otpView);
|
||||
if (field.toLowerCase() === 'otp' && this.model.otpGenerator) {
|
||||
this.fieldViews.push(new FieldViewOtp({ model: { name: '$' + field, title: field,
|
||||
value: function() { return model.otpGenerator; } } }));
|
||||
} else {
|
||||
this.fieldViews.push(new FieldViewCustom({ model: { name: '$' + field, title: field,
|
||||
value: function() { return model.fields[field]; } } }));
|
||||
|
@ -706,88 +688,13 @@ var DetailsView = Backbone.View.extend({
|
|||
Backbone.trigger('toggle-details', false);
|
||||
},
|
||||
|
||||
setOtpByEntry: function() {
|
||||
this.otp = null;
|
||||
this.otpValue = null;
|
||||
this.stopOtpTimer();
|
||||
var otpUrl;
|
||||
if (this.model.fields.otp) {
|
||||
otpUrl = this.model.fields.otp;
|
||||
if (otpUrl.isProtected) {
|
||||
otpUrl = otpUrl.getText();
|
||||
}
|
||||
if (otpUrl.toLowerCase().lastIndexOf('otpauth:', 0) !== 0) {
|
||||
// KeeOTP plugin format
|
||||
var args = {};
|
||||
otpUrl.split('&').forEach(function(part) {
|
||||
var parts = part.split('=', 2);
|
||||
args[parts[0]] = decodeURIComponent(parts[1]).replace(/=/g, '');
|
||||
});
|
||||
if (args.key) {
|
||||
otpUrl = 'otpauth://totp/null?secret=' + args.key +
|
||||
(args.step ? '&period=' + args.step : '') +
|
||||
(args.size ? '&digits=' + args.size : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.model.fields['TOTP Seed']) {
|
||||
// TrayTOTP plugin format
|
||||
var key = this.model.fields['TOTP Seed'];
|
||||
if (key.isProtected) {
|
||||
key = key.getText();
|
||||
}
|
||||
if (key) {
|
||||
otpUrl = 'otpauth://totp/null?secret=' + key;
|
||||
}
|
||||
var settings = this.model.fields['TOTP Settings'];
|
||||
if (settings && settings.isProtected) {
|
||||
settings = settings.getText();
|
||||
}
|
||||
if (settings) {
|
||||
settings = settings.split(';');
|
||||
if (settings.length > 0 && settings[0] > 0) {
|
||||
otpUrl += '&period=' + settings[0];
|
||||
}
|
||||
if (settings.length > 1 && settings[1] > 0) {
|
||||
otpUrl += '&digits=' + settings[1];
|
||||
}
|
||||
}
|
||||
this.model.fields.otp = kdbxweb.ProtectedValue.fromString(otpUrl);
|
||||
}
|
||||
if (otpUrl) {
|
||||
try {
|
||||
this.otp = Otp.parseUrl(otpUrl);
|
||||
this.refreshOtp();
|
||||
} catch (e) {
|
||||
this.otp = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setupOtp: function() {
|
||||
OtpQrReqder.read();
|
||||
},
|
||||
|
||||
otpCodeRead: function(otpParams) {
|
||||
this.otp = otpParams;
|
||||
this.refreshOtp();
|
||||
},
|
||||
|
||||
refreshOtp: function() {
|
||||
this.otpValue = null;
|
||||
if (this.otp) {
|
||||
var that = this;
|
||||
this.otp.next(function (pass, timeLeft) {
|
||||
if (!pass || !that.otp) {
|
||||
return;
|
||||
}
|
||||
that.otpValue = { url: that.otp.url, pass: pass, time: timeLeft };
|
||||
if (that.otpView) {
|
||||
that.otpView.update();
|
||||
}
|
||||
setTimeout(that.refreshOtp.bind(that), timeLeft);
|
||||
});
|
||||
}
|
||||
otpCodeRead: function(otp) {
|
||||
this.model.setOtp(otp);
|
||||
this.entryUpdated();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -3,12 +3,54 @@
|
|||
var FieldViewText = require('./field-view-text');
|
||||
|
||||
var FieldViewOtp = FieldViewText.extend({
|
||||
otpTimeout: null,
|
||||
otpValue: null,
|
||||
otpGenerator: null,
|
||||
|
||||
renderValue: function(value) {
|
||||
return value && value.pass || '';
|
||||
this.resetOtpTimer();
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
if (value !== this.otpGenerator) {
|
||||
this.otpGenerator = value;
|
||||
this.requestOtpUpdate();
|
||||
}
|
||||
return this.otpValue;
|
||||
},
|
||||
|
||||
getEditValue: function(value) {
|
||||
return value ? value.url : '';
|
||||
return value && value.url;
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
this.resetOtpTimer();
|
||||
this.value = null;
|
||||
this.otpGenerator = null;
|
||||
FieldViewText.prototype.remove.apply(this, arguments);
|
||||
},
|
||||
|
||||
resetOtpTimer: function() {
|
||||
if (this.otpTimeout) {
|
||||
clearTimeout(this.otpTimeout);
|
||||
}
|
||||
},
|
||||
|
||||
requestOtpUpdate: function() {
|
||||
if (this.value) {
|
||||
this.value.next(this.otpUpdated.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
otpUpdated: function(pass, timeLeft) {
|
||||
if (!this.value) {
|
||||
return;
|
||||
}
|
||||
this.otpValue = pass || '';
|
||||
this.render();
|
||||
if (this.otpValue) {
|
||||
this.otpTimeout = setTimeout(this.requestOtpUpdate.bind(this), timeLeft);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue