Drop 2FA, read #84

This commit is contained in:
djmaze 2021-04-12 19:15:33 +02:00
parent ca59c21602
commit 48fa1a1fdc
44 changed files with 119 additions and 1461 deletions

View file

@ -79,7 +79,6 @@ import { AbstractApp } from 'App/Abstract';
import { ComposePopupView } from 'View/Popup/Compose';
import { FolderSystemPopupView } from 'View/Popup/FolderSystem';
import { AskPopupView } from 'View/Popup/Ask';
import { TwoFactorConfigurationPopupView } from 'View/Popup/TwoFactorConfiguration';
// Every 5 minutes
const refreshFolders = 300000;
@ -912,136 +911,124 @@ class AppUser extends AbstractApp {
rl.setWindowTitle();
if (SettingsGet('Auth')) {
if (
Settings.capa(Capa.TwoFactor) &&
Settings.capa(Capa.TwoFactorForce) &&
SettingsGet('RequireTwoFactor')
) {
this.bootend();
showScreenPopup(TwoFactorConfigurationPopupView, [true]);
} else {
rl.setWindowTitle(i18n('GLOBAL/LOADING'));
rl.setWindowTitle(i18n('GLOBAL/LOADING'));
// require.ensure([], function() { // require code splitting
this.foldersReload(value => {
try {
this.bootend();
this.foldersReload(value => {
try {
this.bootend();
if (value) {
if (startupUrl) {
rl.route.setHash(root(startupUrl), true);
}
if (window.crypto && crypto.getRandomValues && Settings.capa(Capa.OpenPGP)) {
const openpgpCallback = () => {
if (!window.openpgp) {
return false;
}
PgpUserStore.openpgp = openpgp;
if (window.Worker) {
try {
PgpUserStore.openpgp.initWorker({ path: openPgpWorkerJs() });
} catch (e) {
console.error(e);
}
}
PgpUserStore.openpgpKeyring = new openpgp.Keyring();
PgpUserStore.capaOpenPGP(true);
this.reloadOpenPgpKeys();
return true;
};
if (!openpgpCallback()) {
const script = createElement('script', {src:openPgpJs()});
script.onload = openpgpCallback;
script.onerror = () => console.error(script.src);
doc.head.append(script);
}
} else {
PgpUserStore.capaOpenPGP(false);
}
startScreens([
MailBoxUserScreen,
Settings.capa(Capa.Settings) ? SettingsUserScreen : null
// false ? AboutUserScreen : null
]);
setInterval(() => {
const cF = FolderUserStore.currentFolderFullNameRaw(),
iF = getFolderInboxName();
this.folderInformation(iF);
if (iF !== cF) {
this.folderInformation(cF);
}
this.folderInformationMultiply();
}, refreshFolders);
// Every 15 minutes
setInterval(this.quota, 900000);
// Every 20 minutes
setInterval(this.foldersReload, 1200000);
setTimeout(this.contactsSync, 10000);
contactsSyncInterval = 5 <= contactsSyncInterval ? contactsSyncInterval : 20;
contactsSyncInterval = 320 >= contactsSyncInterval ? contactsSyncInterval : 320;
setInterval(this.contactsSync, contactsSyncInterval * 60000 + 5000);
this.accountsAndIdentities(true);
setTimeout(() => {
const cF = FolderUserStore.currentFolderFullNameRaw();
if (getFolderInboxName() !== cF) {
this.folderInformation(cF);
}
this.folderInformationMultiply(true);
}, 1000);
setTimeout(this.quota, 5000);
setTimeout(() => Remote.appDelayStart(()=>{}), 35000);
// When auto-login is active
if (
!!SettingsGet('AccountSignMe') &&
navigator.registerProtocolHandler &&
Settings.capa(Capa.Composer)
) {
setTimeout(() => {
try {
navigator.registerProtocolHandler(
'mailto',
location.protocol + '//' + location.host + location.pathname + '?mailto&to=%s',
(SettingsGet('Title') || 'SnappyMail')
);
} catch (e) {} // eslint-disable-line no-empty
if (SettingsGet('MailToEmail')) {
mailToHelper(SettingsGet('MailToEmail'));
}
}, 500);
}
['touchstart','mousedown','mousemove','keydown'].forEach(
t => doc.addEventListener(t, SettingsUserStore.delayLogout, {passive:true})
);
SettingsUserStore.delayLogout();
setTimeout(() => this.initVerticalLayoutResizer(), 1);
} else {
this.logout();
if (value) {
if (startupUrl) {
rl.route.setHash(root(startupUrl), true);
}
} catch (e) {
console.error(e);
}
});
// }); // require code splitting
}
if (window.crypto && crypto.getRandomValues && Settings.capa(Capa.OpenPGP)) {
const openpgpCallback = () => {
if (!window.openpgp) {
return false;
}
PgpUserStore.openpgp = openpgp;
if (window.Worker) {
try {
PgpUserStore.openpgp.initWorker({ path: openPgpWorkerJs() });
} catch (e) {
console.error(e);
}
}
PgpUserStore.openpgpKeyring = new openpgp.Keyring();
PgpUserStore.capaOpenPGP(true);
this.reloadOpenPgpKeys();
return true;
};
if (!openpgpCallback()) {
const script = createElement('script', {src:openPgpJs()});
script.onload = openpgpCallback;
script.onerror = () => console.error(script.src);
doc.head.append(script);
}
} else {
PgpUserStore.capaOpenPGP(false);
}
startScreens([
MailBoxUserScreen,
Settings.capa(Capa.Settings) ? SettingsUserScreen : null
// false ? AboutUserScreen : null
]);
setInterval(() => {
const cF = FolderUserStore.currentFolderFullNameRaw(),
iF = getFolderInboxName();
this.folderInformation(iF);
if (iF !== cF) {
this.folderInformation(cF);
}
this.folderInformationMultiply();
}, refreshFolders);
// Every 15 minutes
setInterval(this.quota, 900000);
// Every 20 minutes
setInterval(this.foldersReload, 1200000);
setTimeout(this.contactsSync, 10000);
contactsSyncInterval = 5 <= contactsSyncInterval ? contactsSyncInterval : 20;
contactsSyncInterval = 320 >= contactsSyncInterval ? contactsSyncInterval : 320;
setInterval(this.contactsSync, contactsSyncInterval * 60000 + 5000);
this.accountsAndIdentities(true);
setTimeout(() => {
const cF = FolderUserStore.currentFolderFullNameRaw();
if (getFolderInboxName() !== cF) {
this.folderInformation(cF);
}
this.folderInformationMultiply(true);
}, 1000);
setTimeout(this.quota, 5000);
setTimeout(() => Remote.appDelayStart(()=>{}), 35000);
// When auto-login is active
if (
!!SettingsGet('AccountSignMe') &&
navigator.registerProtocolHandler &&
Settings.capa(Capa.Composer)
) {
setTimeout(() => {
try {
navigator.registerProtocolHandler(
'mailto',
location.protocol + '//' + location.host + location.pathname + '?mailto&to=%s',
(SettingsGet('Title') || 'SnappyMail')
);
} catch (e) {} // eslint-disable-line no-empty
if (SettingsGet('MailToEmail')) {
mailToHelper(SettingsGet('MailToEmail'));
}
}, 500);
}
['touchstart','mousedown','mousemove','keydown'].forEach(
t => doc.addEventListener(t, SettingsUserStore.delayLogout, {passive:true})
);
SettingsUserStore.delayLogout();
setTimeout(() => this.initVerticalLayoutResizer(), 1);
} else {
this.logout();
}
} catch (e) {
console.error(e);
}
});
} else {
this.bootend();
startScreens([LoginUserScreen]);

View file

@ -4,8 +4,6 @@
* @enum {string}
*/
export const Capa = {
TwoFactor: 'TWO_FACTOR',
TwoFactorForce: 'TWO_FACTOR_FORCE',
OpenPGP: 'OPEN_PGP',
Prefetch: 'PREFETCH',
Composer: 'COMPOSER',
@ -86,9 +84,6 @@ export const Notification = {
DomainNotAllowed: 109,
AccountNotAllowed: 110,
AccountTwoFactorAuthRequired: 120,
AccountTwoFactorAuthError: 121,
ContactsSyncError: 140,
CantGetMessageList: 201,

View file

@ -70,61 +70,6 @@ class RemoteUserFetch extends AbstractFetchRemote {
});
}
/**
* @param {?Function} fCallback
*/
getTwoFactor(fCallback) {
this.defaultRequest(fCallback, 'GetTwoFactorInfo');
}
/**
* @param {?Function} fCallback
*/
createTwoFactor(fCallback) {
this.defaultRequest(fCallback, 'CreateTwoFactorSecret');
}
/**
* @param {?Function} fCallback
*/
clearTwoFactor(fCallback) {
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
}
/**
* @param {?Function} fCallback
*/
showTwoFactorSecret(fCallback) {
this.defaultRequest(fCallback, 'ShowTwoFactorSecret');
}
/**
* @param {?Function} fCallback
* @param {string} sCode
*/
testTwoFactor(fCallback, sCode) {
this.defaultRequest(fCallback, 'TestTwoFactorInfo', {
Code: sCode
});
}
/**
* @param {?Function} fCallback
* @param {boolean} bEnable
*/
enableTwoFactor(fCallback, bEnable) {
this.defaultRequest(fCallback, 'EnableTwoFactor', {
Enable: bEnable ? 1 : 0
});
}
/**
* @param {?Function} fCallback
*/
clearTwoFactorInfo(fCallback) {
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
}
/**
* @param {?Function} fCallback
*/

View file

@ -64,7 +64,7 @@ export class SettingsUserScreen extends AbstractSettingsScreen {
settingsAddViewModel(FiltersUserSettings, 'SettingsFilters', 'SETTINGS_LABELS/LABEL_FILTERS_NAME', 'filters');
}
if (Settings.capa(Capa.AutoLogout) || Settings.capa(Capa.TwoFactor)) {
if (Settings.capa(Capa.AutoLogout)) {
settingsAddViewModel(SecurityUserSettings, 'SettingsSecurity', 'SETTINGS_LABELS/LABEL_SECURITY_NAME', 'security');
}

View file

@ -16,10 +16,6 @@ export class SecurityAdminSettings {
verifySslCertificate: !!SettingsGet('VerifySslCertificate'),
allowSelfSigned: !!SettingsGet('AllowSelfSigned'),
isTwoFactorDropperShown: false,
twoFactorDropperUser: '',
twoFactorDropperUserFocused: false,
adminLogin: SettingsGet('AdminLogin'),
adminLoginError: false,
adminPassword: '',
@ -30,9 +26,7 @@ export class SecurityAdminSettings {
adminPasswordUpdateError: false,
adminPasswordUpdateSuccess: false,
capaOpenPGP: Settings.capa(Capa.OpenPGP),
capaTwoFactorAuth: Settings.capa(Capa.TwoFactor),
capaTwoFactorAuthForce: Settings.capa(Capa.TwoFactorForce)
capaOpenPGP: Settings.capa(Capa.OpenPGP)
});
addSubscribablesTo(this, {
@ -60,18 +54,6 @@ export class SecurityAdminSettings {
CapaOpenPGP: value ? 1 : 0
}),
capaTwoFactorAuth: value => {
value || this.capaTwoFactorAuthForce(false);
Remote.saveAdminConfig(null, {
CapaTwoFactorAuth: value ? 1 : 0
});
},
capaTwoFactorAuthForce: value =>
Remote.saveAdminConfig(null, {
CapaTwoFactorAuthForce: value ? 1 : 0
}),
useLocalProxyForExternalImages: value =>
Remote.saveAdminConfig(null, {
UseLocalProxyForExternalImages: value ? 1 : 0
@ -120,15 +102,6 @@ export class SecurityAdminSettings {
return true;
}
showTwoFactorDropper() {
this.twoFactorDropperUser('');
this.isTwoFactorDropperShown(true);
setTimeout(() => {
this.twoFactorDropperUserFocused(true);
}, 50);
}
onNewAdminPasswordResponse(iError, data) {
if (iError) {
this.adminPasswordUpdateError(true);
@ -147,9 +120,5 @@ export class SecurityAdminSettings {
this.adminPassword('');
this.adminPasswordNew('');
this.adminPasswordNew2('');
this.isTwoFactorDropperShown(false);
this.twoFactorDropperUser('');
this.twoFactorDropperUserFocused(false);
}
}

View file

@ -11,12 +11,9 @@ import { SettingsUserStore } from 'Stores/User/Settings';
import Remote from 'Remote/User/Fetch';
import { TwoFactorConfigurationPopupView } from 'View/Popup/TwoFactorConfiguration';
export class SecurityUserSettings {
constructor() {
this.capaAutoLogout = Settings.capa(Capa.AutoLogout);
this.capaTwoFactor = Settings.capa(Capa.TwoFactor);
this.autoLogout = SettingsUserStore.autoLogout;
this.autoLogoutTrigger = ko.observable(SaveSettingsStep.Idle);
@ -43,8 +40,4 @@ export class SecurityUserSettings {
));
}
}
configureTwoFactor() {
showScreenPopup(TwoFactorConfigurationPopupView);
}
}

View file

@ -42,7 +42,6 @@
@import "Filter.less";
@import "Template.less";
@import "OpenPgpKey.less";
@import "TwoFactor.less";
@import "Identity.less";
@import "AdvancedSearch.less";
@import "Attachmnets.less";

View file

@ -1,8 +0,0 @@
.b-two-factor-content {
width: 750px;
.modal-body {
min-height: 100px;
}
}

View file

@ -1,193 +0,0 @@
import { Capa } from 'Common/Enums';
import { Settings } from 'Common/Globals';
import { pString } from 'Common/Utils';
import { i18n, trigger as translatorTrigger } from 'Common/Translator';
import Remote from 'Remote/User/Fetch';
import { showScreenPopup } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { TwoFactorTestPopupView } from 'View/Popup/TwoFactorTest';
class TwoFactorConfigurationPopupView extends AbstractViewPopup {
constructor() {
super('TwoFactorConfiguration');
this.addObservables({
lock: false,
processing: false,
clearing: false,
secreting: false,
viewUser: '',
twoFactorStatus: false,
twoFactorTested: false,
viewSecret: '',
viewBackupCodes: '',
viewUrlTitle: '',
viewUrl: '',
viewEnable_: false
});
this.capaTwoFactor = Settings.capa(Capa.TwoFactor);
const fn = iError => iError && this.viewEnable_(false);
this.addComputables({
viewEnable: {
read: this.viewEnable_,
write: (value) => {
value = !!value;
if (value && this.twoFactorTested()) {
this.viewEnable_(value);
Remote.enableTwoFactor(fn, value);
} else {
if (!value) {
this.viewEnable_(value);
}
Remote.enableTwoFactor(fn, false);
}
}
},
viewTwoFactorEnableTooltip: () => {
translatorTrigger();
return this.twoFactorTested() || this.viewEnable_()
? ''
: i18n('POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_TEST_BEFORE_DESC');
},
viewTwoFactorStatus: () => {
translatorTrigger();
return i18n(
this.twoFactorStatus()
? 'POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_CONFIGURED_DESC'
: 'POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC'
);
},
twoFactorAllowedEnable: () => this.viewEnable() || this.twoFactorTested()
});
this.onResult = this.onResult.bind(this);
this.onShowSecretResult = this.onShowSecretResult.bind(this);
}
showSecret() {
this.secreting(true);
Remote.showTwoFactorSecret(this.onShowSecretResult);
}
hideSecret() {
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrlTitle('');
this.viewUrl('');
}
createTwoFactor() {
this.processing(true);
Remote.createTwoFactor(this.onResult);
}
logout() {
rl.app.logout();
}
testTwoFactor() {
showScreenPopup(TwoFactorTestPopupView, [this.twoFactorTested]);
}
clearTwoFactor() {
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrlTitle('');
this.viewUrl('');
this.twoFactorTested(false);
this.clearing(true);
Remote.clearTwoFactor(this.onResult);
}
onShow(bLock) {
this.lock(!!bLock);
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrlTitle('');
this.viewUrl('');
}
onHide() {
if (this.lock()) {
location.reload();
}
}
getQr() {
return (
'otpauth://totp/' +
encodeURIComponent(this.viewUser()) +
'?secret=' +
encodeURIComponent(this.viewSecret()) +
'&issuer=' +
encodeURIComponent('')
);
}
onResult(iError, oData) {
this.processing(false);
this.clearing(false);
if (iError) {
this.viewUser('');
this.viewEnable_(false);
this.twoFactorStatus(false);
this.twoFactorTested(false);
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrlTitle('');
this.viewUrl('');
} else {
this.viewUser(pString(oData.Result.User));
this.viewEnable_(!!oData.Result.Enable);
this.twoFactorStatus(!!oData.Result.IsSet);
this.twoFactorTested(!!oData.Result.Tested);
this.viewSecret(pString(oData.Result.Secret));
this.viewBackupCodes(pString(oData.Result.BackupCodes).replace(/[\s]+/g, ' '));
this.viewUrlTitle(pString(oData.Result.UrlTitle));
this.viewUrl(qr.toDataURL({ level: 'M', size: 8, value: this.getQr() }));
}
}
onShowSecretResult(iError, data) {
this.secreting(false);
if (iError) {
this.viewSecret('');
this.viewUrlTitle('');
this.viewUrl('');
} else {
this.viewSecret(pString(data.Result.Secret));
this.viewUrlTitle(pString(data.Result.UrlTitle));
this.viewUrl(qr.toDataURL({ level: 'M', size: 6, value: this.getQr() }));
}
}
onBuild() {
if (this.capaTwoFactor) {
this.processing(true);
Remote.getTwoFactor(this.onResult);
}
}
}
export { TwoFactorConfigurationPopupView, TwoFactorConfigurationPopupView as default };

View file

@ -1,51 +0,0 @@
import Remote from 'Remote/User/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews';
class TwoFactorTestPopupView extends AbstractViewPopup {
constructor() {
super('TwoFactorTest');
this.addObservables({
code: '',
codeStatus: null,
testing: false
});
this.koTestedTrigger = null;
decorateKoCommands(this, {
testCodeCommand: self => self.code() && !self.testing()
});
}
testCodeCommand() {
this.testing(true);
Remote.testTwoFactor(iError => {
this.testing(false);
this.codeStatus(!iError);
if (this.koTestedTrigger && this.codeStatus()) {
this.koTestedTrigger(true);
}
}, this.code());
}
clearPopup() {
this.code('');
this.codeStatus(null);
this.testing(false);
this.koTestedTrigger = null;
}
onShow(koTestedTrigger) {
this.clearPopup();
this.koTestedTrigger = koTestedTrigger;
}
}
export { TwoFactorTestPopupView, TwoFactorTestPopupView as default };

View file

@ -141,13 +141,6 @@ class LoginUserView extends AbstractViewCenter {
}
this.submitError(getNotification(iError, oData.ErrorMessage, Notification.UnknownNotification));
this.submitErrorAddidional((oData && oData.ErrorMessageAdditional) || '');
} else if (oData.TwoFactorAuth) {
this.submitRequest(false);
this.additionalCode('');
this.additionalCodeVisibility(true);
let input = this.querySelector('.inputAdditionalCode');
input.required = true;
setTimeout(() => input.focus(), 100);
} else {
rl.route.reload();
}