Built something you're proud of? Tell the story. A quick G2 review of App Engine or Build Agent helps other developers see what's possible on ServiceNow. Share your experience.

dvp
Mega Sage

User criteria is one of the wonderful features SN provided, but it is opened to only a set of tables.  It is a handy configuration to grant access to the record against user information like companies, roles, groups.

Recently there was a requirement where we need to grant read access to records dynamically. After a little research, I found out that there are similar needs by many developers. Below are some of scenarios that I came across

  • Catalogs
  • News
  • Announcements(Fixed in London)

Here is what I had implemented

1. Create a m2m relation between User Criteria and desired table (u_news). Navigate to sys_m2m.LIST and submit the details as shown in the screenshot

2. Add the user criteria related list to News form by Configure > Related Lists

3. Create a script include

Name: UserCriteria

Client callable: False

Script

var UserCriteria = Class.create();
UserCriteria.prototype = {
	initialize: function() {
	},
	
	
	canAccess: function(table, record, id){
		
		//Get all the user criteria's for the seleted record
		var usr_crit = new GlideRecord(table);
		usr_crit.addQuery(record, id);
		usr_crit.query();
		
		// Grant access to record If there are no user criteria's for the record
		if(usr_crit.getRowCount() == 0)
			return true;
		
		while (usr_crit.next()) {
			
			var crit = new GlideRecord('user_criteria');
			crit.addQuery('sys_id', usr_crit.u_user_criteria);
			crit.addActiveQuery();
			crit.query();
			
			while(crit.next()){
				
				//Get company, location and department information of the current user. 
				var gr = new GlideRecord('sys_user');
				gr.addQuery('sys_id', gs.getUserID());
				gr.query();
				
				var company, location, department = '';
				
				if(gr.next()){
					
					company = gr.company;
					location = gr.location;
					department = gr.department;
				}
				
				var groups = [];
				groups = crit.group.toString();
				
				var evaluator = new GlideScopedEvaluator();
				
				var match_arr = [];
				
				// Check if the current user is one of the users in user criteria
				if(crit.user != ''){
					
					if(crit.user.indexOf(gs.getUserID()) > -1)
						match_arr.push(true);
					else
						match_arr.push(false);
				}
				
				// Check if the Groups of a user belong to is one of groups in user criteria
				if(crit.group!= ''){
					match_arr.push(this._isMyGroup(groups));
				}
				
				
				// Check if logged in user roles is one of roles in user criteria
				if(crit.role != ''){
					match_arr.push(gs.hasRole(crit.role.getDisplayValue()));
				}
				
				// Check if the logged in user company is one of companies in user criteria
				if(crit.company != '' && company != ''){
					
					if(crit.company.indexOf(company) > -1)
						match_arr.push(true);
					else
						match_arr.push(false);
				}
				
				// Check if the logged in user location is one of locations in user criteria
				if(crit.location != '' && location != ''){
					if(crit.location.indexOf(location) > -1)
						match_arr.push(true);
					else
						match_arr.push(false);
				}
				
				// Check if the logged in user department is one of departments in user criteria
				if(crit.department != '' && department != ''){
					if(crit.department.indexOf(department) > -1)
						match_arr.push(true);
					else
						match_arr.push(false);
				}
				
				//execute the script in user criteria
				if(crit.advanced == true){
					match_arr.push(evaluator.evaluateScript(crit, 'script', ''));
				}
				
				
				var matched;
				
				//If Match all, in user criteria is marked false, then any one of fields match is enough
				if(crit.match_all == false){
					
					if(match_arr.indexOf(true) > -1)
						matched = true;
					else
						matched = false;
				}
				
				// If Match all is marked true then all of the values should be true
				else{
					if(match_arr.every(this._isValueTrue) == true)
						matched = true;
					else
						matched = false;
				}

				// Matching one of the user criteria should return the value. No need to execute the rest of the user criteria
				if(matched == true){
					return true;
				}
				
			}
		}
		// If none of the user criteria matches then return false
		return false;
		
	},
	
	_isMyGroup: function(groupIDs){
		
		var groups_arr = groupIDs.split(',');
		
		for(i=0; i<groups_arr.length; i++){
			if(gs.getUser().isMemberOf(groups_arr[i]))
				return true;
		}
		return false;
	},
	
	// Function to verify if all the values in an array are true
	_isValueTrue: function (currentValue) {
		return currentValue == true;
	},
	
	type: 'UserCriteria'
};

4. Create a read acl to restrict read access to the record and use the below script

answer = new UserCriteria().canAccess('u_m2m_news_user_criteria', 'u_news', current.sys_id);

/* canAccess menthod accepts the following input paramenters
table: M2M table name
record: Field name of News reference field on M2M table
id: sys_id of news record
*/

5. Use GlideRecordSecure for reference qualifers and glide queries, as GlideRecordSecure enforce read ACL rules.

 

Hope this helps!!

7 Comments