Validate user with Jelly in UI Page

Luiz Lucena
Mega Sage

Hello everyone, 


I'm creating a copy of UI Page $pwd_reset_serviceDesk because we need to enhance that to restrict the list of users to show only certain accounts. 
The first requirement is to show only accounts active in AD, for that we have a custom field that is populated by LDAP, u_ad_account_status, whose value is either active or inactive.

The second requirement is to show in the dropdown only accounts whose description field contains the current logged in user email.

 

How should I query and present the user list in the dropdown based on those requirements?

Not sure if I should evaluate (and how) in the HTML section or the client script section:
Screenshot 2023-08-23 at 5.07.29 PM.png

 

Full script belows:

HTML:

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">

<!-- ACTUAL PAGE STARTS HERE -->

<g:macro_invoke macro="$pwd_reset_stylesheet"/>
<g:requires name="scripts/lib/jquery_includes.js" includes="true"/>
<g:macro_invoke macro="$pwd_error_message" error_message="" />
	
<nav class="navbar navbar-default" role="navigation">
	<div class="container-fluid">
		<div class="navbar-header">
			<button class="btn btn-default icon-chevron-left navbar-btn" onclick="goBack();" aria-label="${gs.getMessage('Back')}" title="${gs.getMessage('Back')}"></button>   
		</div>  
	</div>
</nav>

<g:macro_invoke macro="$pwd_process_flow" stage="1" />

<h1 class="sr-only">${gs.getMessage('ServiceNow Password Reset Service Desk, step 1 out of 3')}</h1>

<center>
  <div class="pwd-reset-box">
    <form>
      <h2 class="pwd-title">${gs.getMessage('Password Reset Assistance')}</h2>
      <h3 class="pwd-subtitle">${gs.getMessage('Select a user, then select a process, and click the "Verify Identity" button.')}</h3>

      <div id="user_id_form_group" class="form-group is-required">
        <label for="sys_display.ref_sys_user" class="control-label col-sm-3">
          <span class="required-marker sn-tooltip-basic" 
title="${gs.getMessage('Required')}" data-original-title="${gs.getMessage('Required')}" aria-label="${gs.getMessage('Required')}"></span>
          ${gs.getMessage('Select user')}
        </label>
        <span class="col-sm-9">
          <j:set var="jvar_user_id" value=""/> 
          <j:set var="jvar_user_name" value=""/> 

          <g:evaluate var="jvar_user_has_next" jelly="true">
          	var renUser = new GlideRecord("sys_user");
            renUser.addQuery("sys_id", jelly.sysparm_user);
			renUser.query();
            renUser.next();
          </g:evaluate>

          <j:if test="${jvar_user_has_next}">
            <j:set var="jvar_user_id" value="$[renUser.sys_id]"/>
            <j:set var="jvar_user_name" value="$[renUser.name]"/>
          </j:if>

          <g:macro_invoke macro="ui_reference" id="ref_sys_user" name="ref_sys_user" table="sys_user" show_popup="true" value="${jvar_user_id}" displayvalue="${jvar_user_name}" onchange="populateProcessSelect();" />
        </span>
        <span class="col-sm-3"/><span id="error_msg_id" class="help-block col-sm-9"/>
      </div>

      <div id="process_select_form_group" class="form-group is-required">
        <label for="processSelectId" class="control-label col-sm-3">
          <span class="required-marker sn-tooltip-basic" 
title="${gs.getMessage('Required')}" data-original-title="${gs.getMessage('Required')}" aria-label="${gs.getMessage('Required')}"></span>
          ${gs.getMessage("Select process")}
        </label>
        <span class="col-sm-9">
          <select id="processSelectId" class="form-control pwd-form-control select2" required="true" aria-required="true" name="processSelectId" onchange="enableVerifyButton();">
            <option value="N/A">${gs.getMessage('-- Select a process --')}</option>
          </select>
        </span>
        <span class="col-sm-3"/><span id="error_msg_select" class="help-block col-sm-9"/>
      </div>

      <input type="hidden" name="sysparm_action" id="sysparm_action" value="password_reset_form" />
      <button name="sysverb_pwd_reset" id="sysverb_pwd_reset" class="btn btn-primary btn-pwd-submit" onclick="return verifyIdentity();" aria-label="${gs.getMessage('Verify Identity')}" disabled="disbaled">${gs.getMessage('Verify Identity')}</button>
    </form>

    <!--  
      Adding a new CSRF token here. This page does not check whether there's a violation or not because this page 
      is the starting point.
    -->

    <g:evaluate var="jvar_csrf_token" jelly="true">
      new SNC.PwdSecurityManager().generateSecureToken();
    </g:evaluate>

    <form id="pwd_master_form" name="pwd_master_form" method="post">
      <input type="hidden" id="sysparm_error" name="sysparm_error" value=""/>
      <input type="hidden" id="pwd_csrf_token" name="pwd_csrf_token" value="${jvar_csrf_token}"/>
    </form>
  </div>
</center>
</j:jelly>

 

Client:

$j('title').text("${gs.getMessage('Password Reset - Service Desk')}");
addLoadEvent(onLoadPopulateProcessSelect);
addLoadEvent(focusOnCorrectField);
	
function goBack() {
    window.history.back();
}

function enableVerifyButton() {
	clearErrorMessage();
	if(document.getElementById("processSelectId").value == 'N/A')
		document.getElementById("sysverb_pwd_reset").disabled = true;
	else
		document.getElementById("sysverb_pwd_reset").disabled = false;
}

function focusOnCorrectField() {
	var user = gel('sys_display.ref_sys_user');
	if ((user != undefined) && (user.value == "")) {
		user.focus();
		return;
	}
	gel('processSelectId').focus();
}

function onLoadPopulateProcessSelect() {
	var fromSysUserForm = gel('ref_sys_user').value;
	if (fromSysUserForm.length > 0) {
		populateProcessSelect();
	}
}

function clearErrorMessage() {
	clearFieldError('error_msg_id', 'user_id_form_group');
	clearFieldError('error_msg_select', 'process_select_form_group');
}

function populateProcessSelect() {
	clearErrorMessage();
	var user_id = gel('ref_sys_user').value;
	
	var sel = document.getElementById("processSelectId");
	sel.options.length = 1;
	
	if (user_id != "") {
		
		var ga = new GlideAjax('PwdAjaxVerifyIdentityServiceDesk');
		ga.addParam('sysparm_name', 'getProcessNamesAsync');
		ga.addParam('sysparm_user', user_id);
		ga.getXML(function(response) {
			var processes = response.responseXML.getElementsByTagName("process");
			var numProcesses = processes.length;
			for (var i = 0; i < processes.length; i++) {
				var name = processes[i].getAttribute("name");
				var procSysId = processes[i].getAttribute("procSysId");
				sel[i + 1] = new Option(name, procSysId);
			}
			if (numProcesses == 0) {
				displayFieldError("error_msg_select", getMessage('No service-desk processes found for the user selected.'), "process_select_form_group");
			}
		});
	}
}

var wasSubmitted = false;
function verifyIdentity() {
	if (wasSubmitted)
		return false;
	wasSubmitted = true;
	
	clearErrorMessage();
	var user_id = gel('ref_sys_user').value;
	if ((user_id == undefined) || (user_id == "")) {
		displayFieldError("error_msg_id", getMessage("Invalid user"), "user_id_form_group");
		wasSubmitted = false;
		return false;
	}
	
	var sel = document.getElementById("processSelectId");
	var procSysId = sel.options[sel.selectedIndex].value;
	
	if (sel.selectedIndex == 0) {
		displayFieldError("error_msg_select", getMessage("Invalid selection"), "process_select_form_group");
		wasSubmitted = false;
		return false;
	}
	
	//showPwdLoadingDialog(); //Removing the PopUp as the Call has been made Asynchronous
	var ga = new GlideAjax('PwdAjaxVerifyIdentityServiceDesk');
	ga.addParam('sysparm_name', 'saveAndProceed');
	ga.addParam('sysparm_user_id', user_id);
	ga.addParam('sysparm_procSysId', procSysId);
	
	ga.getXML(function(response) {
		var result = response.responseXML.getElementsByTagName("result");
		var status = result[0].getAttribute("status");
		
		if (!status.match(/success/i)) {
			displayFieldError("error_msg_id", status, "user_id_form_group");
		} else {
			var submitForm = gel('pwd_master_form');
			submitForm.action = '$pwd_verify.do';
			submitForm.submit();
		}
		wasSubmitted = false;
	});
	return false;
}


function isEmailAddress(str) {
	var email_pattern = /^\w+(\.\w+)*@\w+(\.\w+)+$/;
	return str.match(email_pattern);
}

function errorImage() {
	return '<img src="images/outputmsg_error.gifx" alt="Error Message" />';
}

function showPwdLoadingDialog() {
	loadingDialog = new GlideModal("pwd_request_in_progress", true);
    loadingDialog.setTitle(getMessage('Request in progress'));
    loadingDialog.render();
}

 

6 REPLIES 6

Peter Bodelier
Giga Sage

Hi @Luiz Lucena,

 

I believe it should be enough to change the query of the reference field in HTML:

 

Change 

<g:macro_invoke macro="ui_reference" id="ref_sys_user" name="ref_sys_user" table="sys_user" show_popup="true" value="${jvar_user_id}" displayvalue="${jvar_user_name}" onchange="populateProcessSelect();" />

To

<g:macro_invoke macro="ui_reference" id="ref_sys_user" query="u_ad_account_status=active" name="ref_sys_user" table="sys_user" show_popup="true" value="${jvar_user_id}" displayvalue="${jvar_user_name}" onchange="populateProcessSelect();" />

 

I added your first requirement in the query, not sure what you expect with your second one though.


Help others to find a correct solution by marking the appropriate response as accepted solution and helpful.

Thanks, Peter!

Will try that and post the results here.

For the second requeriments, let me see if I can elaborate better.

We import our user data from Active Directory, some users also have an elevated account, which may start with SA.user_name or DA.user_name. 
They want to allow users to reset only their elevated password, for example:

I'm currently logged in ServiceNow to reset my elevated password, when I search for users in the "Select User" field, only the accounts belonging to me should show up.

 

Let me know if is possible.

 

EDIT: that query in the macro_invoke worked! Thanks, man! I was trying the whole time in the wrong place.

Hi @Peter Bodelier , 

I'm getting very close to the query we need.
In the user table filter, I coluld get a working filter to return exactly what we need, but I'm struggle to add to the HTML code. 

Here is the working query in our user table:

user_nameLIKEjavascript&colon;var user = new GlideRecord("sys_user"); user.get(gs.getUserID()); user.user_name;^sourceLIKEPriv

Screenshot 2023-08-25 at 3.06.44 PM.png

Maybe we don't even need to filter by active accounts, will see, but look how it got on the HTML:
Screenshot 2023-08-25 at 3.13.04 PM.png

the double quotes broke the line, tried with single quote but it didn't do anything.

Hi @Luiz Lucena,

 

I'm not completely sure if you can use a javascript query in the query attribute but its worth a try. It should be accepted if you replace the double quotes around sys_user by single quotes.


Help others to find a correct solution by marking the appropriate response as accepted solution and helpful.