Przeglądaj źródła

More changes to Change Password dialog on HelpDesk detail page

Joseph White 7 lat temu
rodzic
commit
8f39d3bb83

+ 16 - 20
client/src/changepassword/type-change-password.component.html

@@ -28,23 +28,20 @@
         <div class="ias-dialog-content">
             <p ng-bind="$ctrl.message"></p>
 
-            <table>
+            <table ng-model-options="{ debounce: $ctrl.inputDebounce }">
                 <tbody>
                 <tr>
                     <td>
                         <input ng-model="$ctrl.password1" ng-hide="$ctrl.passwordMasked" type="text">
                         <input ng-model="$ctrl.password1" ng-show="$ctrl.passwordMasked" type="password">
-                        <ias-button class="ias-icon-button"
-                                    id="password-icon1"
-                                    ng-attr-title="{{ 'Button_Show' | translate }}"
-                                    ng-click="$ctrl.togglePassword1Masked()"
-                                    ng-if="$ctrl.maskPasswords"
-                                    ng-show="!!$ctrl.password1">
-                            <ias-icon icon="password_thin"></ias-icon>
-                        </ias-button>
                     </td>
                     <td>
-                        <div ng-bind="$ctrl.strength" ng-if="$ctrl.showStrengthMeter" ng-show="!!$ctrl.password1">
+                        <div ng-if="$ctrl.showStrengthMeter">
+                            <ias-icon icon="strength1" ng-show="$ctrl.strength===1"></ias-icon>
+                            <ias-icon icon="strength2" ng-show="$ctrl.strength===2"></ias-icon>
+                            <ias-icon icon="strength3" ng-show="$ctrl.strength===3"></ias-icon>
+                            <ias-icon icon="strength4" ng-show="$ctrl.strength===4"></ias-icon>
+                            <ias-icon icon="strength5" ng-show="$ctrl.strength===5"></ias-icon>
                         </div>
                     </td>
                 </tr>
@@ -52,18 +49,17 @@
                     <td>
                         <input ng-model="$ctrl.password2" ng-hide="$ctrl.passwordMasked" type="text">
                         <input ng-model="$ctrl.password2" ng-show="$ctrl.passwordMasked" type="password">
-                        <ias-button class="ias-icon-button"
-                                    id="password-icon2"
-                                    ng-attr-title="{{ 'Button_Show' | translate }}"
-                                    ng-click="$ctrl.togglePassword2Masked()"
-                                    ng-if="$ctrl.maskPasswords"
-                                    ng-show="!!$ctrl.password2">
-                            <ias-icon icon="password_thin"></ias-icon>
-                        </ias-button>
+                        <div id="password-icon"
+                             ng-attr-title="{{ 'Button_Show' | translate }}"
+                             ng-if="$ctrl.maskPasswords">
+                                <a ng-bind="'Button_Show' | translate"
+                                   ng-click="$ctrl.togglePasswordMasked()"
+                                   tabindex="0"></a>
+                        </div>
                     </td>
                     <td>
-                        <span ng-show="$ctrl.matchStatus==='MATCH'">check</span>
-                        <span ng-show="$ctrl.matchStatus==='NO_MATCH'">X</span>
+                        <ias-icon icon="status_ok_thin" ng-show="$ctrl.matchStatus==='MATCH'"></ias-icon>
+                        <ias-icon icon="message_error_thick" ng-show="$ctrl.matchStatus==='NO_MATCH'"></ias-icon>
                     </td>
                 </tr>
                 </tbody>

+ 28 - 7
client/src/changepassword/type-change-password.component.scss

@@ -20,24 +20,45 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-ias-dialog {
+.ias-dialog {
   &.type-change-password-dialog {
     .ias-dialog-content {
       table {
-        input {
-          vertical-align: middle
-        }
-
         input {
           margin: 7px;
+          vertical-align: middle;
         }
 
         td {
           min-width: 250px;
         }
 
-        .strength-label {
-          font-weight: bold;
+        .ias-icon-message_error_thick {
+          color: #e50000;
+        }
+
+        .ias-icon-status_ok_thin {
+          color: #37c26a;
+        }
+
+        .ias-icon-strength1 {
+          color: #e50000;
+        }
+
+        .ias-icon-strength2 {
+          color: #f17e12;
+        }
+
+        .ias-icon-strength3 {
+          color: #ffd92d;
+        }
+
+        .ias-icon-strength4 {
+          color: #01a9e7;
+        }
+
+        .ias-icon-strength5 {
+          color: #37c26a;
         }
       }
     }

+ 40 - 10
client/src/changepassword/type-change-password.controller.ts

@@ -22,31 +22,37 @@
 
 
 import {IHelpDeskService, ISuccessResponse} from '../services/helpdesk.service';
-import {IQService, IScope, IWindowService} from 'angular';
+import {ILogService, IQService, IScope, ITimeoutService, IWindowService} from 'angular';
 import {IHelpDeskConfigService} from '../services/helpdesk-config.service';
 import {IChangePasswordSuccess} from './success-change-password.controller';
 import {IPasswordService, IValidatePasswordData} from '../services/password.service';
+import IPwmService from '../services/pwm.service';
 
 require('changepassword/type-change-password.component.scss');
 
 const EMPTY_MATCH_STATUS = 'EMPTY';
+const IN_PROGRESS_MESSAGE_WAIT_MS = 5;
 
 export default class TypeChangePasswordController {
-    passwordAcceptable: boolean;
+    inputDebounce: number;
     maskPasswords: boolean;
     matchStatus: string;
     message: string;
     password1: string;
     password2: string;
+    passwordAcceptable: boolean;
     passwordMasked: boolean;
-    passwordUiMode: string;
     passwordSuggestions: string[];
+    passwordUiMode: string;
+    pendingValidation: boolean;
     showStrengthMeter: boolean;
     strength: number;
 
     static $inject = [
+        '$log',
         '$q',
         '$scope',
+        '$timeout',
         '$window',
         'ConfigService',
         'HelpDeskService',
@@ -54,10 +60,13 @@ export default class TypeChangePasswordController {
         'PasswordService',
         'personUsername',
         'personUserKey',
+        'PwmService',
         'translateFilter'
     ];
-    constructor(private $q: IQService,
+    constructor(private $log: ILogService,
+                private $q: IQService,
                 private $scope: IScope,
+                private $timeout: ITimeoutService,
                 private $window: IWindowService,
                 private configService: IHelpDeskConfigService,
                 private HelpDeskService: IHelpDeskService,
@@ -65,13 +74,16 @@ export default class TypeChangePasswordController {
                 private passwordService: IPasswordService,
                 private personUsername: string,
                 private personUserKey: string,
+                private pwmService: IPwmService,
                 private translateFilter: (id: string) => string) {
+        this.inputDebounce = this.pwmService.ajaxTypingWait;
+        this.matchStatus = EMPTY_MATCH_STATUS;
+        this.message = translateFilter('Display_PasswordPrompt');
         this.password1 = '';
         this.password2 = '';
-        this.passwordAcceptable = true;
+        this.passwordAcceptable = false;
         this.passwordSuggestions = Array<string>(20).fill('');
-        this.matchStatus = EMPTY_MATCH_STATUS;
-        this.message = translateFilter('Display_PasswordPrompt');
+        this.pendingValidation = false;
         this.showStrengthMeter = HelpDeskService.showStrengthMeter;
         this.strength = 0;
 
@@ -126,9 +138,18 @@ export default class TypeChangePasswordController {
     }
 
     updateDialog() {
+        // Since user may continue typing, don't process request if another is already in progress
+        if (this.pendingValidation) {
+            return;
+        }
+
+        this.pendingValidation = true;
+
+
         this.passwordService.validatePassword(this.password1, this.password2, this.personUserKey)
-            .onResult(
+            .then(
                 (data: IValidatePasswordData) => {
+                    this.pendingValidation = false;
                     if (data.version !== 2) {
                         // TODO: error message - '[ unexpected version string from server ]'
                     }
@@ -156,9 +177,18 @@ export default class TypeChangePasswordController {
                         this.strength = 5;
                     }
                 },
-                (message: string) => {
-                    this.message = message;
+                (result: any) => {
+                    this.pendingValidation = false;
+                    this.$log.error(result);
+                    this.message = this.translateFilter('Display_CommunicationError');
+
                 }
             );
+
+        this.$timeout(() => {
+            if (this.pendingValidation) {
+                this.message = this.translateFilter('Display_CheckingPassword');
+            }
+        }, IN_PROGRESS_MESSAGE_WAIT_MS);
     }
 }

+ 2 - 0
client/src/helpdesk/main.dev.ts

@@ -28,6 +28,7 @@ import PeopleService from '../services/people.service.dev';
 import HelpDeskConfigService from '../services/helpdesk-config.service.dev';
 import HelpDeskService from '../services/helpdesk.service.dev';
 import PasswordService from '../services/password.service.dev';
+import PwmService from '../services/pwm.service.dev';
 
 
 module('app', [
@@ -45,6 +46,7 @@ module('app', [
     .service('HelpDeskService', HelpDeskService)
     .service('PasswordService', PasswordService)
     .service('PeopleService', PeopleService)
+    .service('PwmService', PwmService)
     .service('ConfigService', HelpDeskConfigService);
 
 // Attach to the page document

+ 1 - 0
client/src/i18n/translations_en.json

@@ -20,6 +20,7 @@
   "Confirm": "Are you sure you wish to proceed?",
   "Confirm_DeleteUser": "Are you sure you wish to proceed?  If you continue, the selected user will be deleted permanently.  This can not be undone.",
   "Display_CaptchaRefresh": "Refresh",
+  "Display_CheckingPassword": "Checking Password....",
   "Display_HelpdeskOtpValidation": "Instruct the user to load their mobile authentication app and share the current pass code.",
   "Display_PasswordGeneration": "The following passwords have been randomly generated for you.  These passwords are based on real words to make them easier to remember, but have been modified to make them difficult to guess.",
   "Display_PasswordPrompt": "Please type your new password",

+ 20 - 28
client/src/services/password.service.dev.ts

@@ -20,27 +20,21 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-const RESPONSE_PROGRESS_WAIT_MS = 10;
-const RESPONSE_WAIT_MS = 100;
+const SIMULATED_RESPONSE_TIME = 100;
 const STRENGTH_PER_PASSWORD_CHARACTER = 10;
 const MAX_STRENGTH = 100;
 const STRENGTH_REQUIRED = 40;
 
-import {IPasswordService, IValidatePasswordResultsFunction} from './password.service';
-import {ILogService, ITimeoutService} from 'angular';
+import {IPasswordService, IValidatePasswordData} from './password.service';
+import {IPromise, IQService, ITimeoutService} from 'angular';
 
 export default class PasswordService implements IPasswordService {
 
-    static $inject = ['$log', '$timeout'];
-    constructor(private $log: ILogService, private $timeout: ITimeoutService) {
+    static $inject = ['$q', '$timeout'];
+    constructor(private $q: IQService, private $timeout: ITimeoutService) {
     }
 
-    validatePassword(password1: string, password2: string, userKey: string): IValidatePasswordResultsFunction {
-        let resultFunctions = {
-            dataCallback: this.$log.info.bind(this.$log),
-            messageCallback: this.$log.error.bind(this.$log)
-        };
-
+    validatePassword(password1: string, password2: string, userKey: string): IPromise<IValidatePasswordData> {
         let strength = Math.min((password1.length * STRENGTH_PER_PASSWORD_CHARACTER), MAX_STRENGTH);
         let match = (password1 === password2);
         let message: string = null;
@@ -79,24 +73,22 @@ export default class PasswordService implements IPasswordService {
             errorCode: 0
         };
 
-        this.$timeout(() => {
-            resultFunctions.messageCallback('Checking Password...');
-        }, RESPONSE_PROGRESS_WAIT_MS);
+        let self = this;
 
-        this.$timeout(() => {
-            resultFunctions.dataCallback(data);
-        }, RESPONSE_WAIT_MS);
+        let deferred = this.$q.defer();
+        let deferredAbort = this.$q.defer();
 
-        return {
-            onResult: (dataCallback, messageCallback) => {
-                if (dataCallback) {
-                    resultFunctions.dataCallback = dataCallback;
-                }
+        let timeoutPromise = this.$timeout(() => {
+            deferred.resolve(data);
+        }, SIMULATED_RESPONSE_TIME);
 
-                if (messageCallback) {
-                    resultFunctions.messageCallback = messageCallback;
-                }
-            }
-        };
+        // To simulate an abortable promise, edit SIMULATED_RESPONSE_TIME
+        deferred.promise['_httpTimeout'] = deferredAbort;
+        deferredAbort.promise.then(() => {
+            self.$timeout.cancel(timeoutPromise);
+            deferred.resolve();
+        });
+
+        return deferred.promise as IPromise<IValidatePasswordData>;
     }
 }

+ 11 - 20
client/src/services/password.service.ts

@@ -21,18 +21,11 @@
  */
 
 
-import {ILogService, IWindowService} from 'angular';
+import {ILogService, IPromise, ITimeoutService, IWindowService} from 'angular';
 import {IPwmService} from './pwm.service';
 
 export interface IPasswordService {
-    validatePassword(password1: string, password2: string, userKey: string): IValidatePasswordResultsFunction;
-}
-
-export interface IValidatePasswordResultsFunction {
-    onResult: (
-        dataCallback: (IValidatePasswordData) => void,
-        messageCallback: (message: string) => void
-    ) => void;
+    validatePassword(password1: string, password2: string, userKey: string): IPromise<IValidatePasswordData>;
 }
 
 export interface IValidatePasswordData {
@@ -47,8 +40,9 @@ export interface IValidatePasswordData {
 export default class PasswordService implements IPasswordService {
     PWM_MAIN: any;
 
-    static $inject = [ '$log', '$window', 'pwmService', 'translateFilter' ];
+    static $inject = [ '$log', '$timeout', '$window', 'pwmService', 'translateFilter' ];
     constructor(private $log: ILogService,
+                private $timeout: ITimeoutService,
                 private $window: IWindowService,
                 private pwmService: IPwmService,
                 private translateFilter: (id: string) => string) {
@@ -60,17 +54,14 @@ export default class PasswordService implements IPasswordService {
         }
     }
 
-    validatePassword(password1: string, password2: string, userKey: string): IValidatePasswordResultsFunction {
-        let data = { password1: password1, password2: password2, userDN: userKey };
-        let processResultsFunction = null;
-        let validationProps = {
-            messageWorking: this.translateFilter('Display_CheckingPassword'),
-            processResultsFunction: processResultsFunction,
-            readDataFunction: () => data,
-            serviceURL: this.pwmService.getServerUrl('checkPassword')
+    validatePassword(password1: string, password2: string, userKey: string): IPromise<IValidatePasswordData> {
+        let data = {
+            password1: password1,
+            password2: password2,
+            userDN: userKey
         };
-        this.PWM_MAIN.pwmFormValidator(validationProps);
+        let url: string = this.pwmService.getServerUrl('checkPassword');
 
-        return null;
+        return this.pwmService.httpRequest(url, { data: data });
     }
 }