Name

global.PwdResetPageInfo

Description

Implements helper functions that are needed for page creating in password reset desktop app

Script

var PwdResetPageInfo = Class.create();
PwdResetPageInfo.prototype = {
  
  VERIFY_TYPE : "2f39b371eb1101004d7763fba206fed1",
  QA_TYPE : "39b15343eb10010045e1a5115206feb7",
  SMS_TYPE : "58a2f398eb6001006a668c505206fed3",
  PWD_MESSAGE : '7cd0c421bf200100710071a7bf0739bd', 
  
  initialize: function() {
  },

  initRequest :function(sysparm_language, sysparm_url){


  	// Set the i18n language if a language plugin is enabled
  	if (typeof I18nUtils != "undefined" && typeof sysparm_language != "undefined")
  		gs.getSession().setLanguage(sysparm_language);

  	var processObj = PwdResetProcessHelper.validateProcessByURL(sysparm_url);
  	if ( processObj.status === PwdConstants.WIN_STATUS_ERROR)
  		return processObj;

  	// Put this in the session for google captcha falling back to text captcha later
  	gs.getSession().putProperty('sysparm_is_desktop_request', 'true');

  	return this._pageFormInit();

  },

  getIdentifyPageInfo : function (sysparm_url){

  	var process = PwdResetProcessHelper.getProcessIdByProcessURL(sysparm_url);
  	if (process.status === PwdConstants.WIN_STATUS_ERROR)
  		return process;

  	var res = {
  		status : "",
  		info : {}
  	};

  	var display_captcha = process.getDisplayCaptcha();
  	var now_captcha_url = "pwd_jcaptcha.do";
  	/* take out to PwdRestHelper*/
  	var idLabel = this._getIdentificationLabel(process.getId());

  	res.info = {
  		process_id : process.getId(),
  		elements : {
  			identify_breadcrumb : gs.getMessage("Identify"),
  			verify_breadcrumb : gs.getMessage("Verify"),
  			reset_breadcrumb : gs.getMessage("Reset"),
  			input_label : idLabel,
  			identifications : this._getIdentificationTypesByProcessId(process.getId()),
  			next_button : gs.getMessage("next"),
  			cancel_button : gs.getMessage("Cancel"),
  			heading : gs.getMessage("Forgot Password?")
  		}
  	};

  	// Google captcha falls back to now captcha
  	if (display_captcha){
  		res.info.elements.now_captcha = {
  			captcha_placeholder : gs.getMessage('Type the CAPTCHA'),
  			url : now_captcha_url,
  			refresh_tooltip: gs.getMessage("Refresh CAPTCHA"),
  			label: gs.getMessage('CAPTCHA'),
  			error_message: gs.getMessage('Characters do not match, try again')
  		};
  	}

  	res.status = "ok";
  	return res;
  },


  getVerifyPageInfo : function(requestId) {

  	var grRequest = PwdResetRequestHelper.getRequestRecord(requestId);
  	var userId =  grRequest.getValue('user');
  	
  	var res = {
  		info : {
  			verifications : [],
  			user_id: userId
  		}
  	};
  	
  	var verificationIds = this._getVerificationIds(requestId, userId);
  	var verifyTypeQuestions = {};
  	var verifications = [];
  	var hasQA;
  	var hasSMS; 
  	var hasVerify;
  	var grVer = new GlideRecord("pwd_verification");
  	for (var i = 0; i < verificationIds.length; i++) {
  		var verId = verificationIds[i];
  		grVer.get(verId);
  		var type = grVer.type;

  		if(type == this.VERIFY_TYPE) {
  		    var verifyTypeQuestion = new SNC.PwdVerificationManager().getVerificationParamValue(verId, 'label');
  		    verifyTypeQuestions[verId] = gs.getMessage(verifyTypeQuestion);
  		    hasVerify = true;
  		}
  		else if (type == this.QA_TYPE) {
  			if (hasQA) {
  				trackingMgr.createActivity("Error", "Verification", "Multiple verifications of the Security Question type are not supported in Password Reset Windows Application", requestId);
  				res.error = "multiple_qa";
  			}
  			else {
  				hasQA = true;
  				verifications.push(this._getQATypeInfo(verId, requestId, userId, res));
  			}
  		}
  		else if (type == this.SMS_TYPE) {
  			if (hasSMS) {
  				trackingMgr.createActivity("Error", "Verification", "Multiple verifications of the SMS Code type are not supported in Password Reset Windows Application", requestId);
  				res.error = "multiple_sms"; 
  			}
  			else {
  				hasSMS = true;
  				var info = this._getSMSCodeTypeInfo( verId, userId, requestId);
  				if (info.error) 
  					res.error = info.error;
  				else
  					verifications.push(info);
  			}
  		}
  		else {
  			var verName = grVer.getDisplayValue('label') ? grVer.getDisplayValue('label') : grVer.getDisplayValue('name');
  			trackingMgr.createActivity("Error", "Verification", 'Verification "'+ verName + '" are not supported in Desktop Integration', requestId);
  			res.error = gs.getMessage('The "{0}" verification method is not supported.', verName);
  		}

  		if (res.error) {
  			res.status = PwdConstants.WIN_STATUS_ERROR;
  			return res;
  		}
  	}	

  	if (hasVerify) 
  		verifications.push(this._getVerifyTypeInfo(verifyTypeQuestions));

  	res.status = PwdConstants.WIN_STATUS_OK;
  	res.info.user_id = userId;
  	res.info.email_reset_url = grRequest.process.email_password_reset_url || false;
  	res.info.verifications = verifications;
       
  	return res;

  },



  getNewPageInfo : function(requestId) {

  	var trackingMgr =  new SNC.PwdTrackingManager();
  	var grRequest  = PwdResetRequestHelper.getRequestRecord(requestId);

  	var supportUnlock = grRequest.process.unlock_account && (grRequest.process.cred_store.type.unlock_user_wf || grRequest.process.cred_store.type.unlock_user_flow);
  	var supportReset = grRequest.process.reset && (grRequest.process.cred_store.type.passd_reset_wf || grRequest.process.cred_store.type.passd_reset_flow);
  	var enablePasswordStrength = grRequest.process.cred_store.enable_password_strength;
  	var ruleHint = new PasswordResetScopedUtil().getPasswordRuleHint(grRequest.process, true);
  	var rules = new PasswordResetScopedUtil().getPasswordRules(grRequest.process);
  	var enablePasswordPolicy = new SNC.PwdCredentialStoreManager().getEnablePasswordPolicy(grRequest.process);
  	var res = {
  		status : PwdConstants.WIN_STATUS_OK,
  		info : {
  			auto_generate_pwd : grRequest.process.auto_gen_password + "",
  			lock_state : grRequest.getValue("lock_state"),
  			support_unlock : !!supportUnlock,
  			support_reset : !!supportReset,
  			enable_password_strength: !!enablePasswordStrength,
  			wf_refresh_rate : gs.getProperty('password_reset.wf.refresh_rate'),
  			wf_time_out : gs.getProperty('password_reset.wf.timeout'),
  			elements : {
  				new_pwd_prompt : gs.getMessage("Click the Reset Password button to generate a password"),
  				new_pwd_label : gs.getMessage("New Password"),
  				retype_pwd_label : gs.getMessage('Retype Password'),
  				waiting_text : gs.getMessage('Request in progress. This process can take up to a few minutes'),
  				submit_button : gs.getMessage('Pwd - Reset Password'),
  				unlock_button : gs.getMessage('Unlock Account'),
  				lock_state_alert : gs.getMessage("Unable to retrieve account lock state"),
  				lock_state_loading : gs.getMessage("Retrieving account lock state..."),
  				lock_state_error: gs.getMessage("Error retrieving account lock state"),
  				account_locked : gs.getMessage("Account is locked"),
  				account_not_locked: gs.getMessage("Account is not locked"),
  				account_locked_prompt: gs.getMessage("To unlock your account, select the Unlock Account."),
  				account_not_locked_prompt: gs.getMessage("You can sign in as your account is not locked."),
  				account_locked_warning: gs.getMessage('The account is locked, resetting the password alone will not enable access. Continue?'),
  				done_button : gs.getMessage("Done"),
  				password_policy_rules_separator: gs.getMessage('We will also check these requirements once you submit'),
  				complexity_rule_hint : ruleHint,
  				enable_password_policy : enablePasswordPolicy,
  				rules : rules,
  				non_fatal_failure: gs.getMessage('The new password could not be accepted because it did not meet the policy requirements. Review the password length, complexity, and history requirements and try again.'),
  				fatal_failure: gs.getMessage("Password reset request failed."),
  				password_match_rule: gs.getMessage("Passwords must match")
  			}
  		}
  	};

  	return res;
  },

   getSuccessPageInfo : function(successType, requestId, params){

  	var grRequest = PwdResetRequestHelper.getRequestRecord(requestId);
  	if(grRequest.status == PwdConstants.WIN_STATUS_ERROR)
  		return grRequest;
  	// Verified successfully, and need to send out email with reset url
  	var elements = {};
  	if(successType == "email_reset_url") {
  		elements = {
  			title : gs.getMessage("An email has been sent to you providing instructions to reset your password"),
  			close_button : gs.getMessage("Close")
  		};
  	}
  	// Unlock account successfully
  	else if(successType == "unlock_account") {
  		elements = {
  			title: gs.getMessage("Account Unlock Success"),
  			close_button : gs.getMessage("Close")
  		};
  	}
  	// Reset password successfully
  	else {
  		elements = {
  			title : gs.getMessage("Password Reset Success"),
  			close_button : gs.getMessage("Close")
  		};
  		if (grRequest.process.send_email)
  			elements.title = gs.getMessage("The new password was emailed to you");

  		var autoGenPwd = grRequest.process.auto_gen_password + "";
  		var displayPwd = grRequest.process.display_password + "";

  		if (autoGenPwd == 'true' && displayPwd == 'true') {
  			autoPass = true;
  			elements.new_pwd_text = gs.getMessage('The new password is');
  			elements.new_pwd = params.temp_password;
  			//session.clearProperty("temp_password");
  		}
  	}

  	var res = {
  		status : PwdConstants.WIN_STATUS_OK,
  		info :{
  			elements: elements
  		}
  	};

  	return res;
  },

  getFailurePageInfo : function(error, requestNumber, sysparmError) {

  	var subtitle = "";
  	var serviceDeskText = "";
  	var content = null;  // for 'block' error
  	var ldapError = GlidePropertiesDB.get("glide.password_reset.ldap_user.restricted.message", 
  						gs.getMessage("You cannot reset your AD password through Service-Now password reset application. Please contact your administrator for instructions on how to change your AD password."));

  	error = error || sysparmError;

  	if (!error) {
  		subtitle = gs.getMessage('General error.');
  		serviceDeskText = gs.getMessage('Contact the service desk for assistance.');
  	}
  	else {
  		switch(error){
  			case "TIMEOUT":
  				subtitle = gs.getMessage('The passsword reset process timed out. Try again later.');
  				break;
  			case "block":
  				content = {
  					title: gs.getMessage('We could not reset your password for one of the following reasons:'),
  					lineItems: [
  						gs.getMessage('You have exceeded the limit on password reset attempts.'),
  						gs.getMessage('You changed your password recently.'),
  						gs.getMessage('You unlocked your account recently.')
  					],
  					foot: gs.getMessage('Try again later. For immediate assistance, call the service desk.')
  				};
  				break;
  			case "invalid_user":
  				content = {
  					title: gs.getMessage('This user cannot use the configured Password Reset process.  Possible reasons:'),
  					lineItems: [
  						gs.getMessage('User does not exist or is not enrolled.'),
  						gs.getMessage('User is not part of the configured password reset process.'),
  						gs.getMessage('User is blocked (exceeded the limit on reset attempts or reset password recently).')
  					],
  					foot: gs.getMessage('Try again later. For immediate assistance, call the service desk.')
  				};
  				break;
  			case "process_does_not_exist":
  				subtitle = gs.getMessage('This page does not exist.');
  				serviceDeskText = gs.getMessage('Contact the service desk for assistance.');
  				break;
  			case "process_not_public":
  				subtitle = gs.getMessage('The process is not publicly available.');
  				serviceDeskText = gs.getMessage('Contact the service desk for assistance.');
  				break;
  			case "process_not_active":
  				subtitle = gs.getMessage('The process is not active.');
  				serviceDeskText = gs.getMessage('Contact the service desk for assistance.');
  				break;
  			case "midserver_not_running":
  				subtitle = gs.getMessage('We could not reset your password because we cannot connect to the Active Directory server. Try again later.');
  				break;
  			case "ldap_user":
  				subtitle = ldapError;
  				break;
  			case "unauthorized":
  				subtitle = gs.getMessage("Unauthorized access");
  				break;
  			case "multiple_qa":
  				subtitle = gs.getMessage("Multiple verifications of the Security Question type are not supported");   
  				break;
  			case "multiple_sms":
  				subtitle = gs.getMessage("Multiple verifications of the SMS Code type are not supported");
  				break;
  			case "unknown":
  				subtitle = gs.getMessage("An unknown error has occurred");    
  				break;
  			case "load_failure_identify":
  				subtitle = gs.getMessage("Failed to load Identify page");
  				break;
  			case "load_failure_verify":
  				subtitle = gs.getMessage("Failed to load Verify page");
  				break;
  			case "load_failure_reset":
  				subtitle = gs.getMessage("Failed to load Reset page");
  				break;
  			case "cannot_receive_email":
  				subtitle = gs.getMessage('We are unable to reset your password at this time. Please contact your administrator for further assistance.');
  				break;
  			default:
  				subtitle = error;
  		}
  	}

  	var res = {
  		info:{
  			elements:{
  				title : gs.getMessage("Password Reset Error"),
  				subtitle: subtitle,
  				content: content,
  				service_desk_text: serviceDeskText,
  				close_button : gs.getMessage("Close")
  			}
  		}
  	};

  	if (requestNumber) {
  		res.info.elements.request_label = gs.getMessage("Reset request number");
  		res.info.elements.request_number = requestNumber;
  	}

  	return res;
  },

  /**
   * helper functions
   **/

   _pageFormInit : function() {

  	var res = {
  		status: PwdConstants.WIN_STATUS_OK,
  		message: []		
  	};
  	// Common strings for all pages
  	res.message.push({key: "Exit",  value: gs.getMessage("Exit")});

  	// Initialization form:
  	res.message.push({key: "Invalid URL: {0}", value: gs.getMessage("Invalid URL: {0}")});
  	res.message.push({key: "Password Reset Error", value: gs.getMessage("Password Reset Error")});
  	res.message.push({key: "Contact the service desk for assistance.", value: gs.getMessage("Contact the service desk for assistance.")});

  	// Identification Panel:
  	res.message.push({key: "Identification Error", value: gs.getMessage("Identification Error")});
  	res.message.push({key: "Cannot load the identification page", value: gs.getMessage("Cannot load the identification page")});
  	res.message.push({key: "Missing user token", value: gs.getMessage("Missing user token")});
  	res.message.push({key: "Identification Warning", value: gs.getMessage("Identification Warning")});
  	res.message.push({key: "Invalid CAPTCHA response", value: gs.getMessage("Invalid CAPTCHA response")});
  	res.message.push({
  		key: "You must fill in all of the required fields to proceed",
  		value: gs.getMessage("You must fill in all of the required fields to proceed")
  	});

  	// Verification Panel:
  	res.message.push({key: "Verification Warning", value: gs.getMessage("Verification Warning")});
  	res.message.push({
  		key: "Invalid response, cannot verify user ({0}). Number of attempts: {1}. Try again",
  		value: gs.getMessage("Invalid response, cannot verify user ({0}). Number of attempts: {1}. Try again")
  	});


  	// Reset Panel:
  	res.message.push({
  		key: "The passwords you entered did not match",
  		value: gs.getMessage("The passwords you entered did not match")
  	});
  	res.message.push({key: "Reset Warning", value: gs.getMessage("Reset Warning")});
  	res.message.push({key: "Warning", value: gs.getMessage("Warning")});
  	res.message.push({key: "Yes", value: gs.getMessage("Yes")});
  	res.message.push({key: "No", value: gs.getMessage("No")});
  	res.message.push({key: "Show passwords", value: gs.getMessage("Show passwords")});

  	// Failure Panel:
  	res.message.push({key: "Failed to load the error details", value: gs.getMessage("Failed to load the error details")});

  	return res;
  },

   _getQATypeInfo: function (verificationId, requestId, userId, res) {
  	var qObj = this._getQuestions(verificationId, requestId, userId);
  	if (qObj.error) {
  		res.error = qObj.error;
  		return;
  	}
  	var info = {
  		id : verificationId,
  		type : "qa",
  		elements: {
  			title : new SNC.PwdVerification(verificationId).getLabel(),
  			questions : qObj.questions	
  		}
  	};
  	
  	return info;
  },
  
  //@checkWithEmail handles the new case where emails are part of devices - otherwise all the same
   _getSMSCodeTypeInfo:function( verificationId, userId, requestId) {
  	var devices;

  	devices = this._getDevices(userId);

  	if (devices.length == 0) {
  		trackingMgr.createActivity("Warning", "Verification", "User needs to authorize at least one device to use the SMS type verification", requestId);
  		return {
  			error : "invalid_user"
  		};
  	}

  	var info = {
  		id : verificationId,
  		type : "sms",
  		elements : {
  			title : new SNC.PwdVerification(verificationId).getLabel(),
  			subtitle : gs.getMessage('Click the Send Verification Code button to send a verification code to the following devices'),
  			send_code : gs.getMessage('Send Verification Code'),
  			enter_code : gs.getMessage('Enter verification code'),
  			resend_code: gs.getMessage('Resend Verification Code'),
  			device_numbers : devices,
  			provider_placeholder: gs.getMessage("Select a provider") 
  		}
  	};

  	return info;
  },

  /** 
   *  Get the full JSON for the personal data verification type
   *  @param {Object} {id : label} questions each entry should come from _getVerifyTypeQuestion
   *  This will lump all the verify types together into a structure common with QA so that
   *  the UI code is simpler.
   */
   _getVerifyTypeInfo : function(questions)  {
  	var info = {
  		type : 'personal_data',
  		elements : {
  			title : gs.getMessage('Personal Data Verification'),
  			questions: questions
  		}
  	};

  	return info;
  },

  _getVerificationIds: function(requestId, userId) {

  	var processMgr = new SNC.PwdProcessManager();
  	var grRequest = new GlideRecord("pwd_reset_request");
  	grRequest.get(requestId);

  	var minVer = grRequest.process.min_verifications;
  	var supportsOptionalVerification = !gs.nil(minVer);

  	var mandatoryVerificationIds = supportsOptionalVerification ?  
  		processMgr.getProcessVerificationIdsByMandatoryFlag(grRequest.process, true) :
  		processMgr.getVerificationIdsByProcessId(grRequest.process);

  	var verificationIds = [];
  	for (var i = 0; i != mandatoryVerificationIds.size(); ++i)
  		verificationIds.push(mandatoryVerificationIds.get(i));

  	if (!supportsOptionalVerification)
  		return verificationIds;

  	// Add ENROLLED optional verifications to meet the process min_verifications 
  	var optionalVerificationIds = processMgr.getProcessVerificationIdsByMandatoryFlag(grRequest.process, false);
  	var enrollmentMgr = new SNC.PwdEnrollmentManager();
  	var verId;

  	for (i = 0; i != optionalVerificationIds.size() && minVer > verificationIds.length; ++i) {
  		verId = optionalVerificationIds.get(i);	
  		if (enrollmentMgr.isUserEnrolledByVerificationId(userId, verId))
  			verificationIds.push(verId);
  	}

  	return verificationIds;
  },

  _getQuestions: function(verificationId, requestId, userId) {
  	var res = {};
  
  	var params = this._getQAResetNumber(verificationId);
  	var enrolledQuestions = this._getAllEnrolledQuestions(verificationId, requestId, userId, params.numReset, params.numEnroll);
  	var qIds = Object.keys(enrolledQuestions);
  	var randomQuestions = {};
  	for (var i = 0; i != params.numReset; i++) {
  		var item = Math.floor(GlideSecureRandomUtil.getSecureRandomIntBound(qIds.length));
  		var qId = qIds.splice(item, 1)[0] + "";
  		var q = enrolledQuestions[qId];
  		randomQuestions[qId] = q;
  	}

  	res.questions = randomQuestions;

  	return res;
  },
  
  _getAllEnrolledQuestions: function(verificationId, requestId, userId, numReset, numEnroll) {
  	var pwdQuestionAnswerHelper = new global.PwdQuestionAnswerHelper();
  	var questions = pwdQuestionAnswerHelper.getQuestionsList(requestId, userId, verificationId, numReset, numEnroll);
  	
  	var enrolledQuestions = {};
  	for (var i = 0; i < questions.q_id.length; i++) {
  		enrolledQuestions[questions.q_id[i]] = questions.q[i];
  	}

  	return enrolledQuestions;
  },
  
  _getQAResetNumber: function(verificationId) {
  	var grParam = new GlideRecord('pwd_verification_param');
  	var param = {
  		'numReset': 0,
  		'numEnroll': 0
  	};
  	grParam.addQuery('verification', verificationId);
  	grParam.query();
  	while (grParam.next()) {
  		if (grParam.getValue('name') == 'num_reset')
  			param.numReset = parseInt(grParam.getValue('value'));
  		if (grParam.getValue('name') == 'num_enroll')
  			param.numEnroll = parseInt(grParam.getValue('value'));
  	}
  	return param;
  },

  _seedRandom: function(seedId, position) {
  	var seed = parseInt(seedId, 16);
  	seed = (seed * 9301 + 49297) % 233280;
  	var rand = seed/(233280.0);
  	return (rand * Math.pow(10, position)) - Math.floor(rand * Math.pow(10, position));
  },
  

  // [xxxx1234, xxxx5678]
   _getDevices: function(userId) {
      var devices = [];
      var gr = new GlideRecord('cmn_notif_device');
      gr.addActiveQuery();
      gr.addQuery('user', userId);
      gr.addQuery('type', 'SMS');
      gr.orderBy('name');
      gr.query();
      while (gr.next()) {
          var grNM  = GlideRecord('cmn_notif_message');
          grNM.addQuery('device', gr.getValue('sys_id'));
          grNM.addActiveQuery();
          grNM.query();
          while (grNM.next()) {
              var filter  = grNM.getValue('notification_filter'); // unsunscribe = c1bfa4040a0a0b8b001eeb0f3f5ee961
              var notif = grNM.getValue('notification');

              // only return subscribed
              if (filter == null && notif == this.PWD_MESSAGE) {
                  devices.push(PwdMaskHelper.maskPhone(gr.getValue('phone_number')));
              }
          }
      }
      return devices;
  },
  
  _getIdentificationLabel: function(processId) {
  	var procGr = new GlideRecord('pwd_process');
  	procGr.get(processId);
  	var idenSysIds = procGr.getValue('identification_type');

  	var idenGr = new GlideRecord('pwd_identification_type');
  	idenGr.addQuery('sys_id', 'IN', idenSysIds);
  	idenGr.query();
  	if (idenGr.next()) {
  		return gs.getMessage(idenGr.getValue('identification_label'));
  	}
  	else {
  		return gs.getMessage('Username');
  	}
  },
  
  _getIdentificationTypesByProcessId: function(processId) {
      	var procGr = new GlideRecord('pwd_process');
  	procGr.get(processId);
  	var idenSysIds = procGr.getValue('identification_type');
  	
  	var idenGr = new GlideRecord('pwd_identification_type');
  	idenGr.addQuery('sys_id', 'IN', idenSysIds);
  	idenGr.query();
  	var ids = [];
  	while (idenGr.next()) {
  		var details = {
  			'processorId': idenGr.getValue('identification_processor'),
  			'label': gs.getMessage(idenGr.getValue('identification_label'))
  		};
  		ids.push(details);
  	}
  	if (ids.length == 0) {
  		ids.push({
  			'processorId': 'default',
  			'label': gs.getMessage('Username')
  		});
  	}
  	return ids;
  },
  
  type:'PwdResetPageInfo',

};

Sys ID

3f7d6f17537103003248cfa018dc347c

Offical Documentation

Official Docs: