acl-security
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseACL 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):
- Table.field - Most specific (e.g., )
incident.assignment_group - **Table.*` - Table-level field wildcard
- Table - Table-level record ACL
- Parent table ACLs - If table extends another
- - Global wildcard (catch-all)
*
ACLs 按照以下顺序进行评估(匹配到第一个即生效):
- Table.field - 最具体的规则(例如:)
incident.assignment_group - **Table.*` - 表级字段通配符规则
- Table - 表级记录ACL规则
- 父表ACLs - 若当前表继承自其他表
- - 全局通配符规则(兜底规则)
*
ACL Types
ACL 类型
| Type | Controls | Example |
|---|---|---|
| record | Row-level access | Can user see this incident? |
| field | Field-level access | Can user see assignment_group? |
| client_callable_script_include | Script Include access | Can user call this API? |
| ui_page | UI Page access | Can user view this page? |
| rest_endpoint | REST API access | Can user call this endpoint? |
| 类型 | 控制范围 | 示例 |
|---|---|---|
| record | 行级访问权限 | 用户能否查看该事件记录? |
| field | 字段级访问权限 | 用户能否查看assignment_group字段? |
| client_callable_script_include | 脚本包含的访问权限 | 用户能否调用该API? |
| ui_page | UI页面的访问权限 | 用户能否查看该页面? |
| rest_endpoint | REST 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 accessjavascript
// 条件:(空 - 仅检查角色)
// 角色: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 recordsjavascript
// 条件:
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 groupsjavascript
// 脚本:
(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 fieldjavascript
// 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 resolutionjavascript
// 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 ownjavascript
// 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_managerjavascript
// ❌ 不良示例 - 权限过于宽松
// 角色:(空)- 允许所有用户访问
// ✅ 良好示例 - 明确指定角色
// 角色: itil, incident_manager2. 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 deniedjavascript
// 创建一个优先级最低的兜底拒绝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 filteredjavascript
// 使用“模拟用户”功能以不同用户身份测试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 detailsjavascript
// 在后台脚本或代码中临时启用:
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
常见错误
| Mistake | Problem | Solution |
|---|---|---|
| No ACLs on custom tables | Anyone can access | Create ACLs immediately |
| Only role-based ACLs | No row-level security | Add conditions for data segregation |
| Scripts that query DB | Performance issues | Use conditions or cache results |
| Testing only as admin | Admin bypasses ACLs | Test as actual end users |
| Forgetting REST APIs | APIs bypass UI ACLs | Create specific REST ACLs |
| 错误 | 问题 | 解决方案 |
|---|---|---|
| 自定义表未配置ACL | 所有用户均可访问 | 立即为自定义表创建ACL |
| 仅使用基于角色的ACL | 缺少行级安全控制 | 添加数据隔离条件 |
| 脚本中查询数据库 | 性能问题 | 尽可能使用条件表达式或缓存结果 |
| 仅以管理员身份测试 | 管理员会绕过ACL | 以实际终端用户身份测试 |
| 忽略REST API | API会绕过UI层ACL | 创建专门的REST API ACL |