Apex Sharing in Salesforce

In addition to configurable methods to share a record, such as Sharing rules and manual Sharing, Salesforce allows sharing through Apex classes or flows. This method of sharing is called as “Programmatic sharing” or Apex sharing in Salesforce.

What is Apex Sharing in Salesforce?

The ability to share programmatically is only at the Record Level. In other words, we cannot set Field-Level Security or object-level Create, Read, Edit, Delete (CRUD) accesses using programmatic sharing. Only extending record-level access is allowed through apex sharing.

  1. Basics:

The following table illustrates the record-level access granted to any of the user in Salesforce:

System settingsusers and their accesses
Org Wide SettingGrant Access using hierarchyRecord Access level for OwnerUser above to the owner in role hierarchyAny other user
PrivateTRUERead, WriteRead, WriteNo Access
FALSERead, WriteNo AccessNo Access
Public Read OnlyTRUERead, WriteRead, WriteRead only
FALSERead, WriteRead onlyRead only
Public Read WriteTRUERead, WriteRead, WriteRead, Write
FALSERead, WriteRead, WriteRead, Write

The table has following two important assumptions:

  1. Only three important OWD settings available for most of the standard and custom objects are considered. Special OWD settings available for specific objects are not considered.
  2. The Create Read Edit access is granted to the user at profile or permission set level.

The Share Object

Every object in Salesforce has an associated object, which is called a share object, where the record-level access for each user or group is stored.

For Standard objects, the shared object has specific API names provided by salesforce, such as AccountShare for Account object. For custom object, the object API name is appended with “__share”, such as for customObject__c there will be an associated share object with API name customObject__share.

Share object property

Each shared object supports all types of sharing, such as Sharing rules, Manual sharing, and Programmatic sharing. The following are the fields of the shared object. We cannot create custom fields on the share object as it is inaccessible via the UI or the setup page.

Property NameDescription
ParentIDThe Record  Id of the record for which the access has to be granted.
UserOrGroupIdThe user Id or public group Id or Queue Id, the members to whom the access has to be granted
AccessLevelThe level of access that has to be granted to members specified in the  UserOrGroupId
This is a picklist field and has the following dropdown values available
All : This value cannot be set when programmatic sharing is used. This is reserved for records automatically created by Salesforce for Owner.
Edit: Edit access
Read: Read access
This field must be set to an access level that is higher than the OWD default access level for the object specified in the ParentId
RowCauseThe reason why UserOrGroupId should be provided access. The value in this field is not updatable.
This is a string field with following reserved keywords which cannot be assigned when creating the share records programmatically
Owner : For the user or geoup who is the owner of the record.
Rule: When Record access is granted using sharing rules
Team: used only for opportunities when record access to opportunity is granted via opportunity team
TerritoryRule: Used only for Territories whenassignment is done via Territory assignment rules
ImplicitChild: Used for Account Sharing
ImplicitParent: Used for Associated record owner or sharing
The following Keyword can be used when share records are created using programmatic sharing.
Manual: When Manual sharing is used. This value can be used in programmatic sharing
In addition to the above keywords, additional keywords can be defined in the “Apex Sharing Reason” of the object specified in the ParentId. This setting is especially important because the problem with using the “Manual” keyword is that when the ownership of the record specified in the “ParentId”changes then the share records with RowCause as “Manual” will be deleted and the access granted to the UserOrGroupId in the deleted record will be revoked.

Let us now understand using an example of how programmatic sharing can be used.

Apex Sharing Use Case:

Let us assume we are building a Salesforce CRM for Universal Bank. The Bank has a custom object called Loan__c. There is a related custom object called Participant__c. The bank wants that each Loan to be accessed only by the participants specified in the related list of the loan.

In this case the OWD settings of the Loan__c must be set to “Private”. We cannot use Sharing rules since the record to be shared with the set of users are stored in the related Participant__c object.

Manual sharing can be used but the bank explicitly wants to use “Participant__c” object. Hence, we have to use programmatic sharing. We will first create a Loan__c object,

  1. assign OWD as “Private”,
  2. Uncheck “Grant Access Using Hierarchies”, and
  3. give Create, Read, Edit access at profile level. Here we have created “Apex Sharing Profile” for demo purposes.

We will now create a Participant__c object having

  1. a lookup field on Loan__c object,
  2. a UserId as one of the other lookup on the user object, and
  3. Access Level as one of the picklist field having values “Read” and Ëdit” that will define the loan access level to be provided to the participant,
  4. assign OWD as “Private”,
  5. Uncheck “Grant Access Using Hierarchies”, and
  6. give Create, Read and Edit access at profile level

To ensure that even if the record owner changes on the Loan__c object, the participants should be able to retain their access hence for the Loan__Share object the RowCause cannot be set to “Manual”. Hence, we will create a Apex Sharing Reason on Loan__c object called “Universal_Bank_Member”.

  1. For that we have to navigate to Salesforce classic -> setup -> create -> Objects

2. Then find for Loan__c object

3. Scroll down to “Apex Sharing Reasons” related list

Apex Sharing Reasons

4. Click on new and enter Reason Label and Reason Name both as “Universal_Bank_Member”

5. Click Save.

    Now we shall create a trigger that whenever a participant__c is created, the Loan__c is shared with the user__c on the Participant__c having the Access level defined by the Access_Level__c field of the Participant__c record. For demo purposes we have considered insert scenario only.

    We will create a helper apex class and write our business logic in the helper class. The helper class will be called from the trigger.

    trigger apexSharingTrigger on Participant__c (after insert) {
        new ApexSHaringHelper().shareLoanRecordsWithParticipants(trigger.new);
    }
    

    Helper

    public without sharing class ApexSHaringHelper {
    	
        public void shareLoanRecordsWithParticipants(List<Participant__c> lstParticipants){
            
            List<Loan__Share> lstLoanShareRecordsToInsert = new List<Loan__Share>();
            //loop over new participant records (trigger.new) to share the loan with each user on the Participant
            for(Participant__c objParticipant: lstParticipants){
                Loan__Share objShare = new Loan__Share();
                objShare.AccessLevel = objParticipant.Access_Level__c; // Access level as defined on the Participant record
                objShare.ParentId = objParticipant.Loan__c; // The Loan Record Id to be Shared
                objShare.UserOrGroupId = objParticipant.User__c; //The user with whom the loan record is to be shared
                objShare.RowCause = Schema.Loan__Share.RowCause.Universal_Bank_Member__c; //Apex Sharing Reason as the RowCause
                lstLoanShareRecordsToInsert.add(objShare);
            }
            
            if(!lstLoanShareRecordsToInsert.isEmpty()){
                INSERT lstLoanShareRecordsToInsert;
            }
        }
    }

    Learn about Apex Trigger Framework.

    Test Apex Sharing

    For testing, we will create one user called “Apex Share User” and assign him the “Apex Sharing Profile.” We will also create a loan with an Admin Profile.

    The participants are 0 on this loan so when we login as “Apex Share User”, we will not be able to access this loan record.

    The moment we create a participant for the “Apex Share User”, the user gets the access to the Loan

    Conclusion:

    • Apex sharing is a very powerful tool provided by salesforce to handle complex sharing scenarios.
    • In the event of changing access levels such as from Read to Edit, the Share records needs to be re-created and not modified as the RowCause field cannot be modified.
    • The ownership if changed on the parent record, the share records with RowCause as “Manual” will be deleted and the accesses of the related UserOrGroupId will be revoked.
    Aditya Jawadekar
    Aditya Jawadekar

    Technical Lead at Wipro
    7X Certified Ranger
    I am a technical enthusiast, a consultant aimed to provide robust and scalable solutions to the clients within the Salesforce CRM space.

    Articles: 1

    Leave a Reply

    Your email address will not be published. Required fields are marked *