Name
global.PwdIdentifyStageBL
Description
Business logic for the first stage, Identify, of the Password Reset flow
Script
var PwdIdentifyStageBL = Class.create();
PwdIdentifyStageBL.prototype = Object.extendsObject(PwdResetStageBaseBL, {
REQUEST_TYPE: 1, // self-service reset password
DEFAULT_SELF_SERVICE_PROCESS_ID: 'c6b0c20667100200a5a0f3b457415ad5',
NOTIFY_EMAIL: "2",
PWD_RESET_URL_NOTIFICATION: "9546d4509f131200f45c7b9ac42e70ca",
UNSUBSCRIBE_NOTIFICATION_FILTER: "c1bfa4040a0a0b8b001eeb0f3f5ee961",
PWD_RESET_NOTITIFICATION_EVENT: 'pwd.reset.identify.error.notification',
USER_NOT_EXIST: 1,
BLOCKED: 2,
LOCKED: 3,
NOT_ENROLLED: 4,
NOT_BELONG_TO_PROCESS: 5,
NOTIFICATIONS_DISABLED: 6,
LDAP_USER: 7,
WEB: "Web",
VIRTUAL_AGENT: "VA",
WINDOWS: "Windows",
IVR: "IVR",
initialize: function() {
this.STAGE = PwdConstants.STAGE_IDENTIFICATION;
this.invalidRequest = false;
this.requestSource = 0; //Default is Web
},
// @param idenObjs [{user_input, processor_id}]
verifyIdentity: function(processId, idenObjs, captchaText, webRequest, processCaptcha) {
// return error if the process is undefined.
var process = new SNC.PwdProcess(processId);
if (!process.exists()) {
this.logError("Process does not exist (" + processId + ").", "");
return "process does not exists";
}
//If captcha is enabled, check if the answer is correct.
if ((processCaptcha == null || processCaptcha == true) && process.getDisplayCaptcha()) {
if (this._isGoogleCaptchaUsed()) {
if (!this._vefifyGoogleCaptchaToken(captchaText)) {
this.logWarning("unsuccessful Google recaptcha validation", "");
return "bad captcha";
}
} else if (!this._verifyDefaultCaptcha(captchaText)) {
// this is the default captcha.
// Check if the passed captch matches the one that was shown to the user:
this.logWarning("Captcha does not match", "");
return "bad captcha";
}
} // end of captcha handling.
// Fetch user sys ID based on the user input
var userSysId;
var res = this._getUserSysId(idenObjs, process, webRequest);
if (!res.status)
return res.returnMsg;
else
userSysId = res.userSysId;
// Start logging the password reset request:
var trackingMgr = this.trackingMgr;
var request_id = trackingMgr.createRequest(processId, userSysId, gs.getSessionID(), this.REQUEST_TYPE);
this.requestSource = this._getRequestSource(webRequest.getParameter('sysparm_request_source') + "");
// Prepare session params to pass to next step:
gs.getSession().putProperty('sysparm_request_id', request_id);
gs.getSession().putProperty('sysparm_sys_user_id', userSysId);
gs.getSession().putProperty('sysparm_user_input', idenObjs[0].user_input);
gs.getSession().putProperty('sysparm_directory', this.type);
if (this.invalidRequest) {
var userDoesnotExistMsg = "User does not exist associated with process_id = " + processId + " and request_id = " + request_id;
this.logWarning(userDoesnotExistMsg, request_id);
return this._updateResetRequest(userSysId, request_id, false, this.USER_NOT_EXIST);
}
var gr = new GlideRecord("pwd_reset_request");
gr.get(request_id);
// For the default self service process, return error if the user is locked out and process unlock_account is off
if (this._lockedOutError(userSysId, processId)) {
var lockedOutMsg = "User cannot reset password as user associated with process_id = " + processId + " and user_sys_id = " + userSysId + ") is locked out";
return this._raiseNotificationEvent(lockedOutMsg, request_id, gr, userSysId, this.LOCKED);
}
// return error if the user is blocked
if (trackingMgr.isRequestLocked(userSysId, processId)) {
var blockedMsg = "User cannot reset password as user associated with process_id = " + processId + " and user_sys_id = " + userSysId + ") is blocked";
return this._raiseNotificationEvent(blockedMsg, request_id, gr, userSysId, this.BLOCKED);
}
// Return error if the user does not belong to this process:
var enrollMgr = new SNC.PwdEnrollmentManager();
if (!enrollMgr.doesUserBelongToProcess(userSysId, processId)) {
var userMsg = "User cannot reset password as user does not belong to process (process_id = " + processId + ", user_sys_id = " + userSysId + ")";
return this._raiseNotificationEvent(userMsg, request_id, gr, userSysId, this.NOT_BELONG_TO_PROCESS);
}
// Return error if the process emails a password reset url or emails new password via email, but the user has disabled notifications, or url notification for primary email
if (process.getEmailPasswordResetUrl() || process.getSendEmail()) {
var userGr = new GlideRecord("sys_user");
userGr.get(userSysId);
var email = userGr.getValue("email");
var mobile = userGr.getValue("mobile_phone");
var disabledNotifMsg = "User cannot reset password as user disabled notifications for email (process_id = " + processId + ", user_sys_id = " + userSysId + ")";
if ((gs.nil(email) && gs.nil(mobile)) || userGr.getValue("notification") != this.NOTIFY_EMAIL) {
return this._raiseNotificationEvent(disabledNotifMsg, request_id, gr, userSysId, this.NOTIFICATIONS_DISABLED);
}
var deviceGr = new GlideRecord("cmn_notif_device");
deviceGr.addQuery("user", userSysId);
deviceGr.addQuery("email_address", email);
deviceGr.addQuery("type", "Email"); //DEF0061843
deviceGr.query();
deviceGr.next();
var notifGr = new GlideRecord("cmn_notif_message");
notifGr.addQuery("user", userSysId);
notifGr.addQuery("device", deviceGr.getValue("sys_id"));
notifGr.addQuery("notification", this.PWD_RESET_URL_NOTIFICATION);
notifGr.query();
notifGr.next();
var notifFilter = notifGr.getValue("notification_filter");
deviceGr = new GlideRecord("cmn_notif_device");
deviceGr.addQuery("user", userSysId);
deviceGr.addQuery("phone_number", mobile);
deviceGr.addQuery("type", "SMS"); //DEF0061843
deviceGr.query();
deviceGr.next();
notifGr = new GlideRecord("cmn_notif_message");
notifGr.addQuery("user", userSysId);
notifGr.addQuery("device", deviceGr.getValue("sys_id"));
notifGr.addQuery("notification", this.PWD_RESET_URL_NOTIFICATION);
notifGr.query();
notifGr.next();
var notifFilterSMS = notifGr.getValue("notification_filter");
if (notifFilterSMS == this.UNSUBSCRIBE_NOTIFICATION_FILTER && notifFilter == this.UNSUBSCRIBE_NOTIFICATION_FILTER) {
return this._raiseNotificationEvent(disabledNotifMsg, request_id, gr, userSysId, this.NOTIFICATIONS_DISABLED);
}
}
var count = 0;
var processManager = new SNC.PwdProcessManager();
// Retrieve and save all MANDATORY verifications:
var mandatoryVerificationIds = processManager.getProcessVerificationIdsByMandatoryFlag(processId, true);
var vArr = mandatoryVerificationIds.toArray();
var verificationId;
var userEnrolled;
var verification;
var notEnrolledMsg;
for (var i = 0; i < vArr.length; i++) {
verificationId = mandatoryVerificationIds.get(i);
userEnrolled = enrollMgr.isUserEnrolledByVerificationId(userSysId, verificationId);
if (!userEnrolled) {
notEnrolledMsg = "User cannot reset password as user is not enrolled (process_id = " + processId + ", user_sys_id = " + userSysId + ")";
return this._raiseNotificationEvent(notEnrolledMsg, request_id, gr, userSysId, this.NOT_ENROLLED);
}
count++;
}
// Retrieve and save all OPTIONAL verifications:
var optionalVerificationIds = processManager.getProcessVerificationIdsByMandatoryFlag(processId, false);
vArr = optionalVerificationIds.toArray();
for (i = 0; i < vArr.length; i++) {
verificationId = optionalVerificationIds.get(i);
userEnrolled = enrollMgr.isUserEnrolledByVerificationId(userSysId, verificationId);
if (userEnrolled) {
count++;
}
}
if (count < process.getMinVerifications()) {
notEnrolledMsg = "User cannot reset password as user is not enrolled (process_id = " + processId + ", user_sys_id = " + userSysId + ")";
return this._raiseNotificationEvent(notEnrolledMsg, request_id, gr, userSysId, this.NOT_ENROLLED);
}
if (this._verifyLDAPUser(userSysId, processId)) {
var LDAPUserMsg = "User cannot reset password as user is verified as LDAP (process_id = " + processId + ", user_sys_id = " + userSysId + ")";
return this._raiseNotificationEvent(LDAPUserMsg, request_id, gr, userSysId, this.LDAP_USER);
}
// Update the password-reset request record (yey!, this is a valid, enrolled user):
return this._updateResetRequest(userSysId, request_id, true);
},
_getRequestSource: function(requestSource) {
switch (requestSource) {
case this.WEB:
return 0;
case this.VIRTUAL_AGENT:
return 1;
case this.WINDOWS:
return 2;
case this.IVR:
return 3;
default:
return 0;
}
},
_raiseNotificationEvent: function(warningMsg, requestId, gr, userSysId, invalidReason) {
this.logWarning(warningMsg, requestId);
this.invalidRequest = true;
gs.eventQueue(this.PWD_RESET_NOTITIFICATION_EVENT, gr, userSysId, null);
this.logInfo("User identified successfully but cannot reset password due to errors (user_sys_id = " + userSysId + ")", requestId);
return this._updateResetRequest(userSysId, requestId, false, invalidReason);
},
_updateResetRequest: function(userSysId, requestId, isValid, invalidReason) {
if (isValid)
this.logInfo("User identified successfully (user_sys_id = " + userSysId + ")", requestId);
var req = new GlideRecord('pwd_reset_request');
if (req.get(requestId)) {
if (this.invalidRequest) {
req.invalid = 1; // request marked as invalid
req.invalid_reason = invalidReason;
req.update();
}
if (req.lock_state != 0) {
req.lock_state = 0; // unknown
req.update();
}
req.source = this.requestSource;
req.update();
}
this._resetVerificationsStatus(requestId);
return "ok";
},
_resetVerificationsStatus: function(requestId) {
var gr = new GlideRecord('pwd_map_request_to_verification');
gr.addQuery('request', requestId);
gr.query();
while (gr.next()) {
gr.setValue('status', 'not_verified');
gr.update();
}
},
/**
* Tests if google captcha is being used or not.
*/
_isGoogleCaptchaUsed: function() {
if (gs.getSession().getProperty('sysparm_is_desktop_request') == 'true') {
return false;
}
return gs.getProperty('password_reset.captcha.google.enabled') == 'true';
},
_vefifyGoogleCaptchaToken: function(token) {
var captcha = new global.GoogleCaptcha();
return captcha.verifyCaptcha(token);
},
/**************
* Provide default behavior. Since identification type is not mandatory
* in paswod reset process.
* mandatory field in pwd_verification table.
*
* returns userSysId
*
* Params:
* @userInput
*/
_getDefaultUserSysId: function(userInput) {
var gr = new GlideRecord('sys_user');
gr.addQuery('user_name', userInput);
gr.query();
if (!gr.next()) {
this.invalidRequest = true;
return userInput;
}
return gr.sys_id;
},
_getUserSysId: function(idenObjs, process, webRequest) {
var userSysId = '';
var res = {
status: false
};
for (var i = 0; i < idenObjs.length; i++) {
var idenObj = idenObjs[i];
// No identification type for the process then go with the default one
if (idenObj.processor_id == 'default') {
res.userSysId = this._getDefaultUserSysId(idenObj.user_input);
if (gs.nil(res.userSysId)) {
this.logWarning("User does not exist (" + idenObj.user_input + ")", "");
res.returnMsg = "user does not exists";
} else
res.status = true;
return res;
}
// Evaluate identification type extension script to get user sys ID
var tmpRes = this._getUserSysIdExtensionScript(idenObj.user_input, idenObj.processor_id, process, webRequest);
// Identification fails
if (!tmpRes.status) {
this.logError(tmpRes.activityLogMsg, "");
res.returnMsg = tmpRes.returnMsg;
return res;
}
// User does not exist or user sys ID does not match
else if (gs.nil(tmpRes.userSysId)) {
this.logWarning("User does not exist (" + idenObj.user_input + ")", "");
res.returnMsg = "user does not exists";
return res;
} else if (i > 0 && tmpRes.userSysId != userSysId) {
this.invalidRequest = true;
tmpRes.userSysId = idenObj.user_input;
}
userSysId = tmpRes.userSysId;
}
res.status = true;
res.userSysId = userSysId;
return res;
},
/**************
* Params:
* @identificationProcessorScript - Script that is used for verification
* @userInput
* @idenProcessorId
* @process
* @returns object {status: - signals if function has valid result
* userSysId: - Sys-id for user or null if not found
* returnMsg
* activityLogMsg}
*/
_getUserSysIdExtensionScript: function(userInput, idenProcessorId, process, webRequest) {
var res = {};
res.status = false;
try {
// Invoke the specific identification form processor extension
// Published interface for identification_form_processors:
//
// @param params.processId The sys-id of the calling password-reset process (table: pwd_process)
// @param params.userInput The user input to verify the identity
// @param request The form request object. fields in the form can be accessed using: request.getParameter('<element-id>')
// @return The sys-id of the user that corresponds to the requested input; if no user was found, null should be returned.
var params = new SNC.PwdExtensionScriptParameter();
params.processId = process.getId();
params.userInput = userInput;
var identificationExtension = new SNC.PwdExtensionScript(idenProcessorId + '');
var identifyResult = identificationExtension.processForm(params, webRequest);
// Check we have either a null (no user found), a string or GlideElement (representing a Sys-id) result - Every other type we consider breaking the contract
var resultType = (typeof(identifyResult) !== 'undefined') ? Object.prototype.toString.call(identifyResult).slice(8, -1).toLowerCase() : undefined;
res.status = true;
if (!((resultType !== undefined && identifyResult === null) ||
(resultType === 'GlideElement'.toLowerCase()) ||
((resultType === 'String'.toLowerCase()) && (identifyResult.trim().length > 0)))) {
res.activityLogMsg = "Error running identification processor " + idenProcessorId + ". " +
"Unexpected return value: " + identifyResult;
res.returnMsg = "user identification failed";
res.userSysId = userInput;
this.invalidRequest = true;
return res;
}
if (resultType !== undefined && identifyResult !== null) {
res.userSysId = identifyResult.toString();
} else {
this.invalidRequest = true;
res.userSysId = userInput;
}
} catch (err) {
// Most likely the processing script does not exists!!!
res.activityLogMsg = "Error running identification processor " + idenProcessorId + ". " + err;
res.returnMsg = "user identification failed";
}
return res;
},
/**************
* returns true/false whether the captcha that is passed to the function is indeed the one that was presented to the user.
*
* Params:
* @captcha - String containing the input captcha
*/
_verifyDefaultCaptcha: function(captchaText) {
return new sn_pwdreset_api.PwdImageCaptcha().validateCaptchaValue(captchaText);
},
_lockedOutError: function(userSysId, processId) {
if (processId == this.DEFAULT_SELF_SERVICE_PROCESS_ID) {
var grProcess = new GlideRecord("pwd_process");
grProcess.get(processId);
if (!grProcess.unlock_account) {
var grUser = new GlideRecord("sys_user");
grUser.get(userSysId);
return grUser.locked_out;
}
}
return false;
},
// Skip the reset process for LDAP users when using self service password reset.
_verifyLDAPUser: function(userSysId, processId) {
if (processId == 'c6b0c20667100200a5a0f3b457415ad5' && SNC.PasswordResetUtil.isLDAPUser(userSysId))
return true;
return false;
},
type: 'PwdIdentifyStageBL'
});
Sys ID
1b54c60667100200a5a0f3b457415aac