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();
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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);

View file

@ -1,262 +0,0 @@
<?php
namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa;
trait TwoFactor
{
/**
* @var \RainLoop\Providers\TwoFactorAuth
*/
private $oTwoFactorAuthProvider;
public function DoGetTwoFactorInfo() : array
{
$oAccount = $this->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;
}
}

View file

@ -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');

View file

@ -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'),

View file

@ -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';

View file

@ -10,9 +10,6 @@ class Notifications
const DomainNotAllowed = 109;
const AccountNotAllowed = 110;
const AccountTwoFactorAuthRequired = 120;
const AccountTwoFactorAuthError = 121;
const ContactsSyncError = 140;
const CantGetMessageList = 201;

View file

@ -1,49 +0,0 @@
<?php
namespace RainLoop\Providers;
class TwoFactorAuth extends \RainLoop\Providers\AbstractProvider
{
/**
* @var \RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface
*/
private $oDriver;
public function __construct(?\RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface $oDriver = null)
{
$this->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;
}
}

View file

@ -1,17 +0,0 @@
<?php
namespace RainLoop\Providers\TwoFactorAuth;
abstract class AbstractTwoFactorAuth
{
public function Label() : string
{
return 'Two Factor Authenticator Code';
}
public function VerifyCode(string $sSecret, string $sCode) : bool
{
return false;
}
}

View file

@ -1,101 +0,0 @@
<?php
namespace RainLoop\Providers\TwoFactorAuth;
class TotpTwoFactorAuth
extends \RainLoop\Providers\TwoFactorAuth\AbstractTwoFactorAuth
implements \RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface
{
public function VerifyCode(string $sSecret, string $sCode) : bool
{
$key = static::Base32Decode($sSecret);
$algo = 'SHA1'; // Google Authenticator doesn't support SHA256
$digits = 6; // Google Authenticator doesn't support 8
$modulo = \pow(10, $digits);
$timeSlice = \floor(\time() / 30);
$discrepancy = 1;
for ($i = -$discrepancy; $i <= $discrepancy; ++$i) {
// Pack time into binary string
$counter = \str_pad(\pack('N*', $timeSlice + $i), 8, "\x00", STR_PAD_LEFT);
// Hash it with users secret key
$hm = \hash_hmac($algo, $counter, $key, true);
// Unpak 4 bytes of the result, use last nipple of result as index/offset
$value = \unpack('N', \substr($hm, (\ord(\substr($hm, -1)) & 0x0F), 4));
// Only 32 bits
$value = $value[1] & 0x7FFFFFFF;
$value = \str_pad($value % $modulo, $digits, '0', STR_PAD_LEFT);
if (\hash_equals($value, $sCode)) {
return true;
}
}
return false;
}
public function CreateSecret() : string
{
$CHARS = \array_keys(static::$map);
$length = 16;
$secret = '';
while (0 < $length--) {
$secret .= $CHARS[\random_int(0,31)];
}
return $secret;
}
protected static $map = array(
'A' => 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;
}
}

View file

@ -1,9 +0,0 @@
<?php
namespace RainLoop\Providers\TwoFactorAuth;
interface TwoFactorAuthInterface
{
public function VerifyCode(string $sSecret, string $sCode) : bool;
}

View file

@ -76,8 +76,6 @@
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "Sicherheit",
"LABEL_ALLOW_TWO_STEP": "Zwei-Faktor-Authentifizierung erlauben",
"LABEL_FORCE_TWO_STEP": "Zwei-Faktor-Authentifizierung erzwingen",
"LABEL_USE_IMAGE_PROXY": "Lokalen Proxy für externe Bilder verwenden",
"LABEL_ALLOW_OPEN_PGP": "OpenPGP erlauben",
"LABEL_SHOW_PHP_INFO": "PHP-Informationen anzeigen",
@ -176,8 +174,6 @@
"CONNECTION_ERROR": "Serververbindung kann nicht hergestellt werden",
"DOMAIN_NOT_ALLOWED": "Diese Domain ist nicht zugelassen.",
"ACCOUNT_NOT_ALLOWED": "Konto ist nicht zugelassen.",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Zwei-Faktor-Authentifizierung erforderlich",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Fehler bei Zwei-Faktor-Authentifizierung",
"CURRENT_PASSWORD_INCORRECT": "Aktuelles Passwort falsch",
"NEW_PASSWORD_SHORT": "Passwort ist zu kurz",
"NEW_PASSWORD_WEAK": "Passwort ist zu einfach",

View file

@ -275,10 +275,6 @@
"LABEL_KEY": "Private Key",
"BUTTON_DECRYPT": "Decrypt"
},
"POPUPS_TWO_FACTOR_TEST": {
"TITLE_TEST_CODE": "Zwei-Faktor-Authentifizierung",
"LABEL_CODE": "Code"
},
"POPUPS_FILTER": {
"TITLE_CREATE_FILTER": "Filter erstellen?",
"TITLE_EDIT_FILTER": "Filter bearbeiten?",
@ -336,25 +332,6 @@
"NOTIFICATION_TRASH": "Sie haben keinen \"Papierkorb\"-Systemordner gewählt, in dem die gelöschten Nachrichten abgelegt werden.\nFalls Sie gelöschte Nachrichten endgültig löschen möchten, wählen Sie die Option \"Nicht anwenden\".\n",
"NOTIFICATION_ARCHIVE": "Sie haben keinen \"Archiv\"-Systemordner gewählt, in dem die archivierten Nachrichten abgelegt werden."
},
"POPUPS_TWO_FACTOR_CFG": {
"LEGEND_TWO_FACTOR_AUTH": "Zwei-Faktor-Authentifizierung",
"LABEL_ENABLE_TWO_FACTOR": "Zwei-Faktor-Authentifizierung aktivieren",
"LABEL_TWO_FACTOR_USER": "Benutzer",
"LABEL_TWO_FACTOR_STATUS": "Status",
"LABEL_TWO_FACTOR_SECRET": "Geheimnis",
"LABEL_TWO_FACTOR_BACKUP_CODES": "Sicherungscodes",
"BUTTON_CREATE": "Neues Geheimnis erstellen",
"BUTTON_ACTIVATE": "Aktivieren",
"LINK_TEST": "test",
"BUTTON_SHOW_SECRET": "Geheimnis einblenden",
"BUTTON_HIDE_SECRET": "Geheminis ausblenden",
"TWO_FACTOR_REQUIRE_DESC": "Ihr Benutzerkonto erfordert die Einrichtung der Zwei-Faktor-Authentifizierung.",
"TWO_FACTOR_SECRET_CONFIGURED_DESC": "Konfiguriert",
"TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC": "Nicht konfiguriert",
"TWO_FACTOR_SECRET_DESC": "Importieren Sie diese Information in Ihre Google-Authenticator-Anwendung (oder andere TOTP-Anwendung), indem Sie den unten bereitgestellten QR-Code verwenden oder den Code manuell eingeben.",
"TWO_FACTOR_BACKUP_CODES_DESC": "Sollten Sie keine Codes über den Google Authenticator erhalten, können Sie einen Sicherungscode zur Anmeldung verwenden. Der Sicherungscode wird inaktiv, sobald Sie ihn zur Anmeldung verwendet haben.",
"TWO_FACTOR_SECRET_TEST_BEFORE_DESC": "Sie können diese Einstellung nicht ohne vorherigen Test verändern."
},
"TITLES": {
"LOGIN": "Anmeldung",
"MAILBOX": "Postfach",
@ -397,7 +374,6 @@
},
"SETTINGS_SECURITY": {
"LEGEND_SECURITY": "Sicherheit",
"LABEL_CONFIGURE_TWO_FACTOR": "Zwei-Faktor-Authentifizierung konfigurieren",
"LABEL_AUTOLOGOUT": "Automatische Abmeldung",
"AUTOLOGIN_NEVER_OPTION_NAME": "Nie",
"AUTOLOGIN_MINUTES_OPTION_NAME": "%MINUTES% Minute(n)",
@ -543,8 +519,6 @@
"CONNECTION_ERROR": "Serververbindung kann nicht hergestellt werden",
"DOMAIN_NOT_ALLOWED": "Diese Domain ist nicht zugelassen.",
"ACCOUNT_NOT_ALLOWED": "Konto ist nicht zugelassen.",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Zwei-Faktor-Authentifizierung erforderlich",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Fehler bei Zwei-Faktor-Authentifizierung",
"CONTACTS_SYNC_ERROR": "Fehler bei Kontakte-Synchronisierung",
"CANT_GET_MESSAGE_LIST": "Die Nachrichtenliste ist nicht verfügbar",
"CANT_GET_MESSAGE": "Diese Nachricht ist nicht verfügbar",

View file

@ -76,8 +76,6 @@
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "Security",
"LABEL_ALLOW_TWO_STEP": "Allow 2-Step Verification",
"LABEL_FORCE_TWO_STEP": "Enforce 2-Step Verification",
"LABEL_USE_IMAGE_PROXY": "Use local proxy for external images",
"LABEL_ALLOW_OPEN_PGP": "Allow OpenPGP",
"LABEL_SHOW_PHP_INFO": "Show PHP information",
@ -176,8 +174,6 @@
"CONNECTION_ERROR": "Can't connect to server",
"DOMAIN_NOT_ALLOWED": "Domain is not allowed",
"ACCOUNT_NOT_ALLOWED": "Account is not allowed",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Two factor verification required",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Two factor verification error",
"CURRENT_PASSWORD_INCORRECT": "Current password incorrect",
"NEW_PASSWORD_SHORT": "Password is too short",
"NEW_PASSWORD_WEAK": "Password is too easy",

View file

@ -275,10 +275,6 @@
"LABEL_KEY": "Private Key",
"BUTTON_DECRYPT": "Decrypt"
},
"POPUPS_TWO_FACTOR_TEST": {
"TITLE_TEST_CODE": "2-Step verification test",
"LABEL_CODE": "Code"
},
"POPUPS_FILTER": {
"TITLE_CREATE_FILTER": "Create a filter?",
"TITLE_EDIT_FILTER": "Update filter?",
@ -336,25 +332,6 @@
"NOTIFICATION_TRASH": "You haven't selected \"Trash\" system folder deleted messages are placed to.\nIf you wish to remove messages permanently, please select \"Do not use\" option.\n",
"NOTIFICATION_ARCHIVE": "You haven't selected \"Archive\" system folder achived messages are placed to."
},
"POPUPS_TWO_FACTOR_CFG": {
"LEGEND_TWO_FACTOR_AUTH": "2-Step Verification (TOTP)",
"LABEL_ENABLE_TWO_FACTOR": "Enable 2-Step verification",
"LABEL_TWO_FACTOR_USER": "User",
"LABEL_TWO_FACTOR_STATUS": "Status",
"LABEL_TWO_FACTOR_SECRET": "Secret",
"LABEL_TWO_FACTOR_BACKUP_CODES": "Backup codes",
"BUTTON_CREATE": "Create a secret",
"BUTTON_ACTIVATE": "Activate",
"LINK_TEST": "test",
"BUTTON_SHOW_SECRET": "Show Secret",
"BUTTON_HIDE_SECRET": "Hide Secret",
"TWO_FACTOR_REQUIRE_DESC": "Your account requires 2-Step verification configuration.",
"TWO_FACTOR_SECRET_CONFIGURED_DESC": "Configured",
"TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC": "Not configured",
"TWO_FACTOR_SECRET_DESC": "Import this info into your Google Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually.\n",
"TWO_FACTOR_BACKUP_CODES_DESC": "If you can't receive codes via Google Authenticator (or other TOTP client), you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive.\n",
"TWO_FACTOR_SECRET_TEST_BEFORE_DESC": "You can't change this setting before test."
},
"TITLES": {
"LOGIN": "Login",
"MAILBOX": "MailBox",
@ -397,7 +374,6 @@
},
"SETTINGS_SECURITY": {
"LEGEND_SECURITY": "Security",
"LABEL_CONFIGURE_TWO_FACTOR": "Configure 2-Step verification",
"LABEL_AUTOLOGOUT": "Auto Logout",
"AUTOLOGIN_NEVER_OPTION_NAME": "Never",
"AUTOLOGIN_MINUTES_OPTION_NAME": "%MINUTES% minute(s)",
@ -543,8 +519,6 @@
"CONNECTION_ERROR": "Can't connect to server",
"DOMAIN_NOT_ALLOWED": "Domain is not allowed",
"ACCOUNT_NOT_ALLOWED": "Account is not allowed",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Two factor verification required",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Two factor verification error",
"CONTACTS_SYNC_ERROR": "Contacts synchronization error",
"CANT_GET_MESSAGE_LIST": "Can't get message list",
"CANT_GET_MESSAGE": "Can't get message",

View file

@ -76,8 +76,6 @@
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "Seguridad",
"LABEL_ALLOW_TWO_STEP": "Activar la verificación de 2 pasos",
"LABEL_FORCE_TWO_STEP": "Forzar la Autenticación en 2 pasos",
"LABEL_USE_IMAGE_PROXY": "Utilizar un proxy local para mostrar imágenes externas",
"LABEL_ALLOW_OPEN_PGP": "Permitir OpenPGP",
"LABEL_SHOW_PHP_INFO": "Mostrar información de PHP",
@ -176,8 +174,6 @@
"CONNECTION_ERROR": "No se puede conectar al servidor",
"DOMAIN_NOT_ALLOWED": "Dominio no permitido",
"ACCOUNT_NOT_ALLOWED": "La cuenta no está permitida",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "La verificación en 2 pasos es obligatoria",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Error en la verificación de dos pasos",
"CURRENT_PASSWORD_INCORRECT": "La contraseña actual es incorrecta",
"NEW_PASSWORD_SHORT": "La contraseña es muy corta",
"NEW_PASSWORD_WEAK": "La contraseña es muy fácil",

View file

@ -275,10 +275,6 @@
"LABEL_KEY": "Clave privada",
"BUTTON_DECRYPT": "Descifrar"
},
"POPUPS_TWO_FACTOR_TEST": {
"TITLE_TEST_CODE": "Prueba de verificación de 2 pasos",
"LABEL_CODE": "Código"
},
"POPUPS_FILTER": {
"TITLE_CREATE_FILTER": "¿Crear un filtro?",
"TITLE_EDIT_FILTER": "¿Actualizar un filtro?",
@ -336,25 +332,6 @@
"NOTIFICATION_TRASH": "Usted no ha seleccionado la carpeta del sistema para alojar los mensajes enviados a la \"Papelera\" .\nSi desea eliminar los mensajes permanentemente, por favor seleccione la opción \"No usar\".\n",
"NOTIFICATION_ARCHIVE": "Usted no ha seleccionado la carpeta del sistema para alojar los mensajes enviados al \"Archivo\"."
},
"POPUPS_TWO_FACTOR_CFG": {
"LEGEND_TWO_FACTOR_AUTH": "Verificación de 2 Pasos",
"LABEL_ENABLE_TWO_FACTOR": "Activar la verificación de 2 pasos",
"LABEL_TWO_FACTOR_USER": "Usuario",
"LABEL_TWO_FACTOR_STATUS": "Estado",
"LABEL_TWO_FACTOR_SECRET": "Clave secreta",
"LABEL_TWO_FACTOR_BACKUP_CODES": "Códigos de copia de seguridad",
"BUTTON_CREATE": "Crear nueva clave secreta",
"BUTTON_ACTIVATE": "Activate",
"LINK_TEST": "probar",
"BUTTON_SHOW_SECRET": "Mostrar clave secreta",
"BUTTON_HIDE_SECRET": "Ocultar clave secreta",
"TWO_FACTOR_REQUIRE_DESC": "Se requiere que configure la verificación de 2-pasos.",
"TWO_FACTOR_SECRET_CONFIGURED_DESC": "Configurado",
"TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC": "No configurado",
"TWO_FACTOR_SECRET_DESC": "Importar esta información en su cliente Google Authenticator (u otro cliente TOTP) utilizando el código QR se indica debajo o introduciendo el código manualmente.",
"TWO_FACTOR_BACKUP_CODES_DESC": "Si usted no puede recibir los códigos a través de Google Authenticator, puede utilizar códigos de copia de seguridad para firmar pulg Después de que usted ha utilizado un código de copia de seguridad para iniciar sesión, se convertirá en inactiva.",
"TWO_FACTOR_SECRET_TEST_BEFORE_DESC": "You can't change this setting before test."
},
"TITLES": {
"LOGIN": "Ingresar",
"MAILBOX": "Buzón",
@ -397,7 +374,6 @@
},
"SETTINGS_SECURITY": {
"LEGEND_SECURITY": "Seguridad",
"LABEL_CONFIGURE_TWO_FACTOR": "Configurar verificación de 2-Pasos",
"LABEL_AUTOLOGOUT": "Salir Automáticamente",
"AUTOLOGIN_NEVER_OPTION_NAME": "Nunca",
"AUTOLOGIN_MINUTES_OPTION_NAME": "%MINUTES% minuto(s)",
@ -543,8 +519,6 @@
"CONNECTION_ERROR": "No se puede conectar al servidor",
"DOMAIN_NOT_ALLOWED": "Dominio no permitido",
"ACCOUNT_NOT_ALLOWED": "La cuenta no está permitida",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Factor de verificación en dos pasos requerido",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Error de verificación en dos pasos",
"CONTACTS_SYNC_ERROR": "Error de sincronización de Contactos",
"CANT_GET_MESSAGE_LIST": "No se puede obtener la lista de mensajes",
"CANT_GET_MESSAGE": "No se puede obtener el mensaje",

View file

@ -76,8 +76,6 @@
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "Sécurité",
"LABEL_ALLOW_TWO_STEP": "Autoriser l'authentification en deux étapes",
"LABEL_FORCE_TWO_STEP": "Forcer l'authentification en deux étapes",
"LABEL_USE_IMAGE_PROXY": "Utiliser un proxy local pour les images externes",
"LABEL_ALLOW_OPEN_PGP": "Autoriser OpenPGP",
"LABEL_SHOW_PHP_INFO": "Voir les informations PHP",
@ -176,8 +174,6 @@
"CONNECTION_ERROR": "Serveur injoignable",
"DOMAIN_NOT_ALLOWED": "Ce domaine n'est pas autorisé",
"ACCOUNT_NOT_ALLOWED": "Ce compte n'est pas autorisé",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Double authentification requise",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Erreur lors de la double authentification",
"CURRENT_PASSWORD_INCORRECT": "Le mot de passe actuel est incorrect",
"NEW_PASSWORD_SHORT": "Le mot de passe est trop court",
"NEW_PASSWORD_WEAK": "Le mot de passe n'est pas assez fort",

View file

@ -275,10 +275,6 @@
"LABEL_KEY": "Clé Privée",
"BUTTON_DECRYPT": "Déchiffrer"
},
"POPUPS_TWO_FACTOR_TEST": {
"TITLE_TEST_CODE": "Test d'authentification en deux étapes",
"LABEL_CODE": "Code"
},
"POPUPS_FILTER": {
"TITLE_CREATE_FILTER": "Créer un filtre ?",
"TITLE_EDIT_FILTER": "Mettre à jour le filtre ?",
@ -336,25 +332,6 @@
"NOTIFICATION_TRASH": "Vous n'avez pas sélectionné de dossier \"Corbeille\" où placer les messages supprimés.\nSi vous ne souhaitez pas supprimer définitivement les messages, sélectionnez l'option \"Ne pas utiliser\".\n",
"NOTIFICATION_ARCHIVE": "Vous n'avez pas sélectionné de dossier \"Archive\" où placer les messages archivés.\n"
},
"POPUPS_TWO_FACTOR_CFG": {
"LEGEND_TWO_FACTOR_AUTH": "Authentification en deux étapes",
"LABEL_ENABLE_TWO_FACTOR": "Activer l'authentification en deux étapes",
"LABEL_TWO_FACTOR_USER": "Utilisateur",
"LABEL_TWO_FACTOR_STATUS": "Statut",
"LABEL_TWO_FACTOR_SECRET": "Secret",
"LABEL_TWO_FACTOR_BACKUP_CODES": "Code de sauvegarde",
"BUTTON_CREATE": "Créer une nouvelle clé secrète",
"BUTTON_ACTIVATE": "Activer",
"LINK_TEST": "test",
"BUTTON_SHOW_SECRET": "Montrer la clé secrète",
"BUTTON_HIDE_SECRET": "Masquer la clé secrète",
"TWO_FACTOR_REQUIRE_DESC": "Votre compte requiert la configuration d'une vérification en deux étapes.",
"TWO_FACTOR_SECRET_CONFIGURED_DESC": "Configuré",
"TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC": "Non configuré",
"TWO_FACTOR_SECRET_DESC": "Importez ces informations dans votre client Google Authenticator (ou un autre client TOTP) en utilisant le code QR fourni ci-dessous ou en entrant les valeurs manuellement.",
"TWO_FACTOR_BACKUP_CODES_DESC": "Si vous ne pouvez pas recevoir les codes par Google Authenticator, vous pouvez utiliser les codes de sauvegarde pour vous connecter. Après avoir fait cela, il deviendra inactif.",
"TWO_FACTOR_SECRET_TEST_BEFORE_DESC": "Vous ne pouvez pas modifier ce paramètre avant de l'avoir essayé."
},
"TITLES": {
"LOGIN": "Identifiant",
"MAILBOX": "Boîte mail",
@ -397,7 +374,6 @@
},
"SETTINGS_SECURITY": {
"LEGEND_SECURITY": "Sécurité",
"LABEL_CONFIGURE_TWO_FACTOR": "Configurer l'authentification en deux étapes",
"LABEL_AUTOLOGOUT": "Déconnexion automatique",
"AUTOLOGIN_NEVER_OPTION_NAME": "Jamais",
"AUTOLOGIN_MINUTES_OPTION_NAME": "%MINUTES% minute(s)",
@ -543,8 +519,6 @@
"CONNECTION_ERROR": "Serveur injoignable",
"DOMAIN_NOT_ALLOWED": "Ce domaine n'est pas autorisé",
"ACCOUNT_NOT_ALLOWED": "Ce compte n'est pas autorisé",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Double authentification requise",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Erreur lors de la double authentification",
"CONTACTS_SYNC_ERROR": "Erreur de synchronisation des contacts",
"CANT_GET_MESSAGE_LIST": "Impossible d'obtenir la liste des messages",
"CANT_GET_MESSAGE": "Impossible d'obtenir le message",

View file

@ -76,8 +76,6 @@
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "Biztonság",
"LABEL_ALLOW_TWO_STEP": "2 lépcsős hitelesítés engedélyezése",
"LABEL_FORCE_TWO_STEP": "2 lépcsős hitelesítés kényszerítése",
"LABEL_USE_IMAGE_PROXY": "Helyi proxy használata a külső képekhez",
"LABEL_ALLOW_OPEN_PGP": "OpenPGP engedélyezése",
"LABEL_SHOW_PHP_INFO": "PHP információ megjelenítése",
@ -176,8 +174,6 @@
"CONNECTION_ERROR": "Nem lehet csatlakozni a szerverhez",
"DOMAIN_NOT_ALLOWED": "A domain nem engedélyezett",
"ACCOUNT_NOT_ALLOWED": "A fiók nem engedélyezett",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Kétlépcsős hitelesítés szükséges",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Kétlépcsős hitelesítés hiba",
"CURRENT_PASSWORD_INCORRECT": "A jelenlegi jelszó érvénytelen",
"NEW_PASSWORD_SHORT": "A jelszó túl rövid",
"NEW_PASSWORD_WEAK": "A jelszó túl könnyű",

View file

@ -275,10 +275,6 @@
"LABEL_KEY": "Privát kulcs",
"BUTTON_DECRYPT": "Visszafejtés"
},
"POPUPS_TWO_FACTOR_TEST": {
"TITLE_TEST_CODE": "2-lépéses hitelesítés teszt",
"LABEL_CODE": "Kód"
},
"POPUPS_FILTER": {
"TITLE_CREATE_FILTER": "Szűrő létrehozás?",
"TITLE_EDIT_FILTER": "Szűrő frissítés?",
@ -336,25 +332,6 @@
"NOTIFICATION_TRASH": "Nem választottál \"Lomtár\" rendszermappát a törölt levelek tárolásához.\nAz üzenetek végleges törléséhez válaszd a \"Ne használd\" lehetőséget.\n",
"NOTIFICATION_ARCHIVE": "Nem választottál \"Archívum\" rendszermappát az archivált üzenetek tárolásához."
},
"POPUPS_TWO_FACTOR_CFG": {
"LEGEND_TWO_FACTOR_AUTH": "2 lépcsős hitelesítés",
"LABEL_ENABLE_TWO_FACTOR": "2 lépcsős hitelesítés engedélyezése",
"LABEL_TWO_FACTOR_USER": "Felhasználó",
"LABEL_TWO_FACTOR_STATUS": "Állapot",
"LABEL_TWO_FACTOR_SECRET": "Titok",
"LABEL_TWO_FACTOR_BACKUP_CODES": "Biztonsági kódok",
"BUTTON_CREATE": "Új titok létrehozás",
"BUTTON_ACTIVATE": "Aktivál",
"LINK_TEST": "teszt",
"BUTTON_SHOW_SECRET": "Titok megjelenítése",
"BUTTON_HIDE_SECRET": "Titok elrejtése",
"TWO_FACTOR_REQUIRE_DESC": "A fiókhoz 2 lépcsős hitelesítés szükséges.",
"TWO_FACTOR_SECRET_CONFIGURED_DESC": "Beállítva",
"TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC": "Nincs beállítva",
"TWO_FACTOR_SECRET_DESC": "Importáld ezt az infót a Google Authenticator kliensedbe (vagy más TOTP kliensbe) az alábbi QR kód használatával vagy a kód manuális megadatásával.\n",
"TWO_FACTOR_BACKUP_CODES_DESC": "Ha nem kapod meg a kódokat a Google Authenticator kliensből (vagy más TOTP kliensből), akkor a bejelentkezéshez használhatod a biztonsági kódot. A biztonsági kód használata után inaktívvá válik.\n",
"TWO_FACTOR_SECRET_TEST_BEFORE_DESC": "Tesztelés nélkül nem lehet megváltoztatni ezt a beállítást."
},
"TITLES": {
"LOGIN": "Bejelentkezés",
"MAILBOX": "Postaláda",
@ -397,7 +374,6 @@
},
"SETTINGS_SECURITY": {
"LEGEND_SECURITY": "Biztonság",
"LABEL_CONFIGURE_TWO_FACTOR": "2 lépcsős hitelesítés beállítása",
"LABEL_AUTOLOGOUT": "Automatikus kijelentkezés",
"AUTOLOGIN_NEVER_OPTION_NAME": "Soha",
"AUTOLOGIN_MINUTES_OPTION_NAME": "%MINUTES% perc",
@ -543,8 +519,6 @@
"CONNECTION_ERROR": "Nem lehet kapcsolódni a szerverhez",
"DOMAIN_NOT_ALLOWED": "A domain nem engedélyezett",
"ACCOUNT_NOT_ALLOWED": "A fiók nem engedélyezett",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Kétlépcsős azonosítás kötelező",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Kétlépcsős azonosítás hiba",
"CONTACTS_SYNC_ERROR": "Hiba lépett fel a névjegyek szinkronizálása közben",
"CANT_GET_MESSAGE_LIST": "Nem tudom letölteni az üzenetlistát",
"CANT_GET_MESSAGE": "Nem tudom letölteni az üzenetet",

View file

@ -76,8 +76,6 @@
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "Beveiliging",
"LABEL_ALLOW_TWO_STEP": "2-Stap verificatie toestaan",
"LABEL_FORCE_TWO_STEP": "2-Stap verificatie afdwingen",
"LABEL_USE_IMAGE_PROXY": "Gebruik de server als proxy voor externe afbeeldingen",
"LABEL_ALLOW_OPEN_PGP": "OpenPGP toestaan",
"LABEL_SHOW_PHP_INFO": "PHP informatie tonen",
@ -176,8 +174,6 @@
"CONNECTION_ERROR": "Verbinding met Server mislukt",
"DOMAIN_NOT_ALLOWED": "Domein is niet toegestaan",
"ACCOUNT_NOT_ALLOWED": "Account is niet toegestaan",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "2-Stap verificatie is vereist",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "2-Stap verificatie fout",
"CURRENT_PASSWORD_INCORRECT": "Huidig wachtwoord is onjuist",
"NEW_PASSWORD_SHORT": "Wachtwoord is te kort",
"NEW_PASSWORD_WEAK": "Wachtwoord is te gemakkelijk",

View file

@ -275,10 +275,6 @@
"LABEL_KEY": "Privé sleutel",
"BUTTON_DECRYPT": "Decrypt"
},
"POPUPS_TWO_FACTOR_TEST": {
"TITLE_TEST_CODE": "2-Stap verificatie test",
"LABEL_CODE": "Code"
},
"POPUPS_FILTER": {
"TITLE_CREATE_FILTER": "Filter toevoegen",
"TITLE_EDIT_FILTER": "Filter aanpassen",
@ -336,25 +332,6 @@
"NOTIFICATION_TRASH": "U heeft nog geen \"Verwijderde items\" folder aangeduid waar de verwijderde berichten geplaatst worden.\nIndien u berichten permanent wilt verwijderen, kies de \"Niet gebruiken\" optie.\n",
"NOTIFICATION_ARCHIVE": "U heeft nog geen \"Archief\" map aangeduid waar de gearchiveerde berichten geplaatst worden."
},
"POPUPS_TWO_FACTOR_CFG": {
"LEGEND_TWO_FACTOR_AUTH": "2-Stap verificatie",
"LABEL_ENABLE_TWO_FACTOR": "Gebruik 2-Stap verificatie",
"LABEL_TWO_FACTOR_USER": "Gebruikersnaam",
"LABEL_TWO_FACTOR_STATUS": "Status",
"LABEL_TWO_FACTOR_SECRET": "Geheime sleutel",
"LABEL_TWO_FACTOR_BACKUP_CODES": "Backup codes",
"BUTTON_CREATE": "Nieuwe geheime sleutel aanmaken",
"BUTTON_ACTIVATE": "Activate",
"LINK_TEST": "test",
"BUTTON_SHOW_SECRET": "Bekijk geheime sleutel",
"BUTTON_HIDE_SECRET": "Verberg geheime sleutel",
"TWO_FACTOR_REQUIRE_DESC": "Uw account vereist 2-Stap verificatie configuratie.",
"TWO_FACTOR_SECRET_CONFIGURED_DESC": "Geconfigureerd",
"TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC": "Niet geconfigureerd",
"TWO_FACTOR_SECRET_DESC": "Importeer deze informatie in uw Google Authenticator-client (of andere TOTP-client) door gebruik te maken van de QR code hier beneden of door de code handmatig in te voeren.",
"TWO_FACTOR_BACKUP_CODES_DESC": "Als u geen codes ontvangt via de Google Authenticator kunt u de backup codes gebruiken om in te loggen. Na gebruik van de backup code wordt deze inactief.",
"TWO_FACTOR_SECRET_TEST_BEFORE_DESC": "U kunt 2-stap verificatie niet activeren voordat u het succesvol getest heeft."
},
"TITLES": {
"LOGIN": "Inloggen",
"MAILBOX": "Mailbox",
@ -397,7 +374,6 @@
},
"SETTINGS_SECURITY": {
"LEGEND_SECURITY": "Beveiliging",
"LABEL_CONFIGURE_TWO_FACTOR": "Configureer 2-stap verificatie",
"LABEL_AUTOLOGOUT": "Automatisch uitloggen",
"AUTOLOGIN_NEVER_OPTION_NAME": "Nooit",
"AUTOLOGIN_MINUTES_OPTION_NAME": "%MINUTES% minuten",
@ -543,8 +519,6 @@
"CONNECTION_ERROR": "Kan geen verbinding maken met de Server",
"DOMAIN_NOT_ALLOWED": "Domein is niet toegestaan",
"ACCOUNT_NOT_ALLOWED": "Account is niet toegestaan",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "2-Stap verificatie vereist",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "2-Stap verificatie fout",
"CONTACTS_SYNC_ERROR": "Contactpersonen synchronisatie fout",
"CANT_GET_MESSAGE_LIST": "Berichtenlijst kan niet worden opgehaald",
"CANT_GET_MESSAGE": "Bericht kan niet worden opgehaald",

View file

@ -76,8 +76,6 @@
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "Säkerhet",
"LABEL_ALLOW_TWO_STEP": "Låt 2-tvåstegsverifiering",
"LABEL_FORCE_TWO_STEP": "Driva 2-tvåstegsverifiering",
"LABEL_USE_IMAGE_PROXY": "Använd lokal proxy för externa bilder",
"LABEL_ALLOW_OPEN_PGP": "Tillåt OpenPGP",
"LABEL_SHOW_PHP_INFO": "Visa PHP-information",
@ -176,8 +174,6 @@
"CONNECTION_ERROR": "Kan inte ansluta till servern",
"DOMAIN_NOT_ALLOWED": "Domänen är inte tillåten",
"ACCOUNT_NOT_ALLOWED": "Kontot är inte tillåtet",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Två faktor kontroll som krävs",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Två faktor verifiering fel",
"CURRENT_PASSWORD_INCORRECT": "Nuvarande lösenord är fel",
"NEW_PASSWORD_SHORT": "Lösenordet är för kort",
"NEW_PASSWORD_WEAK": "Lösenordet är för lätt",

View file

@ -275,10 +275,6 @@
"LABEL_KEY": "Privat Key",
"BUTTON_DECRYPT": "Avkryptera"
},
"POPUPS_TWO_FACTOR_TEST": {
"TITLE_TEST_CODE": "Tvåstegsverifieringstest",
"LABEL_CODE": "Kod"
},
"POPUPS_FILTER": {
"TITLE_CREATE_FILTER": "Skapa ett filter?",
"TITLE_EDIT_FILTER": "Uppdatera filter?",
@ -336,25 +332,6 @@
"NOTIFICATION_TRASH": "Du har inte valt \"Papperskorg\" systemmapp, där borttagna meddelanden ska sparas.\nOm du vill ta meddelanden permanent, väl då \"Använd inte\" valet.\n",
"NOTIFICATION_ARCHIVE": "Du har inte valt \"Arkiv\" systemmapp, där arkiverade meddelanden ska läggas."
},
"POPUPS_TWO_FACTOR_CFG": {
"LEGEND_TWO_FACTOR_AUTH": "Tvåstegsverifiering (TOTP)",
"LABEL_ENABLE_TWO_FACTOR": "Aktivera tvåstegsverifiering",
"LABEL_TWO_FACTOR_USER": "Användare",
"LABEL_TWO_FACTOR_STATUS": "Status",
"LABEL_TWO_FACTOR_SECRET": "Hemlig kod",
"LABEL_TWO_FACTOR_BACKUP_CODES": "Backupkoder",
"BUTTON_CREATE": "Skapa ny hemlig kod",
"BUTTON_ACTIVATE": "Aktivera",
"LINK_TEST": "test",
"BUTTON_SHOW_SECRET": "Visa hemlig kod",
"BUTTON_HIDE_SECRET": "Göm hemlig kod",
"TWO_FACTOR_REQUIRE_DESC": "Ditt konto kräver tvåstegsverifiering.",
"TWO_FACTOR_SECRET_CONFIGURED_DESC": "Konfigurerad",
"TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC": "Inte konfigurarad",
"TWO_FACTOR_SECRET_DESC": "Importera denna information till din Google Authenticator klient (eller annan TOTP klient) med denna QR kod eller manuellt med koden här nedan.",
"TWO_FACTOR_BACKUP_CODES_DESC": "Om du inte kan ta emot koderna med Google Authenticator, så använd backupkoderna för att logga in. När du använt backupkoderna för att logga in så blir dom inaktiva.",
"TWO_FACTOR_SECRET_TEST_BEFORE_DESC": "Du kan inte ändra denna inställning innan test."
},
"TITLES": {
"LOGIN": "Logga in",
"MAILBOX": "Brevlåda",
@ -397,7 +374,6 @@
},
"SETTINGS_SECURITY": {
"LEGEND_SECURITY": "Säkerhet",
"LABEL_CONFIGURE_TWO_FACTOR": "Konfigurera tvåstegsverifiering",
"LABEL_AUTOLOGOUT": "Automatisk utloggning",
"AUTOLOGIN_NEVER_OPTION_NAME": "Aldrig",
"AUTOLOGIN_MINUTES_OPTION_NAME": "%MINUTES% minut(er)",
@ -543,8 +519,6 @@
"CONNECTION_ERROR": "Kan inte ansluta till servern",
"DOMAIN_NOT_ALLOWED": "Domän är inte tillåtet",
"ACCOUNT_NOT_ALLOWED": "Konto är inte tillåtet",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "Tvåstegsverifiering krävs",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "Tvåstegsverifieringsfel",
"CONTACTS_SYNC_ERROR": "Kontakt-synkroniseringsfel",
"CANT_GET_MESSAGE_LIST": "Kan inte hämta meddelandelista",
"CANT_GET_MESSAGE": "Kan inte hämta meddelande",

View file

@ -76,8 +76,6 @@
},
"TAB_SECURITY": {
"LEGEND_SECURITY": "安全",
"LABEL_ALLOW_TWO_STEP": "允许两步验证",
"LABEL_FORCE_TWO_STEP": "强制使用两步验证",
"LABEL_USE_IMAGE_PROXY": "对外部图片使用本地代理",
"LABEL_ALLOW_OPEN_PGP": "允许使用 OpenPGP",
"LABEL_SHOW_PHP_INFO": "显示 PHP 信息",
@ -176,8 +174,6 @@
"CONNECTION_ERROR": "无法连接到服务器",
"DOMAIN_NOT_ALLOWED": "域名不被允许",
"ACCOUNT_NOT_ALLOWED": "账户不允许",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "需要两步验证",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "两步验证错误",
"CURRENT_PASSWORD_INCORRECT": "当前密码不正确",
"NEW_PASSWORD_SHORT": "密码太短",
"NEW_PASSWORD_WEAK": "密码过于简单",

View file

@ -275,10 +275,6 @@
"LABEL_KEY": "私钥",
"BUTTON_DECRYPT": "解密"
},
"POPUPS_TWO_FACTOR_TEST": {
"TITLE_TEST_CODE": "两步验证测试",
"LABEL_CODE": "代码"
},
"POPUPS_FILTER": {
"TITLE_CREATE_FILTER": "创建筛选条件?",
"TITLE_EDIT_FILTER": "更新筛选条件?",
@ -336,25 +332,6 @@
"NOTIFICATION_TRASH": "您没有选择 已删除邮件 文件夹来存储垃圾邮件。\n如果您希望永久删除邮件请选择“无”选项。\n",
"NOTIFICATION_ARCHIVE": "您没有选择“已归档邮件”文件夹来存储归档邮件。"
},
"POPUPS_TWO_FACTOR_CFG": {
"LEGEND_TWO_FACTOR_AUTH": "两步验证 (TOTP)",
"LABEL_ENABLE_TWO_FACTOR": "启用两步验证",
"LABEL_TWO_FACTOR_USER": "用户",
"LABEL_TWO_FACTOR_STATUS": "状态",
"LABEL_TWO_FACTOR_SECRET": "密钥",
"LABEL_TWO_FACTOR_BACKUP_CODES": "备用代码",
"BUTTON_CREATE": "创建新密钥",
"BUTTON_ACTIVATE": "启用",
"LINK_TEST": "测试",
"BUTTON_SHOW_SECRET": "显示密钥",
"BUTTON_HIDE_SECRET": "隐藏密钥",
"TWO_FACTOR_REQUIRE_DESC": "您的账户需要设置两步验证。",
"TWO_FACTOR_SECRET_CONFIGURED_DESC": "已设置",
"TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC": "未设置",
"TWO_FACTOR_SECRET_DESC": "将下面的信息导入 Google Authenticator (或其他 TOTP 客户端)。 扫描下面的二维码或手动输入密钥。\n",
"TWO_FACTOR_BACKUP_CODES_DESC": "如果你无法从验证器获取验证码,你可以使用备用代码登录。 当你使用过某一个备用代码后,它将会失效。\n",
"TWO_FACTOR_SECRET_TEST_BEFORE_DESC": "完成测试前你无法更改此设置。"
},
"TITLES": {
"LOGIN": "登录",
"MAILBOX": "邮箱",
@ -397,7 +374,6 @@
},
"SETTINGS_SECURITY": {
"LEGEND_SECURITY": "安全",
"LABEL_CONFIGURE_TWO_FACTOR": "配置两步验证",
"LABEL_AUTOLOGOUT": "自动登出",
"AUTOLOGIN_NEVER_OPTION_NAME": "从不",
"AUTOLOGIN_MINUTES_OPTION_NAME": "%MINUTES% 分钟",
@ -543,8 +519,6 @@
"CONNECTION_ERROR": "无法连接至服务器",
"DOMAIN_NOT_ALLOWED": "不允许此域名",
"ACCOUNT_NOT_ALLOWED": "不允许此账户",
"ACCOUNT_TWO_FACTOR_AUTH_REQUIRED": "需要进行两步验证",
"ACCOUNT_TWO_FACTOR_AUTH_ERROR": "两步验证错误",
"CONTACTS_SYNC_ERROR": "联系人同步错误",
"CANT_GET_MESSAGE_LIST": "无法获取邮件列表",
"CANT_GET_MESSAGE": "无法获取邮件",

View file

@ -1,51 +1,5 @@
<div class="adminSecurity g-ui-user-select-none">
<div class="legend" data-i18n="TAB_SECURITY/LEGEND_SECURITY"></div>
<div class="form-horizontal">
<div class="control-group">
<div class="controls">
<div data-bind="component: {
name: 'Checkbox',
params: {
value: capaTwoFactorAuth,
label: 'TAB_SECURITY/LABEL_ALLOW_TWO_STEP',
inline: true
}
}"></div>
&nbsp;&nbsp;&nbsp;&nbsp;
<div data-bind="component: {
name: 'Checkbox',
params: {
value: capaTwoFactorAuthForce,
enable: capaTwoFactorAuth,
label: 'TAB_SECURITY/LABEL_FORCE_TWO_STEP',
inline: true
}
}"></div>
</div>
</div>
</div>
<!--
<div class="form-horizontal">
<div class="control-group" data-bind="visible: isTwoFactorDropperShown()">
<div class="controls">
<div class="input-append">
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="textInput: twoFactorDropperUser, hasfocus: twoFactorDropperUserFocused" placeholder="Email"/>
<button class="btn">
<i class="icon-spinner" data-bind="visible: true"></i>
<span data-bind="visible: false">OK</span>
</button>
</div>
</div>
</div>
<div class="control-group" data-bind="visible: !isTwoFactorDropperShown()">
<div class="controls">
<span class="g-ui-link" data-bind="click: showTwoFactorDropper">Drop user keys</span>
</div>
</div>
</div>
<br />
-->
<div class="form-horizontal">
<div class="control-group">
<div class="controls">

View file

@ -1,98 +0,0 @@
<div class="modal fade b-two-factor-content" data-bind="modal: modalVisibility">
<div>
<div class="modal-header">
<button type="button" class="close" data-bind="visible: viewEnable() || !lock(), command: cancelCommand">×</button>
<h3 data-i18n="POPUPS_TWO_FACTOR_CFG/LEGEND_TWO_FACTOR_AUTH"></h3>
</div>
<div class="modal-body">
<div class="form-horizontal" data-bind="visible: capaTwoFactor" style="margin-top: 10px;">
<div class="control-group" data-bind="visible: twoFactorStatus">
<div class="controls">
<div style="display: inline-block" data-bind="attr:{title: viewTwoFactorEnableTooltip}">
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'POPUPS_TWO_FACTOR_CFG/LABEL_ENABLE_TWO_FACTOR',
enable: twoFactorAllowedEnable,
value: viewEnable,
inline: true
}
}"></div>
</div>
&nbsp;&nbsp;&nbsp;
<span class="g-ui-link" data-bind="click: testTwoFactor, visible: twoFactorStatus"
data-i18n="POPUPS_TWO_FACTOR_CFG/LINK_TEST"></span>
</div>
</div>
<div class="control-group">
<label class="control-label">
<span data-i18n="POPUPS_TWO_FACTOR_CFG/LABEL_TWO_FACTOR_USER"></span>
</label>
<div class="controls" style="padding-top: 5px;">
<strong><span data-bind="text: viewUser"></span></strong>
<div style="padding-top: 15px;" data-bind="visible: lock">
<blockquote>
<p class="muted width100-on-mobile" style="width: 550px" data-i18n="POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_REQUIRE_DESC"></p>
</blockquote>
</div>
</div>
</div>
<div class="control-group" data-bind="visible: '' === viewSecret() && twoFactorStatus() && !clearing()">
<div class="controls" style="padding-top: 5px;">
<strong data-bind="visible: secreting">...</strong>
<span class="g-ui-link" data-bind="click: showSecret, visible: !secreting()"
data-i18n="POPUPS_TWO_FACTOR_CFG/BUTTON_SHOW_SECRET"></span>
</div>
</div>
<div class="control-group" data-bind="visible: '' !== viewSecret()">
<label class="control-label">
<span data-i18n="POPUPS_TWO_FACTOR_CFG/LABEL_TWO_FACTOR_SECRET"></span>
</label>
<div class="controls" style="padding-top: 5px;">
<strong data-bind="text: viewSecret"></strong>
&nbsp;&nbsp;
<span class="g-ui-link" data-bind="click: hideSecret" data-i18n="POPUPS_TWO_FACTOR_CFG/BUTTON_HIDE_SECRET"></span>
<br />
<br />
<blockquote>
<p class="muted width100-on-mobile" style="width: 550px" data-i18n="POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_SECRET_DESC"></p>
</blockquote>
<!-- ko if: '' !== viewUrl() -->
<img style="margin-left: -7px;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2P8DwQACgAD/il4QJ8AAAAASUVORK5CYII=" data-bind="attr: {'src': viewUrl}" />
<!-- /ko -->
</div>
</div>
<div class="control-group" data-bind="visible: '' !== viewBackupCodes()">
<label class="control-label">
<span data-i18n="POPUPS_TWO_FACTOR_CFG/LABEL_TWO_FACTOR_BACKUP_CODES"></span>
</label>
<div class="controls" style="padding-top: 5px;">
<pre data-bind="text: viewBackupCodes" style="width: 230px; word-break: break-word;"></pre>
<br />
<blockquote>
<p class="muted width100-on-mobile" style="width: 550px" data-i18n="POPUPS_TWO_FACTOR_CFG/TWO_FACTOR_BACKUP_CODES_DESC"></p>
</blockquote>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a class="btn pull-left" data-bind="visible: lock, click: logout">
<i class="fontastic"></i>
<span data-i18n="GLOBAL/LOGOUT"></span>
</a>
<a class="btn btn-danger" data-bind="click: clearTwoFactor, visible: twoFactorStatus">
<i class="fontastic" data-bind="css: {'icon-spinner': clearing()}"></i>
<span data-i18n="GLOBAL/CLEAR"></span>
</a>
<a class="btn" data-bind="click: createTwoFactor, visible: !twoFactorStatus()">
<i class="fontastic" data-bind="css: {'icon-spinner': processing()}"></i>
<span data-i18n="POPUPS_TWO_FACTOR_CFG/BUTTON_ACTIVATE"></span>
</a>
<a class="btn" data-bind="command: cancelCommand, visible: viewEnable() || !lock()">
<i class="icon-ok" ></i>
<span data-i18n="GLOBAL/DONE"></span>
</a>
</div>
</div>
</div>

View file

@ -1,29 +0,0 @@
<div class="modal fade b-two-factor-test-content g-ui-user-select-none" data-bind="modal: modalVisibility">
<div>
<div class="modal-header">
<button type="button" class="close" data-bind="command: cancelCommand">×</button>
<h3 data-i18n="POPUPS_TWO_FACTOR_TEST/TITLE_TEST_CODE"></h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<br />
<div class="control-group">
<label class="control-label">
<span data-i18n="POPUPS_TWO_FACTOR_TEST/LABEL_CODE"></span>
</label>
<div class="controls">
<input type="text" class="uiInput inputName"
autofocus="" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="textInput: code, onEnter: testCodeCommand" />
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a class="btn" data-bind="command: testCodeCommand, css: { 'btn-success': true === codeStatus(), 'btn-danger': false === codeStatus() }">
<i data-bind="css: {'icon-ok': !testing(), 'icon-spinner': testing()}"></i>
<span data-i18n="GLOBAL/TEST"></span>
</a>
</div>
</div>
</div>

View file

@ -21,14 +21,5 @@
}"></div>
</div>
</div>
<br />
<div class="control-group" data-bind="visible: capaTwoFactor">
<label class="control-label"></label>
<div class="controls">
<i class="fontastic">🔒</i>
&nbsp;
<span class="g-ui-link" tabindex="0" data-i18n="SETTINGS_SECURITY/LABEL_CONFIGURE_TWO_FACTOR" data-bind="click: configureTwoFactor, onSpace: configureTwoFactor, onEnter: configureTwoFactor"></span>
</div>
</div>
</div>
</div>

View file

@ -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',