Implementing Role‑Based Restrictions for On‑Call Schedule Visibility
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 hours ago
Hi Folks,
We are looking to implement a functionality on the Create/Edit Schedule page that restricts on‑call managers from viewing or editing schedules belonging to other on‑call groups.
Our proposed approach is to create custom ACLs on the On‑Call Schedule table (cmn_on_call_schedule) with the following access rules:
Access (read/write) should be allowed only if:
- The logged‑in user matches the value in a Schedule Owner field (this field would store the on‑call group manager), or
- The logged‑in user is a member of the MIM_On‑Call Shift Administrator group.
However, we’ve identified a few challenges:
- The on‑call schedule ID is being passed through an OOB UI page, and since this is out‑of‑the‑box functionality, we want to avoid customizing it.
- Because of this limitation, we are unable to add the Schedule Owner field directly in the Define Schedule area.
- We also attempted to enforce this behavior using ACLs alone, but we were unable to achieve the expected results.
We’re looking for recommendations or alternative approaches that would allow us to enforce this restriction without modifying OOB UI pages and while staying within best practices.
Thanks in advance for your support.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 hours ago
A quick clarification — cmn_on_call_schedule is not an OOB ServiceNow table. The On-Call Scheduling module uses the following tables:
cmn_rota— On-Call Rotation/Shift (has thegroupreference field directly)cmn_schedule— Schedule (generic, shared across SLAs, business hours, etc.)cmn_rota_roster— Rostercmn_rota_member— On-Call Membercmn_schedule_span— Schedule Entry
I'd recommend verifying which table the OOB UI page is querying — most likely it's reading from cmn_rota and cmn_schedule. The key table for your use case is cmn_rota since it has a direct group field linking to sys_user_group.
Here's an approach that enforces your restriction at the data layer without touching the OOB UI page:
1. ACL on cmn_rota (Read/Write) — Record-Level Enforcement
This prevents direct access via URL or sys_id:
// Advanced script in ACL on cmn_rota
answer = gs.hasRole('admin')
|| gs.getUser().isMemberOf('MIM_On-Call Shift Administrator')
|| isGroupManager(current);
function isGroupManager(rota) {
var groupId = rota.group.toString();
if (!groupId) return false;
var grGroup = new GlideRecord('sys_user_group');
if (grGroup.get(groupId)) {
return grGroup.manager.toString() === gs.getUserID();
}
return false;
}
2. Before Query Business Rule on cmn_rota — Row-Level Filtering
This controls which schedules appear in lists and pickers:
(function executeRule(current, previous) {
if (gs.hasRole('admin') || gs.getUser().isMemberOf('MIM_On-Call Shift Administrator')) return;
var managedGroups = [];
var gr = new GlideRecord('sys_user_group');
gr.addQuery('manager', gs.getUserID());
gr.query();
while (gr.next()) managedGroups.push(gr.sys_id.toString());
if (managedGroups.length > 0) {
current.addQuery('group', 'IN', managedGroups.join(','));
} else {
current.addQuery('sys_id', '=', 'NULL');
}
})(current, previous);
3. Extend ACLs to Related Tables
Since the calendar UI page also reads from cmn_schedule and cmn_schedule_span, you'll likely need similar ACLs on those tables to fully restrict visibility. For cmn_schedule, the script would need to reverse-lookup via cmn_rota to find the associated group:
// ACL Script on cmn_schedule (Read)
answer = gs.hasRole('admin')
|| gs.getUser().isMemberOf('MIM_On-Call Shift Administrator')
|| isRelatedGroupManager(current);
function isRelatedGroupManager(schedule) {
var grRota = new GlideRecord('cmn_rota');
grRota.addQuery('schedule', schedule.sys_id);
grRota.query();
while (grRota.next()) {
var grGroup = new GlideRecord('sys_user_group');
if (grGroup.get(grRota.group.toString())) {
if (grGroup.manager.toString() === gs.getUserID()) return true;
}
}
return false;
}
Key Notes:
- Use both the Before Query BR and the ACL together — the BR filters what users see in lists, while the ACL enforces access if someone navigates directly to a record.
- No OOB UI page changes required — all restrictions are enforced at the data layer.
- Make sure no higher-priority wildcard ACL on these tables is granting blanket access — this is the most common reason ACL-only approaches fail silently.
- Since
cmn_scheduleis a shared table (used by SLAs, business hours, etc.), be careful with your ACL conditions so they only apply when the schedule is associated with an on-call rotation. You can add a condition in the script to check if acmn_rotarecord exists for that schedule before restricting access, and fall back toanswer = trueif no rota is linked.
