- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
Hi All,
I have a requirement only opened by should able to see the Req and RITM not other even requested for should not able to see the request for particular catalog item.
I tried restricted via ACL and Query BR but still requested for and other ITIL users able to see the RITM and REQ.
Please guide me which is the best approach.
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
you can enhance that
something like this
(function executeRule(current, previous /*null when async*/ ) {
if (!gs.getSession().isInteractive() || gs.hasRole('admin')) {
return;
}
var catItemId = 'PUT_CATALOG_ITEM_SYS_ID_HERE';
var allowedGroupId = 'PUT_GROUP_SYS_ID_HERE';
var userId = gs.getUserID();
// If user is in the allowed group, allow full access to this catalog item's RITMs
if (gs.getUser().isMemberOf(allowedGroupId)) {
return;
}
// For the restricted catalog item:
// show only records where request.opened_by is current user
// all other catalog items remain visible as usual
current.addEncodedQuery(
'cat_item!=' + catItemId +
'^NQcat_item=' + catItemId + '^request.opened_by=' + userId
);
})(current, previous);
💡 If my response helped, please mark it as correct ✅ and close the thread 🔒— this helps future readers find the solution faster! 🙏
Ankur
✨ Certified Technical Architect || ✨ 10x ServiceNow MVP || ✨ ServiceNow Community Leader
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
Hello,
You can’t reliably achieve this just with basic ACLs or a query BR, because by default ServiceNow allows access to requested_for and ITIL users through standard roles; the better approach is to create record-level ACLs on sc_request and sc_req_item that explicitly allow access only when opened_by == gs.getUserID() (and remove other default conditions/roles if needed), or use a before query Business Rule with addQuery('opened_by', gs.getUserID()) to strictly filter records—just make sure you override OOB access logic carefully so requested_for and ITIL users are excluded only for that specific catalog item.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
Hi @@s_nandhini
This is a very common requirement, but it can be surprisingly frustrating to implement because of how ServiceNow evaluates security.
The reason your ACL approach is failing is due to the "OR" condition rule in ServiceNow ACLs. Out-of-the-box (OOB), the itil role has an ACL granting read access to the entire sc_req_item and sc_request tables. Even if you create a new ACL to restrict your specific catalog item (which evaluates to false), the system still evaluates the OOB itil ACL (which evaluates to true). Because true OR false = true, the ITIL users will still see the records.
Modifying the OOB ACLs to fix this is highly discouraged, as it creates technical debt and upgrade overhead. The cleanest, most maintainable best practice for row-level data segregation—without throwing ugly "Security constraints prevent access..." messages in list views—is to use Before Query Business Rules.
Here is the step-by-step solution to achieve exactly what you need:
Step 1: Hide the RITM (sc_req_item)
Create a Before Query Business Rule on the sc_req_item table. This modifies the database query before the list even loads, ensuring unauthorized users never even query the restricted item.
Table: sc_req_item
When: Before, Query
Advanced: True
Script:
(function executeRule(current, previous /*null when async*/) { // 1. Do not restrict admins if (gs.hasRole('admin')) { return; } var restrictedItemSysId = 'YOUR_CATALOG_ITEM_SYS_ID_HERE'; // Replace with your Item's Sys ID var userId = gs.getUserID(); // 2. Show the record if it is NOT the restricted item, // OR if it IS the restricted item, the current user MUST be the 'opened_by' var qc = current.addQuery('cat_item', '!=', restrictedItemSysId); qc.addOrCondition('opened_by', userId); })(current, previous);
Step 2: Hide the REQ (sc_request)
Because Requests do not have a cat_item field directly (a single REQ can contain multiple different RITMs), hiding the REQ requires querying the related RITMs first.
Create a Before Query Business Rule on the sc_request table.
Table: sc_request
When: Before, Query
Advanced: True
Script:
(function executeRule(current, previous /*null when async*/) { // 1. Do not restrict admins if (gs.hasRole('admin')) { return; } var userId = gs.getUserID(); var restrictedItemSysId = 'YOUR_CATALOG_ITEM_SYS_ID_HERE'; // Replace with your Item's Sys ID // 2. Find all RITMs of this specific catalog item where the user is NOT the opened_by var ritmGr = new GlideRecord('sc_req_item'); ritmGr.addQuery('cat_item', restrictedItemSysId); ritmGr.addQuery('opened_by', '!=', userId); ritmGr.query(); var hiddenReqIds = []; while(ritmGr.next()) { if (ritmGr.request) { hiddenReqIds.push(ritmGr.request.toString()); } } // 3. If we found restricted RITMs, filter their parent REQs out of the current view if (hiddenReqIds.length > 0) { current.addQuery('sys_id', 'NOT IN', hiddenReqIds.join(',')); } })(current, previous);
(Note: If this specific catalog item generates tens of thousands of requests per month, the NOT IN query on the REQ table might need performance tuning in the future, but for standard volumes, this is the most effective approach).
Give this a try in your sub-production environment. It should cleanly hide both the REQ and RITM from everyone except the opened_by user and admins! Let me know if you run into any issues.
Thanks,
Yogesh
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
@Yogesh11bhatt can we also add assignment group?
Only opened_by and particular group should have access to the RITM and req?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
Hi @s_nandhini,
Yes, absolutely! The benefit of using the Before Query Business Rule approach is how easily we can scale it to accommodate new requirements like this.
If you have a "particular group" (like a specific fulfillment or confidential team) that needs to see these records to work on them, we can simply check if the logged-in user is a member of that group. If they are, we allow them to bypass the restriction entirely—just like we did for admins.
You only need to update the first few lines of both scripts to include the gs.getUser().isMemberOf() method.
Here are the updated scripts:
Updated Step 1: Hide the RITM (sc_req_item)
(function executeRule(current, previous /*null when async*/) { // 1. Do not restrict admins OR members of the allowed group var allowedGroup = 'YOUR_ALLOWED_GROUP_SYS_ID_OR_NAME'; // e.g., 'HR Security' or its Sys ID if (gs.hasRole('admin') || gs.getUser().isMemberOf(allowedGroup)) { return; } var restrictedItemSysId = 'YOUR_CATALOG_ITEM_SYS_ID_HERE'; // Replace with your Item's Sys ID var userId = gs.getUserID(); // 2. Show the record if it is NOT the restricted item, // OR if it IS the restricted item, the current user MUST be the 'opened_by' var qc = current.addQuery('cat_item', '!=', restrictedItemSysId); qc.addOrCondition('opened_by', userId); })(current, previous);
Updated Step 2: Hide the REQ (sc_request)
(function executeRule(current, previous /*null when async*/) { // 1. Do not restrict admins OR members of the allowed group var allowedGroup = 'YOUR_ALLOWED_GROUP_SYS_ID_OR_NAME'; // Must match the RITM script if (gs.hasRole('admin') || gs.getUser().isMemberOf(allowedGroup)) { return; } var userId = gs.getUserID(); var restrictedItemSysId = 'YOUR_CATALOG_ITEM_SYS_ID_HERE'; // Replace with your Item's Sys ID // 2. Find all RITMs of this specific catalog item where the user is NOT the opened_by var ritmGr = new GlideRecord('sc_req_item'); ritmGr.addQuery('cat_item', restrictedItemSysId); ritmGr.addQuery('opened_by', '!=', userId); ritmGr.query(); var hiddenReqIds = []; while(ritmGr.next()) { if (ritmGr.request) { hiddenReqIds.push(ritmGr.request.toString()); } } // 3. If we found restricted RITMs, filter their parent REQs out of the current view if (hiddenReqIds.length > 0) { current.addQuery('sys_id', 'NOT IN', hiddenReqIds.join(',')); } })(current, previous);
gs.getUser().isMemberOf('group_name') returns true if the current user belongs to that group. By placing it inside our initial if statement, we tell the system: "If this user is an Admin OR if they are a member of the allowed group, stop the business rule right now and let them see the records normally."
If they aren't an admin and aren't in that group, the script continues and applies the strict opened_by filter we built earlier.
Let me know if that works for your group requirement!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
Did my reply answer your question?
If my response helped, please mark it as correct
— this helps future readers find the solution faster!
Yogesh
