Salesforce Sharing Rules and Manual Sharing

Overview

This document covers sharing rules (owner-based, criteria-based, territory-based), manual sharing, and Apex managed sharing. These mechanisms extend access beyond Org-Wide Defaults and Role Hierarchy.

Related Patterns:

Prerequisites

Sharing Rules

Sharing rules extend access beyond OWD and role hierarchy settings. They provide exceptions to OWD settings by granting additional access to specific users or groups.

Owner-Based Sharing Rules

How Owner-Based Rules Work

Owner-based sharing rules share records owned by specific users or roles with other users, roles, or groups.

Configuration:

Example: Share all Cases owned by users in the “Support Team” role with users in the “Sales Team” role with Read Only access.

Use Cases and Examples

Example Pattern:

Rule: Share Support Cases with Sales Team
- Share With: Sales Team Role
- Based on: Cases owned by Support Team Role
- Access Level: Read Only

Configuration Patterns

Limitations

Criteria-Based Sharing Rules

How Criteria-Based Rules Work

Criteria-based sharing rules share records that meet specific field value criteria with users, roles, or groups.

Configuration:

Example: Share all Cases where Region equals “West” with users in the “West Region Managers” role.

Criteria Evaluation and Selectivity

Best Practices:

Use Cases and Examples

Example Pattern:

Rule: Share High Priority Cases with Management
- Share With: Management Role
- Criteria: Priority = "High"
- Access Level: Read Only

Performance Considerations

Limitations

Territory-Based Sharing Rules

When Territory Management is Used

Territory Management is used when sales organizations need to manage accounts and opportunities based on geographic territories or other business divisions.

Use Cases:

Territory Hierarchy and Sharing

Territory hierarchy allows:

Use Cases

Configuration Patterns

Manual Sharing

When Manual Sharing is Used

Manual sharing allows record owners or users with full access to share individual records with other users or groups on a case-by-case basis.

Use Cases:

How Manual Sharing Works

Limitations and Considerations

Best Practices

Apex Managed Sharing

When to Use Apex Managed Sharing

Apex managed sharing is used for complex sharing requirements that cannot be met through declarative sharing rules.

Use Cases:

Apex Sharing Reasons

Apex Sharing Reasons define why a record is shared programmatically. Each custom object can have up to 10 Apex Sharing Reasons.

Configuration:

Example: “Project Team Member” sharing reason for sharing project records with team members.

Implementation Patterns

Creating Shares

// Share a Case record with a user
CaseShare caseShare = new CaseShare();
caseShare.CaseId = caseRecord.Id;
caseShare.UserOrGroupId = userId;
caseShare.CaseAccessLevel = 'Read';
caseShare.RowCause = Schema.CaseShare.RowCause.Manual; // or custom Apex Sharing Reason
insert caseShare;

Bulk Sharing Pattern

public with sharing class CaseSharingService {
    public static void shareCasesWithUsers(Map<Id, Set<Id>> caseIdToUserIds) {
        List<CaseShare> sharesToInsert = new List<CaseShare>();
        
        for (Id caseId : caseIdToUserIds.keySet()) {
            for (Id userId : caseIdToUserIds.get(caseId)) {
                CaseShare share = new CaseShare();
                share.CaseId = caseId;
                share.UserOrGroupId = userId;
                share.CaseAccessLevel = 'Read';
                share.RowCause = Schema.CaseShare.RowCause.Manual;
                sharesToInsert.add(share);
            }
        }
        
        if (!sharesToInsert.isEmpty()) {
            Database.insert(sharesToInsert, false); // Allow partial success
        }
    }
}

Using Custom Apex Sharing Reasons

// Define custom sharing reason in Apex Sharing Reasons
// Then use in code:
CaseShare caseShare = new CaseShare();
caseShare.CaseId = caseRecord.Id;
caseShare.UserOrGroupId = userId;
caseShare.CaseAccessLevel = 'Read';
caseShare.RowCause = Schema.CaseShare.RowCause.Project_Team_Member__c; // Custom reason
insert caseShare;

Updating Shares

// Update existing share to change access level
CaseShare existingShare = [SELECT Id, CaseAccessLevel 
                            FROM CaseShare 
                            WHERE CaseId = :caseId 
                            AND UserOrGroupId = :userId 
                            AND RowCause = 'Manual'
                            LIMIT 1];
                            
if (existingShare != null) {
    existingShare.CaseAccessLevel = 'Read/Write';
    update existingShare;
}

Deleting Shares

// Delete shares based on criteria
List<CaseShare> sharesToDelete = [
    SELECT Id 
    FROM CaseShare 
    WHERE CaseId = :caseId 
    AND RowCause = 'Manual'
    AND UserOrGroupId IN :userIdsToRemove
];

if (!sharesToDelete.isEmpty()) {
    delete sharesToDelete;
}

Best Practices and Bulkification

Testing Apex Managed Sharing

@isTest
private class CaseSharingServiceTest {
    @isTest
    static void testShareCasesWithUsers() {
        // Create test data
        User testUser = TestDataFactory.createUser('Standard User');
        Case testCase = TestDataFactory.createCase();
        
        Test.startTest();
        // Test sharing
        Map<Id, Set<Id>> caseIdToUserIds = new Map<Id, Set<Id>>{
            testCase.Id => new Set<Id>{ testUser.Id }
        };
        CaseSharingService.shareCasesWithUsers(caseIdToUserIds);
        Test.stopTest();
        
        // Verify share was created
        CaseShare share = [
            SELECT Id, CaseAccessLevel, RowCause 
            FROM CaseShare 
            WHERE CaseId = :testCase.Id 
            AND UserOrGroupId = :testUser.Id
            LIMIT 1
        ];
        
        System.assertNotEquals(null, share, 'Share should be created');
        System.assertEquals('Read', share.CaseAccessLevel, 'Access level should be Read');
    }
}

Decision Frameworks

Sharing Rule vs. Role Hierarchy Decision

Use Role Hierarchy When:

Use Sharing Rules When:

Apex Managed Sharing vs. Declarative Sharing

Use Declarative Sharing (Sharing Rules) When:

Use Apex Managed Sharing When:

Edge Cases and Limitations

Sharing Rule Limitations

Apex Managed Sharing Limitations

Q&A

Q: What is the difference between Owner-based and Criteria-based sharing rules?

A: Owner-based sharing rules share records owned by specific users or roles with other users, roles, or groups. Criteria-based sharing rules share records that meet specific field value criteria (e.g., Region = “West”) with users, roles, or groups. Owner-based rules are simpler but limited to ownership; criteria-based rules are more flexible but have performance considerations.

Q: When should I use sharing rules vs Apex managed sharing?

A: Use sharing rules when sharing patterns are static, criteria can be defined declaratively, and standard sharing rules meet requirements. Use Apex managed sharing when sharing is dynamic based on complex logic, based on custom relationships, changes based on record field values, or when sharing sets are insufficient for Experience Cloud users.

Q: What are the limitations of criteria-based sharing rules?

A: Most objects only support Read Only access via criteria-based rules (limited objects support Read/Write). You cannot use formula fields in criteria. Large numbers of criteria-based rules can impact performance. Rules recalculate when records change, which can cause performance issues with large data volumes.

Q: How do I bulkify Apex managed sharing operations?

A: Always bulkify share creation, updates, and deletions. Collect all shares to create/update/delete in lists, then perform DML operations on the entire list. Use Database.insert/update/delete with allOrNone=false for partial success. Never perform DML operations inside loops.

Q: What are Apex Sharing Reasons and why do I need them?

A: Apex Sharing Reasons define why a record is shared programmatically. Each custom object can have up to 10 Apex Sharing Reasons. They provide a way to categorize and track programmatic shares, making it easier to understand why records are shared and to clean up shares when they’re no longer needed.

Q: When should I use manual sharing?

A: Use manual sharing sparingly and only for exceptions - one-off access requirements, temporary access needs, or exceptions not covered by sharing rules. Manual sharing is not scalable and requires manual maintenance. Consider sharing rules or Apex managed sharing for recurring patterns.

Q: How do I test Apex managed sharing?

A: Create test users with appropriate roles, create test records, execute sharing logic within Test.startTest() and Test.stopTest(), then verify shares were created by querying Share objects. Test with different user roles and record scenarios, including edge cases like record owner changes and role changes.

Q: What happens if I create a share that already exists?

A: Salesforce will throw a DML exception with INVALID_FIELD status code. Handle this gracefully by catching the exception and checking if the share already exists. Alternatively, query for existing shares before creating new ones, or use Database.insert with allOrNone=false to allow partial success.

Edge Cases and Limitations

Edge Case 1: Sharing Rules with Large Data Volumes

Scenario: Sharing rules processing millions of records, causing performance issues or calculation delays.

Consideration:

Edge Case 2: Sharing Rules with Complex Criteria

Scenario: Sharing rules with complex formula criteria or multiple conditions causing calculation issues.

Consideration:

Edge Case 3: Apex Managed Sharing with High Concurrency

Scenario: Apex managed sharing code executing during high-concurrency scenarios, causing lock contention.

Consideration:

Edge Case 4: Manual Sharing with Large User Groups

Scenario: Manually sharing records with large public groups or role hierarchies, causing performance issues.

Consideration:

Edge Case 5: Sharing Rule Conflicts and Overlaps

Scenario: Multiple sharing rules granting different access levels to the same users, causing access conflicts.

Consideration:

Limitations

When to Use This Document