softvowels.com

Enforce Sharing Rules – With Sharing, Without Sharing, Inherited Sharing

Table of Contents

This is such a topic that we as a Salesforce Developer should understand correctly and implement accordingly to not only make a robust record level security but also to avoid any hidden backend impacts. Also, this topic makes us confused while answering in any Salesforce developer interview question. Softvowels promise you here that after going through this article you will understand this topic with full clarity and will never forget.

Apex Execution Mode Basic Prerequisite

There are basically two modes in Salesforce with respect to Apex code execution – System mode and User mode.

System mode – Usually Apex Triggers, Apex Classes, Apex Web Services run in this mode. In this mode, user’s Profile level object permissions, Field Level Securities and sharing rules are NOT taken into consideration.

User mode – Usually standard UIs, API calls (REST/SOAP/UI API), Execute Anonymous, Standard Controllers, Lightning Form Components, LDS endpoints run in this mode. In this mode, user’s Profile level object permissions, Field Level Securities and sharing rules are taken into consideration.

Here, we need to understand how With Sharing and Without Sharing keywords work when adding them in defining a Class. Adding any of these keywords in Class definition impacts on Record Level Sharing only and there is no impact on Object and Field level permissions/securities.

With Sharing – It enforces current user’s sharing rule settings. Setting a class as With Sharing does not enforce object or field-level security permissions.

Without Sharing – Setting a class as Without Sharing runs in System mode. Record Level Sharing security permissions are not enforced in this case.

Inherited Sharing – To inherit the sharing settings from the parent class (the class that calls it), declare the class with Inherited Sharing keyword.

By default, an Apex Class code runs in System mode meaning that current logged in User’s permissions are not taken into consideration at the time of execution.

Deep Dive: Examples to Understand

Let’s first prepare a problem statement and then implement Class level sharing keywords and see the result.

Problem Statement: Suppose there are two custom Objects, TestParentObject and TestChildObject where TestChildObject is the child of TestParentObject via a lookup field.

<Relationship Image>

Also, assume there are two users, one is you as System Administrator and another one having a custom profile named “Sales: Custom Profile”. This profile has Create, Read, Edit permissions on parent object TestParentObject but have no object level access to the child object TestChildObject.

Now requirement is that whenever a parent record is created then automatically a child record needs to be created.

Solution Approach:

  • Login to your developer org as System Administrator.
  • Create two objects as mentioned – TestPaarentObject and TestChildObject. Child object should have one lookup filed to relate with parent. Create a Lookup field named “Test Object Parent” API Test_Object_Parent__c.
  • Create a new Tab for TestObjectParent.
  • Go to Sharing Settings and make both the objects Private as per OWD.
  • Create a custom profile named “Sales: Custom Profile”. Edit the profile and grant CRE access to TestParentObject and grant no access to TestChildObject.
  • Create a User named something having profile named “Sales: Custom Profile”.
  • Write a trigger named “TestObjectParentTrigger” on TestParentObject (Event=After Insert) which will create a child record. In After Insert, trigger will call a method “insertChildRecord” from a class called “TestObjectParentHelper”. The method insertChildRecord will create TestChildObject record relating newly created parent record of TestParentObject. The same trigger will also call another method named “displayChildRecords” defined under the same class.

Below object level access details for Sales: Custom Profile user. Where you as System administrator have full access.

We will now examine “With Sharing,” “Without Sharing,” and “Inherited Sharing” individually to assess their respective impacts and arrive at a conclusion.

Testing Scenario#1: implementing With Sharing keyword

<Trigger Code>

trigger TestObjectParentTrigger on Test_Object_Parent__c (After Insert) {
    TestObjectParentHelper hlp = new TestObjectParentHelper();
    hlp.insertChildRecord(Trigger.newMap);
    hlp.displayChildRecords();
}

<Class Code>

/*
Trigger name:TestObjectParentTrigger
Test_Object_Parent__c --- Parent Custom Object
Test_Object_Child1__c --- Child Custom Object
All methods with code will be excuted enforning Sharing Rules
*/
public with sharing class TestObjectParentHelper{
    public void insertChildRecord(Map<Id,Test_Object_Parent__c> mapParentRecs){
        List<Test_Object_Child1__c> lstChildObj = new List<Test_Object_Child1__c>();
        for(Test_Object_Parent__c parentObj:mapParentRecs.values()){
            Test_Object_Child1__c childObj = new Test_Object_Child1__c();
            childObj.Name = 'Test Child Record from trigger';
            system.debug('---:'+parentObj.Id);
            childObj.Test_Object_Parent__c = parentObj.Id;
            lstChildObj.add(childObj);
        }
        Insert lstChildObj;
    }
    
    /*
    Method displayChildRecords()
    - Class having With Sharing keyword - Will fetch all records where owner is running user, 
    nothing else as the class enforces Sharing Rule
    - Class having Without Sharing keyword - Will fetch all records irrespective of owner
    */
    public void displayChildRecords(){
        for(Test_Object_Child1__c dObj:[SELECT Id, Name, Owner.Name FROM Test_Object_Child1__c]){
            system.debug('---Id:'+dObj.Id+'---Name:'+dObj.Name+'---Owner Name:'+dObj.Owner.Name);
        }
    }
}

Steps to perform testing,

  1. Login to Salesforce org.
  2. Go to Sales app menu and choose “Test Object Parents”
  3. Click on New
  4. Provide some value in Name field and click on Save.

Follow above steps by System Administrator user. Go to Developer Console->Query Editor and run below query.

SELECT Id, Name FROM Test_Object_Parent__c
SELECT Id, Name FROM Test_Object_Child__c

Follow above steps by Sales: Custom Profile user. Go back to your logged in org and go to Developer Console->Query Editor and run below query.

SELECT Id, Name FROM Test_Object_Parent__c
SELECT Id, Name FROM Test_Object_Child__c

Conclusion

  1. With Sharing will only affect current user’s Record Level Sharing only. So, we can see that DML statement of child record creation has no impact and record has been created in both the cases though Sales: Custom Profile user has no child object level access.
  2. displayChildRecords method fetches only those records where record owner is current user or has higher level of role as per role hierarchy because the class has “With Sharing” keyword. Here Subhajit Basak has higher role and hence be able to fetch all the below records following role hierarchy tree. Whereas Maya could only be able to fetch all records where owner is Maya.

Testing Scenario#2: implementing Without Sharing keyword

Code Changes: Same code as above. Just change class level keyword from “with sharing” to “without sharing” for TestObjectParentHelper class.

Testing Step: Follow the same Steps to perform testing.

Conclusion

  1. Without Sharing also has no impact on DML statement Insert. Child records been created for both the user cases.
  2. displayChildRecords method will fetch all the existing child records in both the Users cases.

Testing Scenario#3: implementing Inherited Sharing keyword

Code Changes: Same code as above. Just change class level keyword from “without sharing” to “inherited sharing” for TestObjectParentHelper class.

Testing Step: Follow the same Steps to perform testing.

Conclusion

  1. Inherited Sharing has no impact on DML statement Insert. Child records been created for both the user cases.
  2. displayChildRecords method will fetch all the existing child records in both the Users cases, like without sharing, because this Inherited Sharing Class method is called from a trigger. Trigger runs in system mode.

Best Practices and points to ponder

  • To enforce robust data security/accessibility we should specify Sharing clauses while defining an Apex Class.
  • An Apex Class having no explicit Sharing Clause mentioned, runs in Without Sharing mode.
  • When one class extends or implements another, it inherits the sharing setting from the parent class.
  • The sharing setting applied to a method is determined by the class where it is defined, not the class from which it is called.
  • When a class serves as the entry point into an Apex transaction, specifying inherited sharing defaults to “With Sharing”.
  • When a class is called from a trigger, specifying “inherited sharing” run as “without sharing”

Dangerous Observation

In the above-mentioned implementation, we observed that when the parent record is created automatically its child record been created via trigger without considering running user’s Object level access. Here in our example, the user belongs to the profile named Sales: Custom Profile does not have any access to the child object record. Still from backend it has been created with owner’s name as the user. As per business requirement if that user should not have any access (No CRED access) to the Child object Test_Object_Child1__c then it is a great flaw.

How to overcome this issue?

Need to enforce Object level Permissions while operating on DML statement so that legitimate users can only create record. See Enforce Object & Field Permissions for further study

Recent Post

Batch Apex is usually used to build complex, long-running processes that run on thousands of records(large data volume till 50 million records), at any specific time. Go through to get concrete understanding.
Salesforce has an annotation @future that we can use defining a method making its execution as asynchronous. For concrete understanding read further.
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x