Catalog Item User Criteria "Available For" in custom query

xiaix
Tera Guru

So I was writing a custom query on the sc_cat_item table and realized that the results returned didn't honor any "Available For" or "Not Available For" records that were on an item... so I built a utility.

Example of usage:

var UC = new userCriteria();
var gr = new GlideRecord('sc_cat_item');
/* 
	Query logic
	-------------
	active = true:
		active=true

	Class is Catalog Item:
		sys_class_name=sc_cat_item

	Cagalogs = Service Catalog:
		sc_catalogs=e0d08b13c3330100c8b837659bba8fb4
*/
gr.addEncodedQuery("active=true^sys_class_name=sc_cat_item^sc_catalogs=e0d08b13c3330100c8b837659bba8fb4");
gr.query();
while (gr.next()) {
	var hasAccess = UC.hasAccess(gs.getUserID().toString(), 'sc_cat_item', gr.sys_id);
	if (!hasAccess)
		continue;
	items.push({
		'name':gr.name.toString(),
		'short':gr.short_description.toString(),
		'sysid':gr.sys_id.toString(),
		'meta':gr.meta.toString()
	});
}

The key lines being:

  • var UC = new userCriteria();
  • var hasAccess = UC.hasAccess(gs.getUserID().toString(), 'sc_cat_item', gr.sys_id);

The userCriteria() object handles all the logic needed to honor any "Available For" and "Not Available For" records attached to a catalog item.

I'll display the code here, but also attach it as a Script Include XML file you can import into your instance.

var userCriteria = Class.create();
userCriteria.prototype = {
	initialize: function() {
		this.ITERATION = 1;
	},
	'hasAccess' : function(userSysID, type, typeSysID) {
		var checkingForInclusion = false;
		if (this.ITERATION == 2)
			checkingForInclusion = true;
		if (!this.ITERATION || this.ITERATION > 2) {
			this.ITERATION = 1;
			return false;
		}
		var HAS_ACCESS = false;
		if (!checkingForInclusion)
			HAS_ACCESS = true;
		
		/* 
			We always start looking first to see if they are on the
			list(s) of "Not Available For". this.ITERATION will be 1.
			If on the list(s), don't even bother checking if they are
			on the "Available For" list(s).
			
			If there's no "Not Available For" list(s) to check or if the
			user is not on that list(s), this.ITERATION is set to 2 and
			this script is run again, this time checking to see if the
			user is on the "Available For" list(s).
			
			If there's an "Available For" list(s) and the user is NOT
			on it, user will NOT have access.
			If there's an "Available For" list(s) and the user IS on
			it, user WILL have access.
			
			If there are no lists to check, assume they have access.
		*/

		var criteria = false;
		var tbl = 'sc_cat_item_user_criteria_no_mtom';
		if (checkingForInclusion)
			tbl = 'sc_cat_item_user_criteria_mtom';
		if (type == 'sc_cat_item') {
			var listed_user = false, choice_user = false;
			var listed_groups = false, choice_groups = false;
			var listed_companies = false, choice_companies = false;
			var listed_departments = false, choice_departments = false;
			var listed_locations = false, choice_locations = false;
			var listed_roles = false, choice_roles = false;
			var listed_script = false, choice_script = false;
			var criteria_mtom = new GlideRecord(tbl);
			criteria_mtom.addQuery('sc_cat_item', typeSysID);
			criteria_mtom.query();
			while (criteria_mtom.next()) {
				if (checkingForInclusion && HAS_ACCESS)
					break;
				else if (!checkingForInclusion && !HAS_ACCESS)
					break;
				var matchAll = false;
				var uCrit = new GlideRecord('user_criteria');
				uCrit.addQuery('sys_id', criteria_mtom.user_criteria);
				uCrit.addActiveQuery();
				uCrit.query();
				if (uCrit.next()) {
					criteria = true;
					var i = 0;
					matchAll = uCrit.match_all;

					/* User in "Users" list? */
					if (!listed_user) {
						if (uCrit.user.toString().length)
							choice_user = true;
						if (uCrit.user.toString().indexOf(userSysID) >= 0)
							listed_user = true;
					}

					/* User in "Groups" list? */
					if (!listed_groups) {
						if (uCrit.group.toString().length) {
							choice_groups = true;
							listed_groups = this.isMemberOfGroup(userSysID, uCrit.group.toString());
						}
					}

					/* User in "Companies" list? */
					if (!listed_companies) {
						if (uCrit.company.toString().length) {
							choice_companies = true;
							listed_companies = this.userInfo(userSysID, 'company', uCrit.company.toString());
						}
					}

					/* User in "Departments" list? */
					if (!listed_departments) {
						if (uCrit.department.toString().length) {
							choice_departments = true;
							listed_departments = this.userInfo(userSysID, 'department', uCrit.department.toString());
						}
					}

					/* User in "Locations" list? */
					if (!listed_locations) {
						if (uCrit.location.toString().length) {
							choice_locations = true;
							listed_locations = this.userInfo(userSysID, 'location', uCrit.location.toString());
						}
					}

					/* User in "Roles" list? */
					if (!listed_roles) {
						if (uCrit.role.toString().length) {
							choice_roles = true;
							listed_roles = this.userHasRole(userSysID, uCrit.role.toString());
						}
					}

					/* Does the script evaluate to true? */
					if (!listed_script) {
						if (uCrit.script.toString().length && uCrit.advanced) {
							choice_script = true;
							var evaluator = new GlideScopedEvaluator();
							listed_script = evaluator.evaluateScript(uCrit);
						}
					}
				} else {
					criteria = false;
				}

				if (criteria) {
					if (matchAll) {
						if (choice_user && !listed_user) { checkingForInclusion ? HAS_ACCESS = false:HAS_ACCESS = true; }
						if (choice_groups && !listed_groups) { checkingForInclusion ? HAS_ACCESS = false:HAS_ACCESS = true; }
						if (choice_companies && !listed_companies) { checkingForInclusion ? HAS_ACCESS = false:HAS_ACCESS = true; }
						if (choice_departments && !listed_departments) { checkingForInclusion ? HAS_ACCESS = false:HAS_ACCESS = true; }
						if (choice_locations && !listed_locations) { checkingForInclusion ? HAS_ACCESS = false:HAS_ACCESS = true; }
						if (choice_roles && !listed_roles) { checkingForInclusion ? HAS_ACCESS = false:HAS_ACCESS = true; }
						if (choice_script && !listed_script) { checkingForInclusion ? HAS_ACCESS = false:HAS_ACCESS = true; }
					} else {
						if (listed_user) { checkingForInclusion ? HAS_ACCESS = true:HAS_ACCESS = false; }
						if (listed_groups) { checkingForInclusion ? HAS_ACCESS = true:HAS_ACCESS = false; }
						if (listed_companies) { checkingForInclusion ? HAS_ACCESS = true:HAS_ACCESS = false; }
						if (listed_departments) { checkingForInclusion ? HAS_ACCESS = true:HAS_ACCESS = false; }
						if (listed_locations) { checkingForInclusion ? HAS_ACCESS = true:HAS_ACCESS = false; }
						if (listed_roles) { checkingForInclusion ? HAS_ACCESS = true:HAS_ACCESS = false; }
						if (listed_script) { checkingForInclusion ? HAS_ACCESS = true:HAS_ACCESS = false; }
					}
				}
			}
		}

		if (!checkingForInclusion && !HAS_ACCESS) {
			this.ITERATION = 1;
			return false;
		}
		else if (!checkingForInclusion) {
			this.ITERATION += 1;
			return this.hasAccess(userSysID, type, typeSysID);
		}
		else if (checkingForInclusion) {
			this.ITERATION = 1;
			if (!criteria)
				return true;
			return HAS_ACCESS;
		}
		/* Should not reach here, but if we do... */
		gs.log("***Script Include: userCriteria: this.ITERATION: "+this.ITERATION+"  userSysID: "+userSysID+"  typeSysID: "+typeSysID+"  checkingForInclusion: "+checkingForInclusion+"  HAS_ACCESS: "+HAS_ACCESS+"  criteria: "+criteria);
		gs.addErrorMessage("Error. Please check logs");
		return false;
	},
	'isMemberOfGroup' : function (user, groups) {
		var gr = new GlideRecord("sys_user_grmember");
		gr.addQuery("group", "IN", groups);
		gr.addQuery("user", user);
		gr.query();
		if (gr.next()){
			return true;
		}
		return false;
	},
	'userInfo' : function (user, field, fieldVals) {
		var gr = new GlideRecord('sys_user');
		gr.addQuery('sys_id', user);
		gr.addQuery(field, 'IN', fieldVals);
		gr.addActiveQuery();
		gr.query();
		if (gr.next()) {
			return true;
		}
		return false;
	},
	'userHasRole' : function (userSysID, roles) {
		var g = gs.getUser().getUserByID(userSysID);
		var gr = new GlideRecord('sys_user_role');
		gr.addQuery('sys_id', 'IN', roles);
		gr.query();
		while (gr.next()) {
			if (g.hasRole(gr.name))
				return true;
		}
		return false;
	},

	type: 'userCriteria'
};

 

Enjoy.  I hope someone finds this useful.

2 REPLIES 2

DirkRedeker
Mega Sage

Wow!

 

Great thing, thanks so much for sharing! The functions used by service now are not exposed, so that is cool!

Have fun & Thanks a lot

BR

Dirk

JC S_
Mega Guru

Hi @xiaix - firstly, thanks for this! It has been extremely useful.

You may want to consider updating this script include to take into account the usage of default variable "user_id" on user criteria script which is technically equivalent to gs.getUser() as described here. Current script code returns an error for "user_id" being not defined when validating against a user criteria using the default variable.

We fixed this by simply defining the value of "user_id" variable by mapping "userSysID" to the script evaluator:

var vars = {
                'user_id': userSysID
            };
...

listed_script = evaluator.evaluateScript(uCrit, 'script', vars);

Hope this helps in case someone needs to make this work in their implementations.