From 7a3503a40e64fe8a5d5c45335d9f2320770c64fe Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 3 Jun 2015 00:29:36 -0700 Subject: [PATCH] GUAC-1176: Handle insufficient credentials distinctly from invalid credentials. --- .../app/auth/service/authenticationService.js | 2 +- .../app/index/controllers/indexController.js | 16 ++++- .../main/webapp/app/login/directives/login.js | 68 ++++++++++++++++--- .../webapp/app/login/templates/login.html | 2 +- guacamole/src/main/webapp/index.html | 3 +- 5 files changed, 78 insertions(+), 13 deletions(-) diff --git a/guacamole/src/main/webapp/app/auth/service/authenticationService.js b/guacamole/src/main/webapp/app/auth/service/authenticationService.js index 6671052ed..2c0bd6bc0 100644 --- a/guacamole/src/main/webapp/app/auth/service/authenticationService.js +++ b/guacamole/src/main/webapp/app/auth/service/authenticationService.js @@ -160,7 +160,7 @@ angular.module('auth').factory('authenticationService', ['$injector', else if (error.type === Error.Type.INSUFFICIENT_CREDENTIALS) $rootScope.$broadcast('guacInsufficientCredentials', parameters, error.expected); - authenticationProcess.reject(); + authenticationProcess.reject(error); }); return authenticationProcess.promise; diff --git a/guacamole/src/main/webapp/app/index/controllers/indexController.js b/guacamole/src/main/webapp/app/index/controllers/indexController.js index cd0897b2f..283e4a0cf 100644 --- a/guacamole/src/main/webapp/app/index/controllers/indexController.js +++ b/guacamole/src/main/webapp/app/index/controllers/indexController.js @@ -36,11 +36,22 @@ angular.module('index').controller('indexController', ['$scope', '$injector', */ $scope.guacNotification = guacNotification; + /** + * The credentials that the authentication service is has already accepted, + * pending additional credentials, if any. If the user is logged in, or no + * credentials have been accepted, this will be null. If credentials have + * been accepted, this will be a map of name/value pairs corresponding to + * the parameters submitted in a previous authentication attempt. + * + * @type Object. + */ + $scope.acceptedCredentials = null; + /** * The credentials that the authentication service is currently expecting, * if any. If the user is logged in, this will be null. * - * @type Form[]|Form|Field[]|Field + * @type Field[] */ $scope.expectedCredentials = null; @@ -112,6 +123,7 @@ angular.module('index').controller('indexController', ['$scope', '$injector', $scope.$on('guacInvalidCredentials', function loginInvalid(event, parameters, expected) { $scope.page.title = 'APP.NAME'; $scope.page.bodyClassName = ''; + $scope.acceptedCredentials = {}; $scope.expectedCredentials = expected; }); @@ -119,11 +131,13 @@ angular.module('index').controller('indexController', ['$scope', '$injector', $scope.$on('guacInsufficientCredentials', function loginInsufficient(event, parameters, expected) { $scope.page.title = 'APP.NAME'; $scope.page.bodyClassName = ''; + $scope.acceptedCredentials = parameters; $scope.expectedCredentials = expected; }); // Clear login screen if login was successful $scope.$on('guacLogin', function loginSuccessful() { + $scope.acceptedCredentials = null; $scope.expectedCredentials = null; }); diff --git a/guacamole/src/main/webapp/app/login/directives/login.js b/guacamole/src/main/webapp/app/login/directives/login.js index 1b8b407ca..fd05d4be3 100644 --- a/guacamole/src/main/webapp/app/login/directives/login.js +++ b/guacamole/src/main/webapp/app/login/directives/login.js @@ -39,9 +39,16 @@ angular.module('login').directive('guacLogin', [function guacLogin() { * The login form or set of fields. This will be displayed to the user * to capture their credentials. * - * @type Form[]|Form|Field[]|Field + * @type Field[] */ - form : '=' + form : '=', + + /** + * A map of all field name/value pairs that have already been provided. + * If not null, the user will be prompted to continue their login + * attempt using only the fields which remain. + */ + values : '=' }; @@ -49,6 +56,10 @@ angular.module('login').directive('guacLogin', [function guacLogin() { directive.controller = ['$scope', '$injector', function loginController($scope, $injector) { + // Required types + var Error = $injector.get('Error'); + var Field = $injector.get('Field'); + // Required services var $route = $injector.get('$route'); var authenticationService = $injector.get('authenticationService'); @@ -61,9 +72,37 @@ angular.module('login').directive('guacLogin', [function guacLogin() { $scope.loginError = false; /** - * All form values entered by the user. + * All form values entered by the user, as parameter name/value pairs. + * + * @type Object. */ - $scope.values = {}; + $scope.enteredValues = {}; + + /** + * All form fields which have not yet been filled by the user. + * + * @type Field[] + */ + $scope.remainingFields = []; + + $scope.$watch('values', function resetEnteredValues(values) { + angular.extend($scope.enteredValues, values || {}); + }); + + $scope.$watch('form', function resetRemainingFields(fields) { + + // If no fields are provided, then no fields remain + if (!fields) { + $scope.remainingFields = []; + return; + } + + // Filter provided fields against provided values + $scope.remainingFields = fields.filter(function isRemaining(field) { + return !(field.name in $scope.values); + }); + + }); /** * Submits the currently-specified username and password to the @@ -72,19 +111,30 @@ angular.module('login').directive('guacLogin', [function guacLogin() { $scope.login = function login() { // Attempt login once existing session is destroyed - authenticationService.authenticate($scope.values) + authenticationService.authenticate($scope.enteredValues) // Clear and reload upon success .then(function loginSuccessful() { $scope.loginError = false; - $scope.values = {}; + $scope.enteredValues = {}; $route.reload(); }) // Reset upon failure - ['catch'](function loginFailed() { - $scope.loginError = true; - $scope.values.password = ''; + ['catch'](function loginFailed(error) { + + // Clear out passwords and flag error if credentials are invalid + if (error.type === Error.Type.INVALID_CREDENTIALS) { + $scope.loginError = true; + angular.forEach($scope.form, function clearEnteredValueIfPassword(field) { + + // Remove entered value only if field is a password field + if (field.type === Field.Type.PASSWORD) + delete $scope.enteredValues[field.name]; + + }); + } + }); }; diff --git a/guacamole/src/main/webapp/app/login/templates/login.html b/guacamole/src/main/webapp/app/login/templates/login.html index ac6879a14..db020c86b 100644 --- a/guacamole/src/main/webapp/app/login/templates/login.html +++ b/guacamole/src/main/webapp/app/login/templates/login.html @@ -36,7 +36,7 @@ diff --git a/guacamole/src/main/webapp/index.html b/guacamole/src/main/webapp/index.html index 3303ec1a8..046876f10 100644 --- a/guacamole/src/main/webapp/index.html +++ b/guacamole/src/main/webapp/index.html @@ -50,7 +50,8 @@ - +