mcpField
A
acl-management

acl-management

github.com/happy-technologies-llcSecurity4141 installsUpdated 2d ago
Repository

About acl-management

This skill guides ServiceNow administrators and security staff through inspecting, creating, modifying, and troubleshooting ACLs. It covers table-, field-, and record-level access controls, role requirements, and debugging methods.

Key features

  1. 01Query ACLs and their role requirements
  2. 02Create table-, field-, and record-level ACLs
  3. 03Modify existing ACLs safely
  4. 04Troubleshoot user permission issues
  5. 05Analyze ACL evaluation and conflicts
  6. 06Check for risky role combinations

Use cases

  1. 01Set up access controls for a new table or application
  2. 02Investigate user access-denied problems
  3. 03Implement field- or row-level permissions
  4. 04Review existing permissions and ACL configuration

MCP support

The skill uses ServiceNow MCP tools to query ACLs, create or update rules and roles, and run scripts to support debugging.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/happy-technologies-llc/happy-platform-skills --skill acl-management

After installing, type/acl-managementin Claude Code to confirm it loaded.

Details

From the project README

ACL Management

Overview

Access Control Lists (ACLs) are the foundation of ServiceNow security. This skill covers:

  • Understanding ACL structure and inheritance hierarchy
  • Creating new ACLs for tables, fields, and records
  • Modifying existing ACL rules safely
  • Troubleshooting permission issues
  • ACL debugging techniques and tools
  • Best practices for role-based access control

When to use:

  • Setting up security for new tables or applications
  • Troubleshooting "access denied" issues
  • Implementing field-level security
  • Auditing and reviewing existing permissions
  • Implementing row-level security (query conditions)

Prerequisites

  • Roles: security_admin or admin
  • Access: sys_security_acl, sys_security_acl_role tables
  • Knowledge: Understanding of ServiceNow roles and user groups
  • Plugins: Core platform (ACLs are built-in)

ACL Fundamentals

ACL Hierarchy
                    ┌─────────────────┐
                    │  Table ACL (*)  │  ← Broadest
                    │  incident.*     │
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
                    │  Table.Field    │  ← Field-specific
                    │  incident.state │
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
                    │ Table.None      │  ← Row-level
                    │ (with script)   │
                    └─────────────────┘  ← Most specific
ACL Operations
OperationPurposeCommon Use
readView records/fieldsList, form display
writeModify records/fieldsForm editing
createInsert new recordsNew record creation
deleteRemove recordsRecord deletion
executeRun scripts/actionsUI actions, processors
ACL Evaluation Order
  1. Most specific wins: Field ACL > Table ACL
  2. First match: Evaluation stops at first matching ACL
  3. Deny by default: No matching ACL = access denied
  4. Admin bypass: Admin role bypasses most ACLs

Procedure

Phase 1: Understanding Existing ACLs
1.1 Query ACLs for a Table

Find all ACLs for a specific table:

Tool: SN-Query-Table
Parameters:
  table_name: sys_security_acl
  query: nameLIKEincident
  fields: name,operation,type,condition,script,active
  limit: 50

Find ACLs by operation type:

Tool: SN-Query-Table
Parameters:
  table_name: sys_security_acl
  query: name=incident^operation=write
  fields: name,operation,admin_overrides,condition,script,active
  limit: 20
1.2 Discover ACL Structure

Get the ACL table schema:

Tool: SN-Get-Table-Schema
Parameters:
  table_name: sys_security_acl

Key ACL Fields:

FieldTypePurpose
nameStringTable.field or Table.*
operationChoiceread, write, create, delete, execute
typeChoicerecord, client_callable_script_include
conditionConditionFilter conditions for row-level security
scriptScriptAdvanced condition logic
admin_overridesBooleanAllow admin bypass
activeBooleanACL is enforced
advancedBooleanUses script conditions
1.3 Check ACL Role Requirements

Find roles required for an ACL:

Tool: SN-Query-Table
Parameters:
  table_name: sys_security_acl_role
  query: acl.name=incident.*^acl.operation=write
  fields: acl.name,acl.operation,role.name
  limit: 20
Phase 2: Creating New ACLs
2.1 Create Table-Level ACL

Create read ACL for custom table:

Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl
  data:
    name: "u_custom_table.*"
    operation: read
    type: record
    admin_overrides: true
    active: true
    description: "Read access to custom table"

Add role requirement:

Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl_role
  data:
    acl: [acl_sys_id]
    role: [role_sys_id]
2.2 Create Field-Level ACL

Protect a sensitive field:

Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl
  data:
    name: "u_custom_table.ssn_number"
    operation: read
    type: record
    admin_overrides: true
    active: true
    description: "Restrict SSN field to authorized users only"

Add multiple role requirements:

Tool: SN-Query-Table
Parameters:
  table_name: sys_user_role
  query: nameINhr_admin,security_admin
  fields: sys_id,name
  limit: 10

Then for each role:

Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl_role
  data:
    acl: [field_acl_sys_id]
    role: [hr_admin_role_sys_id]
2.3 Create Row-Level ACL (Condition-Based)

Users can only see their own records:

Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl
  data:
    name: "u_custom_table"
    operation: read
    type: record
    condition: "opened_by=javascript:gs.getUserID()"
    admin_overrides: true
    active: true
    description: "Users can only read their own records"
2.4 Create Script-Based ACL

Complex logic requiring script:

Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl
  data:
    name: "u_custom_table"
    operation: write
    type: record
    advanced: true
    script: |
      // Allow write if user is the owner OR is in the assigned group
      var userId = gs.getUserID();
      var userGroups = gs.getUser().getMyGroups();

      if (current.owner == userId) {
        answer = true;
      } else if (userGroups.indexOf(current.assignment_group.toString()) >= 0) {
        answer = true;
      } else {
        answer = false;
      }
    admin_overrides: true
    active: true
    description: "Allow write for owner or group members"
Phase 3: Modifying ACLs Safely
3.1 Deactivate Before Major Changes

Deactivate ACL temporarily:

Tool: SN-Update-Record
Parameters:
  table_name: sys_security_acl
  sys_id: [acl_sys_id]
  data:
    active: false

Make changes:

Tool: SN-Update-Record
Parameters:
  table_name: sys_security_acl
  sys_id: [acl_sys_id]
  data:
    condition: "state!=6^ORassigned_to=javascript:gs.getUserID()"
    description: "Updated condition - users can access non-closed or own records"

Reactivate after testing:

Tool: SN-Update-Record
Parameters:
  table_name: sys_security_acl
  sys_id: [acl_sys_id]
  data:
    active: true
3.2 Add/Remove Role Requirements

Add a new role requirement:

Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl_role
  data:
    acl: [acl_sys_id]
    role: [new_role_sys_id]

Remove a role requirement:

Tool: SN-Query-Table
Parameters:
  table_name: sys_security_acl_role
  query: acl=[acl_sys_id]^role=[role_sys_id]
  fields: sys_id

# Then delete the record using SN-Update-Record or background script
Phase 4: Troubleshooting Permission Issues
4.1 Identify the Blocking ACL

Enable ACL Debugging (Session-Based):

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // This enables debug output for the session
    // Run manually: Navigate to User menu > Impersonate > Check security
    gs.info('To debug ACLs:');
    gs.info('1. Navigate to System Security > Debugging');
    gs.info('2. Or add ?sysparm_security_debug=true to URL');
    gs.info('3. Or impersonate user and check security diagnostics');
  description: "ACL debugging instructions"

Check user's roles:

Tool: SN-Query-Table
Parameters:
  table_name: sys_user_has_role
  query: user=[user_sys_id]^role.name!=snc_internal
  fields: role.name,granted_by,inherited
  limit: 50

Check user's groups:

Tool: SN-Query-Table
Parameters:
  table_name: sys_user_grmember
  query: user=[user_sys_id]
  fields: group.name,group.sys_id
  limit: 50
4.2 Trace ACL Evaluation

Find which ACL applies:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var table = 'incident';
    var operation = 'write';
    var fieldName = 'state'; // or '*' for table-level

    var acl = new GlideRecord('sys_security_acl');
    acl.addQuery('name', 'CONTAINS', table);
    acl.addQuery('operation', operation);
    acl.addQuery('active', true);
    acl.orderBy('name');
    acl.query();

    gs.info('ACLs for ' + table + '.' + fieldName + ' (' + operation + '):');
    while (acl.next()) {
      var roles = [];
      var roleGr = new GlideRecord('sys_security_acl_role');
      roleGr.addQuery('acl', acl.sys_id);
      roleGr.query();
      while (roleGr.next()) {
        roles.push(roleGr.role.name.toString());
      }

      gs.info(acl.name + ' - Roles: [' + roles.join(', ') + '] - Condition: ' + acl.condition);
    }
  description: "Trace ACL evaluation for table/operation"
4.3 Test User Access

Check if user has required roles:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var userId = '[user_sys_id]';
    var requiredRoles = ['itil', 'incident_manager'];

    var user = new GlideRecord('sys_user');
    user.get(userId);

    gs.info('Checking roles for: ' + user.user_name);

    for (var i = 0; i < requiredRoles.length; i++) {
      var hasRole = new GlideRecord('sys_user_has_role');
      hasRole.addQuery('user', userId);
      hasRole.addQuery('role.name', requiredRoles[i]);
      hasRole.query();

      if (hasRole.hasNext()) {
        gs.info('  HAS: ' + requiredRoles[i]);
      } else {
        gs.warn('  MISSING: ' + requiredRoles[i]);
      }
    }
  description: "Check user role membership"
4.4 Common Permission Issues

Issue: User can see table but not specific records

# Check for row-level ACL conditions
Tool: SN-Query-Table
Parameters:
  table_name: sys_security_acl
  query: name=[table_name]^operation=read^conditionISNOTEMPTY
  fields: name,condition,script

Issue: User can read but not write

# Compare read vs write ACLs
Tool: SN-Query-Table
Parameters:
  table_name: sys_security_acl
  query: nameLIKE[table_name]^operationINread,write
  fields: name,operation,condition
  limit: 20

Issue: Field is read-only unexpectedly

# Check field-level ACLs
Tool: SN-Query-Table
Parameters:
  table_name: sys_security_acl
  query: name=[table_name].[field_name]^operation=write
  fields: name,operation,condition,active
Phase 5: ACL Debugging Tools
5.1 Security Diagnostic Tool

Run security diagnostic for a record:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var table = 'incident';
    var sysId = '[record_sys_id]';
    var userId = '[user_sys_id]';

    // Impersonate and check access
    var gr = new GlideRecord(table);
    gr.get(sysId);

    var canRead = gr.canRead();
    var canWrite = gr.canWrite();
    var canDelete = gr.canDelete();

    gs.info('Security Diagnostic for ' + table + '/' + sysId);
    gs.info('  Can Read: ' + canRead);
    gs.info('  Can Write: ' + canWrite);
    gs.info('  Can Delete: ' + canDelete);

    // Check field-level access
    var elements = ['state', 'priority', 'assigned_to'];
    for (var i = 0; i < elements.length; i++) {
      var field = elements[i];
      gs.info('  Field ' + field + ' - canRead: ' + gr[field].canRead() +
              ', canWrite: ' + gr[field].canWrite());
    }
  description: "Run security diagnostic on record"
5.2 ACL Rule Analyzer

Analyze all ACLs for conflicts:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var table = 'incident';
    var analysis = {
      table_acls: [],
      field_acls: [],
      potential_conflicts: []
    };

    var acl = new GlideRecord('sys_security_acl');
    acl.addQuery('name', 'STARTSWITH', table);
    acl.addQuery('active', true);
    acl.orderBy('name');
    acl.query();

    while (acl.next()) {
      var aclInfo = {
        name: acl.name.toString(),
        operation: acl.operation.toString(),
        has_condition: acl.condition.toString() !== '',
        has_script: acl.advanced.toString() === 'true'
      };

      if (acl.name.toString().indexOf('.') === -1 ||
          acl.name.toString().endsWith('.*')) {
        analysis.table_acls.push(aclInfo);
      } else {
        analysis.field_acls.push(aclInfo);
      }
    }

    // Check for potential issues
    if (analysis.table_acls.length === 0) {
      analysis.potential_conflicts.push('No table-level ACLs - may be too open or rely on inheritance');
    }

    gs.info('ACL Analysis for ' + table + ':\n' + JSON.stringify(analysis, null, 2));
  description: "Analyze ACLs for conflicts and issues"
5.3 Role Elevation Check

Find users with potentially dangerous role combinations:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var dangerousCombos = [
      ['admin', 'maint'],
      ['security_admin', 'personalize_dictionary'],
      ['admin', 'soap']
    ];

    var findings = [];

    for (var i = 0; i < dangerousCombos.length; i++) {
      var combo = dangerousCombos[i];

      var users = new GlideRecord('sys_user');
      users.addQuery('active', true);
      users.query();

      while (users.next()) {
        var hasAll = true;
        for (var j = 0; j < combo.length; j++) {
          var roleCheck = new GlideRecord('sys_user_has_role');
          roleCheck.addQuery('user', users.sys_id);
          roleCheck.addQuery('role.name', combo[j]);
          roleCheck.query();
          if (!roleCheck.hasNext()) {
            hasAll = false;
            break;
          }
        }

        if (hasAll) {
          findings.push({
            user: users.user_name.toString(),
            roles: combo
          });
        }
      }
    }

    if (findings.length > 0) {
      gs.warn('Users with dangerous role combinations:');
      gs.info(JSON.stringify(findings, null, 2));
    } else {
      gs.info('No dangerous role combinations found');
    }
  description: "Find users with dangerous role combinations"

Best Practices

Design Principles
  1. Principle of Least Privilege: Grant minimum access needed
  2. Role-Based Access: Use roles, not individual users
  3. Inherit When Possible: Use table-level ACLs as baseline
  4. Document Everything: Add descriptions to all ACLs
  5. Test Thoroughly: Verify with multiple user roles
Common Patterns

Pattern 1: Owner + Group Access

// Condition for owner or group member access
opened_by=javascript:gs.getUserID()^ORassignment_group.memberSOME=javascript:gs.getUserID()

Pattern 2: Hierarchical Access

// Managers can see their team's records
var mgr = new GlideRecord('sys_user');
mgr.addQuery('manager', gs.getUserID());
mgr.query();
var users = [gs.getUserID()];
while (mgr.next()) users.push(mgr.sys_id.toString());
answer = gs.getUser().getID() in users.join(',').split(',');

Pattern 3: Time-Based Access

// Only allow access during business hours
var now = new GlideDateTime();
var hour = now.getLocalTime().getHourOfDay();
answer = (hour >= 8 && hour < 18);

Tool Usage Summary

OperationMCP ToolREST Endpoint
Query ACLsSN-Query-TableGET /sys_security_acl
Create ACLSN-Create-RecordPOST /sys_security_acl
Update ACLSN-Update-RecordPATCH /sys_security_acl
Add RoleSN-Create-RecordPOST /sys_security_acl_role
Debug ACLSN-Execute-Background-ScriptVia sys_trigger
Check SchemaSN-Get-Table-SchemaGET /sys_dictionary

Troubleshooting

ACL Not Being Evaluated

Symptom: Changes to ACL have no effect Cause: ACL cache not cleared or ACL inactive Solution:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Clear ACL cache
    GlideAccessControl.reset();
    gs.info('ACL cache cleared');
  description: "Clear ACL cache"
Admin Can't Access Record

Symptom: Admin role blocked from record Cause: ACL has admin_overrides=false Solution:

Tool: SN-Query-Table
Parameters:
  table_name: sys_security_acl
  query: name=[table]^admin_overrides=false
  fields: name,operation,admin_overrides
Script ACL Always Fails

Symptom: Script-based ACL never grants access Cause: Script error or incorrect answer variable Solution:

  • Verify script sets answer = true or answer = false
  • Check for script errors in syslog
  • Ensure advanced = true is set
Role Not Granting Access

Symptom: User has role but still denied Cause: ACL requires multiple roles (AND) or condition fails Solution:

Tool: SN-Query-Table
Parameters:
  table_name: sys_security_acl_role
  query: acl=[acl_sys_id]
  fields: role.name

All listed roles are required (AND logic by default).

Examples

Example 1: Secure a Custom Table
# Step 1: Create table-level read ACL
Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl
  data:
    name: "u_employee_data.*"
    operation: read
    type: record
    admin_overrides: true
    active: true
    description: "Base read access to employee data"

# Step 2: Add role requirement
Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl_role
  data:
    acl: [step1_sys_id]
    role: [hr_user_role_sys_id]

# Step 3: Create write ACL (more restrictive)
Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl
  data:
    name: "u_employee_data.*"
    operation: write
    type: record
    admin_overrides: true
    active: true
    description: "Write access to employee data - HR admins only"

# Step 4: Add admin role requirement
Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl_role
  data:
    acl: [step3_sys_id]
    role: [hr_admin_role_sys_id]
Example 2: Field-Level SSN Protection
# Protect SSN field - only security cleared users
Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl
  data:
    name: "u_employee_data.ssn"
    operation: read
    type: record
    admin_overrides: false
    active: true
    description: "SSN access restricted - security clearance required"

# Add special role
Tool: SN-Create-Record
Parameters:
  table_name: sys_security_acl_role
  data:
    acl: [ssn_acl_sys_id]
    role: [security_cleared_role_sys_id]

Related Skills

  • security/audit-compliance - Auditing ACL changes
  • security/incident-response - Responding to access violations
  • security/data-classification - Identifying what needs protection
  • admin/user-provisioning - Managing user roles

References

Comments

Markdown supported