diff --git a/dev/App/User.js b/dev/App/User.js index 31adcad37..c2b9bd087 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -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]); diff --git a/dev/Common/Enums.js b/dev/Common/Enums.js index 84b7f5598..eafe441df 100644 --- a/dev/Common/Enums.js +++ b/dev/Common/Enums.js @@ -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, diff --git a/dev/Remote/User/Fetch.js b/dev/Remote/User/Fetch.js index 64bf5704e..6905abe8d 100644 --- a/dev/Remote/User/Fetch.js +++ b/dev/Remote/User/Fetch.js @@ -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 */ diff --git a/dev/Screen/User/Settings.js b/dev/Screen/User/Settings.js index 88d5675cd..70cce520f 100644 --- a/dev/Screen/User/Settings.js +++ b/dev/Screen/User/Settings.js @@ -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'); } diff --git a/dev/Settings/Admin/Security.js b/dev/Settings/Admin/Security.js index c8c4b3eb3..b1a582db2 100644 --- a/dev/Settings/Admin/Security.js +++ b/dev/Settings/Admin/Security.js @@ -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); } } diff --git a/dev/Settings/User/Security.js b/dev/Settings/User/Security.js index 55ed0e458..6c37c3a4f 100644 --- a/dev/Settings/User/Security.js +++ b/dev/Settings/User/Security.js @@ -5,18 +5,13 @@ import { Capa, SaveSettingsStep } from 'Common/Enums'; import { Settings } from 'Common/Globals'; import { i18n, trigger as translatorTrigger } from 'Common/Translator'; -import { showScreenPopup } from 'Knoin/Knoin'; - 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 +38,4 @@ export class SecurityUserSettings { )); } } - - configureTwoFactor() { - showScreenPopup(TwoFactorConfigurationPopupView); - } } diff --git a/dev/Styles/@Main.less b/dev/Styles/@Main.less index 38ec808e0..b46372da1 100644 --- a/dev/Styles/@Main.less +++ b/dev/Styles/@Main.less @@ -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"; diff --git a/dev/Styles/TwoFactor.less b/dev/Styles/TwoFactor.less deleted file mode 100644 index b2954f915..000000000 --- a/dev/Styles/TwoFactor.less +++ /dev/null @@ -1,8 +0,0 @@ -.b-two-factor-content { - - width: 750px; - - .modal-body { - min-height: 100px; - } -} diff --git a/dev/View/Popup/TwoFactorConfiguration.js b/dev/View/Popup/TwoFactorConfiguration.js deleted file mode 100644 index ab1a3d23e..000000000 --- a/dev/View/Popup/TwoFactorConfiguration.js +++ /dev/null @@ -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 }; diff --git a/dev/View/Popup/TwoFactorTest.js b/dev/View/Popup/TwoFactorTest.js deleted file mode 100644 index 5977a6e26..000000000 --- a/dev/View/Popup/TwoFactorTest.js +++ /dev/null @@ -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 }; diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js index d84524762..eea958187 100644 --- a/dev/View/User/Login.js +++ b/dev/View/User/Login.js @@ -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(); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index 66e6fa5a5..8e66be357 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -283,10 +283,6 @@ class Actions case 'suggestions': $mResult = []; break; - case 'two-factor-auth': - // Providers\TwoFactorAuth\TwoFactorAuthInterface - $mResult = new Providers\TwoFactorAuth\TotpTwoFactorAuth(); - break; } } @@ -1059,7 +1055,6 @@ class Actions 'StartupUrl' => \trim(\ltrim(\trim($oConfig->Get('labs', 'startup_url', '')), '#/')), 'SieveAllowFileintoInbox' => (bool)$oConfig->Get('labs', 'sieve_allow_fileinto_inbox', false), 'ContactsIsAllowed' => false, - 'RequireTwoFactor' => false, 'Admin' => array(), 'Capa' => array(), 'Plugins' => array(), @@ -1087,7 +1082,6 @@ class Actions 'ReplySameFolder' => (bool) $oConfig->Get('defaults', 'mail_reply_same_folder', false), 'ContactsAutosave' => (bool) $oConfig->Get('defaults', 'contacts_autosave', true), 'HideUnsubscribed' => (bool) $oConfig->Get('labs', 'use_imap_list_subscribe', true), - 'EnableTwoFactor' => false, 'ParentEmail' => '', 'InterfaceAnimation' => true, 'UserBackgroundName' => '', @@ -1170,18 +1164,6 @@ class Actions } $aResult['Capa'] = $this->Capa(false, $oAccount); - - if ($aResult['Auth'] && !$aResult['RequireTwoFactor']) { - if ($this->GetCapa(false, Enumerations\Capa::TWO_FACTOR, $oAccount) && - $this->GetCapa(false, Enumerations\Capa::TWO_FACTOR_FORCE, $oAccount) && - $this->TwoFactorAuthProvider()->IsActive()) { - $aData = $this->getTwoFactorInfo($oAccount, true); - - $aResult['RequireTwoFactor'] = !$aData || - !isset($aData['User'], $aData['IsSet'], $aData['Enable']) || - !($aData['IsSet'] && $aData['Enable']); - } - } } else { $aResult['Auth'] = $this->IsAdminLoggined(false); if ($aResult['Auth']) { @@ -1260,8 +1242,6 @@ class Actions $aResult['UserBackgroundName'] = (string)$oSettings->GetConf('UserBackgroundName', $aResult['UserBackgroundName']); $aResult['UserBackgroundHash'] = (string)$oSettings->GetConf('UserBackgroundHash', $aResult['UserBackgroundHash']); } - - $aResult['EnableTwoFactor'] = (bool)$oSettings->GetConf('EnableTwoFactor', $aResult['EnableTwoFactor']); } if ($oSettingsLocal instanceof Settings) { @@ -1436,7 +1416,7 @@ class Actions * @throws \RainLoop\Exceptions\ClientException */ public function LoginProcess(string &$sEmail, string &$sPassword, string $sSignMeToken = '', - string $sAdditionalCode = '', bool $bAdditionalCodeSignMe = false, bool $bSkipTwoFactorAuth = false): Model\Account + string $sAdditionalCode = '', bool $bAdditionalCodeSignMe = false): Model\Account { $sInputEmail = $sEmail; @@ -1544,49 +1524,6 @@ class Actions throw $oException; } - // 2FA - if (!$bSkipTwoFactorAuth && $this->TwoFactorAuthProvider()->IsActive()) { - $aData = $this->getTwoFactorInfo($oAccount); - if ($aData && isset($aData['IsSet'], $aData['Enable']) && !empty($aData['Secret']) && $aData['IsSet'] && $aData['Enable']) { - $sSecretHash = \md5(APP_SALT . $aData['Secret'] . Utils::Fingerprint()); - $sSecretCookieHash = Utils::GetCookie(self::AUTH_TFA_SIGN_ME_TOKEN_KEY, ''); - - if (empty($sSecretCookieHash) || $sSecretHash !== $sSecretCookieHash) { - $sAdditionalCode = \trim($sAdditionalCode); - if (empty($sAdditionalCode)) { - $this->Logger()->Write('TFA: Required Code for ' . $oAccount->ParentEmailHelper() . ' account.'); - - throw new Exceptions\ClientException(Notifications::AccountTwoFactorAuthRequired); - } else { - $this->Logger()->Write('TFA: Verify Code for ' . $oAccount->ParentEmailHelper() . ' account.'); - - $bUseBackupCode = false; - if (6 < \strlen($sAdditionalCode) && !empty($aData['BackupCodes'])) { - $aBackupCodes = \explode(' ', \trim(\preg_replace('/[^\d]+/', ' ', $aData['BackupCodes']))); - $bUseBackupCode = \in_array($sAdditionalCode, $aBackupCodes); - - if ($bUseBackupCode) { - $this->removeBackupCodeFromTwoFactorInfo($oAccount->ParentEmailHelper(), $sAdditionalCode); - } - } - - if (!$bUseBackupCode && !$this->TwoFactorAuthProvider()->VerifyCode($aData['Secret'], $sAdditionalCode)) { - $this->loginErrorDelay(); - - $this->LoggerAuthHelper($oAccount); - - throw new Exceptions\ClientException(Notifications::AccountTwoFactorAuthError); - } - - if ($bAdditionalCodeSignMe) { - Utils::SetCookie(self::AUTH_TFA_SIGN_ME_TOKEN_KEY, $sSecretHash, - \time() + 60 * 60 * 24 * 14); - } - } - } - } - } - try { $this->CheckMailConnection($oAccount, true); } catch (\Throwable $oException) { @@ -2084,16 +2021,6 @@ class Actions } } - if ($oConfig->Get('security', 'allow_two_factor_auth', false) && - ($bAdmin || ($oAccount && !$oAccount->IsAdditionalAccount()))) { - $aResult[] = Enumerations\Capa::TWO_FACTOR; - - if ($oConfig->Get('security', 'force_two_factor_auth', false) && - ($bAdmin || ($oAccount && !$oAccount->IsAdditionalAccount()))) { - $aResult[] = Enumerations\Capa::TWO_FACTOR_FORCE; - } - } - if ($oConfig->Get('capa', 'help', true)) { $aResult[] = Enumerations\Capa::HELP; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php index d3641a51e..7ffb6ba8a 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php @@ -39,7 +39,7 @@ trait Accounts throw new ClientException(Notifications::AccountDoesNotExist); } - $oNewAccount = $this->LoginProcess($sEmail, $sPassword, '', '', false, true); + $oNewAccount = $this->LoginProcess($sEmail, $sPassword); $oNewAccount->SetParentEmail($sParentEmail); $aAccounts[$oNewAccount->Email()] = $oNewAccount->GetAuthToken(); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php index f7255d28f..3ef545f6d 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php @@ -86,12 +86,6 @@ trait Admin case Capa::TEMPLATES: $this->setConfigFromParams($oConfig, $sParamName, 'capa', 'x-templates', 'bool'); break; - case Capa::TWO_FACTOR: - $this->setConfigFromParams($oConfig, $sParamName, 'security', 'allow_two_factor_auth', 'bool'); - break; - case Capa::TWO_FACTOR_FORCE: - $this->setConfigFromParams($oConfig, $sParamName, 'security', 'force_two_factor_auth', 'bool'); - break; case Capa::ATTACHMENT_THUMBNAILS: $this->setConfigFromParams($oConfig, $sParamName, 'interface', 'show_attachment_thumbnail', 'bool'); break; @@ -157,8 +151,6 @@ trait Admin $this->setCapaFromParams($oConfig, 'CapaAdditionalAccounts', Capa::ADDITIONAL_ACCOUNTS); $this->setCapaFromParams($oConfig, 'CapaIdentities', Capa::IDENTITIES); $this->setCapaFromParams($oConfig, 'CapaTemplates', Capa::TEMPLATES); - $this->setCapaFromParams($oConfig, 'CapaTwoFactorAuth', Capa::TWO_FACTOR); - $this->setCapaFromParams($oConfig, 'CapaTwoFactorAuthForce', Capa::TWO_FACTOR_FORCE); $this->setCapaFromParams($oConfig, 'CapaOpenPGP', Capa::OPEN_PGP); $this->setCapaFromParams($oConfig, 'CapaThemes', Capa::THEMES); $this->setCapaFromParams($oConfig, 'CapaUserBackground', Capa::USER_BACKGROUND); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/TwoFactor.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/TwoFactor.php deleted file mode 100644 index 1f1a741f7..000000000 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/TwoFactor.php +++ /dev/null @@ -1,262 +0,0 @@ -getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - return $this->DefaultResponse(__FUNCTION__, - $this->getTwoFactorInfo($oAccount, true)); - } - - public function DoCreateTwoFactorSecret() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sEmail = $oAccount->ParentEmailHelper(); - - $sSecret = $this->TwoFactorAuthProvider()->CreateSecret(); - - $aCodes = array(); - for ($iIndex = 9; $iIndex > 0; $iIndex--) - { - $aCodes[] = \rand(100000000, 900000000); - } - - $this->StorageProvider()->Put($oAccount, - \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, - 'two_factor', - \RainLoop\Utils::EncodeKeyValues(array( - 'User' => $sEmail, - 'Enable' => false, - 'Secret' => $sSecret, - 'BackupCodes' => \implode(' ', $aCodes) - )) - ); - - $this->requestSleep(); - - return $this->DefaultResponse(__FUNCTION__, - $this->getTwoFactorInfo($oAccount)); - } - - public function DoShowTwoFactorSecret() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $aResult = $this->getTwoFactorInfo($oAccount); - unset($aResult['BackupCodes']); - - return $this->DefaultResponse(__FUNCTION__, $aResult); - } - - public function DoEnableTwoFactor() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sEmail = $oAccount->ParentEmailHelper(); - - $bResult = false; - $mData = $this->getTwoFactorInfo($oAccount); - if (isset($mData['Secret'], $mData['BackupCodes'])) - { - $bResult = $this->StorageProvider()->Put($oAccount, - \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, - 'two_factor', - \RainLoop\Utils::EncodeKeyValues(array( - 'User' => $sEmail, - 'Enable' => '1' === \trim($this->GetActionParam('Enable', '0')), - 'Secret' => $mData['Secret'], - 'BackupCodes' => $mData['BackupCodes'] - )) - ); - } - - return $this->DefaultResponse(__FUNCTION__, $bResult); - } - - public function DoTestTwoFactorInfo() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sCode = \trim($this->GetActionParam('Code', '')); - - $aData = $this->getTwoFactorInfo($oAccount); - $sSecret = !empty($aData['Secret']) ? $aData['Secret'] : ''; - -// $this->Logger()->WriteDump(array( -// $sCode, $sSecret, $aData, -// $this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode) -// )); - - $this->requestSleep(); - - return $this->DefaultResponse(__FUNCTION__, - $this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode)); - } - - public function DoClearTwoFactorInfo() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $this->StorageProvider()->Clear($oAccount, - \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, - 'two_factor' - ); - - return $this->DefaultResponse(__FUNCTION__, - $this->getTwoFactorInfo($oAccount, true)); - } - - protected function TwoFactorAuthProvider() : \RainLoop\Providers\TwoFactorAuth - { - if (!$this->oTwoFactorAuthProvider) { - $this->oTwoFactorAuthProvider = new \RainLoop\Providers\TwoFactorAuth( - $this->Config()->Get('security', 'allow_two_factor_auth', false) ? $this->fabrica('two-factor-auth') : null - ); - } - return $this->oTwoFactorAuthProvider; - } - - protected function getTwoFactorInfo(\RainLoop\Model\Account $oAccount, bool $bRemoveSecret = false) : array - { - $sEmail = $oAccount->ParentEmailHelper(); - - $mData = null; - - $aResult = array( - 'User' => '', - 'IsSet' => false, - 'Enable' => false, - 'Secret' => '', - 'UrlTitle' => '', - 'BackupCodes' => '' - ); - - if (!empty($sEmail)) - { - $aResult['User'] = $sEmail; - - $sData = $this->StorageProvider()->Get($oAccount, - \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, - 'two_factor' - ); - - if ($sData) - { - $mData = \RainLoop\Utils::DecodeKeyValues($sData); - } - } - - if (!empty($aResult['User']) && - !empty($mData['User']) && !empty($mData['Secret']) && - !empty($mData['BackupCodes']) && $sEmail === $mData['User']) - { - $aResult['IsSet'] = true; - $aResult['Enable'] = isset($mData['Enable']) ? !!$mData['Enable'] : false; - $aResult['Secret'] = $mData['Secret']; - $aResult['BackupCodes'] = $mData['BackupCodes']; - $aResult['UrlTitle'] = $this->Config()->Get('webmail', 'title', ''); - } - - if ($bRemoveSecret) - { - if (isset($aResult['Secret'])) - { - unset($aResult['Secret']); - } - - if (isset($aResult['UrlTitle'])) - { - unset($aResult['UrlTitle']); - } - - if (isset($aResult['BackupCodes'])) - { - unset($aResult['BackupCodes']); - } - } - - return $aResult; - } - - protected function removeBackupCodeFromTwoFactorInfo(\RainLoop\Model\Account $oAccount, string $sCode) : bool - { - if (!$oAccount || empty($sCode)) - { - return false; - } - - $sData = $this->StorageProvider()->Get($oAccount, - \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, - 'two_factor' - ); - - if ($sData) - { - $mData = \RainLoop\Utils::DecodeKeyValues($sData); - - if (!empty($mData['BackupCodes'])) - { - $sBackupCodes = \preg_replace('/[^\d]+/', ' ', ' '.$mData['BackupCodes'].' '); - $sBackupCodes = \str_replace(' '.$sCode.' ', '', $sBackupCodes); - - $mData['BackupCodes'] = \trim(\preg_replace('/[^\d]+/', ' ', $sBackupCodes)); - - return $this->StorageProvider()->Put($oAccount, - \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, - 'two_factor', - \RainLoop\Utils::EncodeKeyValues($mData) - ); - } - } - - return false; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php index e56044690..b4cf8bd29 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php @@ -15,7 +15,6 @@ trait User use Folders; use Messages; use Templates; - use TwoFactor; /** * @throws \MailSo\Base\Exceptions\Exception @@ -34,15 +33,6 @@ trait User $this->Logger()->AddSecret($sPassword); - if ('sleep@sleep.dev' === $sEmail && 0 < \strlen($sPassword) && - \is_numeric($sPassword) && $this->Config()->Get('debug', 'enable', false) && - 0 < (int) $sPassword && 30 > (int) $sPassword - ) - { - \sleep((int) $sPassword); - throw new ClientException(Notifications::AuthError); - } - try { $oAccount = $this->LoginProcess($sEmail, $sPassword, @@ -51,17 +41,7 @@ trait User } catch (ClientException $oException) { - if ($oException && - Notifications::AccountTwoFactorAuthRequired === $oException->getCode()) - { - return $this->DefaultResponse(__FUNCTION__, true, array( - 'TwoFactorAuth' => true - )); - } - else - { - throw $oException; - } + throw $oException; } $this->AuthToken($oAccount); @@ -364,8 +344,6 @@ trait User $this->setSettingsFromParams($oSettings, 'AllowDraftAutosave', 'bool'); $this->setSettingsFromParams($oSettings, 'AutoLogout', 'int'); - $this->setSettingsFromParams($oSettings, 'EnableTwoFactor', 'bool'); - $this->setSettingsFromParams($oSettingsLocal, 'UseThreads', 'bool'); $this->setSettingsFromParams($oSettingsLocal, 'ReplySameFolder', 'bool'); $this->setSettingsFromParams($oSettingsLocal, 'HideUnsubscribed', 'bool'); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php index d89da53a3..fc86ba406 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -160,8 +160,6 @@ class Application extends \RainLoop\Config\AbstractConfig 'admin_login' => array('admin', 'Login and password for web admin panel'), 'admin_password' => array(''), 'allow_admin_panel' => array(true, 'Access settings'), - 'allow_two_factor_auth' => array(false), - 'force_two_factor_auth' => array(false), 'hide_x_mailer_header' => array(true), 'admin_panel_host' => array(''), 'admin_panel_key' => array('admin'), diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php index ee7c4fcd4..8cb98bc1d 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php @@ -5,8 +5,6 @@ namespace RainLoop\Enumerations; class Capa { const PREM = 'PREM'; - const TWO_FACTOR = 'TWO_FACTOR'; - const TWO_FACTOR_FORCE = 'TWO_FACTOR_FORCE'; const OPEN_PGP = 'OPEN_PGP'; const PREFETCH = 'PREFETCH'; const THEMES = 'THEMES'; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Notifications.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Notifications.php index fba260a86..61287cbe0 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Notifications.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Notifications.php @@ -10,9 +10,6 @@ class Notifications const DomainNotAllowed = 109; const AccountNotAllowed = 110; - const AccountTwoFactorAuthRequired = 120; - const AccountTwoFactorAuthError = 121; - const ContactsSyncError = 140; const CantGetMessageList = 201; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/TwoFactorAuth.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/TwoFactorAuth.php deleted file mode 100644 index 031440623..000000000 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/TwoFactorAuth.php +++ /dev/null @@ -1,49 +0,0 @@ -oDriver = $oDriver; - } - - public function IsActive() : bool - { - return $this->oDriver instanceof \RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface; - } - - public function GetQRCodeGoogleUrl(string $sName, string $sSecret, string $sTitle = '') : string - { - $sUrl = sprintf('otpauth://%s/%s?secret=%s&issuer=%s', 'totp', urlencode($sName), $sSecret, urlencode($sTitle)); - return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.\urlencode($sUrl); - } - - public function CreateSecret() : string - { - $sResult = ''; - if ($this->IsActive()) - { - $sResult = $this->oDriver->CreateSecret(); - } - - return $sResult; - } - - public function VerifyCode(string $sSecret, string $sCode) : bool - { - $bResult = false; - if ($this->IsActive()) - { - $bResult = $this->oDriver->VerifyCode($sSecret, $sCode); - } - - return $bResult; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/TwoFactorAuth/AbstractTwoFactorAuth.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/TwoFactorAuth/AbstractTwoFactorAuth.php deleted file mode 100644 index b98c529d7..000000000 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/TwoFactorAuth/AbstractTwoFactorAuth.php +++ /dev/null @@ -1,17 +0,0 @@ - 0, // ord 65 - 'B' => 1, - 'C' => 2, - 'D' => 3, - 'E' => 4, - 'F' => 5, - 'G' => 6, - 'H' => 7, - 'I' => 8, - 'J' => 9, - 'K' => 10, - 'L' => 11, - 'M' => 12, - 'N' => 13, - 'O' => 14, - 'P' => 15, - 'Q' => 16, - 'R' => 17, - 'S' => 18, - 'T' => 19, - 'U' => 20, - 'V' => 21, - 'W' => 22, - 'X' => 23, - 'Y' => 24, - 'Z' => 25, // ord 90 - '2' => 26, // ord 50 - '3' => 27, - '4' => 28, - '5' => 29, - '6' => 30, - '7' => 31 // ord 55 - ); - - protected static function Base32Decode(string $data) - { - $data = \strtoupper(\rtrim($data, "=\x20\t\n\r\0\x0B")); - $dataSize = \strlen($data); - $buf = 0; - $bufSize = 0; - $res = ''; - for ($i = 0; $i < $dataSize; ++$i) { - $c = $data[$i]; - if (isset(static::$map[$c])) { - $buf = ($buf << 5) | static::$map[$c]; - $bufSize += 5; - if ($bufSize > 7) { - $bufSize -= 8; - $res .= \chr(($buf & (0xff << $bufSize)) >> $bufSize); - } - } - } - return $res; - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/TwoFactorAuth/TwoFactorAuthInterface.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/TwoFactorAuth/TwoFactorAuthInterface.php deleted file mode 100644 index e49da9c4d..000000000 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/TwoFactorAuth/TwoFactorAuthInterface.php +++ /dev/null @@ -1,9 +0,0 @@ -
-
-
-
-
-      -
-
-
-
-
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorConfiguration.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorConfiguration.html deleted file mode 100644 index 72eaa45d1..000000000 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorConfiguration.html +++ /dev/null @@ -1,98 +0,0 @@ - diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorTest.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorTest.html deleted file mode 100644 index bb3c48c76..000000000 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsTwoFactorTest.html +++ /dev/null @@ -1,29 +0,0 @@ - diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsSecurity.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsSecurity.html index e44fe8d19..35c9d1c7f 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsSecurity.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsSecurity.html @@ -21,14 +21,5 @@ }">
-
-
- -
- 🔒 -   - -
-
diff --git a/tasks/config.js b/tasks/config.js index 7fd55a30b..8a4f45525 100644 --- a/tasks/config.js +++ b/tasks/config.js @@ -68,7 +68,6 @@ config.paths.js = { 'vendors/routes/hasher.js', 'vendors/routes/crossroads.js', 'vendors/jua/jua.js', - 'vendors/qr.js/qr.min.js', 'vendors/bootstrap/js/bootstrap.native.js', 'vendors/knockout/build/output/knockout-latest.js', 'vendors/squire/build/squire-raw.js',