This commit is contained in:
Yuri Kuznetsov
2024-09-11 13:40:42 +03:00
parent 9ee39b5d4a
commit 103c6a7068
3 changed files with 437 additions and 430 deletions
+127 -123
View File
@@ -26,137 +26,141 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/user-security/modals/totp', ['views/modal', 'model'], function (Dep, Model) {
import ModalView from 'views/modal';
import Model from 'model';
let QRCode;
export default class TotpModalView extends ModalView {
return Dep.extend({
template = 'user-security/modals/totp'
template: 'user-security/modals/totp',
className = 'dialog dialog-record'
className: 'dialog dialog-record',
shortcutKeys = {
'Control+Enter': 'apply'
}
shortcutKeys: {
'Control+Enter': 'apply',
},
setup() {
this.buttonList = [
{
name: 'apply',
label: 'Apply',
style: 'danger',
onClick: () => this.actionApply(),
},
{
name: 'cancel',
label: 'Cancel',
},
];
setup: function () {
this.buttonList = [
{
name: 'apply',
label: 'Apply',
style: 'danger',
},
{
name: 'cancel',
label: 'Cancel',
},
];
this.headerHtml = '&nbsp';
this.headerHtml = '&nbsp';
const model = new Model();
var model = new Model();
model.name = 'UserSecurity';
model.name = 'UserSecurity';
this.wait(
Espo.Ajax
.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
id: this.model.id,
password: this.model.get('password'),
auth2FAMethod: this.model.get('auth2FAMethod'),
reset: this.options.reset,
})
.then(data => {
this.label = data.label;
this.secret = data.auth2FATotpSecret;
model.set('secret', data.auth2FATotpSecret);
})
);
model.setDefs({
fields: {
'code': {
type: 'varchar',
required: true,
maxLength: 7,
},
'secret': {
type: 'varchar',
readOnly: true,
},
}
});
this.createView('record', 'views/record/edit-for-modal', {
scope: 'None',
selector: '.record',
model: model,
detailLayout: [
{
rows: [
[
{
name: 'secret',
labelText: this.translate('Secret', 'labels', 'User'),
},
false
],
[
{
name: 'code',
labelText: this.translate('Code', 'labels', 'User'),
},
false
]
]
}
],
});
Espo.loader.requirePromise('lib!qrcodejs').then(lib => {
QRCode = lib;
})
},
afterRender: function () {
new QRCode(this.$el.find('.qrcode').get(0), {
text: 'otpauth://totp/' + this.label + '?secret=' + this.secret,
width: 256,
height: 256,
colorDark : '#000000',
colorLight : '#ffffff',
correctLevel : QRCode.CorrectLevel.H,
});
},
actionApply: function () {
var data = this.getView('record').processFetch();
if (!data) {
return;
}
this.model.set('code', data.code);
this.hideButton('apply');
this.hideButton('cancel');
Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
this.model
.save()
.then(() => {
Espo.Ui.notify(false);
this.trigger('done');
this.wait(
Espo.Ajax
.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
id: this.model.id,
password: this.model.get('password'),
auth2FAMethod: this.model.get('auth2FAMethod'),
reset: this.options.reset,
})
.catch(() => {
this.showButton('apply');
this.showButton('cancel');
});
},
.then(/** Record */data => {
this.label = data.label;
this.secret = data.auth2FATotpSecret;
});
});
model.set('secret', data.auth2FATotpSecret);
})
);
model.setDefs({
fields: {
code: {
type: 'varchar',
required: true,
maxLength: 7,
},
secret: {
type: 'varchar',
readOnly: true,
},
}
});
this.createView('record', 'views/record/edit-for-modal', {
scope: 'None',
selector: '.record',
model: model,
detailLayout: [
{
rows: [
[
{
name: 'secret',
labelText: this.translate('Secret', 'labels', 'User'),
},
false
],
[
{
name: 'code',
labelText: this.translate('Code', 'labels', 'User'),
},
false
]
]
}
],
});
Espo.loader.requirePromise('lib!qrcodejs').then(lib => {
QRCode = lib;
})
}
afterRender() {
new QRCode(this.$el.find('.qrcode').get(0), {
text: `otpauth://totp/${this.label}?secret=${this.secret}`,
width: 256,
height: 256,
colorDark : '#000000',
colorLight : '#ffffff',
correctLevel : QRCode.CorrectLevel.H,
});
}
/**
* @return {import('views/record/edit').default}
*/
getRecordView() {
return this.getView('record');
}
actionApply() {
const data = this.getRecordView().processFetch();
if (!data) {
return;
}
this.model.set('code', data.code);
this.hideActionItem('apply');
this.hideActionItem('cancel');
Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
this.model.save()
.then(() => {
Espo.Ui.notify(false);
this.trigger('done');
})
.catch(() => {
this.showActionItem('apply');
this.showActionItem('cancel');
});
}
}
@@ -26,170 +26,172 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/user-security/modals/two-factor-email',
['views/modal', 'model'],
function (Dep, Model) {
import ModalView from 'views/modal';
import Model from 'model';
return Dep.extend({
// noinspection JSUnusedGlobalSymbols
export default class TwoFactorEmailModalView extends ModalView {
template: 'user-security/modals/two-factor-email',
template = 'user-security/modals/two-factor-email'
className: 'dialog dialog-record',
className = 'dialog dialog-record'
shortcutKeys: {
'Control+Enter': 'apply',
},
shortcutKeys = {
'Control+Enter': 'apply',
}
events: {
'click [data-action="sendCode"]': function () {
this.actionSendCode();
setup() {
this.addActionHandler('sendCode', () => this.actionSendCode());
this.buttonList = [
{
name: 'apply',
label: 'Apply',
style: 'danger',
hidden: true,
onClick: () => this.actionApply(),
},
},
{
name: 'cancel',
label: 'Cancel',
},
];
setup: function () {
this.buttonList = [
{
name: 'apply',
label: 'Apply',
style: 'danger',
hidden: true,
this.headerHtml = '&nbsp';
const codeLength = this.getConfig().get('auth2FAEmailCodeLength') || 7;
const model = new Model();
model.entityType = model.name = 'UserSecurity';
model.set('emailAddress', null);
model.setDefs({
fields: {
'code': {
type: 'varchar',
required: true,
maxLength: codeLength,
},
{
name: 'cancel',
label: 'Cancel',
'emailAddress': {
type: 'enum',
required: true,
},
];
this.headerHtml = '&nbsp';
let codeLength = this.getConfig().get('auth2FAEmailCodeLength') || 7;
let model = new Model();
model.entityType = model.name = 'UserSecurity';
model.set('emailAddress', null);
model.setDefs({
fields: {
'code': {
type: 'varchar',
required: true,
maxLength: codeLength,
},
'emailAddress': {
type: 'enum',
required: true,
},
}
});
this.internalModel = model;
this.wait(
Espo.Ajax
.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
id: this.model.id,
password: this.model.get('password'),
auth2FAMethod: this.model.get('auth2FAMethod'),
reset: this.options.reset,
})
.then(data => {
this.emailAddressList = data.emailAddressList;
this.createView('record', 'views/record/edit-for-modal', {
scope: 'None',
selector: '.record',
model: model,
detailLayout: [
{
rows: [
[
{
name: 'emailAddress',
labelText: this.translate('emailAddress', 'fields', 'User'),
},
false
],
[
{
name: 'code',
labelText: this.translate('Code', 'labels', 'User'),
},
false
],
]
}
],
}, view => {
view.setFieldOptionList('emailAddress', this.emailAddressList);
if (this.emailAddressList.length) {
model.set('emailAddress', this.emailAddressList[0]);
}
view.hideField('code');
});
})
);
},
afterRender: function () {
this.$sendCode = this.$el.find('[data-action="sendCode"]');
this.$pInfo = this.$el.find('p.p-info');
this.$pButton = this.$el.find('p.p-button');
this.$pInfoAfter = this.$el.find('p.p-info-after');
},
actionSendCode: function () {
this.$sendCode.attr('disabled', 'disabled').addClass('disabled');
Espo.Ajax
.postRequest('TwoFactorEmail/action/sendCode', {
id: this.model.id,
emailAddress: this.internalModel.get('emailAddress'),
})
.then(() => {
this.showButton('apply');
this.$pInfo.addClass('hidden');
this.$pButton.addClass('hidden');
this.$pInfoAfter.removeClass('hidden');
this.getView('record').setFieldReadOnly('emailAddress');
this.getView('record').showField('code');
})
.catch(() => {
this.$sendCode.removeAttr('disabled').removeClass('disabled');
});
},
actionApply: function () {
let data = this.getView('record').processFetch();
if (!data) {
return;
}
});
this.model.set('code', data.code);
this.internalModel = model;
this.hideButton('apply');
this.hideButton('cancel');
Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
this.model
.save()
.then(() => {
Espo.Ui.notify(false);
this.trigger('done');
this.wait(
Espo.Ajax
.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
id: this.model.id,
password: this.model.get('password'),
auth2FAMethod: this.model.get('auth2FAMethod'),
reset: this.options.reset,
})
.catch(() => {
this.showButton('apply');
this.showButton('cancel');
});
},
.then(data => {
this.emailAddressList = data.emailAddressList;
});
});
this.createView('record', 'views/record/edit-for-modal', {
scope: 'None',
selector: '.record',
model: model,
detailLayout: [
{
rows: [
[
{
name: 'emailAddress',
labelText: this.translate('emailAddress', 'fields', 'User'),
},
false
],
[
{
name: 'code',
labelText: this.translate('Code', 'labels', 'User'),
},
false
],
]
}
],
}, view => {
view.setFieldOptionList('emailAddress', this.emailAddressList);
if (this.emailAddressList.length) {
model.set('emailAddress', this.emailAddressList[0]);
}
view.hideField('code');
});
})
);
}
afterRender() {
this.$sendCode = this.$el.find('[data-action="sendCode"]');
this.$pInfo = this.$el.find('p.p-info');
this.$pButton = this.$el.find('p.p-button');
this.$pInfoAfter = this.$el.find('p.p-info-after');
}
actionSendCode() {
this.$sendCode.attr('disabled', 'disabled').addClass('disabled');
Espo.Ajax
.postRequest('TwoFactorEmail/action/sendCode', {
id: this.model.id,
emailAddress: this.internalModel.get('emailAddress'),
})
.then(() => {
this.showActionItem('apply');
this.$pInfo.addClass('hidden');
this.$pButton.addClass('hidden');
this.$pInfoAfter.removeClass('hidden');
this.getRecordView().setFieldReadOnly('emailAddress');
this.getRecordView().showField('code');
})
.catch(() => {
this.$sendCode.removeAttr('disabled').removeClass('disabled');
});
}
/**
* @return {import('views/record/edit').default}
*/
getRecordView() {
return this.getView('record');
}
actionApply() {
const data = this.getRecordView().processFetch();
if (!data) {
return;
}
this.model.set('code', data.code);
this.hideActionItem('apply');
this.hideActionItem('cancel');
Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
this.model
.save()
.then(() => {
Espo.Ui.notify(false);
this.trigger('done');
})
.catch(() => {
this.showActionItem('apply');
this.showActionItem('cancel');
});
}
}
@@ -26,171 +26,172 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/user-security/modals/two-factor-sms',
['views/modal', 'model'],
function (Dep, Model) {
import ModalView from 'views/modal';
import Model from 'model';
return Dep.extend({
// noinspection JSUnusedGlobalSymbols
export default class TwoFactorSmsModalView extends ModalView {
template: 'user-security/modals/two-factor-sms',
template = 'user-security/modals/two-factor-sms'
className: 'dialog dialog-record',
className = 'dialog dialog-record'
shortcutKeys: {
'Control+Enter': 'apply',
},
shortcutKeys = {
'Control+Enter': 'apply',
}
events: {
'click [data-action="sendCode"]': function () {
this.actionSendCode();
setup() {
this.addActionHandler('sendCode', () => this.actionSendCode());
this.buttonList = [
{
name: 'apply',
label: 'Apply',
style: 'danger',
hidden: true,
onClick: () => this.actionApply(),
},
},
{
name: 'cancel',
label: 'Cancel',
},
];
setup: function () {
this.buttonList = [
{
name: 'apply',
label: 'Apply',
style: 'danger',
hidden: true,
this.headerHtml = '&nbsp';
const codeLength = this.getConfig().get('auth2FASmsCodeLength') || 7;
const model = new Model();
model.name = 'UserSecurity';
model.set('phoneNumber', null);
model.setDefs({
fields: {
'code': {
type: 'varchar',
required: true,
maxLength: codeLength,
},
{
name: 'cancel',
label: 'Cancel',
'phoneNumber': {
type: 'enum',
required: true,
},
];
this.headerHtml = '&nbsp';
let codeLength = this.getConfig().get('auth2FASmsCodeLength') || 7;
let model = new Model();
model.name = 'UserSecurity';
model.set('phoneNumber', null);
model.setDefs({
fields: {
'code': {
type: 'varchar',
required: true,
maxLength: codeLength,
},
'phoneNumber': {
type: 'enum',
required: true,
},
}
});
this.internalModel = model;
this.wait(
Espo.Ajax
.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
id: this.model.id,
password: this.model.get('password'),
auth2FAMethod: this.model.get('auth2FAMethod'),
reset: this.options.reset,
})
.then(data => {
this.phoneNumberList = data.phoneNumberList;
this.createView('record', 'views/record/edit-for-modal', {
scope: 'None',
selector: '.record',
model: model,
detailLayout: [
{
rows: [
[
{
name: 'phoneNumber',
labelText: this.translate('phoneNumber', 'fields', 'User'),
},
false
],
[
{
name: 'code',
labelText: this.translate('Code', 'labels', 'User'),
},
false
],
]
}
],
}, view => {
view.setFieldOptionList('phoneNumber', this.phoneNumberList);
if (this.phoneNumberList.length) {
model.set('phoneNumber', this.phoneNumberList[0]);
}
view.hideField('code');
});
})
);
},
afterRender: function () {
this.$sendCode = this.$el.find('[data-action="sendCode"]');
this.$pInfo = this.$el.find('p.p-info');
this.$pButton = this.$el.find('p.p-button');
this.$pInfoAfter = this.$el.find('p.p-info-after');
},
actionSendCode: function () {
this.$sendCode.attr('disabled', 'disabled').addClass('disabled');
Espo.Ajax
.postRequest('TwoFactorSms/action/sendCode', {
id: this.model.id,
phoneNumber: this.internalModel.get('phoneNumber'),
})
.then(() => {
this.showButton('apply');
this.$pInfo.addClass('hidden');
this.$pButton.addClass('hidden');
this.$pInfoAfter.removeClass('hidden');
this.getView('record').setFieldReadOnly('phoneNumber');
this.getView('record').showField('code');
})
.catch(() => {
this.$sendCode.removeAttr('disabled').removeClass('disabled');
});
},
actionApply: function () {
let data = this.getView('record').processFetch();
if (!data) {
return;
}
});
this.model.set('code', data.code);
this.internalModel = model;
this.hideButton('apply');
this.hideButton('cancel');
Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
this.model
.save()
.then(() => {
Espo.Ui.notify(false);
this.trigger('done');
this.wait(
Espo.Ajax
.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
id: this.model.id,
password: this.model.get('password'),
auth2FAMethod: this.model.get('auth2FAMethod'),
reset: this.options.reset,
})
.catch(() => {
this.showButton('apply');
this.showButton('cancel');
});
},
.then(data => {
this.phoneNumberList = data.phoneNumberList;
});
});
this.createView('record', 'views/record/edit-for-modal', {
scope: 'None',
selector: '.record',
model: model,
detailLayout: [
{
rows: [
[
{
name: 'phoneNumber',
labelText: this.translate('phoneNumber', 'fields', 'User'),
},
false
],
[
{
name: 'code',
labelText: this.translate('Code', 'labels', 'User'),
},
false
],
]
}
],
}, view => {
view.setFieldOptionList('phoneNumber', this.phoneNumberList);
if (this.phoneNumberList.length) {
model.set('phoneNumber', this.phoneNumberList[0]);
}
view.hideField('code');
});
})
);
}
afterRender() {
this.$sendCode = this.$el.find('[data-action="sendCode"]');
this.$pInfo = this.$el.find('p.p-info');
this.$pButton = this.$el.find('p.p-button');
this.$pInfoAfter = this.$el.find('p.p-info-after');
}
actionSendCode() {
this.$sendCode.attr('disabled', 'disabled').addClass('disabled');
Espo.Ajax
.postRequest('TwoFactorSms/action/sendCode', {
id: this.model.id,
phoneNumber: this.internalModel.get('phoneNumber'),
})
.then(() => {
this.showActionItem('apply');
this.$pInfo.addClass('hidden');
this.$pButton.addClass('hidden');
this.$pInfoAfter.removeClass('hidden');
this.getRecordView().setFieldReadOnly('phoneNumber');
this.getRecordView().showField('code');
})
.catch(() => {
this.$sendCode.removeAttr('disabled').removeClass('disabled');
});
}
/**
* @return {import('views/record/edit').default}
*/
getRecordView() {
return this.getView('record');
}
actionApply() {
const data = this.getRecordView().processFetch();
if (!data) {
return;
}
this.model.set('code', data.code);
this.hideActionItem('apply');
this.hideActionItem('cancel');
Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
this.model.save()
.then(() => {
Espo.Ui.notify(false);
this.trigger('done');
})
.catch(() => {
this.showActionItem('apply');
this.showActionItem('cancel');
});
}
}