关于 sf-permissions
sf-permissions 是 Salesforce 权限管理与访问审计技能,面向配置权限集、检查用户访问和排查权限错误的人员。它涵盖权限集组、字段级安全、共享规则及相关查询与部署流程。
主要功能
- 01生成权限集和权限集组元数据
- 02审计用户及对象访问
- 03检查字段级安全设置
- 04诊断权限错误与记录访问
- 05管理自定义权限和静默权限集
- 06提供部署与分配命令
适用场景
- 01为新功能配置最小权限访问
- 02排查用户无法编辑记录的问题
- 03审计哪些用户可访问对象或 Apex 类
- 04将配置从 Profile 迁移到权限集
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ clientell-ai/ salesforce-skills --skill sf-permissions安装后在 Claude Code 中输入/sf-permissions确认已加载。
附带文件references/permissions-reference.md
详细介绍
Salesforce 权限管理与访问审计
你是 Salesforce 权限专家。管理权限集、审计访问权限、诊断权限错误,并落实最小权限安全原则。
1. 权限模型概览
| 层级 | 控制内容 | 范围 |
|---|---|---|
| Profiles | 登录时间、IP 范围、页面布局、记录类型、默认应用 | 每位用户一个(必需) |
| Permission Sets | 对象 CRUD、FLS、Apex 类、VF 页面、选项卡、自定义权限 | 每位用户可有多个(累加) |
| Permission Set Groups | Permission Sets 组合包 + 可选的静默权限 | 每位用户可有多个(累加) |
最佳实践:精简 Profile + Permission Sets。 为用户分配精简的 Profile(例如,“Minimum Access - Salesforce”),并通过 Permission Sets 和 Permission Set Groups 授予其他所有权限。
为何优先使用 Permission Sets,而非 Profiles:
- 用户只能拥有一个 Profile,但可以拥有多个 Permission Set
- Permission Sets 可累加且可组合
- Profiles 会导致源代码管理中的合并冲突
- Permission Set Groups 支持按角色打包权限,并可通过静默权限处理例外情况
- Salesforce 正在积极转向不再基于 Profile 管理权限
2. Permission Set XML(SFDX 源格式)
<?xml version="1.0" encoding="UTF-8"?>
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
<label>Order Manager</label>
<description>Full CRUD on Order__c, read on Account</description>
<hasActivationRequired>false</hasActivationRequired>
<license>Salesforce</license>
<objectPermissions>
<object>Order__c</object>
<allowCreate>true</allowCreate>
<allowDelete>false</allowDelete>
<allowEdit>true</allowEdit>
<allowRead>true</allowRead>
<modifyAllRecords>false</modifyAllRecords>
<viewAllRecords>true</viewAllRecords>
</objectPermissions>
<fieldPermissions>
<field>Order__c.Amount__c</field>
<editable>true</editable>
<readable>true</readable>
</fieldPermissions>
<tabSettings>
<tab>Order__c</tab>
<visibility>Visible</visibility>
</tabSettings>
<classAccesses>
<apexClass>OrderService</apexClass>
<enabled>true</enabled>
</classAccesses>
<pageAccesses>
<apexPage>OrderEntryPage</apexPage>
<enabled>true</enabled>
</pageAccesses>
<customPermissions>
<name>Bypass_Validation</name>
<enabled>true</enabled>
</customPermissions>
<userPermissions>
<name>RunReports</name>
<enabled>true</enabled>
</userPermissions>
</PermissionSet>
hasActivationRequired:为true时,必须在会话中激活后才能生效license:限制只能分配给具有相应许可证类型的用户- 对象权限层级:Edit 需要 Read;Delete 需要 Edit;
viewAllRecords/modifyAllRecords可覆盖共享设置
3. Permission Set Group XML
<?xml version="1.0" encoding="UTF-8"?>
<PermissionSetGroup xmlns="http://soap.sforce.com/2006/04/metadata">
<label>Sales Team</label>
<description>All permissions needed by sales reps</description>
<status>Updated</status>
<permissionSets>
<permissionSet>Account_Reader</permissionSet>
<permissionSet>Opportunity_Manager</permissionSet>
<permissionSet>Report_Viewer</permissionSet>
</permissionSets>
<mutingPermissionSet>Sales_Team_Muting</mutingPermissionSet>
</PermissionSetGroup>
静默权限集
静默权限集会从组中移除特定权限。它只能在 Permission Set Group 中使用。
<?xml version="1.0" encoding="UTF-8"?>
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
<label>Sales Team Muting</label>
<description>Removes delete access granted by Opportunity_Manager</description>
<objectPermissions>
<object>Opportunity</object>
<allowDelete>true</allowDelete>
</objectPermissions>
</PermissionSet>
静默权限仅对通过此组获得访问权限的用户撤销。直接分配的权限不受影响。
4. Profile 元数据
以下场景仍需要 Profiles:登录时间/IP 限制、页面布局分配、记录类型默认值、默认应用分配。
精简 Profile 策略
<?xml version="1.0" encoding="UTF-8"?>
<Profile xmlns="http://soap.sforce.com/2006/04/metadata">
<custom>true</custom>
<description>Minimal profile - all access via Permission Sets</description>
<userLicense>Salesforce</userLicense>
<loginHours>
<mondayStart>480</mondayStart>
<mondayEnd>1080</mondayEnd>
</loginHours>
<layoutAssignments>
<layout>Account-Account Layout</layout>
</layoutAssignments>
</Profile>
避免在 Profiles 中设置对象/字段权限。Profile 仅用于无法通过 Permission Sets 实现的功能。
5. 对象与字段级安全性(CRUD/FLS)
CRUD 权限层级
Read ─── required for ──→ Edit ─── required for ──→ Delete
│ │
└── viewAllRecords └── modifyAllRecords
(bypasses sharing) (bypasses sharing + ownership)
FLS(字段级安全性)
每个字段有两个标志:可读和可编辑(可编辑需要可读)。FLS 适用于 UI、报表、列表视图和 API。被 FLS 隐藏的字段在使用 WITH USER_MODE 的 SOQL 中会返回 null。
FLS 审计查询
-- Field permissions for a permission set
SELECT SobjectType, Field, PermissionsRead, PermissionsEdit
FROM FieldPermissions WHERE Parent.Name = 'Order_Manager'
-- Fields a user can edit (across all permission sets)
SELECT SobjectType, Field, PermissionsRead, PermissionsEdit
FROM FieldPermissions WHERE ParentId IN (
SELECT PermissionSetId FROM PermissionSetAssignment
WHERE AssigneeId = '005xx000001234AAA'
)
-- Who can edit a sensitive field?
SELECT Parent.Label, Parent.IsOwnedByProfile
FROM FieldPermissions
WHERE Field = 'Contact.SSN__c' AND PermissionsEdit = true
6. 自定义权限
自定义权限是布尔标志,可用于控制功能访问,而无需修改代码。
<?xml version="1.0" encoding="UTF-8"?>
<CustomPermission xmlns="http://soap.sforce.com/2006/04/metadata">
<label>Can Export Data</label>
<description>Allows user to export data from custom UI</description>
<isLicensed>false</isLicensed>
</CustomPermission>
Apex(首选):
if (FeatureManagement.checkPermission('Can_Export_Data')) {
// user has the custom permission
}
LWC:
import hasExportPermission from '@salesforce/customPermission/Can_Export_Data';
Flow:在决策元素中使用 $Permission.Can_Export_Data(返回 true/false)。
7. 访问审计
PermissionSetAssignment 查询
-- All users assigned a permission set
SELECT Assignee.Name, Assignee.Username, Assignee.IsActive
FROM PermissionSetAssignment WHERE PermissionSet.Name = 'Order_Manager'
-- All permission sets for a user (excluding profile-based)
SELECT PermissionSet.Label, PermissionSet.Name, PermissionSetGroupId
FROM PermissionSetAssignment
WHERE AssigneeId = '005xx000001234AAA'
AND PermissionSet.IsOwnedByProfile = false
ObjectPermissions 查询
-- Who has Delete on an object?
SELECT Parent.Label, Parent.IsOwnedByProfile,
PermissionsDelete, PermissionsViewAllRecords, PermissionsModifyAllRecords
FROM ObjectPermissions
WHERE SobjectType = 'Account' AND PermissionsDelete = true
-- Over-privileged check: ModifyAll on any object
SELECT Parent.Label, SobjectType FROM ObjectPermissions
WHERE PermissionsModifyAllRecords = true AND Parent.IsOwnedByProfile = false
SetupEntityAccess(Apex/VF/Connected App)
-- Who has access to an Apex class?
SELECT Parent.Label FROM SetupEntityAccess
WHERE SetupEntityType = 'ApexClass'
AND SetupEntityId IN (SELECT Id FROM ApexClass WHERE Name = 'OrderService')
-- Connected App access
SELECT Parent.Label FROM SetupEntityAccess
WHERE SetupEntityType = 'ConnectedApplication'
AND SetupEntityId IN (SELECT Id FROM ConnectedApplication WHERE Name = 'DataLoader')
Permission Set Group 成员资格
-- Permission sets in a group
SELECT PermissionSetGroup.MasterLabel, PermissionSet.Label
FROM PermissionSetGroupComponent
WHERE PermissionSetGroup.MasterLabel = 'Sales Team'
-- Groups containing a permission set
SELECT PermissionSetGroup.MasterLabel FROM PermissionSetGroupComponent
WHERE PermissionSet.Name = 'Opportunity_Manager'
8. 权限问题排查
INSUFFICIENT_ACCESS_OR_READONLY
用户缺少对象或记录的 Edit 权限。检查对象级 Edit 权限、共享访问权限、记录所有权、角色层级,以及记录锁定状态(审批流程)。
SELECT Parent.Label FROM ObjectPermissions
WHERE SobjectType = 'TargetObject__c' AND PermissionsEdit = true
AND ParentId IN (
SELECT PermissionSetId FROM PermissionSetAssignment WHERE AssigneeId = :userId
)
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY
用户无权访问某条关联记录。常见原因:插入子记录时缺少对父记录的 Read 权限;将查找关系更改为用户无法查看的记录;触发器/Flow 更新了关联记录。
“Insufficient Privileges”错误
通用错误,表示可能缺少以下任一权限:Apex 类访问权限、VF 页面访问权限、Lightning 组件访问权限、选项卡可见性、Connected App 访问权限,或未激活基于会话的权限集。
# Quick CLI diagnosis
sf data query -q "SELECT PermissionSet.Label, PermissionSet.Name \
FROM PermissionSetAssignment \
WHERE Assignee.Username = '[email protected]' \
AND PermissionSet.IsOwnedByProfile = false" --target-org myOrg
9. 共享与权限
权限(CRUD/FLS)和共享是相互独立的层:
| 层 | 问题 | 范围 |
|---|---|---|
| CRUD | 用户能否创建/读取/编辑/删除此对象类型? | 整个对象 |
| FLS | 用户能否查看/编辑此特定字段? | 整个字段 |
| 共享 | 用户能访问哪些特定记录? | 记录级别 |
用户必须同时具备相应的 CRUD/FLS 权限和共享访问权限。
OWD 设置
| 设置 | 效果 |
|---|---|
| Private | 仅所有者及其上级角色可访问 |
| Public Read Only | 所有用户均可读取,只有所有者可以编辑 |
| Public Read/Write | 所有用户均可读取和编辑 |
| Controlled by Parent | 由父记录决定(主从关系) |
记录访问权限判定顺序
1. Record owner? → Full access
2. Above owner in role hierarchy? → Access per OWD
3. Sharing rules? → Read or Read/Write
4. Apex managed sharing? → Read or Read/Write
5. View All / Modify All on object? → Bypasses sharing
6. View All Data / Modify All Data? → Full access
关键区别
viewAllRecords/modifyAllRecords会绕过该对象的共享规则- Apex 中的
with sharing会强制执行共享规则,但不会强制执行 CRUD/FLS - SOQL 中的
WITH USER_MODE会同时强制执行共享规则和 CRUD/FLS
10. 注意事项
- 权限集组会异步重新计算 — 变更可能需要几分钟才生效。检查
PermissionSetGroup.Status,确认其状态为Updated而非Outdated。 - 配置文件会导致合并冲突 — 配置文件 XML 文件非常庞大,且条目顺序可能以非确定性方式变化。优先使用权限集。
- FLS 默认不会限制 API 访问 — Apex 以系统模式运行。请使用
WITH USER_MODE或Security.stripInaccessible()。 - 自定义权限会被缓存 — 分配变更可能要等用户重新进行身份验证后才会生效。
- 权限屏蔽集仅在组内有效 — 直接分配给用户不会产生任何效果。
- 权限集许可证 — 有些权限集需要特定许可证。将其分配给没有相应许可证的用户时,可能会静默失败。
- 基于会话的权限集 —
hasActivationRequired=true需要通过 Flow 或SessionPermSetActivation激活,不会自动激活。 viewAllRecords不会授予字段访问权限 — 用户可以看到记录,但无法看到受 FLS 限制的字段(在强制执行 FLS 时)。- IsOwnedByProfile — 每个配置文件都有一个隐藏权限集。查询时使用
PermissionSet.IsOwnedByProfile = false进行筛选。 - 分配数量限制 — 每位用户最多可分配 1,000 个权限集(包括通过组分配的权限集)。
11. 工作流
为新功能设置权限
- 确定所需访问权限:列出对象、字段、Apex 类、VF 页面、选项卡和自定义权限。
- 创建权限集:生成
.permissionset-meta.xml,并遵循最小权限原则。 - 创建自定义权限(如有需要):为功能开关生成
.customPermission-meta.xml。 - 添加到权限集组(如适用):更新
.permissionsetgroup-meta.xml。 - 创建权限屏蔽集(如有需要):仅当组授予某些用户过多权限时才创建。
- 部署:
sf project deploy start -d force-app/main/default/permissionsets \ -d force-app/main/default/permissionsetgroups \ -d force-app/main/default/customPermissions --target-org myOrg - 分配:
sf org assign permset --name Order_Manager --target-org myOrg sf org assign permsetgroup --name Sales_Team --target-org myOrg - 审计:
sf data query -q "SELECT Assignee.Name, PermissionSet.Label \ FROM PermissionSetAssignment \ WHERE PermissionSet.Name = 'Order_Manager'" --target-org myOrg
从配置文件迁移到权限集
- 查询配置文件上所有非默认权限
- 为每个功能领域创建对应的权限集
- 创建一个与该配置文件角色相匹配的权限集组
- 将该组分配给受影响的用户
- 从配置文件中移除权限(仅保留布局、记录类型和登录时间)
- 使用第 7 节中的审计查询进行验证