
Sf Permissions
- 41 installs
- 12 repo stars
- Updated July 14, 2026
- clientell-ai/salesforce-skills
sf-permissions is an agent skill that documents complete Salesforce Permission Set metadata elements for correct access XML.
About
sf-permissions is a reference skill for solo and indie builders shipping on Salesforce who need accurate Permission Set metadata without guessing XML tags. It catalogs the elements you can declare in a permission set—object and field rights, tab visibility, Apex and page access, custom permissions, and related access types—so agents can generate or review security configs that match org reality. Use it when you are modeling profiles and permission sets for integrations, sandboxes, or CI-deployed metadata, not when you only need a business process diagram. The skill is documentation-forward: it anchors agent output to Salesforce’s metadata schema so access changes stay auditable and deployable. It matters because mis-modeled permissions are a common source of production surprises and failed deployments for small teams without a dedicated admin.
- Complete Permission Set XML reference with objectPermissions, fieldPermissions, tabSettings, classAccesses, pageAccesses
- Documents tab visibility options: DefaultOn, DefaultOff, Hidden, Visible
- Shows CRUD and view/modify-all flags per object for least-privilege design
- Covers Apex class, Visualforce page, custom metadata type, and named custom permission toggles
- Pairs with Salesforce implementation workflows where access must be deployable as metadata
Sf Permissions by the numbers
- 41 all-time installs (skills.sh)
- Ranked #752 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/clientell-ai/salesforce-skills --skill sf-permissionsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 41 |
|---|---|
| repo stars | ★ 12 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 14, 2026 |
| Repository | clientell-ai/salesforce-skills ↗ |
What it does
Look up complete Salesforce Permission Set metadata elements while modeling object, field, tab, Apex, and custom permission access for a developer’s CRM org.
Who is it for?
Best when you're implementing Salesforce CRM, CPQ, or custom objects and deploy permission sets via metadata API or source control.
Skip if: Skip if you only need high-level role descriptions without deployable XML, or non-Salesforce auth (OAuth to your own API only).
When should I use this skill?
Modeling or reviewing Salesforce Permission Set XML for object, field, tab, Apex, page, or custom permission access.
What you get
You get a schema-accurate permission set structure your agent can paste into metadata or compare against an existing org export.
- Permission Set XML skeleton aligned to Salesforce metadata schema
- Checklist of access elements to enable per object or feature
Files
Salesforce Permission Management & Access Auditing
You are a Salesforce permissions specialist. Manage permission sets, audit access, diagnose permission errors, and enforce least-privilege security.
1. Permission Model Overview
| Layer | Controls | Scope |
|---|---|---|
| Profiles | Login hours, IP ranges, page layouts, record types, default app | One per user (required) |
| Permission Sets | Object CRUD, FLS, Apex class, VF page, tab, custom permissions | Many per user (additive) |
| Permission Set Groups | Bundle of Permission Sets + optional muting | Many per user (additive) |
Best practice: Minimal Profile + Permission Sets. Assign a stripped-down profile (e.g., "Minimum Access - Salesforce") and grant everything else through Permission Sets and Permission Set Groups.
Why Permission Sets over Profiles:
- A user can have only ONE profile but MANY permission sets
- Permission sets are additive and composable
- Profiles cause merge conflicts in source control
- Permission Set Groups enable role-based bundling with muting for exceptions
- Salesforce is actively moving away from profile-based permissions
2. Permission Set XML (SFDX Source Format)
<!-- force-app/main/default/permissionsets/Order_Manager.permissionset-meta.xml -->
<?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: Whentrue, must be activated in a session before taking effectlicense: Restricts assignment to users with that license type- Object permissions hierarchy: Read required for Edit; Edit required for Delete;
viewAllRecords/modifyAllRecordsoverride sharing
3. Permission Set Group XML
<!-- force-app/main/default/permissionsetgroups/Sales_Team.permissionsetgroup-meta.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>Muting Permission Set
A muting permission set removes specific permissions from the group. It only works inside a 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>
<!-- "true" in a muting PS means MUTE this permission -->
</objectPermissions>
</PermissionSet>Muting revokes the permission only for users who get access through this group. Direct assignments are unaffected.
4. Profile Metadata
Profiles are still required for: login hours/IP restrictions, page layout assignments, record type defaults, default app assignment.
Minimal Profile Strategy
<?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>Avoid putting object/field permissions in profiles. Use profiles only for what cannot be done through permission sets.
5. Object & Field Level Security (CRUD/FLS)
CRUD Permissions Hierarchy
Read ─── required for ──→ Edit ─── required for ──→ Delete
│ │
└── viewAllRecords └── modifyAllRecords
(bypasses sharing) (bypasses sharing + ownership)FLS (Field-Level Security)
Each field has two flags: Readable and Editable (Editable requires Readable). FLS applies across UI, reports, list views, and API. A field hidden by FLS returns null in SOQL with WITH USER_MODE.
FLS Audit Queries
-- 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 = true6. Custom Permissions
Custom permissions are boolean flags to control feature access without modifying code.
<!-- force-app/main/default/customPermissions/Can_Export_Data.customPermission-meta.xml -->
<?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 (preferred):
if (FeatureManagement.checkPermission('Can_Export_Data')) {
// user has the custom permission
}LWC:
import hasExportPermission from '@salesforce/customPermission/Can_Export_Data';Flow: Use $Permission.Can_Export_Data in Decision elements (returns true/false).
7. Access Auditing
PermissionSetAssignment Queries
-- 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 = falseObjectPermissions Queries
-- 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 = falseSetupEntityAccess (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 Membership
-- 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. Permission Troubleshooting
INSUFFICIENT_ACCESS_OR_READONLY
User lacks Edit permission on the object or record. Check object-level Edit, sharing access, record ownership, role hierarchy, and record locks (approval process).
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
User lacks access to a related record. Common causes: inserting a child without Read on the parent, changing a lookup to a record the user cannot see, trigger/flow updating a related record.
"Insufficient Privileges" Error
Generic error meaning any of: missing Apex class access, VF page access, Lightning component access, tab visibility, Connected App access, or session-based permission set not activated.
# Quick CLI diagnosis
sf data query -q "SELECT PermissionSet.Label, PermissionSet.Name \
FROM PermissionSetAssignment \
WHERE Assignee.Username = 'user@example.com' \
AND PermissionSet.IsOwnedByProfile = false" --target-org myOrg9. Sharing vs Permissions
Permissions (CRUD/FLS) and sharing are independent layers:
| Layer | Question | Scope |
|---|---|---|
| CRUD | Can the user create/read/edit/delete this object type? | Object-wide |
| FLS | Can the user see/edit this specific field? | Field-wide |
| Sharing | Which specific records can the user access? | Record-level |
A user needs BOTH the right CRUD/FLS permissions AND sharing access.
OWD Settings
| Setting | Effect |
|---|---|
| Private | Only owner + role hierarchy above |
| Public Read Only | All users read, only owner edits |
| Public Read/Write | All users read and edit |
| Controlled by Parent | Determined by parent record (master-detail) |
Record Access Determination Order
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 accessKey Distinctions
viewAllRecords/modifyAllRecordsbypasses sharing for that objectwith sharingin Apex enforces sharing but NOT CRUD/FLSWITH USER_MODEin SOQL enforces both sharing AND CRUD/FLS
10. Gotchas
- Permission Set Groups recalculate asynchronously — changes may take minutes. Check
PermissionSetGroup.StatusforUpdatedvsOutdated. - Profiles cause merge conflicts — profile XML files are enormous and reorder non-deterministically. Prefer permission sets.
- FLS does not restrict API access by default — Apex runs in system mode. Use
WITH USER_MODEorSecurity.stripInaccessible(). - Custom permissions are cached — assignment changes may not reflect until the user re-authenticates.
- Muting permission sets only work inside groups — assigning one directly to a user has no effect.
- Permission set licenses — some require specific licenses. Assignment to users without the license may fail silently.
- Session-based permission sets —
hasActivationRequired=truerequires activation via Flow orSessionPermSetActivation. Not automatic. - `viewAllRecords` does not grant field access — user sees the record but not FLS-restricted fields (when enforced).
- IsOwnedByProfile — every profile has a hidden permission set. Filter with
PermissionSet.IsOwnedByProfile = falsein queries. - Assignment limit — maximum 1,000 permission set assignments per user (including group-based).
11. Workflow
Setting Up Permissions for a New Feature
1. Identify required access: List objects, fields, Apex classes, VF pages, tabs, and custom permissions. 2. Create Permission Set: Generate .permissionset-meta.xml with least-privilege access. 3. Create Custom Permissions (if needed): Generate .customPermission-meta.xml for feature flags. 4. Add to Permission Set Group (if applicable): Update .permissionsetgroup-meta.xml. 5. Create Muting Permission Set (if needed): Only if the group over-grants for some users. 6. Deploy:
sf project deploy start -d force-app/main/default/permissionsets \
-d force-app/main/default/permissionsetgroups \
-d force-app/main/default/customPermissions --target-org myOrg7. Assign:
sf org assign permset --name Order_Manager --target-org myOrg
sf org assign permsetgroup --name Sales_Team --target-org myOrg8. Audit:
sf data query -q "SELECT Assignee.Name, PermissionSet.Label \
FROM PermissionSetAssignment \
WHERE PermissionSet.Name = 'Order_Manager'" --target-org myOrgMigrating from Profile to Permission Sets
1. Query all non-default permissions on the profile 2. Create equivalent permission sets for each functional area 3. Create a Permission Set Group matching the profile's role 4. Assign the group to affected users 5. Remove permissions from the profile (keep only layout, record type, login hours) 6. Validate with audit queries from Section 7
References
- Permissions Reference — complete Permission Set XML, audit queries, Apex/LWC/Flow permission checks, deployment best practices
- Governor Limits — per-transaction limits reference
Permissions Reference
Complete Permission Set XML
All possible elements in a Permission Set metadata file:
<?xml version="1.0" encoding="UTF-8"?>
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
<label>Complete Example</label>
<description>Shows all available permission set elements</description>
<hasActivationRequired>false</hasActivationRequired>
<license>Salesforce</license>
<objectPermissions>
<object>Account</object>
<allowCreate>true</allowCreate>
<allowDelete>false</allowDelete>
<allowEdit>true</allowEdit>
<allowRead>true</allowRead>
<modifyAllRecords>false</modifyAllRecords>
<viewAllRecords>false</viewAllRecords>
</objectPermissions>
<fieldPermissions>
<field>Account.AnnualRevenue</field>
<editable>true</editable>
<readable>true</readable>
</fieldPermissions>
<tabSettings>
<tab>standard-Account</tab>
<visibility>Visible</visibility>
<!-- Options: DefaultOn, DefaultOff, Hidden, Visible -->
</tabSettings>
<classAccesses>
<apexClass>AccountService</apexClass>
<enabled>true</enabled>
</classAccesses>
<pageAccesses>
<apexPage>AccountOverview</apexPage>
<enabled>true</enabled>
</pageAccesses>
<customPermissions>
<name>Can_Export_Data</name>
<enabled>true</enabled>
</customPermissions>
<customMetadataTypeAccesses>
<name>App_Config__mdt</name>
<enabled>true</enabled>
</customMetadataTypeAccesses>
<customSettingAccesses>
<name>Feature_Flags__c</name>
<enabled>true</enabled>
</customSettingAccesses>
<externalDataSourceAccesses>
<externalDataSource>ERP_System</externalDataSource>
<enabled>true</enabled>
</externalDataSourceAccesses>
<flowAccesses>
<flow>Order_Approval_Process</flow>
<enabled>true</enabled>
</flowAccesses>
<recordTypeVisibilities>
<recordType>Account.Enterprise</recordType>
<visible>true</visible>
</recordTypeVisibilities>
<userPermissions>
<name>RunReports</name>
<enabled>true</enabled>
</userPermissions>
<applicationVisibilities>
<application>Sales_Console</application>
<visible>true</visible>
</applicationVisibilities>
</PermissionSet>Permission Set Group with Muting
<?xml version="1.0" encoding="UTF-8"?>
<PermissionSetGroup xmlns="http://soap.sforce.com/2006/04/metadata">
<label>Support Agent</label>
<description>Permissions for tier-1 support agents</description>
<status>Updated</status>
<permissionSets>
<permissionSet>Case_Manager</permissionSet>
<permissionSet>Knowledge_Reader</permissionSet>
<permissionSet>Account_Reader</permissionSet>
</permissionSets>
<mutingPermissionSet>Support_Agent_Muting</mutingPermissionSet>
</PermissionSetGroup>Muting permission set (revokes Case delete granted by Case_Manager):
<?xml version="1.0" encoding="UTF-8"?>
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
<label>Support Agent Muting</label>
<objectPermissions>
<object>Case</object>
<allowDelete>true</allowDelete>
<!-- true = MUTED in the group context -->
</objectPermissions>
</PermissionSet>Access Audit SOQL Queries
User Permission Summary
-- Permission sets assigned to a user (excluding profile-based)
SELECT PermissionSet.Label, PermissionSet.Name, PermissionSetGroup.MasterLabel
FROM PermissionSetAssignment
WHERE AssigneeId = '005xx000001234AAA'
AND PermissionSet.IsOwnedByProfile = false
-- Users with Modify All Data
SELECT Assignee.Name, Assignee.Username
FROM PermissionSetAssignment
WHERE PermissionSetId IN (
SELECT Id FROM PermissionSet WHERE PermissionsModifyAllData = true
) AND Assignee.IsActive = true
-- Permission set count per user
SELECT Assignee.Name, COUNT(Id) permSetCount
FROM PermissionSetAssignment
WHERE Assignee.IsActive = true AND PermissionSet.IsOwnedByProfile = false
GROUP BY Assignee.Name ORDER BY COUNT(Id) DESCObject & Field Permission Audit
-- Permission sets granting Delete on an object
SELECT Parent.Label, Parent.IsOwnedByProfile, PermissionsDelete,
PermissionsViewAllRecords, PermissionsModifyAllRecords
FROM ObjectPermissions
WHERE SobjectType = 'Opportunity' AND PermissionsDelete = true
-- Over-privileged: ModifyAllRecords on any object
SELECT Parent.Label, SobjectType FROM ObjectPermissions
WHERE PermissionsModifyAllRecords = true AND Parent.IsOwnedByProfile = false
-- Who can edit a sensitive field?
SELECT Parent.Label, Parent.IsOwnedByProfile
FROM FieldPermissions
WHERE Field = 'Contact.SSN__c' AND PermissionsEdit = true
-- Unassigned permission sets (no users)
SELECT Parent.Label, COUNT(Field) FROM FieldPermissions
WHERE ParentId NOT IN (SELECT PermissionSetId FROM PermissionSetAssignment)
AND Parent.IsOwnedByProfile = false
GROUP BY Parent.LabelSetup Entity & Group Queries
-- Apex class access
SELECT Parent.Label FROM SetupEntityAccess
WHERE SetupEntityType = 'ApexClass'
AND SetupEntityId IN (SELECT Id FROM ApexClass WHERE Name = 'OrderService')
-- Custom permissions granted to a user
SELECT Parent.Label, SetupEntityId FROM SetupEntityAccess
WHERE SetupEntityType = 'CustomPermission'
AND ParentId IN (
SELECT PermissionSetId FROM PermissionSetAssignment
WHERE AssigneeId = '005xx000001234AAA'
)
-- Permission sets in a group
SELECT PermissionSetGroup.MasterLabel, PermissionSet.Label
FROM PermissionSetGroupComponent ORDER BY PermissionSetGroup.MasterLabelApex Permission Checking Patterns
Schema Describe for CRUD/FLS
public with sharing class PermissionChecker {
public static void checkObjectAccess(String objectName, String accessType) {
Schema.DescribeSObjectResult objDescribe =
Schema.getGlobalDescribe().get(objectName).getDescribe();
Map<String, Boolean> accessMap = new Map<String, Boolean>{
'create' => objDescribe.isCreateable(),
'read' => objDescribe.isAccessible(),
'update' => objDescribe.isUpdateable(),
'delete' => objDescribe.isDeletable()
};
if (!accessMap.get(accessType.toLowerCase())) {
throw new InsufficientAccessException(
'No ' + accessType + ' access on ' + objectName);
}
}
public static void checkFieldAccess(String objectName, String fieldName, String accessType) {
Schema.DescribeFieldResult fieldDescribe =
Schema.getGlobalDescribe().get(objectName)
.getDescribe().fields.getMap().get(fieldName).getDescribe();
Boolean hasAccess = accessType == 'read'
? fieldDescribe.isAccessible() : fieldDescribe.isUpdateable();
if (!hasAccess) {
throw new InsufficientAccessException(
'No ' + accessType + ' access on ' + objectName + '.' + fieldName);
}
}
}FeatureManagement for Custom Permissions
public with sharing class FeatureGate {
public static Boolean canExportData() {
return FeatureManagement.checkPermission('Can_Export_Data');
}
public static void exportData(List<SObject> records) {
if (!FeatureManagement.checkPermission('Can_Export_Data')) {
throw new FeatureDisabledException('Data export is not enabled');
}
// proceed with export logic
}
}Security.stripInaccessible
public with sharing class SecureDataService {
public static void updateAccounts(List<Account> accounts) {
SObjectAccessDecision decision =
Security.stripInaccessible(AccessType.UPDATABLE, accounts);
update decision.getRecords();
// Check stripped fields
Map<String, Set<String>> removed = decision.getRemovedFields();
}
}LWC Permission Imports
// Standard user permissions
import hasModifyAllData from '@salesforce/userPermission/ModifyAllData';
import hasViewSetup from '@salesforce/userPermission/ViewSetup';
import hasRunReports from '@salesforce/userPermission/RunReports';
// Custom permissions
import canExportData from '@salesforce/customPermission/Can_Export_Data';
import canBypassValidation from '@salesforce/customPermission/Bypass_Validation';
export default class FeaturePanel extends LightningElement {
get isAdmin() { return hasModifyAllData; }
get showExportButton() { return canExportData; }
}Flow Permission Checks
| Variable | Returns | Use Case |
|---|---|---|
$Permission.Custom_Permission_Name | Boolean | Check custom permission |
$Profile.Name | String | Check profile (avoid; use custom permissions instead) |
$User.Id | String | Current user for permission queries |
$UserRole.Name | String | Current user's role |
Use custom permissions in Flow Decision elements instead of profile name checks. Profiles change; permissions are stable.
Permission Comparison Queries
-- Object permissions in PS_A but not PS_B
SELECT SobjectType, PermissionsCreate, PermissionsRead, PermissionsEdit, PermissionsDelete
FROM ObjectPermissions WHERE Parent.Name = 'Permission_Set_A'
AND SobjectType NOT IN (
SELECT SobjectType FROM ObjectPermissions WHERE Parent.Name = 'Permission_Set_B'
)
-- Full access picture for a user on a specific object
SELECT Parent.Label, Parent.IsOwnedByProfile,
PermissionsCreate, PermissionsRead, PermissionsEdit, PermissionsDelete
FROM ObjectPermissions WHERE SobjectType = 'Case'
AND ParentId IN (
SELECT PermissionSetId FROM PermissionSetAssignment
WHERE Assignee.Username = 'user@example.com'
)Permission Matrix Template
| Access | Reader PS | Editor PS | Manager PS | Admin PS |
|---|---|---|---|---|
| Order__c | ||||
| Create | - | Yes | Yes | Yes |
| Read | Yes | Yes | Yes | Yes |
| Edit | - | Yes | Yes | Yes |
| Delete | - | - | Yes | Yes |
| View All | - | - | Yes | Yes |
| Fields | ||||
| Amount__c | Read | Read/Edit | Read/Edit | Read/Edit |
| InternalNotes__c | - | - | Read/Edit | Read/Edit |
| Apex Classes | ||||
| OrderService | - | Yes | Yes | Yes |
| Custom Permissions | ||||
| Can_Export_Data | - | - | Yes | Yes |
Permission Deployment Best Practices
Deploy Order (dependencies)
1. Custom Objects & Fields
2. Custom Permissions
3. Apex Classes & VF Pages
4. Permission Sets
5. Permission Set Groups
6. Permission Set AssignmentsCLI Commands
# Deploy permissions
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
# Retrieve permission sets
sf project retrieve start -m PermissionSet:Order_Manager --target-org myOrg
sf project retrieve start -m PermissionSetGroup:Sales_Team --target-org myOrg
# Assign
sf org assign permset --name Order_Manager --target-org myOrg
sf org assign permset --name Order_Manager \
--on-behalf-of user@example.com --target-org myOrg
sf org assign permsetgroup --name Sales_Team --target-org myOrgSource Control Tips
- Exclude profiles via
.forceignore(**/profiles/**) to avoid merge conflicts - One permission set per functional area; name descriptively (
Object_Action) - Document purpose in
<description>; version control muting sets alongside their group - Review permission changes in PRs like code changes
Related skills
How it compares
Reference for Permission Set XML fields—not a full org security audit runner or a generic RBAC policy engine.
FAQ
Who is sf-permissions for?
Developers wiring Salesforce into a SaaS or services workflow who need correct permission set metadata while coding with an agent.
When should I use sf-permissions?
During Build integrations when drafting permission sets; in Ship security when reviewing least privilege before go-live; in Operate when adjusting field or tab access after feedback.
Is sf-permissions safe to install?
Treat it as documentation-only guidance; review the Security Audits panel on this Prism page and your own org change process before applying generated XML in production.