COE Security Policies & Write ACLs: Technical Breakdown<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } 🔐 How the Write ACL Resolves Access on HR Case Records Write access on HR Case tables looks like a single yes/no decision from the outside, but underneath it's a chain of script include calls, role checks, and Center of Excellence policy lookups. This article walks through that chain end to end so the evaluation logic stops being a black box. Overview Write access to Human Resources Service Delivery cases — including case types such as those stored on sn_hr_core_case_payroll, which extends the base sn_hr_core_case table — is governed by a single write Access Control List defined on sys_security_acl. That ACL does not contain the access logic itself; it runs a short security script that delegates the real decision-making to two script includes, hr_Case and hr_SecurityUtils, along with supporting helper logic in hr_Utils. Understanding the order in which these are called, and what each one checks, is essential for correctly predicting or troubleshooting access outcomes. 1. How Center of Excellence Security Policies Are Actually Evaluated Center of Excellence security policies are stored on sn_hr_core_coe_security_policy and are the most configurable, and most misunderstood, layer of this evaluation chain. Each policy scopes read or write access by table hierarchy, HR Service, and an optional filter condition, and is linked to one or more authorized groups through the many-to-many table sn_hr_core_m2m_security_policy_group. The evaluation itself is handled by getCoeSecurityPolicy() on the hr_SecurityUtils script include, with group matching delegated to its internal helper _evaluateRule(). The method first resolves the table hierarchy for the record using GlideTableHierarchy, so that policies defined against a parent Center of Excellence table are also considered, not just policies defined against the exact table.It then queries sn_hr_core_coe_security_policy for active records matching that hierarchy and the requested operation type, read or write.If no active policy record matches at all, the method returns true by default — meaning the absence of a policy is treated as open access, not restricted access.If one or more policies do match, each is filtered further by HR Service, unless the policy is flagged to apply to all services, and by its optional applies_when condition evaluated with GlideFilter.checkRecord().For every policy that survives those filters, _evaluateRule() retrieves the requesting user's full group membership and checks it against sn_hr_core_m2m_security_policy_group for that policy. A single matching group membership is enough for the policy check to pass.If at least one matching policy was found but none of them pass the group check, the overall result is false; if no policy matched the table and operation at all, the result defaults to true. 2. Technical Breakdown: The Full Call Chain With the Center of Excellence layer understood, the rest of the chain is the sequence of calls that leads into it. The write ACL script on sys_security_acl executes in this order whenever a write is attempted against the HR Case table. The ACL script first instantiates sn_hr_core.hr_license and calls hasHrAccess(current.getTableName()). This is a hard gate — if the requesting user has no HR entitlement for the table, the ACL short-circuits to false and none of the remaining logic is evaluated.If the license check passes, the ACL evaluates three conditions joined by logical OR, stopping as soon as one returns true. The first is hr_Case.canEditCase(), instantiated against the current record and the current session.Inside canEditCase(), if the record is not new, the method checks whether the user holds the case writer role through hr_Utils.checkUserHasRole(). If so, it immediately calls hr_SecurityUtils.getCoeSecurityPolicy() — the exact evaluation described above — and returns true if both the role and the policy check pass.If that combined check fails, canEditCase() falls back to direct relationship checks against the case record itself: whether the user opened the case, is the person the case was opened for, appears on the watch list, or is listed as a collaborator, the last of which internally re-checks the case writer role through _userHasCaseWriterRole().If canEditCase() returns false, the ACL evaluates the second condition, hr_Case.isApproverUserForCase(userId), which calls hr_Delegation.isApproverForRecord() and, failing that, queries the sysapproval_approver table using approver identities resolved by hr_Utils.getApprovals().If that also returns false, the ACL evaluates the third and final condition, the static method hr_Case.userHasSubjectPersonAccess(), which checks whether the requesting user is the case's subject person and whether the case's linked HR Service, queried against sn_hr_core_service, has subject-person access enabled.If all three conditions return false, the overall ACL evaluates to false and write access is denied, regardless of the initial license check having passed. Key Tables and Script Includes Involved ComponentTypeCalled By / Role in the Chainsys_security_aclTableStores the write ACL and the entry-point security scriptsn_hr_core_caseTableBase table for all HR Case recordssn_hr_core_case_payrollTable (extends sn_hr_core_case)Example case type evaluated by the same ACL logichr_licenseScript IncludeCalled first by the ACL script via hasHrAccess() to confirm entitlementhr_CaseScript IncludeProvides canEditCase(), isApproverUserForCase(), userHasSubjectPersonAccess()hr_SecurityUtilsScript IncludeCalled from within canEditCase() via getCoeSecurityPolicy() and _evaluateRule()hr_UtilsScript IncludeSupplies role checks and approver resolution used by hr_Casehr_DelegationScript IncludeCalled from isApproverUserForCase() to check delegated approvalsn_hr_core_coe_security_policyTableQueried by getCoeSecurityPolicy() for matching active policiessn_hr_core_m2m_security_policy_groupTableQueried by _evaluateRule() to match user groups to a policysn_hr_core_serviceTableQueried by userHasSubjectPersonAccess() for subject-person eligibilitysysapproval_approverTableQueried by isApproverUserForCase() as a fallback approver check Gotchas Symptom: Write access appears to be granted with no security policy visibly restricting it.Cause: getCoeSecurityPolicy() defaults to true when no active policy record matches the table hierarchy and operation type — absence of configuration means open access, not denial.Fix: Confirm an active, correctly scoped policy exists on sn_hr_core_coe_security_policy if restriction is the intended behavior. Symptom: A user without the case writer role can still edit a case.Cause: The ACL's OR logic means canEditCase() is only one of three independent paths — the user may qualify through isApproverUserForCase() or userHasSubjectPersonAccess() instead, both of which are role-independent.Fix: When troubleshooting unexpected access, check all three ACL conditions individually rather than assuming role is the only factor. Best Practices When auditing HR Case access, trace the ACL's three OR conditions individually rather than assuming a single script include controls the outcome.Remember that Center of Excellence policy evaluation is user-membership based, so a complete audit needs to look at the requesting user's full group profile, not just the record's fields.Treat a missing Center of Excellence policy as an open-access configuration, and validate that intentionally before relying on it as a restriction.Keep policy-to-group mappings on sn_hr_core_m2m_security_policy_group reviewed periodically, since this table is the final gate in the write evaluation chain.For deeper platform reference material on HRSD security configuration, consult https://www.servicenow.com/docs under the Xanadu, Yokohama, or Zurich release documentation sets. Summary Write access on HR Case tables such as sn_hr_core_case_payroll resolves through a fixed call chain: an HR licensing gate, followed by three independent OR conditions on hr_Case, one of which — canEditCase() — itself calls into hr_SecurityUtils.getCoeSecurityPolicy() to evaluate Center of Excellence policy coverage by table, HR Service, and group membership. Any single condition passing is sufficient to grant access, and a missing policy defaults to allowing it. Tracing an access outcome means walking this exact sequence rather than inspecting any one script include in isolation. Table names, script includes, and configuration references are provided for guidance only and may vary by instance configuration and release version.