mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-08 22:18:28 +03:00
2-Step Verification (Google Authenticator)
Small fixes
This commit is contained in:
parent
cae0cc2f77
commit
419b47a81e
51 changed files with 1747 additions and 419 deletions
|
|
@ -285,6 +285,7 @@ module.exports = function (grunt) {
|
|||
"dev/ViewModels/PopupsComposeOpenPgpViewModel.js",
|
||||
"dev/ViewModels/PopupsIdentityViewModel.js",
|
||||
"dev/ViewModels/PopupsLanguagesViewModel.js",
|
||||
"dev/ViewModels/PopupsTwoFactorTestViewModel.js",
|
||||
"dev/ViewModels/PopupsAskViewModel.js",
|
||||
|
||||
"dev/ViewModels/LoginViewModel.js",
|
||||
|
|
@ -305,6 +306,7 @@ module.exports = function (grunt) {
|
|||
"dev/Settings/Accounts.js",
|
||||
"dev/Settings/Identity.js",
|
||||
"dev/Settings/Identities.js",
|
||||
"dev/Settings/Security.js",
|
||||
"dev/Settings/Social.js",
|
||||
"dev/Settings/ChangePassword.js",
|
||||
"dev/Settings/Folders.js",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ function AdminSecurity()
|
|||
{
|
||||
this.csrfProtection = ko.observable(!!RL.settingsGet('UseTokenProtection'));
|
||||
this.openPGP = ko.observable(!!RL.settingsGet('OpenPGP'));
|
||||
this.allowTwoFactorAuth = ko.observable(!!RL.settingsGet('AllowTwoFactorAuth'));
|
||||
|
||||
this.adminLogin = ko.observable(RL.settingsGet('AdminLogin'));
|
||||
this.adminPassword = ko.observable('');
|
||||
|
|
@ -72,6 +73,12 @@ AdminSecurity.prototype.onBuild = function ()
|
|||
'OpenPGP': bValue ? '1' : '0'
|
||||
});
|
||||
});
|
||||
|
||||
this.allowTwoFactorAuth.subscribe(function (bValue) {
|
||||
RL.remote().saveAdminConfig(Utils.emptyFunction, {
|
||||
'AllowTwoFactorAuth': bValue ? '1' : '0'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
AdminSecurity.prototype.onHide = function ()
|
||||
|
|
|
|||
|
|
@ -937,6 +937,11 @@ RainLoopApp.prototype.bootstart = function ()
|
|||
Utils.removeSettingsViewModel(SettingsOpenPGP);
|
||||
}
|
||||
|
||||
if (!RL.settingsGet('AllowTwoFactorAuth'))
|
||||
{
|
||||
Utils.removeSettingsViewModel(SettingsSecurity);
|
||||
}
|
||||
|
||||
if (!bGoogle && !bFacebook && !bTwitter)
|
||||
{
|
||||
Utils.removeSettingsViewModel(SettingsSocialScreen);
|
||||
|
|
|
|||
|
|
@ -317,6 +317,9 @@ Enums.Notification = {
|
|||
'DomainNotAllowed': 109,
|
||||
'AccountNotAllowed': 110,
|
||||
|
||||
'AccountTwoFactorAuthRequired': 120,
|
||||
'AccountTwoFactorAuthError': 121,
|
||||
|
||||
'CantGetMessageList': 201,
|
||||
'CantGetMessage': 202,
|
||||
'CantDeleteMessage': 203,
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ NewHtmlEditorWrapper.prototype.init = function ()
|
|||
{
|
||||
self.editor.on('instanceReady', function () {
|
||||
|
||||
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65/* A */, 'selectAll');
|
||||
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65 /* A */, 'selectAll');
|
||||
|
||||
self.fOnReady();
|
||||
self.__resizable = true;
|
||||
|
|
|
|||
|
|
@ -568,6 +568,9 @@ Utils.initNotificationLanguage = function ()
|
|||
NotificationI18N[Enums.Notification.DomainNotAllowed] = Utils.i18n('NOTIFICATIONS/DOMAIN_NOT_ALLOWED');
|
||||
NotificationI18N[Enums.Notification.AccountNotAllowed] = Utils.i18n('NOTIFICATIONS/ACCOUNT_NOT_ALLOWED');
|
||||
|
||||
NotificationI18N[Enums.Notification.AccountTwoFactorAuthRequired] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_REQUIRED');
|
||||
NotificationI18N[Enums.Notification.AccountTwoFactorAuthError] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_ERROR');
|
||||
|
||||
NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST');
|
||||
NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE');
|
||||
NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE');
|
||||
|
|
@ -672,14 +675,14 @@ Utils.delegateRun = function (oObject, sMethodName, aParameters, nDelay)
|
|||
Utils.killCtrlAandS = function (oEvent)
|
||||
{
|
||||
oEvent = oEvent || window.event;
|
||||
if (oEvent)
|
||||
if (oEvent && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey)
|
||||
{
|
||||
var
|
||||
oSender = oEvent.target || oEvent.srcElement,
|
||||
iKey = oEvent.keyCode || oEvent.which
|
||||
;
|
||||
|
||||
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.S)
|
||||
if (iKey === Enums.EventKeyCode.S)
|
||||
{
|
||||
oEvent.preventDefault();
|
||||
return;
|
||||
|
|
@ -690,7 +693,7 @@ Utils.killCtrlAandS = function (oEvent)
|
|||
return;
|
||||
}
|
||||
|
||||
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.A)
|
||||
if (iKey === Enums.EventKeyCode.A)
|
||||
{
|
||||
if (window.getSelection)
|
||||
{
|
||||
|
|
@ -1716,48 +1719,3 @@ Utils.selectElement = function (element)
|
|||
}
|
||||
/* jshint onevar: true */
|
||||
};
|
||||
|
||||
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
|
||||
{
|
||||
if (window.openpgp && RL)
|
||||
{
|
||||
var
|
||||
oOpenpgpKeyring = RL.data().openpgpKeyring,
|
||||
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
|
||||
;
|
||||
|
||||
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
|
||||
0 < oImported.keys.length)
|
||||
{
|
||||
_.each(oImported.keys, function (oPrivKey) {
|
||||
if (oPrivKey)
|
||||
{
|
||||
window.console.log(oPrivKey);
|
||||
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
|
||||
// if (oKey && oKey[0])
|
||||
// {
|
||||
// if (oKey[0].isPublic())
|
||||
// {
|
||||
// oPrivKey.update(oKey[0]);
|
||||
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
|
||||
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// oKey[0].update(oPrivKey);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// oOpenpgpKeyring.importKey(oPrivKey.armored);
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
oOpenpgpKeyring.store();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -151,15 +151,14 @@ FolderModel.prototype.initComputed = function ()
|
|||
{
|
||||
return '' + iCount;
|
||||
}
|
||||
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.SentItems !== iType)
|
||||
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.Archive !== iType && Enums.FolderType.SentItems !== iType)
|
||||
{
|
||||
return '' + iUnread;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
// return 0 < iUnread && (Enums.FolderType.Inbox === iType || Enums.FolderType.Spam === iType) ? '' + iUnread :
|
||||
// (0 < iCount && Enums.FolderType.Draft === iType ? '' + iCount : '');
|
||||
|
||||
}, this);
|
||||
|
||||
this.canBeDeleted = ko.computed(function () {
|
||||
|
|
|
|||
120
dev/Settings/Security.js
Normal file
120
dev/Settings/Security.js
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function SettingsSecurity()
|
||||
{
|
||||
this.processing = ko.observable(false);
|
||||
this.clearing = ko.observable(false);
|
||||
|
||||
this.viewUser = ko.observable('');
|
||||
this.viewEnable = ko.observable(false);
|
||||
this.viewEnable.subs = true;
|
||||
this.twoFactorStatus = ko.observable(false);
|
||||
|
||||
this.viewSecret = ko.observable('');
|
||||
this.viewBackupCodes = ko.observable('');
|
||||
this.viewUrl = ko.observable('');
|
||||
|
||||
this.bFirst = true;
|
||||
|
||||
this.viewTwoFactorStatus = ko.computed(function () {
|
||||
Globals.langChangeTrigger();
|
||||
return Utils.i18n(
|
||||
this.twoFactorStatus() ?
|
||||
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_CONFIGURED_DESC' :
|
||||
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC'
|
||||
);
|
||||
}, this);
|
||||
|
||||
this.onResult = _.bind(this.onResult, this);
|
||||
}
|
||||
|
||||
Utils.addSettingsViewModel(SettingsSecurity, 'SettingsSecurity', 'SETTINGS_LABELS/LABEL_SECURITY_NAME', 'security');
|
||||
|
||||
SettingsSecurity.prototype.createTwoFactor = function ()
|
||||
{
|
||||
this.processing(true);
|
||||
RL.remote().createTwoFactor(this.onResult);
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.enableTwoFactor = function ()
|
||||
{
|
||||
this.processing(true);
|
||||
RL.remote().enableTwoFactor(this.onResult, this.viewEnable());
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.testTwoFactor = function ()
|
||||
{
|
||||
kn.showScreenPopup(PopupsTwoFactorTestViewModel);
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.clearTwoFactor = function ()
|
||||
{
|
||||
this.viewSecret('');
|
||||
this.viewBackupCodes('');
|
||||
this.viewUrl('');
|
||||
|
||||
this.clearing(true);
|
||||
RL.remote().clearTwoFactor(this.onResult);
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.onShow = function ()
|
||||
{
|
||||
this.viewSecret('');
|
||||
this.viewBackupCodes('');
|
||||
this.viewUrl('');
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.onResult = function (sResult, oData)
|
||||
{
|
||||
this.processing(false);
|
||||
this.clearing(false);
|
||||
|
||||
if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
|
||||
{
|
||||
this.viewUser(Utils.pString(oData.Result.User));
|
||||
this.viewEnable(!!oData.Result.Enable);
|
||||
this.twoFactorStatus(!!oData.Result.IsSet);
|
||||
|
||||
this.viewSecret(Utils.pString(oData.Result.Secret));
|
||||
this.viewBackupCodes(Utils.pString(oData.Result.BackupCodes).replace(/[\s]+/g, ' '));
|
||||
this.viewUrl(Utils.pString(oData.Result.Url));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.viewUser('');
|
||||
this.viewEnable(false);
|
||||
this.twoFactorStatus(false);
|
||||
|
||||
this.viewSecret('');
|
||||
this.viewBackupCodes('');
|
||||
this.viewUrl('');
|
||||
}
|
||||
|
||||
if (this.bFirst)
|
||||
{
|
||||
this.bFirst = false;
|
||||
var self = this;
|
||||
this.viewEnable.subscribe(function (bValue) {
|
||||
if (this.viewEnable.subs)
|
||||
{
|
||||
RL.remote().enableTwoFactor(function (sResult, oData) {
|
||||
if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result)
|
||||
{
|
||||
self.viewEnable.subs = false;
|
||||
self.viewEnable(false);
|
||||
self.viewEnable.subs = true;
|
||||
}
|
||||
}, bValue);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.onBuild = function ()
|
||||
{
|
||||
this.processing(true);
|
||||
RL.remote().getTwoFactor(this.onResult);
|
||||
};
|
||||
|
|
@ -34,18 +34,74 @@ WebMailAjaxRemoteStorage.prototype.folders = function (fCallback)
|
|||
* @param {string} sPassword
|
||||
* @param {boolean} bSignMe
|
||||
* @param {string=} sLanguage
|
||||
* @param {string=} sAdditionalCode
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage)
|
||||
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage, sAdditionalCode)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'Login', {
|
||||
'Email': sEmail,
|
||||
'Login': sLogin,
|
||||
'Password': sPassword,
|
||||
'Language': sLanguage || '',
|
||||
'AdditionalCode': sAdditionalCode || '',
|
||||
'SignMe': bSignMe ? '1' : '0'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.getTwoFactor = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'GetTwoFactorInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.createTwoFactor = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'CreateTwoFactorSecret');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.clearTwoFactor = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sCode
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.testTwoFactor = function (fCallback, sCode)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'TestTwoFactorInfo', {
|
||||
'Code': sCode
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {boolean} bEnable
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.enableTwoFactor = function (fCallback, bEnable)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'EnableTwoFactor', {
|
||||
'Enable': bEnable ? '1' : '0'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.clearTwoFactorInfo = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sEmail
|
||||
|
|
|
|||
|
|
@ -72,6 +72,9 @@ function WebMailDataStorage()
|
|||
this.signatureToAll = ko.observable(false);
|
||||
this.replyTo = ko.observable('');
|
||||
|
||||
// security
|
||||
this.enableTwoFactor = ko.observable(false);
|
||||
|
||||
// accounts
|
||||
this.accounts = ko.observableArray([]);
|
||||
this.accountsLoading = ko.observable(false).extend({'throttle': 100});
|
||||
|
|
@ -434,6 +437,7 @@ WebMailDataStorage.prototype.populateDataOnStart = function()
|
|||
this.replyTo(RL.settingsGet('ReplyTo'));
|
||||
this.signature(RL.settingsGet('Signature'));
|
||||
this.signatureToAll(!!RL.settingsGet('SignatureToAll'));
|
||||
this.enableTwoFactor(!!RL.settingsGet('EnableTwoFactor'));
|
||||
|
||||
this.lastFoldersHash = RL.local().get(Enums.ClientSideKeyName.FoldersLashHash) || '';
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
@import "Languages.less";
|
||||
@import "AddAccount.less";
|
||||
@import "OpenPgpKey.less";
|
||||
@import "TwoFactor.less";
|
||||
@import "Identity.less";
|
||||
@import "AdvancedSearch.less";
|
||||
@import "MessageList.less";
|
||||
|
|
|
|||
7
dev/Styles/TwoFactor.less
Normal file
7
dev/Styles/TwoFactor.less
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.popups {
|
||||
.b-two-factor-test-content {
|
||||
.modal-header {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,11 @@ function LoginViewModel()
|
|||
this.password = ko.observable('');
|
||||
this.signMe = ko.observable(false);
|
||||
|
||||
this.additionalCode = ko.observable('');
|
||||
this.additionalCode.error = ko.observable(false);
|
||||
this.additionalCode.focused = ko.observable(false);
|
||||
this.additionalCode.visibility = ko.observable(false);
|
||||
|
||||
this.logoImg = Utils.trim(RL.settingsGet('LoginLogo'));
|
||||
this.loginDescription = Utils.trim(RL.settingsGet('LoginDescription'));
|
||||
this.logoCss = Utils.trim(RL.settingsGet('LoginCss'));
|
||||
|
|
@ -29,6 +34,8 @@ function LoginViewModel()
|
|||
|
||||
this.email.subscribe(function () {
|
||||
this.emailError(false);
|
||||
this.additionalCode('');
|
||||
this.additionalCode.visibility(false);
|
||||
}, this);
|
||||
|
||||
this.login.subscribe(function () {
|
||||
|
|
@ -39,6 +46,14 @@ function LoginViewModel()
|
|||
this.passwordError(false);
|
||||
}, this);
|
||||
|
||||
this.additionalCode.subscribe(function () {
|
||||
this.additionalCode.error(false);
|
||||
}, this);
|
||||
|
||||
this.additionalCode.visibility.subscribe(function () {
|
||||
this.additionalCode.error(false);
|
||||
}, this);
|
||||
|
||||
this.submitRequest = ko.observable(false);
|
||||
this.submitError = ko.observable('');
|
||||
|
||||
|
|
@ -68,7 +83,12 @@ function LoginViewModel()
|
|||
this.emailError('' === Utils.trim(this.email()));
|
||||
this.passwordError('' === Utils.trim(this.password()));
|
||||
|
||||
if (this.emailError() || this.passwordError())
|
||||
if (this.additionalCode.visibility())
|
||||
{
|
||||
this.additionalCode.error('' === Utils.trim(this.additionalCode()));
|
||||
}
|
||||
|
||||
if (this.emailError() || this.passwordError() || this.additionalCode.error())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -81,12 +101,28 @@ function LoginViewModel()
|
|||
{
|
||||
if (oData.Result)
|
||||
{
|
||||
RL.loginAndLogoutReload();
|
||||
if (oData.TwoFactorAuth)
|
||||
{
|
||||
this.additionalCode('');
|
||||
this.additionalCode.visibility(true);
|
||||
this.additionalCode.focused(true);
|
||||
|
||||
this.submitRequest(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RL.loginAndLogoutReload();
|
||||
}
|
||||
}
|
||||
else if (oData.ErrorCode)
|
||||
{
|
||||
this.submitRequest(false);
|
||||
this.submitError(Utils.getNotification(oData.ErrorCode));
|
||||
|
||||
if ('' === this.submitError())
|
||||
{
|
||||
this.submitError(Utils.getNotification(Enums.Notification.UnknownError));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -100,7 +136,8 @@ function LoginViewModel()
|
|||
}
|
||||
|
||||
}, this), this.email(), this.login(), this.password(), !!this.signMe(),
|
||||
this.bSendLanguage ? this.mainLanguage() : '');
|
||||
this.bSendLanguage ? this.mainLanguage() : '',
|
||||
this.additionalCode.visibility() ? this.additionalCode() : '');
|
||||
|
||||
return true;
|
||||
|
||||
|
|
|
|||
|
|
@ -929,12 +929,12 @@ PopupsComposeViewModel.prototype.onBuild = function ()
|
|||
|
||||
if (oEvent && self.modalVisibility() && RL.data().useKeyboardShortcuts())
|
||||
{
|
||||
if (self.bAllowCtrlS && oEvent.ctrlKey && Enums.EventKeyCode.S === oEvent.keyCode)
|
||||
if (self.bAllowCtrlS && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.S === oEvent.keyCode)
|
||||
{
|
||||
self.saveCommand();
|
||||
bResult = false;
|
||||
}
|
||||
else if (oEvent.ctrlKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
|
||||
else if (oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
|
||||
{
|
||||
self.sendCommand();
|
||||
bResult = false;
|
||||
|
|
|
|||
55
dev/ViewModels/PopupsTwoFactorTestViewModel.js
Normal file
55
dev/ViewModels/PopupsTwoFactorTestViewModel.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends KnoinAbstractViewModel
|
||||
*/
|
||||
function PopupsTwoFactorTestViewModel()
|
||||
{
|
||||
KnoinAbstractViewModel.call(this, 'Popups', 'PopupsTwoFactorTest');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.code = ko.observable('');
|
||||
this.code.focused = ko.observable(false);
|
||||
this.code.status = ko.observable(null);
|
||||
|
||||
this.testing = ko.observable(false);
|
||||
|
||||
// commands
|
||||
this.testCode = Utils.createCommand(this, function () {
|
||||
|
||||
this.testing(true);
|
||||
RL.remote().testTwoFactor(function (sResult, oData) {
|
||||
|
||||
self.testing(false);
|
||||
self.code.status(Enums.StorageResultType.Success === sResult && oData && oData.Result ? true : false);
|
||||
|
||||
}, this.code());
|
||||
|
||||
}, function () {
|
||||
return '' !== this.code() && !this.testing();
|
||||
});
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
||||
Utils.extendAsViewModel('PopupsTwoFactorTestViewModel', PopupsTwoFactorTestViewModel);
|
||||
|
||||
PopupsTwoFactorTestViewModel.prototype.clearPopup = function ()
|
||||
{
|
||||
this.code('');
|
||||
this.code.focused(false);
|
||||
this.code.status(null);
|
||||
this.testing(false);
|
||||
};
|
||||
|
||||
PopupsTwoFactorTestViewModel.prototype.onShow = function ()
|
||||
{
|
||||
this.clearPopup();
|
||||
};
|
||||
|
||||
PopupsTwoFactorTestViewModel.prototype.onFocus = function ()
|
||||
{
|
||||
this.code.focused(true);
|
||||
};
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "RainLoop",
|
||||
"title": "RainLoop Webmail",
|
||||
"version": "1.6.3",
|
||||
"release": "804",
|
||||
"version": "1.6.4",
|
||||
"release": "890",
|
||||
"description": "Simple, modern & fast web-based email client",
|
||||
"homepage": "http://rainloop.net",
|
||||
"main": "Gruntfile.js",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP Class for handling Google Authenticator 2-factor authentication
|
||||
*
|
||||
* @author Michael Kliewe
|
||||
* @copyright 2012 Michael Kliewe
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
* @link http://www.phpgangsta.de/
|
||||
*/
|
||||
|
||||
class PHPGangsta_GoogleAuthenticator
|
||||
{
|
||||
protected $_codeLength = 6;
|
||||
|
||||
/**
|
||||
* Create new secret.
|
||||
* 16 characters, randomly chosen from the allowed base32 characters.
|
||||
*
|
||||
* @param int $secretLength
|
||||
* @return string
|
||||
*/
|
||||
public function createSecret($secretLength = 16)
|
||||
{
|
||||
$validChars = $this->_getBase32LookupTable();
|
||||
unset($validChars[32]);
|
||||
|
||||
$secret = '';
|
||||
for ($i = 0; $i < $secretLength; $i++) {
|
||||
$secret .= $validChars[array_rand($validChars)];
|
||||
}
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the code, with given secret and point in time
|
||||
*
|
||||
* @param string $secret
|
||||
* @param int|null $timeSlice
|
||||
* @return string
|
||||
*/
|
||||
public function getCode($secret, $timeSlice = null)
|
||||
{
|
||||
if ($timeSlice === null) {
|
||||
$timeSlice = floor(time() / 30);
|
||||
}
|
||||
|
||||
$secretkey = $this->_base32Decode($secret);
|
||||
|
||||
// Pack time into binary string
|
||||
$time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice);
|
||||
// Hash it with users secret key
|
||||
$hm = hash_hmac('SHA1', $time, $secretkey, true);
|
||||
// Use last nipple of result as index/offset
|
||||
$offset = ord(substr($hm, -1)) & 0x0F;
|
||||
// grab 4 bytes of the result
|
||||
$hashpart = substr($hm, $offset, 4);
|
||||
|
||||
// Unpak binary value
|
||||
$value = unpack('N', $hashpart);
|
||||
$value = $value[1];
|
||||
// Only 32 bits
|
||||
$value = $value & 0x7FFFFFFF;
|
||||
|
||||
$modulo = pow(10, $this->_codeLength);
|
||||
return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get QR-Code URL for image, from google charts
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $secret
|
||||
* @return string
|
||||
*/
|
||||
public function getQRCodeGoogleUrl($name, $secret) {
|
||||
$urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
|
||||
return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$urlencoded.'';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now
|
||||
*
|
||||
* @param string $secret
|
||||
* @param string $code
|
||||
* @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after)
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyCode($secret, $code, $discrepancy = 1)
|
||||
{
|
||||
$currentTimeSlice = floor(time() / 30);
|
||||
|
||||
for ($i = -$discrepancy; $i <= $discrepancy; $i++) {
|
||||
$calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
|
||||
if ($calculatedCode == $code ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the code length, should be >=6
|
||||
*
|
||||
* @param int $length
|
||||
* @return PHPGangsta_GoogleAuthenticator
|
||||
*/
|
||||
public function setCodeLength($length)
|
||||
{
|
||||
$this->_codeLength = $length;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to decode base32
|
||||
*
|
||||
* @param $secret
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function _base32Decode($secret)
|
||||
{
|
||||
if (empty($secret)) return '';
|
||||
|
||||
$base32chars = $this->_getBase32LookupTable();
|
||||
$base32charsFlipped = array_flip($base32chars);
|
||||
|
||||
$paddingCharCount = substr_count($secret, $base32chars[32]);
|
||||
$allowedValues = array(6, 4, 3, 1, 0);
|
||||
if (!in_array($paddingCharCount, $allowedValues)) return false;
|
||||
for ($i = 0; $i < 4; $i++){
|
||||
if ($paddingCharCount == $allowedValues[$i] &&
|
||||
substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) return false;
|
||||
}
|
||||
$secret = str_replace('=','', $secret);
|
||||
$secret = str_split($secret);
|
||||
$binaryString = "";
|
||||
for ($i = 0; $i < count($secret); $i = $i+8) {
|
||||
$x = "";
|
||||
if (!in_array($secret[$i], $base32chars)) return false;
|
||||
for ($j = 0; $j < 8; $j++) {
|
||||
$x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
|
||||
}
|
||||
$eightBits = str_split($x, 8);
|
||||
for ($z = 0; $z < count($eightBits); $z++) {
|
||||
$binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:"";
|
||||
}
|
||||
}
|
||||
return $binaryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to encode base32
|
||||
*
|
||||
* @param string $secret
|
||||
* @param bool $padding
|
||||
* @return string
|
||||
*/
|
||||
protected function _base32Encode($secret, $padding = true)
|
||||
{
|
||||
if (empty($secret)) return '';
|
||||
|
||||
$base32chars = $this->_getBase32LookupTable();
|
||||
|
||||
$secret = str_split($secret);
|
||||
$binaryString = "";
|
||||
for ($i = 0; $i < count($secret); $i++) {
|
||||
$binaryString .= str_pad(base_convert(ord($secret[$i]), 10, 2), 8, '0', STR_PAD_LEFT);
|
||||
}
|
||||
$fiveBitBinaryArray = str_split($binaryString, 5);
|
||||
$base32 = "";
|
||||
$i = 0;
|
||||
while ($i < count($fiveBitBinaryArray)) {
|
||||
$base32 .= $base32chars[base_convert(str_pad($fiveBitBinaryArray[$i], 5, '0'), 2, 10)];
|
||||
$i++;
|
||||
}
|
||||
if ($padding && ($x = strlen($binaryString) % 40) != 0) {
|
||||
if ($x == 8) $base32 .= str_repeat($base32chars[32], 6);
|
||||
elseif ($x == 16) $base32 .= str_repeat($base32chars[32], 4);
|
||||
elseif ($x == 24) $base32 .= str_repeat($base32chars[32], 3);
|
||||
elseif ($x == 32) $base32 .= $base32chars[32];
|
||||
}
|
||||
return $base32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array with all 32 characters for decoding from/encoding to base32
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _getBase32LookupTable()
|
||||
{
|
||||
return array(
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
|
||||
'=' // padding char
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -509,7 +509,7 @@ class Actions
|
|||
if (null === $this->oTwoFactorAuthProvider)
|
||||
{
|
||||
$this->oTwoFactorAuthProvider = new \RainLoop\Providers\TwoFactorAuth(
|
||||
$this->fabrica('two-factor-auth')
|
||||
$this->Config()->Get('security', 'allow_two_factor_auth', false) ? $this->fabrica('two-factor-auth') : null
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -932,6 +932,7 @@ class Actions
|
|||
'LoginCss' => $oConfig->Get('branding', 'login_css', ''),
|
||||
'Token' => $oConfig->Get('security', 'csrf_protection', false) ? \RainLoop\Utils::GetCsrfToken() : '',
|
||||
'OpenPGP' => $oConfig->Get('security', 'openpgp', false),
|
||||
'AllowTwoFactorAuth' => (bool) $oConfig->Get('security', 'allow_two_factor_auth', false),
|
||||
'InIframe' => (bool) $oConfig->Get('labs', 'in_iframe', false),
|
||||
'AllowAdminPanel' => (bool) $oConfig->Get('security', 'allow_admin_panel', true),
|
||||
'AllowHtmlEditorSourceButton' => (bool) $oConfig->Get('labs', 'allow_html_editor_source_button', false),
|
||||
|
|
@ -1141,6 +1142,7 @@ class Actions
|
|||
$aResult['ReplyTo'] = '';
|
||||
$aResult['Signature'] = '';
|
||||
$aResult['SignatureToAll'] = false;
|
||||
$aResult['EnableTwoFactor'] = false;
|
||||
$aResult['ParentEmail'] = '';
|
||||
$aResult['InterfaceAnimation'] = \RainLoop\Enumerations\InterfaceAnimation::NORMAL;
|
||||
$aResult['CustomThemeType'] = \RainLoop\Enumerations\CustomThemeType::LIGHT;
|
||||
|
|
@ -1181,6 +1183,7 @@ class Actions
|
|||
$aResult['ReplyTo'] = $oSettings->GetConf('ReplyTo', $aResult['ReplyTo']);
|
||||
$aResult['Signature'] = $oSettings->GetConf('Signature', $aResult['Signature']);
|
||||
$aResult['SignatureToAll'] = !!$oSettings->GetConf('SignatureToAll', $aResult['SignatureToAll']);
|
||||
$aResult['EnableTwoFactor'] = !!$oSettings->GetConf('EnableTwoFactor', $aResult['EnableTwoFactor']);
|
||||
|
||||
$aResult['ParentEmail'] = $oAccount->ParentEmail();
|
||||
}
|
||||
|
|
@ -1356,27 +1359,36 @@ class Actions
|
|||
|
||||
if ($oAccount && $this->TwoFactorAuthProvider()->IsActive())
|
||||
{
|
||||
$oSettings = $this->SettingsProvider()->Load($oAccount);
|
||||
if ($oSettings)
|
||||
$aData = $this->getTwoFactorInfo($oAccount->ParentEmailHelper());
|
||||
if ($aData && isset($aData['IsSet'], $aData['Enable']) && !empty($aData['Secret']) && $aData['IsSet'] && $aData['Enable'])
|
||||
{
|
||||
$sTwoFactorAuthSecret = $oSettings->GetConf('TwoFactorAuthEnabled', false) ?
|
||||
$oSettings->GetConf('TwoFactorAuthSecret', '') : '';
|
||||
|
||||
if (!empty($sTwoFactorAuthSecret))
|
||||
$sTwoFactorAuthCode = \trim($sTwoFactorAuthCode);
|
||||
if (empty($sTwoFactorAuthCode))
|
||||
{
|
||||
if (empty($sTwoFactorAuthCode))
|
||||
$this->Logger()->Write('TwoFactorAuth: Required Code for '.$oAccount->ParentEmailHelper().' account.');
|
||||
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthRequired);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->Logger()->Write('TwoFactorAuth: Verify Code for '.$oAccount->ParentEmailHelper().' account.');
|
||||
|
||||
$bGood = false;
|
||||
if (6 < \strlen($sTwoFactorAuthCode) && !empty($aData['BackupCodes']))
|
||||
{
|
||||
$this->Logger()->Write('TwoFactorAuth: Required Code for '.$oAccount->Email().' account.');
|
||||
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthRequired);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->Logger()->Write('TwoFactorAuth: Verify Code for '.$oAccount->Email().' account.');
|
||||
if (!$this->TwoFactorAuthProvider()->VerifyCode($sTwoFactorAuthSecret, $sTwoFactorAuthCode))
|
||||
$aBackupCodes = \explode(' ', \trim(\preg_replace('/[^\d]+/', ' ', $aData['BackupCodes'])));
|
||||
$bGood = \in_array($sTwoFactorAuthCode, $aBackupCodes);
|
||||
|
||||
if ($bGood)
|
||||
{
|
||||
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthError);
|
||||
$this->removeBackupCodeFromTwoFactorInfo($oAccount->ParentEmailHelper(), $sTwoFactorAuthCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bGood && !$this->TwoFactorAuthProvider()->VerifyCode($aData['Secret'], $sTwoFactorAuthCode))
|
||||
{
|
||||
$this->loginErrorDelay();
|
||||
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1420,7 +1432,7 @@ class Actions
|
|||
$sLanguage = $this->GetActionParam('Language', '');
|
||||
$bSignMe = '1' === $this->GetActionParam('SignMe', '0');
|
||||
|
||||
$sTwoFactorAuthCode = $this->GetActionParam('TwoFactorAuthCode', '');
|
||||
$sAdditionalCode = $this->GetActionParam('AdditionalCode', '');
|
||||
|
||||
$this->Logger()->AddSecret($sPassword);
|
||||
|
||||
|
|
@ -1430,7 +1442,7 @@ class Actions
|
|||
{
|
||||
$oAccount = $this->LoginProcess($sEmail, $sLogin, $sPassword,
|
||||
$bSignMe ? \md5(\microtime(true).APP_SALT.\rand(10000, 99999).$sEmail) : '',
|
||||
$sTwoFactorAuthCode);
|
||||
$sAdditionalCode);
|
||||
}
|
||||
catch (\RainLoop\Exceptions\ClientException $oException)
|
||||
{
|
||||
|
|
@ -2054,6 +2066,7 @@ class Actions
|
|||
|
||||
$this->setConfigFromParams($oConfig, 'TokenProtection', 'security', 'csrf_protection', 'bool');
|
||||
$this->setConfigFromParams($oConfig, 'OpenPGP', 'security', 'openpgp', 'bool');
|
||||
$this->setConfigFromParams($oConfig, 'AllowTwoFactorAuth', 'security', 'allow_two_factor_auth', 'bool');
|
||||
$this->setConfigFromParams($oConfig, 'EnabledPlugins', 'plugins', 'enable', 'bool');
|
||||
|
||||
$this->setConfigFromParams($oConfig, 'GoogleEnable', 'social', 'google_enable', 'bool');
|
||||
|
|
@ -3199,6 +3212,7 @@ class Actions
|
|||
$this->setSettingsFromParams($oSettings, 'ReplyTo', 'string');
|
||||
$this->setSettingsFromParams($oSettings, 'Signature', 'string');
|
||||
$this->setSettingsFromParams($oSettings, 'SignatureToAll', 'bool');
|
||||
$this->setSettingsFromParams($oSettings, 'EnableTwoFactor', 'bool');
|
||||
|
||||
$this->setSettingsFromParams($oSettings, 'CustomThemeImg', 'string');
|
||||
|
||||
|
|
@ -4545,6 +4559,236 @@ class Actions
|
|||
return $this->DefaultResponse(__FUNCTION__, $aQuota);
|
||||
}
|
||||
|
||||
private function getTwoFactorInfo($sEmail, $bRemoveSecret = false)
|
||||
{
|
||||
$mData = null;
|
||||
|
||||
$aResult = array(
|
||||
'User' => '',
|
||||
'IsSet' => false,
|
||||
'Enable' => false,
|
||||
'Secret' => '',
|
||||
'Url' => '',
|
||||
'BackupCodes' => ''
|
||||
);
|
||||
|
||||
if (!empty($sEmail))
|
||||
{
|
||||
$aResult['User'] = $sEmail;
|
||||
|
||||
$sData = $this->StorageProvider()->Get(null,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
|
||||
'TwoFactorAuth/User/'.$sEmail.'/Data/'
|
||||
);
|
||||
|
||||
if ($sData)
|
||||
{
|
||||
$mData = \RainLoop\Utils::DecodeKeyValues($sData);
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_array($mData) && !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['Url'] = $this->TwoFactorAuthProvider()->GetQRCodeGoogleUrl(
|
||||
$aResult['User'], $aResult['Secret'], $this->Config()->Get('webmail', 'title', ''));
|
||||
}
|
||||
|
||||
if ($bRemoveSecret)
|
||||
{
|
||||
if (isset($aResult['Secret']))
|
||||
{
|
||||
unset($aResult['Secret']);
|
||||
}
|
||||
|
||||
if (isset($aResult['Url']))
|
||||
{
|
||||
unset($aResult['Url']);
|
||||
}
|
||||
|
||||
if (isset($aResult['BackupCodes']))
|
||||
{
|
||||
unset($aResult['BackupCodes']);
|
||||
}
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sEmail
|
||||
* @param string $sCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function removeBackupCodeFromTwoFactorInfo($sEmail, $sCode)
|
||||
{
|
||||
if (empty($sEmail) || empty($sCode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$sData = $this->StorageProvider()->Get(null,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
|
||||
'TwoFactorAuth/User/'.$sEmail.'/Data/'
|
||||
);
|
||||
|
||||
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(null,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
|
||||
'TwoFactorAuth/User/'.$sEmail.'/Data/',
|
||||
\RainLoop\Utils::EncodeKeyValues($mData)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function DoGetTwoFactorInfo()
|
||||
{
|
||||
if (!$this->TwoFactorAuthProvider()->IsActive())
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__,
|
||||
$this->getTwoFactorInfo($oAccount->ParentEmailHelper(), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function DoCreateTwoFactorSecret()
|
||||
{
|
||||
if (!$this->TwoFactorAuthProvider()->IsActive())
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
$sEmail = $oAccount->ParentEmailHelper();
|
||||
|
||||
$sSecret = $this->TwoFactorAuthProvider()->CreateSecret();
|
||||
|
||||
$aCodes = array();
|
||||
for ($iIndex = 9; $iIndex > 0; $iIndex--)
|
||||
{
|
||||
$aCodes[] = \rand(100000000, 900000000);
|
||||
}
|
||||
|
||||
$this->StorageProvider()->Put(null,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
|
||||
'TwoFactorAuth/User/'.$sEmail.'/Data/',
|
||||
\RainLoop\Utils::EncodeKeyValues(array(
|
||||
'User' => $sEmail,
|
||||
'Enable' => false,
|
||||
'Secret' => $sSecret,
|
||||
'BackupCodes' => \implode(' ', $aCodes)
|
||||
))
|
||||
);
|
||||
|
||||
\sleep(1);
|
||||
return $this->DefaultResponse(__FUNCTION__,
|
||||
$this->getTwoFactorInfo($sEmail));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function DoEnableTwoFactor()
|
||||
{
|
||||
if (!$this->TwoFactorAuthProvider()->IsActive())
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
$sEmail = $oAccount->ParentEmailHelper();
|
||||
|
||||
$bResult = false;
|
||||
$mData = $this->getTwoFactorInfo($sEmail);
|
||||
if (isset($mData['Secret'], $mData['BackupCodes']))
|
||||
{
|
||||
$bResult = $this->StorageProvider()->Put(null,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
|
||||
'TwoFactorAuth/User/'.$sEmail.'/Data/',
|
||||
\RainLoop\Utils::EncodeKeyValues(array(
|
||||
'User' => $sEmail,
|
||||
'Enable' => '1' === \trim($this->GetActionParam('Enable', '0')),
|
||||
'Secret' => $mData['Secret'],
|
||||
'BackupCodes' => $mData['BackupCodes']
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__, $bResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function DoTestTwoFactorInfo()
|
||||
{
|
||||
if (!$this->TwoFactorAuthProvider()->IsActive())
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
|
||||
$sCode = \trim($this->GetActionParam('Code', ''));
|
||||
|
||||
$oData = $this->getTwoFactorInfo($oAccount->ParentEmailHelper());
|
||||
$sSecret = !empty($oData['Secret']) ? $oData['Secret'] : '';
|
||||
|
||||
\sleep(1);
|
||||
return $this->DefaultResponse(__FUNCTION__,
|
||||
$this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function DoClearTwoFactorInfo()
|
||||
{
|
||||
if (!$this->TwoFactorAuthProvider()->IsActive())
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
|
||||
$this->StorageProvider()->Clear(null,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
|
||||
'TwoFactorAuth/User/'.$oAccount->ParentEmailHelper().'/Data/'
|
||||
);
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__,
|
||||
$this->getTwoFactorInfo($oAccount->ParentEmailHelper(), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ class Application extends \RainLoop\Config\AbstractConfig
|
|||
'admin_login' => array('admin', 'Login and password for web admin panel'),
|
||||
'admin_password' => array('12345'),
|
||||
'allow_admin_panel' => array(true, 'Access settings'),
|
||||
'allow_two_factor_auth' => array(false),
|
||||
'admin_panel_host' => array(''),
|
||||
'core_install_access_domains' => array('')
|
||||
),
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ class Notifications
|
|||
self::SocialGoogleLoginAccessDisable => 'SocialGoogleLoginAccessDisable',
|
||||
self::DomainNotAllowed => 'DomainNotAllowed',
|
||||
self::AccountNotAllowed => 'AccountNotAllowed',
|
||||
self::AccountTwoFactorAuthRequired => 'AccountTwoFactorAuthRequired',
|
||||
self::AccountTwoFactorAuthError => 'AccountTwoFactorAuthError',
|
||||
self::CantGetMessageList => 'CantGetMessageList',
|
||||
self::CantGetMessage => 'CantGetMessage',
|
||||
self::CantDeleteMessage => 'CantDeleteMessage',
|
||||
|
|
|
|||
|
|
@ -27,9 +27,37 @@ class TwoFactorAuth extends \RainLoop\Providers\AbstractProvider
|
|||
return $this->oDriver instanceof \RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sName
|
||||
* @param string $sSecret
|
||||
* @param string $sTitle = ''
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetQRCodeGoogleUrl($sName, $sSecret, $sTitle = '')
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function CreateSecret()
|
||||
{
|
||||
$sResult = '';
|
||||
if ($this->IsActive())
|
||||
{
|
||||
$sResult = $this->oDriver->CreateSecret();
|
||||
}
|
||||
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sSecret
|
||||
* @param string $sCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function VerifyCode($sSecret, $sCode)
|
||||
|
|
|
|||
|
|
@ -1,206 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace RainLoop\Providers\TwoFactorAuth;
|
||||
|
||||
class GoogleTwoFactorAuth
|
||||
extends \RainLoop\Providers\TwoFactorAuth\AbstractTwoFactorAuth
|
||||
implements \RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface
|
||||
{
|
||||
private $iCodeLength = 6;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function Label()
|
||||
{
|
||||
return 'Google Authenticator Code';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get QR-Code URL for image, from google charts
|
||||
*
|
||||
* Function from PHP Class for handling Google Authenticator 2-factor authentication
|
||||
*
|
||||
* @author Michael Kliewe
|
||||
* @copyright 2012 Michael Kliewe
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
* @link http://www.phpgangsta.de/
|
||||
*
|
||||
* @param string $sName
|
||||
* @param string $sSecret
|
||||
* @param string $sTitle
|
||||
* @return string
|
||||
*/
|
||||
private function getQRCodeGoogleUrl($sName, $sSecret, $sTitle = null)
|
||||
{
|
||||
$sUrlEncoded = \urlencode('otpauth://totp/'.$sName.'?secret='.$sSecret.'');
|
||||
if(null !== $sTitle)
|
||||
{
|
||||
$sUrlEncoded .= \urlencode('&issuer='.$sTitle);
|
||||
}
|
||||
|
||||
return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$sUrlEncoded.'';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array with all 32 characters for decoding from/encoding to base32
|
||||
*
|
||||
* Function from PHP Class for handling Google Authenticator 2-factor authentication
|
||||
*
|
||||
* @author Michael Kliewe
|
||||
* @copyright 2012 Michael Kliewe
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
* @link http://www.phpgangsta.de/
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBase32LookupTable()
|
||||
{
|
||||
return array(
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
|
||||
'=' // padding char
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to decode base32
|
||||
*
|
||||
* Function from PHP Class for handling Google Authenticator 2-factor authentication
|
||||
*
|
||||
* @author Michael Kliewe
|
||||
* @copyright 2012 Michael Kliewe
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
* @link http://www.phpgangsta.de/
|
||||
*
|
||||
* @param $sSecret
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
private function base32Decode($sSecret)
|
||||
{
|
||||
if (empty($sSecret))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$aBase32chars = $this->getBase32LookupTable();
|
||||
$aBase32charsFlipped = \array_flip($aBase32chars);
|
||||
|
||||
$iPaddingCharCount = \substr_count($sSecret, $aBase32chars[32]);
|
||||
$aAllowedValues = array(6, 4, 3, 1, 0);
|
||||
|
||||
if (!\in_array($iPaddingCharCount, $aAllowedValues))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($iIndex = 0; $iIndex < 4; $iIndex++)
|
||||
{
|
||||
if ($iPaddingCharCount === $aAllowedValues[$iIndex] &&
|
||||
\substr($sSecret, -($aAllowedValues[$iIndex])) !== \str_repeat($aBase32chars[32], $aAllowedValues[$iIndex]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$sSecret = \str_replace('=', '', $sSecret);
|
||||
$sSecret = \str_split($sSecret);
|
||||
|
||||
$sBinaryString = '';
|
||||
|
||||
for ($iIndex = 0; $iIndex < \count($sSecret); $iIndex = $iIndex + 8)
|
||||
{
|
||||
$sX = '';
|
||||
if (!\in_array($sSecret[$iIndex], $aBase32chars))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($iJ = 0; $iJ < 8; $iJ++)
|
||||
{
|
||||
$sX .= \str_pad(\base_convert(@$aBase32charsFlipped[@$sSecret[$iIndex + $iJ]], 10, 2), 5, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
$eightBits = \str_split($sX, 8);
|
||||
for ($iZ = 0; $iZ < \count($eightBits); $iZ++)
|
||||
{
|
||||
$sBinaryString .= (($y = \chr(\base_convert($eightBits[$iZ], 2, 10))) || ord($y) == 48 ) ? $y : '';
|
||||
}
|
||||
}
|
||||
|
||||
return $sBinaryString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the code, with given secret and point in time
|
||||
*
|
||||
* Function from PHP Class for handling Google Authenticator 2-factor authentication
|
||||
*
|
||||
* @author Michael Kliewe
|
||||
* @copyright 2012 Michael Kliewe
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
* @link http://www.phpgangsta.de/
|
||||
*
|
||||
* @param string $sSecret
|
||||
* @param int|null $mTimeSlice
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getCode($sSecret, $mTimeSlice = null)
|
||||
{
|
||||
if (null === $mTimeSlice)
|
||||
{
|
||||
$mTimeSlice = \floor(\time() / 30);
|
||||
}
|
||||
|
||||
$sSecretKey = $this->base32Decode($sSecret);
|
||||
|
||||
// Pack time into binary string
|
||||
$sTime = \chr(0).\chr(0).\chr(0).\chr(0).\pack('N*', $mTimeSlice);
|
||||
// Hash it with users secret key
|
||||
$sHm = \hash_hmac('SHA1', $sTime, $sSecretKey, true);
|
||||
// Use last nipple of result as index/offset
|
||||
$iOffset = \ord(\substr($sHm, -1)) & 0x0F;
|
||||
// grab 4 bytes of the result
|
||||
$sHashPart = \substr($sHm, $iOffset, 4);
|
||||
|
||||
// Unpak binary value
|
||||
$sValue = \unpack('N', $sHashPart);
|
||||
$sValue = $sValue{1};
|
||||
// Only 32 bits
|
||||
$sValue = $sValue & 0x7FFFFFFF;
|
||||
|
||||
$iMod = \pow(10, $this->iCodeLength);
|
||||
|
||||
return \str_pad($sValue % $iMod, $this->iCodeLength, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the code is correct. This will accept codes starting
|
||||
* from $iDiscrepancy * 30sec ago to $iDiscrepancy * 30sec from now
|
||||
*
|
||||
* @param string $sSecret
|
||||
* @param string $sCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function VerifyCode($sSecret, $sCode)
|
||||
{
|
||||
$iDiscrepancy = 1;
|
||||
$iTimeSlice = \floor(\time() / 30);
|
||||
|
||||
for ($iIndex = -$iDiscrepancy; $iIndex <= $iDiscrepancy; $iIndex++)
|
||||
{
|
||||
if ($this->getCode($sSecret, $iTimeSlice + $iIndex) === $sCode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace RainLoop\Providers\TwoFactorAuth;
|
||||
|
||||
class GoogleTwoFactorAuth
|
||||
extends \RainLoop\Providers\TwoFactorAuth\AbstractTwoFactorAuth
|
||||
implements \RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface
|
||||
{
|
||||
/**
|
||||
* @param string $sSecret
|
||||
* @param string $sCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function VerifyCode($sSecret, $sCode)
|
||||
{
|
||||
include_once APP_VERSION_ROOT_PATH.'app/libraries/PHPGangsta/GoogleAuthenticator.php';
|
||||
|
||||
$oGoogleAuthenticator = new \PHPGangsta_GoogleAuthenticator();
|
||||
return $oGoogleAuthenticator->verifyCode($sSecret, $sCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function CreateSecret()
|
||||
{
|
||||
include_once APP_VERSION_ROOT_PATH.'app/libraries/PHPGangsta/GoogleAuthenticator.php';
|
||||
|
||||
$oGoogleAuthenticator = new \PHPGangsta_GoogleAuthenticator();
|
||||
return $oGoogleAuthenticator->createSecret();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace RainLoop\Providers\TwoFactorAuth;
|
||||
|
||||
interface TwoFactorAuthInterface
|
||||
{
|
||||
/**
|
||||
* @param string $sSecret
|
||||
* @param string $sCode
|
||||
* @return bool
|
||||
*/
|
||||
public function VerifyCode($sSecret, $sCode);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace RainLoop\Providers\TwoFactorAuth;
|
||||
|
||||
interface TwoFactorAuthInterface
|
||||
{
|
||||
/**
|
||||
* @param string $sSecret
|
||||
* @param string $sCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function VerifyCode($sSecret, $sCode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,16 @@
|
|||
<label data-bind="click: function () { openPGP(!openPGP()); }">
|
||||
<i data-bind="css: openPGP() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
|
||||
|
||||
Allow OpenPGP (unstable)
|
||||
Allow OpenPGP (beta)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<label data-bind="click: function () { allowTwoFactorAuth(!allowTwoFactorAuth()); }">
|
||||
<i data-bind="css: allowTwoFactorAuth() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
|
||||
|
||||
Allow 2-Step Verification (Google Authenticator) (beta)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -45,6 +45,15 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="visible: additionalCode.visibility(), css: {'error': additionalCode.error}">
|
||||
<div class="input-append">
|
||||
<input class="i18n inputPassword span4" type="password" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="value: additionalCode, hasFocus: additionalCode.focused" data-i18n-placeholder="LOGIN/LABEL_VERIFICATION_CODE" />
|
||||
<span class="add-on">
|
||||
<i class="icon-key"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{INCLUDE/BottomControlGroup/PLACE}}
|
||||
<div class="control-group">
|
||||
<button type="submit" class="btn btn-large span4 buttonLogin" data-bind="command: submitCommand">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<div class="popups">
|
||||
<div class="modal hide 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>
|
||||
<span class="i18n" data-i18n-text="POPUPS_TWO_FACTOR_TEST/TITLE_TEST_CODE"></span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<br />
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
<span class="i18n" data-i18n-text="POPUPS_TWO_FACTOR_TEST/LABEL_CODE"></span>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<input class="uiInput inputName" type="text" data-bind="value: code, hasfocus: code.focused, valueUpdate: 'afterkeydown', onEnter: testCode" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="btn" data-bind="command: testCode, css: { 'btn-success': true === code.status(), 'btn-danger': false === code.status() }">
|
||||
<i data-bind="css: {'icon-ok': !testing(), 'icon-spinner animated': testing(), 'icon-white': true === code.status() || false === code.status() }"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="POPUPS_TWO_FACTOR_TEST/BUTTON_TEST"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
82
rainloop/v/0.0.0/app/templates/Views/SettingsSecurity.html
Normal file
82
rainloop/v/0.0.0/app/templates/Views/SettingsSecurity.html
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<div class="b-settings-security">
|
||||
<div class="form-horizontal">
|
||||
<div class="legend">
|
||||
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LEGEND_TWO_FACTOR_AUTH"></span>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<label data-bind="click: function () { viewEnable(!viewEnable()); }">
|
||||
<i data-bind="css: viewEnable() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_ENABLE_TWO_FACTOR"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_TWO_FACTOR_USER"></span>
|
||||
</label>
|
||||
<div class="controls" style="padding-top: 5px;">
|
||||
<strong><span data-bind="text: viewUser"></span></strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">
|
||||
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_TWO_FACTOR_STATUS"></span>
|
||||
</label>
|
||||
<div class="controls" style="padding-top: 5px;">
|
||||
<strong data-bind="visible: !processing()"><span data-bind="text: viewTwoFactorStatus"></span></strong>
|
||||
<strong data-bind="visible: processing()">...</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<a class="btn" data-bind="click: clearTwoFactor, visible: twoFactorStatus">
|
||||
<i class="icon-remove" data-bind="css: {'icon-remove': !clearing(), 'icon-spinner animated': clearing()}" ></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/BUTTON_CLEAR"></span>
|
||||
</a>
|
||||
<span data-bind="visible: twoFactorStatus"> </span>
|
||||
<a class="btn" data-bind="click: createTwoFactor">
|
||||
<i class="icon-ok" data-bind="css: {'icon-ok': !processing(), 'icon-spinner animated': processing()}" ></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/BUTTON_CREATE"></span>
|
||||
</a>
|
||||
<span> </span>
|
||||
<a class="btn" data-bind="click: testTwoFactor, visible: twoFactorStatus">
|
||||
<i class="icon-info"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/BUTTON_TEST"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="visible: '' !== viewSecret()">
|
||||
<label class="control-label">
|
||||
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_TWO_FACTOR_SECRET"></span>
|
||||
</label>
|
||||
<div class="controls" style="padding-top: 5px;">
|
||||
<strong data-bind="text: viewSecret"></strong>
|
||||
<br />
|
||||
<br />
|
||||
<blockquote>
|
||||
<p class="muted i18n" style="width: 550px" data-i18n-text="SETTINGS_SECURITY/TWO_FACTOR_SECRET_DESC"></p>
|
||||
</blockquote>
|
||||
<!-- ko if: '' !== viewUrl() -->
|
||||
<img style="margin-left: -7px;" src="javascript:void();" data-bind="attr: {'src': viewUrl}" />
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="visible: '' !== viewBackupCodes()">
|
||||
<label class="control-label">
|
||||
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/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 i18n" style="width: 550px" data-i18n-text="SETTINGS_SECURITY/TWO_FACTOR_BACKUP_CODES_DESC"></p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
|
|||
LABEL_LOGIN = "Login"
|
||||
LABEL_PASSWORD = "Passwort"
|
||||
LABEL_SIGN_ME = "Anmeldung merken"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Anmelden"
|
||||
TITLE_SIGN_IN_GOOGLE = "Mit Google anmelden"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Mit Facebook anmelden"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Wählen Sie den System-Ordner aus"
|
||||
SELECT_CHOOSE_ONE = "Wählen Sie einen aus"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Ordner"
|
|||
LABEL_ACCOUNTS_NAME = "Konten"
|
||||
LABEL_IDENTITY_NAME = "Identity"
|
||||
LABEL_IDENTITIES_NAME = "Identities"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Vorlagen"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Passwort"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reply To"
|
|||
LABEL_SIGNATURE = "Signatur"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Allgemein"
|
||||
LABEL_LANGUAGE = "Sprache"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Diese Social ID ist bisher mit keinem eMa
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Diese Social ID ist bisher mit keinem eMail-Konto verbunden. Melden Sie sich mit Ihren eMail-Daten ein und aktivieren Sie dieses Feature in Ihren Konten-Einstellungen."
|
||||
DOMAIN_NOT_ALLOWED = "Diese Domain ist nicht zugelassen."
|
||||
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"
|
||||
CANT_GET_MESSAGE_LIST = "Die Nachrichten-Liste ist nicht verfügbar"
|
||||
CANT_GET_MESSAGE = "Diese Nachricht ist nicht verfügbar"
|
||||
CANT_DELETE_MESSAGE = "Diese Nachricht kann nicht gelöscht werden"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
|
|||
LABEL_LOGIN = "Login"
|
||||
LABEL_PASSWORD = "Password"
|
||||
LABEL_SIGN_ME = "Remember Me"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Sign In"
|
||||
TITLE_SIGN_IN_GOOGLE = "Sign In using Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Sign In using Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Select system folders"
|
||||
SELECT_CHOOSE_ONE = "Choose one"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Folders"
|
|||
LABEL_ACCOUNTS_NAME = "Accounts"
|
||||
LABEL_IDENTITY_NAME = "Identity"
|
||||
LABEL_IDENTITIES_NAME = "Identities"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Themes"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Password"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reply-To"
|
|||
LABEL_SIGNATURE = "Signature"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "General"
|
||||
LABEL_LANGUAGE = "Language"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
|
||||
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"
|
||||
CANT_GET_MESSAGE_LIST = "Can't get message list"
|
||||
CANT_GET_MESSAGE = "Can't get message"
|
||||
CANT_DELETE_MESSAGE = "Can't delete message"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
|
|||
LABEL_LOGIN = "Login"
|
||||
LABEL_PASSWORD = "Contraseña"
|
||||
LABEL_SIGN_ME = "Recuerdame"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Ingresar"
|
||||
TITLE_SIGN_IN_GOOGLE = "Ingresar usando Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Ingresar usando Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Seleccione las carpetas del sistema"
|
||||
SELECT_CHOOSE_ONE = "Elija una"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Carpetas"
|
|||
LABEL_ACCOUNTS_NAME = "Cuentas"
|
||||
LABEL_IDENTITY_NAME = "Identity"
|
||||
LABEL_IDENTITIES_NAME = "Identities"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Temas"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Contraseña"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Responder a"
|
|||
LABEL_SIGNATURE = "Firma"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "General"
|
||||
LABEL_LANGUAGE = "Idioma"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Este ID no está asignado a ninguna cuent
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Este ID no está asignado a ninguna cuenta de email todavía. Inicie sesión con las credenciales de correo electrónico y habilite esta característica en la configuración de cuenta."
|
||||
DOMAIN_NOT_ALLOWED = "Dominio no permitido"
|
||||
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"
|
||||
CANT_GET_MESSAGE_LIST = "No se puede obtener la lista de mensajes"
|
||||
CANT_GET_MESSAGE = "No se puede obtener el mensaje"
|
||||
CANT_DELETE_MESSAGE = "No se puede eliminar el mensaje"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
|
|||
LABEL_LOGIN = "Identifiant"
|
||||
LABEL_PASSWORD = "Mot de passe"
|
||||
LABEL_SIGN_ME = "Se souvenir de moi"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Se connecter"
|
||||
TITLE_SIGN_IN_GOOGLE = "Se connecter avec Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Se connecter avec Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Sélectionner les dossiers système"
|
||||
SELECT_CHOOSE_ONE = "Faites un choix"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Dossiers"
|
|||
LABEL_ACCOUNTS_NAME = "Comptes"
|
||||
LABEL_IDENTITY_NAME = "Identité"
|
||||
LABEL_IDENTITIES_NAME = "Identities"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Thèmes"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Mot de passe"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Répondre à"
|
|||
LABEL_SIGNATURE = "Signature"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Ajouter votre signature à tous les messages sortants"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Général"
|
||||
LABEL_LANGUAGE = "Langue"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Cet ID sociale n'est pas encore assigné
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Cet ID sociale n'est pas encore assigné à une adresse email. Connectez-vous en utilisant les informations d'identification du courrier électronique et activer cette fonctionnalité dans les paramètres de votre compte."
|
||||
DOMAIN_NOT_ALLOWED = "Ce domaine n'est pas autorisé"
|
||||
ACCOUNT_NOT_ALLOWED = "Ce compte n'est pas autorisé"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
|
||||
CANT_GET_MESSAGE_LIST = "Impossible d'obtenir la liste des messages"
|
||||
CANT_GET_MESSAGE = "Impossible d'obtenir le message"
|
||||
CANT_DELETE_MESSAGE = "Impossible de supprimer le message"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "E-mail"
|
|||
LABEL_LOGIN = "Belépés"
|
||||
LABEL_PASSWORD = "Jelszó"
|
||||
LABEL_SIGN_ME = "Megjegyzés"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Belépés"
|
||||
TITLE_SIGN_IN_GOOGLE = "Belépés Google használatával"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Belépés Facebook használatával"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Select system folders"
|
||||
SELECT_CHOOSE_ONE = "Choose one"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Könyvtárak"
|
|||
LABEL_ACCOUNTS_NAME = "Fiókok"
|
||||
LABEL_IDENTITY_NAME = "Identitás"
|
||||
LABEL_IDENTITIES_NAME = "Identitások"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Közösség"
|
||||
LABEL_THEMES_NAME = "Sablonok"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Jelszó"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reply-To"
|
|||
LABEL_SIGNATURE = "Signature"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "General"
|
||||
LABEL_LANGUAGE = "Language"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
|
||||
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"
|
||||
CANT_GET_MESSAGE_LIST = "Can't get message list"
|
||||
CANT_GET_MESSAGE = "Can't get message"
|
||||
CANT_DELETE_MESSAGE = "Can't delete message"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Netfang"
|
|||
LABEL_LOGIN = "Innskrá"
|
||||
LABEL_PASSWORD = "Lykilorð"
|
||||
LABEL_SIGN_ME = "Muna"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Innskrá"
|
||||
TITLE_SIGN_IN_GOOGLE = "Innskráning með Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Innskráning með Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Veldu kerfismöppur"
|
||||
SELECT_CHOOSE_ONE = "Veldu eitt"
|
||||
|
|
@ -321,7 +327,8 @@ LABEL_FOLDERS_NAME = "Möppur"
|
|||
LABEL_ACCOUNTS_NAME = "Aðgangar"
|
||||
LABEL_IDENTITY_NAME = "Identity"
|
||||
LABEL_IDENTITIES_NAME = "Identities"
|
||||
LABEL_SOCIAL_NAME = "Félagsmiðlar"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAMELABEL_SOCIAL_NAME = "Félagsmiðlar"
|
||||
LABEL_THEMES_NAME = "Þemur"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Lykilorð"
|
||||
LABEL_OPEN_PGP_NAME = "OpenPGP"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Svara"
|
|||
LABEL_SIGNATURE = "Undirskrift"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Almennt"
|
||||
LABEL_LANGUAGE = "Tungumál"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Þetta samfélags ID er ekki úthlutað
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Þetta samfélags ID er ekki úthlutað á neitt netfang ennþá. Skráðu þig inn með netfangs upplýsingum og bættu þessum valmöguleika við í aðgangsstillingum."
|
||||
DOMAIN_NOT_ALLOWED = "Lén ekki leyft"
|
||||
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"
|
||||
CANT_GET_MESSAGE_LIST = "Get ekki sótt bréfa lista"
|
||||
CANT_GET_MESSAGE = "Get ekki sótt bréf"
|
||||
CANT_DELETE_MESSAGE = "Get ekki eytt bréfi"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
|
|||
LABEL_LOGIN = "Login"
|
||||
LABEL_PASSWORD = "Password"
|
||||
LABEL_SIGN_ME = "Ricordami"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Accedi"
|
||||
TITLE_SIGN_IN_GOOGLE = "Accedi usando Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Accedi usando Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Seleziona le cartelle di sistema"
|
||||
SELECT_CHOOSE_ONE = "Selezionane una"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Cartelle"
|
|||
LABEL_ACCOUNTS_NAME = "Account"
|
||||
LABEL_IDENTITY_NAME = "Identità"
|
||||
LABEL_IDENTITIES_NAME = "Identità"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Temi"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Password"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Rispondi a"
|
|||
LABEL_SIGNATURE = "Firma"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Aggiungi la firma a tutti i messaggi in uscita"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Generali"
|
||||
LABEL_LANGUAGE = "Lingua"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Questo ID sociale non è collegato ad alc
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Questo ID sociale non è collegato ad alcun indirizzo email. Entra utilizzando le credenziali delle email e abilita questa funzionalità nelle impostazioni."
|
||||
DOMAIN_NOT_ALLOWED = "Il dominio non è autorizzato"
|
||||
ACCOUNT_NOT_ALLOWED = "L'account non è autorizzato"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
|
||||
CANT_GET_MESSAGE_LIST = "Impossibile ottenere la lista dei messaggi"
|
||||
CANT_GET_MESSAGE = "Impossibile ottenere il messaggio"
|
||||
CANT_DELETE_MESSAGE = "Impossibile cancellare il messaggio"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "メールアドレス"
|
|||
LABEL_LOGIN = "ログイン"
|
||||
LABEL_PASSWORD = "パスワード"
|
||||
LABEL_SIGN_ME = "サインイン状態を保持する"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "サインイン"
|
||||
TITLE_SIGN_IN_GOOGLE = "Googleアカウントでログイン"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Facebookアカウントでログイン"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Select system folders"
|
||||
SELECT_CHOOSE_ONE = "Choose one"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "フォルダ"
|
|||
LABEL_ACCOUNTS_NAME = "アカウント"
|
||||
LABEL_IDENTITY_NAME = "Identity"
|
||||
LABEL_IDENTITIES_NAME = "表示名"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "テーマ"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Password"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reply-To"
|
|||
LABEL_SIGNATURE = "署名"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "すべての送信メッセージに署名を追加する"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "全般"
|
||||
LABEL_LANGUAGE = "言語"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
|
||||
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"
|
||||
CANT_GET_MESSAGE_LIST = "Can't get message list"
|
||||
CANT_GET_MESSAGE = "Can't get message"
|
||||
CANT_DELETE_MESSAGE = "Can't delete message"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "이메일 주소"
|
|||
LABEL_LOGIN = "아이디"
|
||||
LABEL_PASSWORD = "비밀번호"
|
||||
LABEL_SIGN_ME = "아이디 기억"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "로그인"
|
||||
TITLE_SIGN_IN_GOOGLE = "구글 계정으로 로그인"
|
||||
TITLE_SIGN_IN_FACEBOOK = "페이스북 계정으로 로그인"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "메일함을 지정해주세요"
|
||||
SELECT_CHOOSE_ONE = "선택해주세요"
|
||||
|
|
@ -318,6 +324,7 @@ LABEL_FOLDERS_NAME = "메일함"
|
|||
LABEL_ACCOUNTS_NAME = "계정"
|
||||
LABEL_IDENTITY_NAME = "신원"
|
||||
LABEL_IDENTITIES_NAME = "신원"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "소셜"
|
||||
LABEL_THEMES_NAME = "테마"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "비밀번호"
|
||||
|
|
@ -331,6 +338,21 @@ LABEL_REPLY_TO = "답장"
|
|||
LABEL_SIGNATURE = "서명"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "일반"
|
||||
LABEL_LANGUAGE = "언어"
|
||||
|
|
@ -449,6 +471,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "이 트위터 아이디는 아직 어떠
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "이 구글 아이디는 아직 어떠한 이메일 계정에도 연결되지 않았습니다. 이메일 계정으로 로그인 하신 후 소셜 설정에서 소셜 계정에 연결해주세요."
|
||||
DOMAIN_NOT_ALLOWED = "허용된 도메인이 아닙니다."
|
||||
ACCOUNT_NOT_ALLOWED = "허용된 계정이 아닙니다."
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
|
||||
CANT_GET_MESSAGE_LIST = "메시지 목록을 불러 올 수 없습니다."
|
||||
CANT_GET_MESSAGE = "메시지를 가져올 수 없습니다."
|
||||
CANT_DELETE_MESSAGE = "메시지를 삭제할 수 없습니다."
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "E-pasts"
|
|||
LABEL_LOGIN = "Ielogoties"
|
||||
LABEL_PASSWORD = "Parole"
|
||||
LABEL_SIGN_ME = "Atcerēties mani"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Ielogoties"
|
||||
TITLE_SIGN_IN_GOOGLE = "Ielogoties izmantojot Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Ielogoties izmantojot Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Izvēlaties sistēmas mapes"
|
||||
SELECT_CHOOSE_ONE = "Izvēlaties vienu"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Mapes"
|
|||
LABEL_ACCOUNTS_NAME = "Konti"
|
||||
LABEL_IDENTITY_NAME = "Identity"
|
||||
LABEL_IDENTITIES_NAME = "Identities"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Sociālie"
|
||||
LABEL_THEMES_NAME = "Tēmas"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Parole"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Atbildēt uz"
|
|||
LABEL_SIGNATURE = "Paraksts"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Vispārējie"
|
||||
LABEL_LANGUAGE = "Valoda"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Sociālā ID nav pievienot nevienam e-pas
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Sociālā ID nav pievienot nevienam e-pastam. Ielogojaties ar e-pastu un ieslēdzat šādu iespeju uzstādijumos."
|
||||
DOMAIN_NOT_ALLOWED = "Šis domēns nav atļauts"
|
||||
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"
|
||||
CANT_GET_MESSAGE_LIST = "Nevar ielādēt ziņojumu sarakstu"
|
||||
CANT_GET_MESSAGE = "Nevar ielādēt ziņojumu"
|
||||
CANT_DELETE_MESSAGE = "Nevar izdzēst ziņojumu"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
|
|||
LABEL_LOGIN = "Login"
|
||||
LABEL_PASSWORD = "Paswoord"
|
||||
LABEL_SIGN_ME = "Aangemeld blijven"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Aanmelden"
|
||||
TITLE_SIGN_IN_GOOGLE = "Aanmelden met Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Aanmelden met Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Selecteer systeem mappen"
|
||||
SELECT_CHOOSE_ONE = "Kies één"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Mappen"
|
|||
LABEL_ACCOUNTS_NAME = "Accounts"
|
||||
LABEL_IDENTITY_NAME = "Identity"
|
||||
LABEL_IDENTITIES_NAME = "Identities"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Themes"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Paswoord"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Antwoorden naar"
|
|||
LABEL_SIGNATURE = "Handtekening"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Algemeen"
|
||||
LABEL_LANGUAGE = "Taal"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
|
||||
DOMAIN_NOT_ALLOWED = "domein is niet toegestaan"
|
||||
ACCOUNT_NOT_ALLOWED = "Account is niet toegestaan"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
|
||||
CANT_GET_MESSAGE_LIST = "Can't get message list"
|
||||
CANT_GET_MESSAGE = "Can't get message"
|
||||
CANT_DELETE_MESSAGE = "Kan bericht niet verwijderen"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Epost"
|
|||
LABEL_LOGIN = "Logg inn"
|
||||
LABEL_PASSWORD = "Passord"
|
||||
LABEL_SIGN_ME = "Husk meg"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Logg Inn"
|
||||
TITLE_SIGN_IN_GOOGLE = "Logg inn Med Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Logg inn MED Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Velg systemmapper"
|
||||
SELECT_CHOOSE_ONE = "Velg en"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Mapper"
|
|||
LABEL_ACCOUNTS_NAME = "Kontoer"
|
||||
LABEL_IDENTITY_NAME = "Identitet"
|
||||
LABEL_IDENTITIES_NAME = "identiteter"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Sosialt"
|
||||
LABEL_THEMES_NAME = "Tema"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Passord"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Svar til"
|
|||
LABEL_SIGNATURE = "Signatur"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Generelt"
|
||||
LABEL_LANGUAGE = "Språk"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Denne sosiale ID er ikke tildelt for alle
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Denne sosiale ID er ikke tildelt for alle e-post -konto ennå. Logg inn med e- legitimasjon og legge aktivere denne funksjonen i kontoinnstillingene ."
|
||||
DOMAIN_NOT_ALLOWED = "Domene er ikke tillatt"
|
||||
ACCOUNT_NOT_ALLOWED = "Kontoen er ikke tillatt"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
|
||||
CANT_GET_MESSAGE_LIST = "Kan ikke få meldingsliste"
|
||||
CANT_GET_MESSAGE = "Kan ikke få meldingen"
|
||||
CANT_DELETE_MESSAGE = "Kan ikke slette meldingen"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Login/Email"
|
|||
LABEL_LOGIN = "Login"
|
||||
LABEL_PASSWORD = "Hasło"
|
||||
LABEL_SIGN_ME = "Zapamiętaj mnie"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Zaloguj"
|
||||
TITLE_SIGN_IN_GOOGLE = "Zaloguj się przez Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Zaloguj się przez Facebook"
|
||||
|
|
@ -266,6 +267,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Wybierz foldery systemowe"
|
||||
SELECT_CHOOSE_ONE = "Wybierz"
|
||||
|
|
@ -320,7 +326,8 @@ LABEL_FOLDERS_NAME = "Foldery"
|
|||
LABEL_ACCOUNTS_NAME = "Konta"
|
||||
LABEL_IDENTITY_NAME = "Tożsamość"
|
||||
LABEL_IDENTITIES_NAME = "Tożsamości"
|
||||
LABEL_SOCIAL_NAME = "Social Media"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Wygląd"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Hasło"
|
||||
LABEL_OPEN_PGP_NAME = "OpenPGP"
|
||||
|
|
@ -333,6 +340,21 @@ LABEL_REPLY_TO = "Zwrotny adres e-mail"
|
|||
LABEL_SIGNATURE = "Podpis"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Dadawaj podpis do wszystkich wysyłanych wiadomości"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Ogólne"
|
||||
LABEL_LANGUAGE = "Język"
|
||||
|
|
@ -451,6 +473,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "To społecznościowe ID na Twitter nie je
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "To społecznościowe ID (Google Acounts) nie jest powiązane z żadnym kontem pocztowym. W związku z tym zaloguj się używając danych swojego konta e-mail i skonfiguruj tę opcję w Ustawieniach > Social Media"
|
||||
DOMAIN_NOT_ALLOWED = "Domena nie dozwolona"
|
||||
ACCOUNT_NOT_ALLOWED = "Konto nie jest dozwolone"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
|
||||
CANT_GET_MESSAGE_LIST = "Nie jest możliwe pobranie listy wiadomości"
|
||||
CANT_GET_MESSAGE = "Nie można pobrać wiadomości"
|
||||
CANT_DELETE_MESSAGE = "Nie można usunąć wiadomości"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
|
|||
LABEL_LOGIN = "Usuário"
|
||||
LABEL_PASSWORD = "Senha"
|
||||
LABEL_SIGN_ME = "Lembre-me"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Entre"
|
||||
TITLE_SIGN_IN_GOOGLE = "Entre com a conta do Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Entre com a conta do Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Selecione as pastas do sistema"
|
||||
SELECT_CHOOSE_ONE = "Escolha um"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Pastas"
|
|||
LABEL_ACCOUNTS_NAME = "Contas"
|
||||
LABEL_IDENTITY_NAME = "Identidade"
|
||||
LABEL_IDENTITIES_NAME = "Identidades"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Temas"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Senha"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reenviar para"
|
|||
LABEL_SIGNATURE = "Assinatura"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Adicionar a sua assinatura a todas as mensagens de saída"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Geral"
|
||||
LABEL_LANGUAGE = "Linguagem"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Esta ID social não é atribuído para qu
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Esta ID social não é atribuído para qualquer conta de e-mail ainda. Entrar usando credenciais de e-mail e adicione ativar esse recurso em configurações de conta."
|
||||
DOMAIN_NOT_ALLOWED = "Este domínio não é permitido"
|
||||
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"
|
||||
CANT_GET_MESSAGE_LIST = "Não é possível obter a lista de mensagens"
|
||||
CANT_GET_MESSAGE = "Não é possível obter a mensagem"
|
||||
CANT_DELETE_MESSAGE = "Não é possível excluir a mensagem"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
|
|||
LABEL_LOGIN = "Usuário"
|
||||
LABEL_PASSWORD = "Senha"
|
||||
LABEL_SIGN_ME = "Lembre-me"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Entre"
|
||||
TITLE_SIGN_IN_GOOGLE = "Entre com a conta do Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Entre com a conta do Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Selecione as pastas do sistema"
|
||||
SELECT_CHOOSE_ONE = "Escolha um"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Pastas"
|
|||
LABEL_ACCOUNTS_NAME = "Contas"
|
||||
LABEL_IDENTITY_NAME = "Identidade"
|
||||
LABEL_IDENTITIES_NAME = "Identidades"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Temas"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Senha"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reenviar para"
|
|||
LABEL_SIGNATURE = "Assinatura"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Adicionar a sua assinatura a todas as mensagens de saída"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Geral"
|
||||
LABEL_LANGUAGE = "Linguagem"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Esta ID social não é atribuído para qu
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Esta ID social não é atribuído para qualquer conta de e-mail ainda. Entrar usando credenciais de e-mail e adicione ativar esse recurso em configurações de conta."
|
||||
DOMAIN_NOT_ALLOWED = "Este domínio não é permitido"
|
||||
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"
|
||||
CANT_GET_MESSAGE_LIST = "Não é possível obter a lista de mensagens"
|
||||
CANT_GET_MESSAGE = "Não é possível obter a mensagem"
|
||||
CANT_DELETE_MESSAGE = "Não é possível excluir a mensagem"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
|
|||
LABEL_LOGIN = "Conectare"
|
||||
LABEL_PASSWORD = "Parolă"
|
||||
LABEL_SIGN_ME = "Ține-mă minte"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "Conectare"
|
||||
TITLE_SIGN_IN_GOOGLE = "Войти, используя Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Войти, используя Facebook"
|
||||
|
|
@ -266,6 +267,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Selectați dosarul de sistem"
|
||||
SELECT_CHOOSE_ONE = "Nu a fost încă selectat"
|
||||
|
|
@ -320,6 +326,7 @@ LABEL_FOLDERS_NAME = "Dosare"
|
|||
LABEL_ACCOUNTS_NAME = "Conturi"
|
||||
LABEL_IDENTITY_NAME = "Profil"
|
||||
LABEL_IDENTITIES_NAME = "Profiluri"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "Subiecte"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Parolă"
|
||||
|
|
@ -333,6 +340,21 @@ LABEL_REPLY_TO = "Răspunde la"
|
|||
LABEL_SIGNATURE = "Semnătură"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Adăugă semnătura la toate mesajele trimise"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Setări de bază"
|
||||
LABEL_LANGUAGE = "Limbă"
|
||||
|
|
@ -451,6 +473,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Pentru acest utilizator nu sa asigurat î
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Pentru acest utilizator nu sa asigurat încă un cont de e-mail. Conectați-vă cu contul dvs. de e-mail și activați această caracteristică în setările."
|
||||
DOMAIN_NOT_ALLOWED = "Domeniul nu apartine de Wey5"
|
||||
ACCOUNT_NOT_ALLOWED = "Contul nu are permisiunea de conectare"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
|
||||
CANT_GET_MESSAGE_LIST = "Nu găsesc o lista de scrisori"
|
||||
CANT_GET_MESSAGE = "Nu pot obține scrisoarea. Încercați din nou"
|
||||
CANT_DELETE_MESSAGE = "Nu pot șterge scrisoarea. Încercați din nou"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "Почта"
|
|||
LABEL_LOGIN = "Логин"
|
||||
LABEL_PASSWORD = "Пароль"
|
||||
LABEL_SIGN_ME = "Запомнить меня"
|
||||
LABEL_VERIFICATION_CODE = "Код подтверждения"
|
||||
BUTTON_SIGN_IN = "Войти"
|
||||
TITLE_SIGN_IN_GOOGLE = "Войти, используя Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Войти, используя Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Подпись"
|
|||
BUTTON_ENCRYPT = "Шифрование"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Подпись и шифрование"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "Тест двухфакторной верификации"
|
||||
LABEL_CODE = "Код"
|
||||
BUTTON_TEST = "Тест"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "Выберите системые папки"
|
||||
SELECT_CHOOSE_ONE = "Еще не выбрана"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Папки"
|
|||
LABEL_ACCOUNTS_NAME = "Аккаунты"
|
||||
LABEL_IDENTITY_NAME = "Профиль"
|
||||
LABEL_IDENTITIES_NAME = "Профили"
|
||||
LABEL_SECURITY_NAME = "Безопасность"
|
||||
LABEL_SOCIAL_NAME = "Социальные"
|
||||
LABEL_THEMES_NAME = "Темы"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "Пароль"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Отвечать на"
|
|||
LABEL_SIGNATURE = "Подпись"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "Двухфакторная верификация (Google Authenticator)"
|
||||
LABEL_ENABLE_TWO_FACTOR = "Включить двухфакторную верификацию"
|
||||
LABEL_TWO_FACTOR_USER = "Пользователь"
|
||||
LABEL_TWO_FACTOR_STATUS = "Статус"
|
||||
LABEL_TWO_FACTOR_SECRET = "Секретный ключ"
|
||||
LABEL_TWO_FACTOR_BACKUP_CODES = "Резервные коды"
|
||||
BUTTON_CREATE = "Создать новый секретный ключ"
|
||||
BUTTON_CLEAR = "Очистить"
|
||||
BUTTON_TEST = "Тест"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Настроен"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Не настроен"
|
||||
TWO_FACTOR_SECRET_DESC = "Используйте эту информацию для настройки Gooogle Authenticator клиента (или другого TOTP клиента) с помощью прилагаемого QR-кода, или вручную."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "Если вы не можете получить коды через Google Authenticator, вы можете использовать резервные коды, чтобы войти. После того как вы использовали резервный код для входа в систему, он становится неактивным."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "Основные Настройки"
|
||||
LABEL_LANGUAGE = "Язык"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "К данному социальному
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "К данному социальному пользователю еще не прикреплен почтовый аккаунт. Войдите в систему под своим почтовым аккаунтом и включите эту возможность в настройках."
|
||||
DOMAIN_NOT_ALLOWED = "Данный домен не разрешен"
|
||||
ACCOUNT_NOT_ALLOWED = "Данный аккаунт не разрешен"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Необходима двухфакторная верификация"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Ошибка двухфакторной верификации"
|
||||
CANT_GET_MESSAGE_LIST = "Не могу получить список писем"
|
||||
CANT_GET_MESSAGE = "Не могу получить письмо"
|
||||
CANT_DELETE_MESSAGE = "Не могу удалить письмо"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ LABEL_EMAIL = "邮件"
|
|||
LABEL_LOGIN = "登陆"
|
||||
LABEL_PASSWORD = "密码"
|
||||
LABEL_SIGN_ME = "记住我"
|
||||
LABEL_VERIFICATION_CODE = "Verification Code"
|
||||
BUTTON_SIGN_IN = "登入"
|
||||
TITLE_SIGN_IN_GOOGLE = "Sign In using Google"
|
||||
TITLE_SIGN_IN_FACEBOOK = "Sign In using Facebook"
|
||||
|
|
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
|
|||
BUTTON_ENCRYPT = "Encrypt"
|
||||
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
|
||||
|
||||
[POPUPS_TWO_FACTOR_TEST]
|
||||
TITLE_TEST_CODE = "2-Step verification test"
|
||||
LABEL_CODE = "Code"
|
||||
BUTTON_TEST = "Test"
|
||||
|
||||
[POPUPS_SYSTEM_FOLDERS]
|
||||
TITLE_SYSTEM_FOLDERS = "选择系统文件夹"
|
||||
SELECT_CHOOSE_ONE = "选择一个"
|
||||
|
|
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "文件夹"
|
|||
LABEL_ACCOUNTS_NAME = "账户"
|
||||
LABEL_IDENTITY_NAME = "签名"
|
||||
LABEL_IDENTITIES_NAME = "签名"
|
||||
LABEL_SECURITY_NAME = "Security"
|
||||
LABEL_SOCIAL_NAME = "Social"
|
||||
LABEL_THEMES_NAME = "主题"
|
||||
LABEL_CHANGE_PASSWORD_NAME = "密码"
|
||||
|
|
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "回复"
|
|||
LABEL_SIGNATURE = "签名"
|
||||
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
|
||||
|
||||
[SETTINGS_SECURITY]
|
||||
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
|
||||
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 New Secret"
|
||||
BUTTON_CLEAR = "Clear"
|
||||
BUTTON_TEST = "Test"
|
||||
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
|
||||
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
|
||||
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
|
||||
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After you’ve used a backup code to sign in, it will become inactive."
|
||||
|
||||
[SETTINGS_GENERAL]
|
||||
LEGEND_GENERAL = "一般设置"
|
||||
LABEL_LANGUAGE = "语言"
|
||||
|
|
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
|
|||
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
|
||||
DOMAIN_NOT_ALLOWED = "域不允许"
|
||||
ACCOUNT_NOT_ALLOWED = "账户不允许"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
|
||||
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
|
||||
CANT_GET_MESSAGE_LIST = "无法获取邮件列表"
|
||||
CANT_GET_MESSAGE = "无法获取邮件"
|
||||
CANT_DELETE_MESSAGE = "无法删除邮件"
|
||||
|
|
|
|||
|
|
@ -6607,6 +6607,9 @@ html.rl-no-preview-pane #rl-right .ui-resizable-handle {
|
|||
max-height: 500px;
|
||||
overflow: auto;
|
||||
}
|
||||
.popups .b-two-factor-test-content .modal-header {
|
||||
background-color: #fff;
|
||||
}
|
||||
.popups .b-identity-content .modal-header {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
|
|
|||
2
rainloop/v/0.0.0/static/css/app.min.css
vendored
2
rainloop/v/0.0.0/static/css/app.min.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -657,6 +657,9 @@ Enums.Notification = {
|
|||
'DomainNotAllowed': 109,
|
||||
'AccountNotAllowed': 110,
|
||||
|
||||
'AccountTwoFactorAuthRequired': 120,
|
||||
'AccountTwoFactorAuthError': 121,
|
||||
|
||||
'CantGetMessageList': 201,
|
||||
'CantGetMessage': 202,
|
||||
'CantDeleteMessage': 203,
|
||||
|
|
@ -1266,6 +1269,9 @@ Utils.initNotificationLanguage = function ()
|
|||
NotificationI18N[Enums.Notification.DomainNotAllowed] = Utils.i18n('NOTIFICATIONS/DOMAIN_NOT_ALLOWED');
|
||||
NotificationI18N[Enums.Notification.AccountNotAllowed] = Utils.i18n('NOTIFICATIONS/ACCOUNT_NOT_ALLOWED');
|
||||
|
||||
NotificationI18N[Enums.Notification.AccountTwoFactorAuthRequired] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_REQUIRED');
|
||||
NotificationI18N[Enums.Notification.AccountTwoFactorAuthError] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_ERROR');
|
||||
|
||||
NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST');
|
||||
NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE');
|
||||
NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE');
|
||||
|
|
@ -1370,14 +1376,14 @@ Utils.delegateRun = function (oObject, sMethodName, aParameters, nDelay)
|
|||
Utils.killCtrlAandS = function (oEvent)
|
||||
{
|
||||
oEvent = oEvent || window.event;
|
||||
if (oEvent)
|
||||
if (oEvent && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey)
|
||||
{
|
||||
var
|
||||
oSender = oEvent.target || oEvent.srcElement,
|
||||
iKey = oEvent.keyCode || oEvent.which
|
||||
;
|
||||
|
||||
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.S)
|
||||
if (iKey === Enums.EventKeyCode.S)
|
||||
{
|
||||
oEvent.preventDefault();
|
||||
return;
|
||||
|
|
@ -1388,7 +1394,7 @@ Utils.killCtrlAandS = function (oEvent)
|
|||
return;
|
||||
}
|
||||
|
||||
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.A)
|
||||
if (iKey === Enums.EventKeyCode.A)
|
||||
{
|
||||
if (window.getSelection)
|
||||
{
|
||||
|
|
@ -2415,51 +2421,6 @@ Utils.selectElement = function (element)
|
|||
/* jshint onevar: true */
|
||||
};
|
||||
|
||||
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
|
||||
{
|
||||
if (window.openpgp && RL)
|
||||
{
|
||||
var
|
||||
oOpenpgpKeyring = RL.data().openpgpKeyring,
|
||||
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
|
||||
;
|
||||
|
||||
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
|
||||
0 < oImported.keys.length)
|
||||
{
|
||||
_.each(oImported.keys, function (oPrivKey) {
|
||||
if (oPrivKey)
|
||||
{
|
||||
window.console.log(oPrivKey);
|
||||
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
|
||||
// if (oKey && oKey[0])
|
||||
// {
|
||||
// if (oKey[0].isPublic())
|
||||
// {
|
||||
// oPrivKey.update(oKey[0]);
|
||||
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
|
||||
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// oKey[0].update(oPrivKey);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// oOpenpgpKeyring.importKey(oPrivKey.armored);
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
oOpenpgpKeyring.store();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Base64 encode / decode
|
||||
// http://www.webtoolkit.info/
|
||||
|
||||
|
|
@ -6087,6 +6048,7 @@ function AdminSecurity()
|
|||
{
|
||||
this.csrfProtection = ko.observable(!!RL.settingsGet('UseTokenProtection'));
|
||||
this.openPGP = ko.observable(!!RL.settingsGet('OpenPGP'));
|
||||
this.allowTwoFactorAuth = ko.observable(!!RL.settingsGet('AllowTwoFactorAuth'));
|
||||
|
||||
this.adminLogin = ko.observable(RL.settingsGet('AdminLogin'));
|
||||
this.adminPassword = ko.observable('');
|
||||
|
|
@ -6152,6 +6114,12 @@ AdminSecurity.prototype.onBuild = function ()
|
|||
'OpenPGP': bValue ? '1' : '0'
|
||||
});
|
||||
});
|
||||
|
||||
this.allowTwoFactorAuth.subscribe(function (bValue) {
|
||||
RL.remote().saveAdminConfig(Utils.emptyFunction, {
|
||||
'AllowTwoFactorAuth': bValue ? '1' : '0'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
AdminSecurity.prototype.onHide = function ()
|
||||
|
|
|
|||
8
rainloop/v/0.0.0/static/js/admin.min.js
vendored
8
rainloop/v/0.0.0/static/js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -661,6 +661,9 @@ Enums.Notification = {
|
|||
'DomainNotAllowed': 109,
|
||||
'AccountNotAllowed': 110,
|
||||
|
||||
'AccountTwoFactorAuthRequired': 120,
|
||||
'AccountTwoFactorAuthError': 121,
|
||||
|
||||
'CantGetMessageList': 201,
|
||||
'CantGetMessage': 202,
|
||||
'CantDeleteMessage': 203,
|
||||
|
|
@ -1270,6 +1273,9 @@ Utils.initNotificationLanguage = function ()
|
|||
NotificationI18N[Enums.Notification.DomainNotAllowed] = Utils.i18n('NOTIFICATIONS/DOMAIN_NOT_ALLOWED');
|
||||
NotificationI18N[Enums.Notification.AccountNotAllowed] = Utils.i18n('NOTIFICATIONS/ACCOUNT_NOT_ALLOWED');
|
||||
|
||||
NotificationI18N[Enums.Notification.AccountTwoFactorAuthRequired] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_REQUIRED');
|
||||
NotificationI18N[Enums.Notification.AccountTwoFactorAuthError] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_ERROR');
|
||||
|
||||
NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST');
|
||||
NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE');
|
||||
NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE');
|
||||
|
|
@ -1374,14 +1380,14 @@ Utils.delegateRun = function (oObject, sMethodName, aParameters, nDelay)
|
|||
Utils.killCtrlAandS = function (oEvent)
|
||||
{
|
||||
oEvent = oEvent || window.event;
|
||||
if (oEvent)
|
||||
if (oEvent && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey)
|
||||
{
|
||||
var
|
||||
oSender = oEvent.target || oEvent.srcElement,
|
||||
iKey = oEvent.keyCode || oEvent.which
|
||||
;
|
||||
|
||||
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.S)
|
||||
if (iKey === Enums.EventKeyCode.S)
|
||||
{
|
||||
oEvent.preventDefault();
|
||||
return;
|
||||
|
|
@ -1392,7 +1398,7 @@ Utils.killCtrlAandS = function (oEvent)
|
|||
return;
|
||||
}
|
||||
|
||||
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.A)
|
||||
if (iKey === Enums.EventKeyCode.A)
|
||||
{
|
||||
if (window.getSelection)
|
||||
{
|
||||
|
|
@ -2419,51 +2425,6 @@ Utils.selectElement = function (element)
|
|||
/* jshint onevar: true */
|
||||
};
|
||||
|
||||
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
|
||||
{
|
||||
if (window.openpgp && RL)
|
||||
{
|
||||
var
|
||||
oOpenpgpKeyring = RL.data().openpgpKeyring,
|
||||
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
|
||||
;
|
||||
|
||||
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
|
||||
0 < oImported.keys.length)
|
||||
{
|
||||
_.each(oImported.keys, function (oPrivKey) {
|
||||
if (oPrivKey)
|
||||
{
|
||||
window.console.log(oPrivKey);
|
||||
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
|
||||
// if (oKey && oKey[0])
|
||||
// {
|
||||
// if (oKey[0].isPublic())
|
||||
// {
|
||||
// oPrivKey.update(oKey[0]);
|
||||
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
|
||||
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// oKey[0].update(oPrivKey);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// oOpenpgpKeyring.importKey(oPrivKey.armored);
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
oOpenpgpKeyring.store();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Base64 encode / decode
|
||||
// http://www.webtoolkit.info/
|
||||
|
||||
|
|
@ -3841,7 +3802,7 @@ NewHtmlEditorWrapper.prototype.init = function ()
|
|||
{
|
||||
self.editor.on('instanceReady', function () {
|
||||
|
||||
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65/* A */, 'selectAll');
|
||||
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65 /* A */, 'selectAll');
|
||||
|
||||
self.fOnReady();
|
||||
self.__resizable = true;
|
||||
|
|
@ -7355,15 +7316,14 @@ FolderModel.prototype.initComputed = function ()
|
|||
{
|
||||
return '' + iCount;
|
||||
}
|
||||
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.SentItems !== iType)
|
||||
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.Archive !== iType && Enums.FolderType.SentItems !== iType)
|
||||
{
|
||||
return '' + iUnread;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
// return 0 < iUnread && (Enums.FolderType.Inbox === iType || Enums.FolderType.Spam === iType) ? '' + iUnread :
|
||||
// (0 < iCount && Enums.FolderType.Draft === iType ? '' + iCount : '');
|
||||
|
||||
}, this);
|
||||
|
||||
this.canBeDeleted = ko.computed(function () {
|
||||
|
|
@ -8880,12 +8840,12 @@ PopupsComposeViewModel.prototype.onBuild = function ()
|
|||
|
||||
if (oEvent && self.modalVisibility() && RL.data().useKeyboardShortcuts())
|
||||
{
|
||||
if (self.bAllowCtrlS && oEvent.ctrlKey && Enums.EventKeyCode.S === oEvent.keyCode)
|
||||
if (self.bAllowCtrlS && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.S === oEvent.keyCode)
|
||||
{
|
||||
self.saveCommand();
|
||||
bResult = false;
|
||||
}
|
||||
else if (oEvent.ctrlKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
|
||||
else if (oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
|
||||
{
|
||||
self.sendCommand();
|
||||
bResult = false;
|
||||
|
|
@ -10904,6 +10864,60 @@ PopupsLanguagesViewModel.prototype.changeLanguage = function (sLang)
|
|||
this.cancelCommand();
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends KnoinAbstractViewModel
|
||||
*/
|
||||
function PopupsTwoFactorTestViewModel()
|
||||
{
|
||||
KnoinAbstractViewModel.call(this, 'Popups', 'PopupsTwoFactorTest');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.code = ko.observable('');
|
||||
this.code.focused = ko.observable(false);
|
||||
this.code.status = ko.observable(null);
|
||||
|
||||
this.testing = ko.observable(false);
|
||||
|
||||
// commands
|
||||
this.testCode = Utils.createCommand(this, function () {
|
||||
|
||||
this.testing(true);
|
||||
RL.remote().testTwoFactor(function (sResult, oData) {
|
||||
|
||||
self.testing(false);
|
||||
self.code.status(Enums.StorageResultType.Success === sResult && oData && oData.Result ? true : false);
|
||||
|
||||
}, this.code());
|
||||
|
||||
}, function () {
|
||||
return '' !== this.code() && !this.testing();
|
||||
});
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
||||
Utils.extendAsViewModel('PopupsTwoFactorTestViewModel', PopupsTwoFactorTestViewModel);
|
||||
|
||||
PopupsTwoFactorTestViewModel.prototype.clearPopup = function ()
|
||||
{
|
||||
this.code('');
|
||||
this.code.focused(false);
|
||||
this.code.status(null);
|
||||
this.testing(false);
|
||||
};
|
||||
|
||||
PopupsTwoFactorTestViewModel.prototype.onShow = function ()
|
||||
{
|
||||
this.clearPopup();
|
||||
};
|
||||
|
||||
PopupsTwoFactorTestViewModel.prototype.onFocus = function ()
|
||||
{
|
||||
this.code.focused(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends KnoinAbstractViewModel
|
||||
|
|
@ -11039,6 +11053,11 @@ function LoginViewModel()
|
|||
this.password = ko.observable('');
|
||||
this.signMe = ko.observable(false);
|
||||
|
||||
this.additionalCode = ko.observable('');
|
||||
this.additionalCode.error = ko.observable(false);
|
||||
this.additionalCode.focused = ko.observable(false);
|
||||
this.additionalCode.visibility = ko.observable(false);
|
||||
|
||||
this.logoImg = Utils.trim(RL.settingsGet('LoginLogo'));
|
||||
this.loginDescription = Utils.trim(RL.settingsGet('LoginDescription'));
|
||||
this.logoCss = Utils.trim(RL.settingsGet('LoginCss'));
|
||||
|
|
@ -11053,6 +11072,8 @@ function LoginViewModel()
|
|||
|
||||
this.email.subscribe(function () {
|
||||
this.emailError(false);
|
||||
this.additionalCode('');
|
||||
this.additionalCode.visibility(false);
|
||||
}, this);
|
||||
|
||||
this.login.subscribe(function () {
|
||||
|
|
@ -11063,6 +11084,14 @@ function LoginViewModel()
|
|||
this.passwordError(false);
|
||||
}, this);
|
||||
|
||||
this.additionalCode.subscribe(function () {
|
||||
this.additionalCode.error(false);
|
||||
}, this);
|
||||
|
||||
this.additionalCode.visibility.subscribe(function () {
|
||||
this.additionalCode.error(false);
|
||||
}, this);
|
||||
|
||||
this.submitRequest = ko.observable(false);
|
||||
this.submitError = ko.observable('');
|
||||
|
||||
|
|
@ -11092,7 +11121,12 @@ function LoginViewModel()
|
|||
this.emailError('' === Utils.trim(this.email()));
|
||||
this.passwordError('' === Utils.trim(this.password()));
|
||||
|
||||
if (this.emailError() || this.passwordError())
|
||||
if (this.additionalCode.visibility())
|
||||
{
|
||||
this.additionalCode.error('' === Utils.trim(this.additionalCode()));
|
||||
}
|
||||
|
||||
if (this.emailError() || this.passwordError() || this.additionalCode.error())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -11105,12 +11139,28 @@ function LoginViewModel()
|
|||
{
|
||||
if (oData.Result)
|
||||
{
|
||||
RL.loginAndLogoutReload();
|
||||
if (oData.TwoFactorAuth)
|
||||
{
|
||||
this.additionalCode('');
|
||||
this.additionalCode.visibility(true);
|
||||
this.additionalCode.focused(true);
|
||||
|
||||
this.submitRequest(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RL.loginAndLogoutReload();
|
||||
}
|
||||
}
|
||||
else if (oData.ErrorCode)
|
||||
{
|
||||
this.submitRequest(false);
|
||||
this.submitError(Utils.getNotification(oData.ErrorCode));
|
||||
|
||||
if ('' === this.submitError())
|
||||
{
|
||||
this.submitError(Utils.getNotification(Enums.Notification.UnknownError));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -11124,7 +11174,8 @@ function LoginViewModel()
|
|||
}
|
||||
|
||||
}, this), this.email(), this.login(), this.password(), !!this.signMe(),
|
||||
this.bSendLanguage ? this.mainLanguage() : '');
|
||||
this.bSendLanguage ? this.mainLanguage() : '',
|
||||
this.additionalCode.visibility() ? this.additionalCode() : '');
|
||||
|
||||
return true;
|
||||
|
||||
|
|
@ -13314,6 +13365,125 @@ SettingsIdentities.prototype.onBuild = function (oDom)
|
|||
|
||||
}, 50);
|
||||
};
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function SettingsSecurity()
|
||||
{
|
||||
this.processing = ko.observable(false);
|
||||
this.clearing = ko.observable(false);
|
||||
|
||||
this.viewUser = ko.observable('');
|
||||
this.viewEnable = ko.observable(false);
|
||||
this.viewEnable.subs = true;
|
||||
this.twoFactorStatus = ko.observable(false);
|
||||
|
||||
this.viewSecret = ko.observable('');
|
||||
this.viewBackupCodes = ko.observable('');
|
||||
this.viewUrl = ko.observable('');
|
||||
|
||||
this.bFirst = true;
|
||||
|
||||
this.viewTwoFactorStatus = ko.computed(function () {
|
||||
Globals.langChangeTrigger();
|
||||
return Utils.i18n(
|
||||
this.twoFactorStatus() ?
|
||||
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_CONFIGURED_DESC' :
|
||||
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC'
|
||||
);
|
||||
}, this);
|
||||
|
||||
this.onResult = _.bind(this.onResult, this);
|
||||
}
|
||||
|
||||
Utils.addSettingsViewModel(SettingsSecurity, 'SettingsSecurity', 'SETTINGS_LABELS/LABEL_SECURITY_NAME', 'security');
|
||||
|
||||
SettingsSecurity.prototype.createTwoFactor = function ()
|
||||
{
|
||||
this.processing(true);
|
||||
RL.remote().createTwoFactor(this.onResult);
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.enableTwoFactor = function ()
|
||||
{
|
||||
this.processing(true);
|
||||
RL.remote().enableTwoFactor(this.onResult, this.viewEnable());
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.testTwoFactor = function ()
|
||||
{
|
||||
kn.showScreenPopup(PopupsTwoFactorTestViewModel);
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.clearTwoFactor = function ()
|
||||
{
|
||||
this.viewSecret('');
|
||||
this.viewBackupCodes('');
|
||||
this.viewUrl('');
|
||||
|
||||
this.clearing(true);
|
||||
RL.remote().clearTwoFactor(this.onResult);
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.onShow = function ()
|
||||
{
|
||||
this.viewSecret('');
|
||||
this.viewBackupCodes('');
|
||||
this.viewUrl('');
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.onResult = function (sResult, oData)
|
||||
{
|
||||
this.processing(false);
|
||||
this.clearing(false);
|
||||
|
||||
if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
|
||||
{
|
||||
this.viewUser(Utils.pString(oData.Result.User));
|
||||
this.viewEnable(!!oData.Result.Enable);
|
||||
this.twoFactorStatus(!!oData.Result.IsSet);
|
||||
|
||||
this.viewSecret(Utils.pString(oData.Result.Secret));
|
||||
this.viewBackupCodes(Utils.pString(oData.Result.BackupCodes).replace(/[\s]+/g, ' '));
|
||||
this.viewUrl(Utils.pString(oData.Result.Url));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.viewUser('');
|
||||
this.viewEnable(false);
|
||||
this.twoFactorStatus(false);
|
||||
|
||||
this.viewSecret('');
|
||||
this.viewBackupCodes('');
|
||||
this.viewUrl('');
|
||||
}
|
||||
|
||||
if (this.bFirst)
|
||||
{
|
||||
this.bFirst = false;
|
||||
var self = this;
|
||||
this.viewEnable.subscribe(function (bValue) {
|
||||
if (this.viewEnable.subs)
|
||||
{
|
||||
RL.remote().enableTwoFactor(function (sResult, oData) {
|
||||
if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result)
|
||||
{
|
||||
self.viewEnable.subs = false;
|
||||
self.viewEnable(false);
|
||||
self.viewEnable.subs = true;
|
||||
}
|
||||
}, bValue);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.onBuild = function ()
|
||||
{
|
||||
this.processing(true);
|
||||
RL.remote().getTwoFactor(this.onResult);
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
|
|
@ -14097,6 +14267,9 @@ function WebMailDataStorage()
|
|||
this.signatureToAll = ko.observable(false);
|
||||
this.replyTo = ko.observable('');
|
||||
|
||||
// security
|
||||
this.enableTwoFactor = ko.observable(false);
|
||||
|
||||
// accounts
|
||||
this.accounts = ko.observableArray([]);
|
||||
this.accountsLoading = ko.observable(false).extend({'throttle': 100});
|
||||
|
|
@ -14459,6 +14632,7 @@ WebMailDataStorage.prototype.populateDataOnStart = function()
|
|||
this.replyTo(RL.settingsGet('ReplyTo'));
|
||||
this.signature(RL.settingsGet('Signature'));
|
||||
this.signatureToAll(!!RL.settingsGet('SignatureToAll'));
|
||||
this.enableTwoFactor(!!RL.settingsGet('EnableTwoFactor'));
|
||||
|
||||
this.lastFoldersHash = RL.local().get(Enums.ClientSideKeyName.FoldersLashHash) || '';
|
||||
|
||||
|
|
@ -15542,18 +15716,74 @@ WebMailAjaxRemoteStorage.prototype.folders = function (fCallback)
|
|||
* @param {string} sPassword
|
||||
* @param {boolean} bSignMe
|
||||
* @param {string=} sLanguage
|
||||
* @param {string=} sAdditionalCode
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage)
|
||||
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage, sAdditionalCode)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'Login', {
|
||||
'Email': sEmail,
|
||||
'Login': sLogin,
|
||||
'Password': sPassword,
|
||||
'Language': sLanguage || '',
|
||||
'AdditionalCode': sAdditionalCode || '',
|
||||
'SignMe': bSignMe ? '1' : '0'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.getTwoFactor = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'GetTwoFactorInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.createTwoFactor = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'CreateTwoFactorSecret');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.clearTwoFactor = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sCode
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.testTwoFactor = function (fCallback, sCode)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'TestTwoFactorInfo', {
|
||||
'Code': sCode
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {boolean} bEnable
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.enableTwoFactor = function (fCallback, bEnable)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'EnableTwoFactor', {
|
||||
'Enable': bEnable ? '1' : '0'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.clearTwoFactorInfo = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sEmail
|
||||
|
|
@ -18252,6 +18482,11 @@ RainLoopApp.prototype.bootstart = function ()
|
|||
Utils.removeSettingsViewModel(SettingsOpenPGP);
|
||||
}
|
||||
|
||||
if (!RL.settingsGet('AllowTwoFactorAuth'))
|
||||
{
|
||||
Utils.removeSettingsViewModel(SettingsSecurity);
|
||||
}
|
||||
|
||||
if (!bGoogle && !bFacebook && !bTwitter)
|
||||
{
|
||||
Utils.removeSettingsViewModel(SettingsSocialScreen);
|
||||
|
|
|
|||
16
rainloop/v/0.0.0/static/js/app.min.js
vendored
16
rainloop/v/0.0.0/static/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue