acl-security

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

ACL Security Patterns for ServiceNow

ServiceNow 的 ACL 安全模式

Access Control Lists (ACLs) are the foundation of ServiceNow security. They control who can read, write, create, and delete records.
访问控制列表(ACLs)是ServiceNow安全体系的基础,用于控制哪些用户可以对记录进行读取、写入、创建和删除操作。

ACL Evaluation Order

ACL 评估顺序

ACLs are evaluated in this order (first match wins):
  1. Table.field - Most specific (e.g.,
    incident.assignment_group
    )
  2. **Table.*` - Table-level field wildcard
  3. Table - Table-level record ACL
  4. Parent table ACLs - If table extends another
  5. *
    - Global wildcard (catch-all)
ACLs 按照以下顺序进行评估(匹配到第一个即生效):
  1. Table.field - 最具体的规则(例如:
    incident.assignment_group
  2. **Table.*` - 表级字段通配符规则
  3. Table - 表级记录ACL规则
  4. 父表ACLs - 若当前表继承自其他表
  5. *
    - 全局通配符规则(兜底规则)

ACL Types

ACL 类型

TypeControlsExample
recordRow-level accessCan user see this incident?
fieldField-level accessCan user see assignment_group?
client_callable_script_includeScript Include accessCan user call this API?
ui_pageUI Page accessCan user view this page?
rest_endpointREST API accessCan user call this endpoint?
类型控制范围示例
record行级访问权限用户能否查看该事件记录?
field字段级访问权限用户能否查看assignment_group字段?
client_callable_script_include脚本包含的访问权限用户能否调用该API?
ui_pageUI页面的访问权限用户能否查看该页面?
rest_endpointREST API的访问权限用户能否调用该端点?

Creating ACLs via MCP

通过 MCP 创建 ACL

javascript
// Table-level READ ACL
snow_create_acl({
  name: 'incident',
  operation: 'read',
  admin_overrides: true,
  active: true,
  roles: ['itil', 'incident_manager'],
  condition: 'current.active == true',
  script: ''
});

// Field-level WRITE ACL
snow_create_acl({
  name: 'incident.priority',
  operation: 'write',
  roles: ['incident_manager'],
  condition: '',
  script: 'answer = current.state < 6;'  // Only if not resolved
});
javascript
// 表级 READ ACL
snow_create_acl({
  name: 'incident',
  operation: 'read',
  admin_overrides: true,
  active: true,
  roles: ['itil', 'incident_manager'],
  condition: 'current.active == true',
  script: ''
});

// 字段级 WRITE ACL
snow_create_acl({
  name: 'incident.priority',
  operation: 'write',
  roles: ['incident_manager'],
  condition: '',
  script: 'answer = current.state < 6;'  // 仅当记录未解决时允许
});

Common ACL Patterns

常见 ACL 模式

Pattern 1: Role-Based Access

模式1:基于角色的访问

javascript
// Condition: (empty - role check only)
// Roles: itil, incident_manager
// Script: (empty)

// Users with itil OR incident_manager role can access
javascript
// 条件:(空 - 仅检查角色)
// 角色:itil, incident_manager
// 脚本:(空)

// 拥有itil或incident_manager角色的用户可访问

Pattern 2: Ownership-Based Access

模式2:基于所有权的访问

javascript
// Condition:
current.caller_id == gs.getUserID() ||
current.assigned_to == gs.getUserID() ||
current.opened_by == gs.getUserID()

// User can access their own records
javascript
// 条件:
current.caller_id == gs.getUserID() ||
current.assigned_to == gs.getUserID() ||
current.opened_by == gs.getUserID()

// 用户可访问自己的记录

Pattern 3: Group-Based Access

模式3:基于组的访问

javascript
// Script:
(function() {
  var userGroups = gs.getUser().getMyGroups();
  answer = userGroups.indexOf(current.assignment_group.toString()) >= 0;
})();

// User can access records assigned to their groups
javascript
// 脚本:
(function() {
  var userGroups = gs.getUser().getMyGroups();
  answer = userGroups.indexOf(current.assignment_group.toString()) >= 0;
})();

// 用户可访问分配到其所在组的记录

Pattern 4: Manager Chain Access

模式4:基于管理链的访问

javascript
// Script:
(function() {
  var callerManager = current.caller_id.manager;
  var currentUser = gs.getUserID();

  // Check if current user is in caller's management chain
  while (callerManager && !callerManager.nil()) {
    if (callerManager.toString() == currentUser) {
      answer = true;
      return;
    }
    callerManager = callerManager.manager;
  }
  answer = false;
})();
javascript
// 脚本:
(function() {
  var callerManager = current.caller_id.manager;
  var currentUser = gs.getUserID();

  // 检查当前用户是否在呼叫者的管理链中
  while (callerManager && !callerManager.nil()) {
    if (callerManager.toString() == currentUser) {
      answer = true;
      return;
    }
    callerManager = callerManager.manager;
  }
  answer = false;
})();

Pattern 5: Time-Based Access

模式5:基于时间的访问

javascript
// Script:
(function() {
  var now = new GlideDateTime();
  var hour = parseInt(now.getLocalTime().getHourOfDayLocalTime());

  // Only allow access during business hours (8 AM - 6 PM)
  answer = hour >= 8 && hour < 18;
})();
javascript
// 脚本:
(function() {
  var now = new GlideDateTime();
  var hour = parseInt(now.getLocalTime().getHourOfDayLocalTime());

  // 仅允许在工作时间(上午8点 - 下午6点)访问
  answer = hour >= 8 && hour < 18;
})();

Pattern 6: Data Classification

模式6:基于数据分类的访问

javascript
// Script:
(function() {
  var classification = current.u_data_classification.toString();
  var userClearance = gs.getUser().getRecord().getValue('u_security_clearance');

  var levels = {'public': 0, 'internal': 1, 'confidential': 2, 'secret': 3};
  answer = levels[userClearance] >= levels[classification];
})();
javascript
// 脚本:
(function() {
  var classification = current.u_data_classification.toString();
  var userClearance = gs.getUser().getRecord().getValue('u_security_clearance');

  var levels = {'public': 0, 'internal': 1, 'confidential': 2, 'secret': 3};
  answer = levels[userClearance] >= levels[classification];
})();

Field-Level Security Patterns

字段级安全模式

Hide Sensitive Fields

隐藏敏感字段

javascript
// ACL: incident.u_ssn (Social Security Number)
// Operation: read
// Script:
answer = gs.hasRole('hr_admin');

// Only HR admins can see SSN field
javascript
// ACL: incident.u_ssn(社会安全号码)
// 操作: read
// 脚本:
answer = gs.hasRole('hr_admin');

// 仅HR管理员可查看SSN字段

Read-Only After State Change

状态变更后设为只读

javascript
// ACL: incident.short_description
// Operation: write
// Script:
answer = current.state < 6;  // Can't edit after Resolved

// Prevent editing after resolution
javascript
// ACL: incident.short_description
// 操作: write
// 脚本:
answer = current.state < 6;  // 记录解决后无法编辑

// 防止记录解决后被编辑

Conditional Field Visibility

条件字段可见性

javascript
// ACL: incident.u_internal_notes
// Operation: read
// Condition:
gs.hasRole('itil') || current.caller_id == gs.getUserID()

// ITIL users see all, callers see their own
javascript
// ACL: incident.u_internal_notes
// 操作: read
// 条件:
gs.hasRole('itil') || current.caller_id == gs.getUserID()

// ITIL用户可查看所有内部备注,呼叫者仅能查看自己记录的备注

Security Best Practices

安全最佳实践

1. Principle of Least Privilege

1. 最小权限原则

javascript
// ❌ BAD - Too permissive
// Roles: (empty) - allows everyone

// ✅ GOOD - Explicit roles
// Roles: itil, incident_manager
javascript
// ❌ 不良示例 - 权限过于宽松
// 角色:(空)- 允许所有用户访问

// ✅ 良好示例 - 明确指定角色
// 角色: itil, incident_manager

2. Deny by Default

2. 默认拒绝

javascript
// Create a catch-all deny ACL at lowest priority
// Name: *
// Operation: read
// Condition: false
// This ensures anything not explicitly allowed is denied
javascript
// 创建一个优先级最低的兜底拒绝ACL
// 名称: *
// 操作: read
// 条件: false
// 确保所有未明确允许的访问都被拒绝

3. Avoid Complex Scripts

3. 避免复杂脚本

javascript
// ❌ BAD - Complex script ACL (slow)
(function() {
  var gr = new GlideRecord('sys_user_grmember');
  gr.addQuery('user', gs.getUserID());
  gr.query();
  while (gr.next()) {
    // Complex logic...
  }
})();

// ✅ GOOD - Use conditions when possible
// Condition: gs.getUser().isMemberOf(current.assignment_group)
javascript
// ❌ 不良示例 - 复杂脚本ACL(性能较慢)
(function() {
  var gr = new GlideRecord('sys_user_grmember');
  gr.addQuery('user', gs.getUserID());
  gr.query();
  while (gr.next()) {
    // 复杂逻辑...
  }
})();

// ✅ 良好示例 - 尽可能使用条件表达式
// 条件: gs.getUser().isMemberOf(current.assignment_group)

4. Test ACLs Thoroughly

4. 全面测试ACL

javascript
// Use "Impersonate User" to test ACLs as different users
// Check: Navigation, List views, Forms, Related lists
// Verify: Fields hidden, buttons disabled, records filtered
javascript
// 使用“模拟用户”功能以不同用户身份测试ACL
// 检查:导航、列表视图、表单、相关列表
// 验证:字段隐藏、按钮禁用、记录过滤效果

Debug ACLs

调试ACL

Enable ACL Debugging

启用ACL调试

javascript
// In a background script or temporarily in your code:
gs.setProperty('glide.security.debug', 'true');
gs.log('ACL Debug enabled');

// Check System Logs for ACL evaluation details
javascript
// 在后台脚本或代码中临时启用:
gs.setProperty('glide.security.debug', 'true');
gs.log('ACL Debug enabled');

// 在系统日志中查看ACL评估详情

Check User Permissions

检查用户权限

javascript
// Check if current user can read a record
var gr = new GlideRecord('incident');
gr.get('sys_id_here');

gs.info('Can Read: ' + gr.canRead());
gs.info('Can Write: ' + gr.canWrite());
gs.info('Can Delete: ' + gr.canDelete());

// Check field-level
gs.info('Can read assignment_group: ' +
        gr.assignment_group.canRead());
gs.info('Can write assignment_group: ' +
        gr.assignment_group.canWrite());
javascript
// 检查当前用户能否读取某条记录
var gr = new GlideRecord('incident');
gr.get('sys_id_here');

gs.info('Can Read: ' + gr.canRead());
gs.info('Can Write: ' + gr.canWrite());
gs.info('Can Delete: ' + gr.canDelete());

// 检查字段级权限
gs.info('Can read assignment_group: ' +
        gr.assignment_group.canRead());
gs.info('Can write assignment_group: ' +
        gr.assignment_group.canWrite());

Common Mistakes

常见错误

MistakeProblemSolution
No ACLs on custom tablesAnyone can accessCreate ACLs immediately
Only role-based ACLsNo row-level securityAdd conditions for data segregation
Scripts that query DBPerformance issuesUse conditions or cache results
Testing only as adminAdmin bypasses ACLsTest as actual end users
Forgetting REST APIsAPIs bypass UI ACLsCreate specific REST ACLs
错误问题解决方案
自定义表未配置ACL所有用户均可访问立即为自定义表创建ACL
仅使用基于角色的ACL缺少行级安全控制添加数据隔离条件
脚本中查询数据库性能问题尽可能使用条件表达式或缓存结果
仅以管理员身份测试管理员会绕过ACL以实际终端用户身份测试
忽略REST APIAPI会绕过UI层ACL创建专门的REST API ACL