Salesforce Security and Sharing Model - Mastering Access Control
Understand the foundational Salesforce security and sharing model to control data access effectively. This module covers profiles, permission sets, OWDs, role hierarchy, sharing rules, and programmatic sharing.
1. Introduction to Salesforce Security
Salesforce's robust security and sharing model is a cornerstone of its multi-tenant architecture, designed to protect your data and ensure that users only access what they are authorized to see and do. Understanding this model is critical for administrators and developers alike to build secure and compliant applications.
Why is Security and Sharing Important?
- Data Protection: Prevents unauthorized access to sensitive information.
- Compliance: Helps meet regulatory and industry compliance requirements.
- Business Logic: Ensures that business rules regarding data visibility and modification are consistently applied.
- User Experience: Presents users with only the relevant data and functionality, reducing clutter and potential errors.
- Scalability: Provides a scalable framework for managing access across large organizations with diverse user roles.
The Salesforce security model operates on multiple layers, from organization-wide settings down to individual record access. This module will explore these layers and how they interact to control who sees what data and who can do what with it.
2. Declarative Security (Profiles & Permission Sets)
Declarative security settings control what users can do (object and field permissions) and what they can see (record access, which is further refined by sharing settings). Profiles and Permission Sets are the primary tools for managing these permissions.
a. Profiles:
A Profile defines a user's baseline permissions and access settings. Every user in Salesforce must be assigned exactly one Profile.
- Object Permissions: Read, Create, Edit, Delete, View All, Modify All for standard and custom objects.
- Field-Level Security (FLS): Read and Edit access for individual fields on objects.
- User Permissions: Access to various system functions (e.g., "Manage Users," "Modify All Data").
- Tab Settings: Visibility of tabs (Default On, Default Off, Hidden).
- Record Type & Page Layout Assignments: Which record types and page layouts are available to users.
- Login Hours & IP Ranges: Restrictions on when and from where users can log in.
Limitation: Profiles are broad and apply to all users assigned to them. Customization can lead to "profile sprawl" if many unique permission sets are needed.
b. Permission Sets:
Permission Sets extend a user's functional access without changing their profile. A user can have multiple Permission Sets assigned to them.
- Granular Permissions: Provide additional object, field, user, and tab permissions.
- Additive Model: Permissions in a Permission Set are always additive. They grant access; they cannot restrict access granted by a Profile.
- Flexibility: Ideal for granting specific, temporary, or role-based permissions without creating new profiles.
Best Practice: Salesforce recommends using a "minimum access" profile (e.g., "Standard User" or a custom clone with minimal permissions) and then granting additional permissions via Permission Sets. This simplifies administration and makes it easier to manage user access.
3. Data Access (OWD, Role Hierarchy, Sharing Rules)
While profiles and permission sets control object and field access, the sharing model determines which *records* a user can see and edit. This operates from the broadest settings down to the most granular.
a. Organization-Wide Defaults (OWD):
OWDs are the most restrictive baseline settings for each object. They define the default level of access users have to each other's records.
- Private: Only the record owner and users higher in the role hierarchy can view, edit, and report on those records. No one else can see them by default.
- Public Read Only: All users can view records, but only the owner and users higher in the role hierarchy can edit them.
- Public Read/Write: All users can view and edit all records.
- Public Read/Write/Transfer (for Leads/Cases): Similar to Public Read/Write, but also allows transferring ownership.
- Controlled by Parent: Access is determined by the user's access to the parent record (for detail records in a master-detail relationship).
Recommendation: Always set OWDs to the most restrictive level possible, then open up access using other sharing mechanisms.
b. Role Hierarchy:
The Role Hierarchy opens up vertical access. Users at any given role level can automatically view, edit, and report on all records owned by users below them in the hierarchy, regardless of OWD settings. This is primarily for reporting and management visibility.
- Vertical Access: Grants access up the hierarchy.
- Not for Horizontal Sharing: Does not grant access to peers at the same level.
c. Sharing Rules:
Sharing Rules open up horizontal access, granting additional access to groups of users based on record ownership or criteria. They are exceptions to OWDs.
- Owner-Based Sharing Rules: Share records owned by certain users (or roles/groups) with other users (or roles/groups).
- Criteria-Based Sharing Rules: Share records that meet specific criteria (e.g., "Opportunity Type = 'New Business'") with other users (or roles/groups).
- Additive: Sharing rules only grant access; they never restrict it.
d. Manual Sharing:
Allows record owners or users with "Full Access" to a record to share it manually with other users, groups, or roles. This is for one-off sharing scenarios.
- Ad-hoc Access: Provides temporary or specific access to individual records.
- Not Scalable: Not suitable for large-scale sharing requirements.
4. Programmatic Sharing (Apex Managed Sharing)
For complex sharing requirements that cannot be met by declarative tools (OWDs, Role Hierarchy, Sharing Rules, Manual Sharing), Apex Managed Sharing allows developers to programmatically grant and revoke access to records.
Apex Managed Sharing is used when sharing needs to be dynamic, based on complex calculations, or involves relationships that standard sharing rules cannot handle.
Key Concepts:
- Share Objects: Every standard or custom object in Salesforce has an associated "Share" object (e.g., `AccountShare`, `MyCustomObject__Share`). These objects store the explicit sharing records.
- `AccessLevel` Field: On the Share object, this field defines the level of access granted (e.g., `Read`, `Edit`, `All`).
- `RowCause` Field: This field indicates why a user has access to a record. For Apex Managed Sharing, you must use a custom `RowCause` that has its `Type` set to `Apex`.
- `with sharing` vs. `without sharing`:
- Apex classes that create, update, or delete records on share objects should generally be `without sharing` to ensure they can modify sharing records regardless of the running user's access to the records being shared. However, the logic *within* the method should still respect permissions of the data being shared.
Example (Simplified):
public class CustomSharingManager {
// Custom RowCause must be defined in Setup -> Sharing Settings -> Your Custom Object -> New Sharing Reason
// Type should be "Apex"
public static final Schema.SObjectRowCause CustomShareReason = Schema.SObjectRowCause.MyCustomShareReason__c;
public static void shareAccountWithUser(Id accountId, Id userId, String accessLevel) {
// Ensure the AccountShare object is updateable and the user has permission to share
if (!Schema.sObjectType.AccountShare.isCreateable()) {
throw new AuraHandledException('Insufficient permissions to create AccountShare records.');
}
AccountShare accShare = new AccountShare();
accShare.ParentId = accountId;
accShare.UserOrGroupId = userId;
accShare.AccessLevel = accessLevel; // e.g., 'Read', 'Edit', 'All'
accShare.RowCause = CustomShareReason; // Use your custom RowCause
try {
insert accShare;
} catch (DmlException e) {
System.debug('Error sharing account: ' + e.getMessage());
// Handle error, e.g., log it or throw a custom exception
}
}
public static void removeAccountSharing(Id accountId, Id userId) {
List<AccountShare> sharesToDelete = [
SELECT Id
FROM AccountShare
WHERE ParentId = :accountId
AND UserOrGroupId = :userId
AND RowCause = :CustomShareReason // Crucially, only delete shares created by this Apex
];
if (!sharesToDelete.isEmpty()) {
try {
delete sharesToDelete;
} catch (DmlException e) {
System.debug('Error removing sharing: ' + e.getMessage());
// Handle error
}
}
}
}
Considerations:
- Apex Managed Sharing is complex and should be used judiciously.
- Always define a custom `RowCause` to distinguish your programmatic shares from other sharing types.
- Ensure proper error handling and testing.
- Be mindful of governor limits when performing DML on share objects.
5. Security in Apex (with sharing, FLS, CRUD)
Apex code, by default, runs in system context, meaning it has access to all objects and fields regardless of the running user's permissions. To enforce security and sharing, developers must explicitly implement mechanisms.
a. `with sharing` and `without sharing` Keywords:
with sharing
: When a class is declared `with sharing`, the sharing rules of the current user are enforced. This means queries will only return records the user has access to, and DML operations will fail if the user doesn't have permission to modify the record. This is the recommended default for most Apex classes that interact with user data.without sharing
: When a class is declared `without sharing`, the sharing rules of the current user are *not* enforced. The code runs in system context, allowing access to all data regardless of the user's sharing settings. Use this with extreme caution and only when necessary (e.g., for utility classes that perform operations like creating audit logs where the user's sharing shouldn't prevent the operation).- Inherited Sharing: (API version 44.0 and later) If a class is declared `inherited sharing`, it runs in the sharing mode of the class that called it. This promotes flexibility and reusability.
public with sharing class AccountService { // Enforces sharing rules
public static List<Account> getMyAccounts() {
return [SELECT Id, Name FROM Account]; // Only returns accounts user can see
}
}
public without sharing class AuditLogService { // Bypasses sharing rules (use with caution)
public static void createLog(String message) {
// Create an audit log record, even if the user doesn't have access to the Audit_Log__c object
// (assuming FLS/CRUD are handled elsewhere or for system-level operations)
insert new Audit_Log__c(Message__c = message);
}
}
b. Field-Level Security (FLS) and CRUD Enforcement:
Even with `with sharing`, Apex doesn't automatically enforce FLS or CRUD permissions. Developers must explicitly check these permissions, especially before performing DML operations or displaying sensitive data.
- CRUD Checks (Object-level):
Schema.SObjectType.ObjectName.isAccessible()
: Can the user read this object?Schema.SObjectType.ObjectName.isCreateable()
: Can the user create records for this object?Schema.SObjectType.ObjectName.isUpdateable()
: Can the user update records for this object?Schema.SObjectType.ObjectName.isDeletable()
: Can the user delete records for this object?
- FLS Checks (Field-level):
Schema.SObjectType.ObjectName.fields.FieldName.isAccessible()
: Can the user read this field?Schema.SObjectType.ObjectName.fields.FieldName.isCreateable()
: Can the user create this field?Schema.SObjectType.ObjectName.fields.FieldName.isUpdateable()
: Can the user update this field?
WITH SECURITY_ENFORCED
(SOQL/SOSL): (API version 45.0 and later) This clause automatically enforces object and field-level security for the query. If the user doesn't have access to an object or a field in the query, an error is thrown. This is highly recommended for all queries.public with sharing class SecureQueryExample { @AuraEnabled(cacheable=true) public static List<Account> getAccountsSecurely() { // Enforces FLS and CRUD for Account object and Name/Industry fields return [SELECT Id, Name, Industry FROM Account WITH SECURITY_ENFORCED LIMIT 5]; } }
Best Practice: Always use `with sharing` for Apex classes that handle user data, and use `WITH SECURITY_ENFORCED` in your SOQL/SOSL queries. For DML operations, explicitly check CRUD and FLS before proceeding.
6. Security in LWC (Locker Service, UI API)
Lightning Web Components are built with security in mind, leveraging modern web standards and Salesforce-specific security features to protect your application and data.
a. Locker Service:
- Purpose: Locker Service is a security architecture that isolates Lightning components from each other and from the global DOM. It prevents components from accessing or manipulating resources they don't own, enhancing security and preventing malicious or poorly written code from impacting the entire application.
- How it Works: It wraps components in a secure container, providing a strict API for interaction. All LWC components automatically run in Locker Service.
- Benefits: Prevents cross-site scripting (XSS), protects sensitive data, and ensures component interoperability.
b. UI API (Wire Adapters):
- Automatic Security Enforcement: When you use standard UI API wire adapters (e.g.,
lightning/uiRecordApi
,lightning/uiListApi
), LWC automatically enforces Field-Level Security (FLS) and Create, Read, Update, Delete (CRUD) permissions based on the running user's profile and permission sets.- If a user doesn't have access to a field, that field's data will not be returned by the wire adapter.
- If a user doesn't have CRUD access to an object, corresponding UI API operations will fail.
- Example:
import { LightningElement, wire, api } from 'lwc'; import { getRecord, getFieldValue } from 'lightning/uiRecordApi'; import NAME_FIELD from '@salesforce/schema/Account.Name'; import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry'; export default class AccountViewer extends LightningElement { @api recordId; @wire(getRecord, { recordId: '$recordId', fields: [NAME_FIELD, INDUSTRY_FIELD] }) account; get name() { return getFieldValue(this.account.data, NAME_FIELD); } get industry() { return getFieldValue(this.account.data, INDUSTRY_FIELD); } // If the user doesn't have FLS for Name or Industry, those fields will be null // If the user doesn't have Read access to the Account record, account.data will be undefined }
c. Imperative Apex Calls:
As discussed in the "Working with Apex in LWC" section, when calling Apex imperatively from LWC, the security enforcement shifts to the Apex controller. It is crucial that your Apex methods use `with sharing` and/or `WITH SECURITY_ENFORCED` to respect the running user's permissions.
d. Sanitization:
Always sanitize any user-provided input before rendering it in the DOM or using it in queries/DML. While LWC provides some level of protection, explicit sanitization (e.g., using `LightningSanitize` utility or custom logic) is a good practice to prevent Cross-Site Scripting (XSS) vulnerabilities.
7. Security Best Practices
Implementing a robust security model requires a layered approach and adherence to best practices across your Salesforce implementation.
- Principle of Least Privilege: Grant users only the minimum access necessary to perform their job functions. Start with restrictive settings and open up access as needed.
- Use Permission Sets for Access: Assign users to a minimal profile and use permission sets to grant additional object, field, and user permissions. Avoid creating numerous profiles.
- Most Restrictive OWDs: Set Organization-Wide Defaults to the most restrictive level (e.g., Private) and then use sharing mechanisms (role hierarchy, sharing rules, manual sharing, Apex managed sharing) to open up access.
- Secure Apex:
- Always use `public with sharing class` for Apex controllers that handle user data.
- Use `WITH SECURITY_ENFORCED` in all SOQL/SOSL queries.
- Perform explicit FLS and CRUD checks in Apex, especially before DML operations.
- Avoid `without sharing` unless absolutely necessary, and if used, ensure comprehensive manual security checks.
- Secure LWC:
- Leverage UI API wire adapters for data access as they automatically enforce FLS/CRUD.
- Understand Locker Service and how it isolates your components.
- Sanitize any user-provided input before rendering it or using it in data operations.
- Regular Security Audits: Periodically review your organization's security settings, profiles, permission sets, and sharing rules to ensure they align with your current business needs and security policies.
- Encrypt Sensitive Data: Utilize Salesforce Platform Encryption for highly sensitive data at rest.
- Implement Two-Factor Authentication (2FA): Encourage or enforce 2FA for all users to add an extra layer of login security.
By consistently applying these best practices, you can build a secure and compliant Salesforce environment that protects your valuable data.
8. Conclusion & Key Takeaways
Salesforce's security and sharing model is a sophisticated system designed to provide granular control over data access. From declarative tools like profiles and permission sets to programmatic options like Apex managed sharing, the platform offers a comprehensive suite of features to ensure data integrity and confidentiality.
Key Takeaways:
- Layered Security: Salesforce security operates on multiple layers (Org, Object, Field, Record).
- Profiles & Permission Sets: Control "what users can do" (object/field permissions). Use permission sets to extend access.
- OWD, Role Hierarchy, Sharing Rules: Control "what records users can see." OWDs are the baseline, hierarchy extends vertical access, and sharing rules extend horizontal access.
- Apex Managed Sharing: For complex, programmatic sharing requirements.
- Apex Security: Use
with sharing
,WITH SECURITY_ENFORCED
, and manual FLS/CRUD checks. - LWC Security: Benefits from Locker Service and UI API's automatic FLS/CRUD enforcement.
- Best Practices: Adhere to the principle of least privilege, sanitize input, and conduct regular audits.
A deep understanding of the Salesforce security and sharing model is fundamental for any professional working on the platform. It enables you to build secure, scalable, and compliant applications that meet your organization's unique access control requirements.