소스 검색

Reworked the verifications dialog to provide more flexibility when dealing with token verifications. Also fixed issues with being shown verification options that don't apply to the user (can't send an email if the user doesn't have an email address, etc.).

jalbr74 6 년 전
부모
커밋
0e9d51e635

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

@@ -17,6 +17,8 @@
   "Button_More": "More",
   "Button_OK": "OK",
   "Button_OTP": "OTP",
+  "Button_TokenVerification": "Token Verification",
+  "Button_SendToken": "Send Token",
   "Button_Remove": "Remove",
   "Button_Show": "Show",
   "Button_SMS": "SMS",
@@ -42,6 +44,8 @@
   "Display_StrengthMeter": "Password Strength",
   "Display_TokenDestination": "Token Destination",
   "Display_ViewDetails": "View Details",
+  "Display_EmailPrefix": "Email - ",
+  "Display_SmsPrefix": "SMS - ",
   "Field_DateTime": "Date/Time",
   "Field_Display": "Display",
   "Field_LdapProfile": "LDAP Profile",

+ 1 - 1
client/src/modules/helpdesk/helpdesk-detail.component.ts

@@ -430,7 +430,7 @@ export default class HelpDeskDetailComponent {
                 templateUrl: verificationsDialogTemplateUrl,
                 locals: {
                     personUserKey: this.getUserKey(),
-                    search: false
+                    showRequiredOnly: false
                 }
             });
     }

+ 1 - 1
client/src/modules/helpdesk/helpdesk-search-base.component.ts

@@ -332,7 +332,7 @@ export default abstract class HelpDeskSearchBaseComponent {
                 templateUrl: verificationsDialogTemplateUrl,
                 locals: {
                     personUserKey: person.userKey,
-                    search: true
+                    showRequiredOnly: true
                 }
             });
     }

+ 16 - 0
client/src/modules/helpdesk/verifications-dialog.component.scss

@@ -0,0 +1,16 @@
+#verification-token-destination {
+    margin: 0;
+    width: 210px;
+}
+
+.token-destination-submitter {
+    display: grid;
+    grid-template-columns: max-content max-content max-content;
+    grid-column-gap: 10px;
+    grid-row-gap: 5px;
+    align-items: center;
+
+    select {
+        grid-column-start: 1;
+    }
+}

+ 77 - 37
client/src/modules/helpdesk/verifications-dialog.controller.ts

@@ -20,14 +20,19 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+require('./verifications-dialog.component.scss');
 
 import {ui, ITimeoutService} from 'angular';
 import {
-    IHelpDeskConfigService, IVerificationMap, TOKEN_CHOICE,
+    IHelpDeskConfigService, IVerificationMap, TOKEN_CHOICE, VERIFICATION_METHOD_LABELS,
     VERIFICATION_METHOD_NAMES
 } from '../../services/helpdesk-config.service';
-import {IHelpDeskService, IVerificationTokenResponse} from '../../services/helpdesk.service';
-import {IPerson} from '../../models/person.model';
+import {
+    IHelpDeskService,
+    IVerificationOptions,
+    IVerificationStatus,
+    IVerificationTokenResponse
+} from '../../services/helpdesk.service';
 import ObjectService from '../../services/object.service';
 
 const STATUS_FAILED = 'failed';
@@ -38,6 +43,11 @@ const STATUS_VERIFY = 'verify';
 const STATUS_WAIT = 'wait';
 
 export default class VerificationsDialogController {
+    verificationOptions: IVerificationOptions;
+    tokenDestinationID: string;
+    sendingVerificationToken = false;
+    verificationTokenSent = false;
+
     availableVerificationMethods: IVerificationMap;
     formData: any = {};
     inputs: { name: string, label: string }[];
@@ -56,6 +66,7 @@ export default class VerificationsDialogController {
         'IasDialogService',
         'ObjectService',
         'personUserKey',
+        'showRequiredOnly'
     ];
     constructor(private $state: ui.IStateService,
                 private $timeout: ITimeoutService,
@@ -64,26 +75,44 @@ export default class VerificationsDialogController {
                 private IasDialogService: any,
                 private objectService: ObjectService,
                 private personUserKey: string,
-                private search: boolean) {
+                private showRequiredOnly: boolean) {
+
         this.isDetailsView = (this.$state.current.name === 'details');
         this.status = STATUS_WAIT;
         this.verificationStatus = STATUS_NONE;
         this.viewDetailsEnabled = false;
 
-        if (this.isDetailsView) {
-            this.loadVerificationOptions();
-        }
-        else {
-            this.helpDeskService
-                .checkVerification(this.personUserKey)
-                .then((response) => {
-                    if (response.passed) {
-                        this.gotoDetailsPage();
-                    }
-                    else {
-                        this.loadVerificationOptions();
-                    }
+        this.helpDeskService
+            .checkVerification(this.personUserKey)
+            .then((response: IVerificationStatus) => {
+                this.verificationOptions = response.verificationOptions;
+
+                if (!this.isDetailsView && response.passed) {
+                    // If we're not on the details page already, and verifications have been passed, then just go right
+                    // to the details page:
+                    this.gotoDetailsPage();
+                }
+                else {
+                    this.status = STATUS_SELECT;
+                    this.determineAvailableVerificationMethods();
+                }
+            });
+    }
+
+    determineAvailableVerificationMethods() {
+        this.availableVerificationMethods = [];
+
+        const methodNames: string[] = this.showRequiredOnly ?
+            this.verificationOptions.verificationMethods.required :
+            this.verificationOptions.verificationMethods.optional;
+
+        if (methodNames) {
+            for (let methodName of methodNames) {
+                this.availableVerificationMethods.push({
+                    name: methodName,
+                    label: VERIFICATION_METHOD_LABELS[methodName]
                 });
+            }
         }
     }
 
@@ -100,15 +129,6 @@ export default class VerificationsDialogController {
         });
     }
 
-    private loadVerificationOptions() {
-        this.configService
-            .getVerificationMethods({includeOptional: this.isDetailsView})
-            .then((methods) => {
-                this.status = STATUS_SELECT;
-                this.availableVerificationMethods = methods;
-            });
-    }
-
     selectVerificationMethod(method: string) {
         this.verificationMethod = method;
 
@@ -125,17 +145,13 @@ export default class VerificationsDialogController {
                     }
                 });
         }
-        else if (method === VERIFICATION_METHOD_NAMES.SMS || method === VERIFICATION_METHOD_NAMES.EMAIL) {
-            this.status = STATUS_WAIT;
-            this.configService.getTokenSendMethod()
-                .then((tokenSendMethod) => {
-                    let choice = (tokenSendMethod === TOKEN_CHOICE) ? method.toLowerCase() : null;
-                    return this.helpDeskService.sendVerificationToken(this.personUserKey, choice);
-                })
-                .then((response: any) => {
-                    this.status = STATUS_VERIFY;
-                    this.tokenData = response.data;
-                });
+        else if (method === VERIFICATION_METHOD_NAMES.TOKEN) {
+            this.status = STATUS_VERIFY;
+
+            try {
+                // Select the first destination in the list as default.
+                this.tokenDestinationID = this.verificationOptions.tokenDestinations[0].id;
+            } catch (error) {}
         }
         else if (method === VERIFICATION_METHOD_NAMES.OTP) {
             this.status = STATUS_VERIFY;
@@ -157,6 +173,30 @@ export default class VerificationsDialogController {
                 else {
                     this.verificationStatus = STATUS_FAILED;
                 }
+            })
+            .catch((reason) => {
+                this.verificationStatus = STATUS_FAILED;
+            })
+    }
+
+    onTokenDestinationChanged() {
+        this.verificationTokenSent = false;
+    }
+
+    sendVerificationTokenToDestination() {
+        this.sendingVerificationToken = true;
+        this.verificationTokenSent = false;
+
+        this.helpDeskService.sendVerificationToken(this.personUserKey, this.tokenDestinationID)
+            .then((response) => {
+                this.verificationTokenSent = true;
+            })
+            .catch((reason) => {
+                this.verificationTokenSent = false;
+                alert(reason);
+            })
+            .finally(() => {
+                this.sendingVerificationToken = false;
             });
     }
 

+ 17 - 3
client/src/modules/helpdesk/verifications-dialog.template.html

@@ -65,10 +65,24 @@
                                        ng-model="$ctrl.formData[input.name]">
                             </div>
                         </div>
-                        <div ng-switch-when="EMAIL|SMS" ng-switch-when-separator="|">
-                            <div class="ias-input-container">
+                        <div ng-switch-when="TOKEN" ng-switch-when-separator="|">
+                            <div class="ias-input-container token-destination-submitter">
                                 <label ng-bind="'Display_TokenDestination' | translate"></label>
-                                <input type="text" ng-value="$ctrl.tokenData.destination" readonly>
+
+                                <select id="verification-token-destination" ng-model="$ctrl.tokenDestinationID"
+                                        ng-change="$ctrl.onTokenDestinationChanged()">
+                                    <option ng-repeat="tokenDestination in $ctrl.verificationOptions.tokenDestinations"
+                                            ng-attr-value="{{tokenDestination.id}}">
+                                        {{ tokenDestination.type == 'sms'?'Display_SmsPrefix':'Display_EmailPrefix' | translate}}
+                                        {{tokenDestination.display}}
+                                    </option>
+                                </select>
+                                <ias-button ng-click="$ctrl.sendVerificationTokenToDestination()"
+                                            ng-disabled="$ctrl.sendingVerificationToken">
+                                    {{ 'Button_SendToken' | translate }}
+                                </ias-button>
+                                <div class="loading-gif-25" ng-if="$ctrl.sendingVerificationToken"></div>
+                                <ias-icon icon="status_ok_thick" style="color:#37c26a;" class="ias-success" ng-if="$ctrl.verificationTokenSent"></ias-icon>
                             </div>
                             <div class="ias-input-container">
                                 <label>Token</label>

+ 2 - 49
client/src/services/helpdesk-config.service.ts

@@ -54,15 +54,13 @@ export const PASSWORD_UI_MODES = {
 
 export const VERIFICATION_METHOD_NAMES = {
     ATTRIBUTES: 'ATTRIBUTES',
-    EMAIL: 'EMAIL',
-    SMS: 'SMS',
+    TOKEN: 'TOKEN',
     OTP: 'OTP'
 };
 
 export const VERIFICATION_METHOD_LABELS = {
     ATTRIBUTES: 'Button_Attributes',
-    EMAIL: 'Button_Email',
-    SMS: 'Button_SMS',
+    TOKEN: 'Button_TokenVerification',
     OTP: 'Button_OTP'
 };
 
@@ -84,7 +82,6 @@ export interface IHelpDeskConfigService extends IConfigService {
     getPasswordUiMode(): IPromise<string>;
     getTokenSendMethod(): IPromise<string>;
     getVerificationAttributes(): IPromise<IVerificationMap>;
-    getVerificationMethods(options?: {includeOptional: boolean}): IPromise<IVerificationMap>;
     maskPasswordsEnabled(): IPromise<boolean>;
     verificationsEnabled(): IPromise<boolean>;
     advancedSearchConfig(): IPromise<IAdvancedSearchConfig>;
@@ -117,50 +114,6 @@ export default class HelpDeskConfigService extends ConfigBaseService implements
         return this.getValue(VERIFICATION_FORM_CONFIG);
     }
 
-    private getVerificationMethod(methodName): {name: string, label: string} {
-        return {
-            name: methodName,
-            label: VERIFICATION_METHOD_LABELS[methodName]
-        };
-    }
-
-    getVerificationMethods(options?: {includeOptional: boolean}): IPromise<IVerificationMap> {
-        let promise = this.$q.all([
-            this.getValue(VERIFICATION_METHODS_CONFIG),
-            this.getTokenSendMethod()
-        ]);
-
-        return promise.then((result) => {
-            let availableMethods: string[];
-            if (options && options.includeOptional) {
-                availableMethods = result[0].optional;
-            }
-            else {
-                availableMethods = result[0].required;
-            }
-
-            let tokenSendMethod: string = result[1];
-
-            let verificationMethods: IVerificationMap = [];
-            availableMethods.forEach((method) => {
-                if (method === TOKEN_VERIFICATION_METHOD) {
-                    if (tokenSendMethod === TOKEN_EMAIL_ONLY || tokenSendMethod === TOKEN_CHOICE) {
-                        verificationMethods.push(this.getVerificationMethod(VERIFICATION_METHOD_NAMES.EMAIL));
-                    }
-
-                    if (tokenSendMethod === TOKEN_SMS_ONLY || tokenSendMethod === TOKEN_CHOICE) {
-                        verificationMethods.push(this.getVerificationMethod(VERIFICATION_METHOD_NAMES.SMS));
-                    }
-                }
-                else {
-                    verificationMethods.push(this.getVerificationMethod(method));
-                }
-            });
-
-            return verificationMethods;
-        });
-    }
-
     maskPasswordsEnabled(): IPromise<boolean> {
         return this.getValue(MASK_PASSWORDS_CONFIG);
     }

+ 23 - 8
client/src/services/helpdesk.service.ts

@@ -30,8 +30,7 @@ import {IQuery} from './people.service';
 
 const VERIFICATION_PROCESS_ACTIONS = {
     ATTRIBUTES: 'validateAttributes',
-    EMAIL: 'verifyVerificationToken',
-    SMS: 'verifyVerificationToken',
+    TOKEN: 'verifyVerificationToken',
     OTP: 'validateOtpCode'
 };
 
@@ -77,8 +76,25 @@ interface IValidationStatus extends IVerificationStatus {
     verificationState: string;
 }
 
+export interface IVerificationOptions {
+    verificationMethods: {
+        optional: string[];
+        required: string[];
+    },
+    verificationForm: [{
+        name: string;
+        label: string;
+    }],
+    tokenDestinations: [{
+        id: string;
+        display: string;
+        type: string;
+    }]
+}
+
 export interface IVerificationStatus {
     passed: boolean;
+    verificationOptions: IVerificationOptions;
 }
 
 export interface IVerificationTokenResponse {
@@ -269,13 +285,12 @@ export default class HelpDeskService implements IHelpDeskService {
             });
     }
 
-    sendVerificationToken(userKey: string, choice: string): IPromise<IVerificationTokenResponse> {
+    sendVerificationToken(userKey: string, destinationID: string): IPromise<IVerificationTokenResponse> {
         let url: string = this.pwmService.getServerUrl('sendVerificationToken');
-        let data: any = { userKey: userKey };
-
-        if (choice) {
-            data.method = choice;
-        }
+        let data: any = {
+            userKey: userKey,
+            id: destinationID
+        };
 
         return this.pwmService
             .httpRequest(url, { data: data })