parent
f78300a12e
commit
e8f0471bcf
|
@ -21,6 +21,12 @@
|
|||
"browserNotCompatible" = "We've detected that your browser version is currently not supported on this site. Our recommendation is to use Firefox. Click on the link below to download the most current version of this browser.";
|
||||
"alternativeBrowsers" = "Alternatively, you can also use the following compatible browsers";
|
||||
"alternativeBrowserSafari" = "Alternatively, you can also use Safari.";
|
||||
|
||||
/* 2FA */
|
||||
"Verification Code" = "Verification Code";
|
||||
"Enter the 6-digit verification code from your Google Authenticator application." = "Enter the 6-digit verification code from your Google Authenticator application.";
|
||||
"You provided an invalid Google Authenticator key." = "You provided an invalid Google Authenticator key.";
|
||||
|
||||
"Download" = "Download";
|
||||
"Language" = "Language";
|
||||
"choose" = "Choose ...";
|
||||
|
|
|
@ -448,6 +448,11 @@
|
|||
"animation_LIMITED" = "Limited";
|
||||
"animation_NONE" = "None";
|
||||
|
||||
/* 2FA */
|
||||
"Enable two-factor authentication using Google Authenticator" = "Enable two-factor authentication using Google Authenticator";
|
||||
"You must enter this key into your Google Authenticator application." = "You must enter this key into your Google Authenticator application.";
|
||||
"If you do not and you log out you will not be able to login again." = "If you do not and you log out you will not be able to login again.";
|
||||
|
||||
/* External Sieve scripts */
|
||||
"An external Sieve script is active" = "An external Sieve script is active";
|
||||
"Sieve is a programming language that can be used for email filtering. If you let SOGo handle your filters, vacation and forward settings, your active script will be disabled." = "Sieve is a programming language that can be used for email filtering. If you let SOGo handle your filters, vacation and forward settings, your active script will be disabled.";
|
||||
|
|
|
@ -52,16 +52,6 @@
|
|||
<input type="password" ng-model="app.creds.password" ng-required="true"/>
|
||||
</md-input-container>
|
||||
|
||||
<var:if condition="isGoogleAuthenticatorEnabled">
|
||||
<md-input-container class="md-block"
|
||||
ng-show="app.showGoogleAuthenticatorCode">
|
||||
<label><var:string label:value="Google Authenticator
|
||||
Verification Code"/></label>
|
||||
<md-icon>email</md-icon>
|
||||
<input type="text" ng-model="app.creds.verificationCode" ng-required="false"/>
|
||||
</md-input-container>
|
||||
</var:if>
|
||||
|
||||
<!-- LANGUAGES SELECT -->
|
||||
<div layout="row" layout-align="start end">
|
||||
<md-icon>language</md-icon>
|
||||
|
@ -112,13 +102,13 @@
|
|||
</md-button>
|
||||
<md-button class="md-fab md-accent md-hue-2" type="submit"
|
||||
label:aria-label="Connect"
|
||||
ng-disabled="loginForm.$invalid" sg-ripple-click="loginContent">
|
||||
ng-if="!app.loginState"
|
||||
ng-disabled="loginForm.$invalid"
|
||||
sg-ripple-click="loginContent">
|
||||
<md-icon>arrow_forward</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<sg-ripple class="md-default-theme md-accent md-bg"
|
||||
ng-class="{ 'md-warn': app.loginState == 'error' }"><!-- ripple background --></sg-ripple>
|
||||
<sg-ripple-content class="md-flex ng-hide"
|
||||
|
@ -136,6 +126,36 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Google Authenticator Code -->
|
||||
<var:if condition="isGoogleAuthenticatorEnabled">
|
||||
<div layout="row" layout-align="center center" layout-fill="layout-fill"
|
||||
ng-switch-when="googleauthenticatorcode">
|
||||
<div flex="80" flex-sm="50" flex-gt-sm="40">
|
||||
<md-input-container class="md-block">
|
||||
<label><var:string label:value="Verification Code"/></label>
|
||||
<md-icon>lock</md-icon>
|
||||
<input type="text" ng-pattern="app.verificationCodePattern" ng-model="app.creds.verificationCode" ng-required="app.loginState == 'googleauthenticatorcode'"/>
|
||||
<div class="sg-hint"><var:string label:value="Enter the 6-digit verification code from your Google Authenticator application."/></div>
|
||||
</md-input-container>
|
||||
<div layout="row" layout-align="space-between center">
|
||||
<md-button class="md-icon-button"
|
||||
label:aria-label="Cancel"
|
||||
ng-click="app.restoreLogin()"
|
||||
sg-ripple-click="loginContent">
|
||||
<md-icon>arrow_backward</md-icon>
|
||||
</md-button>
|
||||
<md-button class="md-fab md-accent md-hue-2" type="submit"
|
||||
label:aria-label="Connect"
|
||||
ng-if="app.loginState == 'googleauthenticatorcode'"
|
||||
ng-disabled="loginForm.$invalid"
|
||||
ng-click="app.login()">
|
||||
<md-icon>arrow_forward</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</var:if>
|
||||
|
||||
<!-- Logged in -->
|
||||
<div layout="column" layout-align="center center"
|
||||
ng-switch-when="logged">
|
||||
|
@ -152,10 +172,14 @@
|
|||
<div class="md-default-theme md-accent md-hue-1 md-fg md-padding">
|
||||
{{app.errorMessage}}
|
||||
</div>
|
||||
<md-button sg-ripple-click="loginContent"><var:string label:value="Retry"/></md-button>
|
||||
<md-button
|
||||
ng-click="app.restoreLogin()"
|
||||
sg-ripple-click="loginContent"><var:string label:value="Retry"/></md-button>
|
||||
</div>
|
||||
|
||||
</sg-ripple-content>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</md-content>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
xmlns:label="OGo:label"
|
||||
className="UIxPageFrame"
|
||||
title="title"
|
||||
const:jsFiles="vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.min.js, vendor/ng-sortable.min.js, Common.js, Preferences.js, Preferences.services.js, Mailer.services.js, Contacts.services.js">
|
||||
const:jsFiles="vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.min.js, vendor/ng-sortable.min.js, vendor/qrcode.min.js, Common.js, Preferences.js, Preferences.services.js, Mailer.services.js, Contacts.services.js">
|
||||
|
||||
<main layout-fill="layout-fill" ui-view="preferences"
|
||||
ng-controller="navController"><!-- preferences --> </main>
|
||||
|
@ -232,24 +232,22 @@
|
|||
</md-input-container>
|
||||
|
||||
<var:if condition="isGoogleAuthenticatorEnabled">
|
||||
<md-checkbox flex="20"
|
||||
ng-model="app.preferences.defaults.SOGoGoogleAuthenticatorEnabled"
|
||||
<md-checkbox ng-model="app.preferences.defaults.SOGoGoogleAuthenticatorEnabled"
|
||||
ng-true-value="1"
|
||||
ng-false-value="0"
|
||||
label:aria-label="Enable two-factor authentication using Google Authenticator">
|
||||
<var:string label:value="Enable two-factor authentication using Google Authenticator"/>
|
||||
</md-checkbox>
|
||||
<input type="text"
|
||||
ng-readonly="true"
|
||||
ng-show="app.preferences.defaults.SOGoGoogleAuthenticatorEnabled == 1"
|
||||
var:value="googleAuthenticatorKey"/>
|
||||
<label
|
||||
ng-show="app.preferences.defaults.SOGoGoogleAuthenticatorEnabled
|
||||
== 1"><var:string label:value="You must enter
|
||||
this key into your Google Authenticator
|
||||
application. If you do not and you log out
|
||||
you will not be able to access SOGo
|
||||
again."/></label>
|
||||
<div layout="row" layout-align="start center" layout-wrap="layout-wrap"
|
||||
layout-padding="layout-padding" layout-margin="layout-margin"
|
||||
ng-show="app.preferences.defaults.SOGoGoogleAuthenticatorEnabled">
|
||||
<div>
|
||||
<sg-qr-code var:text="googleAuthenticatorKey" />
|
||||
</div>
|
||||
<div flex="100" flex-sm="60" flex-gt-sm="50">
|
||||
<var:string label:value="You must enter this key into your Google Authenticator application."/> <b><var:string label:value="If you do not and you log out you will not be able to login again."/></b>
|
||||
</div>
|
||||
</div>
|
||||
</var:if>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -139,7 +139,8 @@ module.exports = function(grunt) {
|
|||
'<%= src %>/angular-ui-router/release/angular-ui-router{,.min}.js{,.map}',
|
||||
//'<%= src %>/ng-file-upload/ng-file-upload{,.min}.js{,map}',
|
||||
'<%= src %>/ng-sortable/dist/ng-sortable.min.js{,map}',
|
||||
'<%= src %>/lodash/lodash{,.min}.js'
|
||||
'<%= src %>/lodash/lodash{,.min}.js',
|
||||
'<%= src %>/qrcodejs/qrcode{,.min}.js'
|
||||
];
|
||||
for (var j = 0; j < js.length; j++) {
|
||||
var files = grunt.file.expand(grunt.template.process(js[j], {data: options}));
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
(function() {
|
||||
/* jshint validthis: true, newcap: false */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* sgQrCode - Build a otpauth URI and generate a QR Code for the provided secret.
|
||||
* @see {@link https://davidshimjs.github.io/qrcodejs/|QRCode.js}
|
||||
* @memberof SOGo.Common
|
||||
* @example:
|
||||
<sg-qr-code text="secret"/>
|
||||
*/
|
||||
sgQrCode.$inject = ['sgSettings'];
|
||||
function sgQrCode(Settings) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
text: '@',
|
||||
width: '@',
|
||||
height: '@'
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
function link(scope, element, attrs) {
|
||||
var width = parseInt(scope.width) || 256,
|
||||
height = parseInt(scope.height) || width,
|
||||
uri = 'otpauth://totp/SOGo:' + Settings.activeUser('email') + '?secret=' + scope.text.replace(/=+$/, '') + '&issuer=SOGo';
|
||||
new QRCode(element[0], {
|
||||
text: uri,
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('SOGo.Common')
|
||||
.directive('sgQrCode', sgQrCode);
|
||||
})();
|
|
@ -23,7 +23,9 @@
|
|||
if (/\blanguage=/.test($window.location.search))
|
||||
this.creds.language = $window.language;
|
||||
this.loginState = false;
|
||||
this.showGoogleAuthenticatorCode = false;
|
||||
|
||||
// Code pattern for Google verification code
|
||||
this.verificationCodePattern = '\\d{6}';
|
||||
|
||||
// Show login once everything is initialized
|
||||
this.showLogin = false;
|
||||
|
@ -35,9 +37,8 @@
|
|||
Authentication.login(vm.creds)
|
||||
.then(function(data) {
|
||||
|
||||
if (typeof data.gamissingkey != 'undefined' && data.gamissingkey == 1) {
|
||||
vm.showGoogleAuthenticatorCode = true;
|
||||
vm.loginState = 'error';
|
||||
if (data.gamissingkey) {
|
||||
vm.loginState = 'googleauthenticatorcode';
|
||||
}
|
||||
else {
|
||||
vm.loginState = 'logged';
|
||||
|
@ -58,6 +59,11 @@
|
|||
return false;
|
||||
};
|
||||
|
||||
this.restoreLogin = function() {
|
||||
vm.loginState = false;
|
||||
delete vm.creds.verificationCode;
|
||||
};
|
||||
|
||||
this.showAbout = function($event) {
|
||||
$mdDialog.show({
|
||||
targetEvent: $event,
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
"grunt-postcss": ">=0.6.0",
|
||||
"grunt-sass": "^3.1.0",
|
||||
"kss": "^3.0.1",
|
||||
"node-sass": "^4.14.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"qrcodejs": "^1.0.0",
|
||||
"time-grunt": "latest"
|
||||
},
|
||||
"browserslist": [
|
||||
|
|
Loading…
Reference in New Issue