function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
vbadhwarvbadhwar 

Bypassing org sharing rules in Apex with org admin approval

As an application developer, you may run into a use-case where the application must run in system context ("without sharing"), thereby bypassing org sharing rules. You must ensure that the org administrator explicitly approves any such behavior. 


The recommended way of achieving this is by using the Force.com ESAPI: http://code.google.com/p/force-dot-com-esapi/  


You must first create a custom field (preferably boolean) only accessible by an Admin user that stores the preference (Bypass Sharing: Yes/No). Then at runtime, call one of the following functions to set with/without sharing based on that setting.
 
http://force-dot-com-esapi.googlecode.com/svn/trunk/doc/SFDCAccessController.html#setSharingMode%28SFDCAccessController.SharingMode%29
http://force-dot-com-esapi.googlecode.com/svn/trunk/doc/SFDCAccessController.html#SFDCAccessController%28SFDCAccessController.SharingMode,%20SFDCAccessController.OperationMode%29


The first approach is to set the sharing mode on an existing SFDCAccessController object. (such as used with the ESAPI.accessController() - this function returns a static SFDCAccessController object)


If (org admin setting is with sharing)

     ESAPI.accessController().setSharingMode(SFDCAccessController.SharingMode.WITH);

else

     ESAPI.accessController().setSharingMode(SFDCAccessController.SharingMode.WITHOUT);


// later in the code

ESAPI.accessController().updateAsUser(c, new List<Schema.SObjectField>{Contact.LastName});



The second approach is to use the constructor that allows setting the sharing mode. In this approach, create a SFDCAccessController object and set it to whatever sharing mode the org admin wants. 


SFDCAccessController ac = null;

If (org admin setting is with sharing)  

     ac = new SFDCAccessController(SFDCAccessController.SharingMode.WITH, SFDCAccessController.OperationMode.ALL_OR_NONE);

else

    ac = new SFDCAccessController(SFDCAccessController.SharingMode.WITHOUT, SFDCAccessController.OperationMode.ALL_OR_NONE);


// later in the code

ac.updateAsUser(c, new List< Schema.SObjectField>{Contact.LastName});


Note that in both cases you can also allow for inherit sharing.







 

Best Answer chosen by Admin (Salesforce Developers) 
vbadhwarvbadhwar

First, to clarify, the problem the post is trying to solving is a case where a specific user needs to update a record which is not shared with him.
 
The insecure way to implement this would be to declare your class “without sharing”. However, the post recommends you set your classes to “with sharing”, then use some secure logic to decide if the current user should be allowed to update this record, and then use the ESAPI functions to handle sharing enforcement.
In a simple example you would have a field on the same record the user is trying to access, or another object that defines this permission. (Obviously, this field must not be accessible by regular users and the current user – you can define the logic for this as well). Then at run time, your class should check this logic, and if the user should be allowed to update the record, you use the ESAPI sharing mode set to WITHOUT, and if not you use the WITH mode.
 
Here are some answers to the specific questions:
1.       The field defining the logic of whether or not to allow the current user to update this record can be in any object, including the one we are attempting to check if the user can modify a record in. However, this field should only be modified by an Administrator. You may also create a new object where an admin can allow your package to entirely bypassing his org's sharing rules.

2.       Usually you should not modify the ESAPI classes. That will break the desired functionality of allowing some function calls to be executed without sharing when you need (like in the case this post is talking about).

3.       You don’t need to remove any with sharing from your classes. In fact it is recommended that all your classes have “with sharing” and if you have a legitimate reason for without sharing, you should use the ESAPI library to do that operation as described in the post. (In general you probably want
all db operations to be using the ESAPI calls and enforcing the logic for you).

4.       Sharing is enforced based on the execution path. It is examined when executing a db statement that has sharing enforced on it. What happens is, the run time system traverses the path backwards until it finds an explicit with/without sharing declaration. As soon as it finds that, it stops traversing and uses that mode. When you call the ESAPI functions, you have three sharing modes, WITH, WITHOUT, and INHERIT. If you use WITH and WITHOUT, it does not matter what your classes are defined with, because you call into the ESAPI functions that are defined in classes with the matching modes declared on them, and therefore they will take priority over whatever is declared on your calling class. Only when you call ESAPI with inherit it would use your calling class hierarchy to decide which mode to operate in.


Hope this help, and feel free to ask if anything needs more clarification.

All Answers

OnDem DevOnDem Dev

Hi Varun,

 

I have few questions on implementing ESAPI in my app,

 

  • Should the custom field be created on the object on which CRUD operation will be performed or any other object?
  • If i include the Apex classes from ESAPI into my package, should i modify and declare the ESAPI classes to use "With Sharing" keyword.
  • As existing classes in my app are declared with "With Sharing" keyword, do i need to remove the "With sharing" keyword for all the classes?
  • As ESAPI classes are global and don't use sharing keyword, what will be the impact if a class declared with "With Sharing"  keyword invokes any of the ESAPI classes. Will the sharing be enforced or not?

I hope that these questions will be answered as it will help me in understanding on how i should ESAPI in my app.

 

vbadhwarvbadhwar

First, to clarify, the problem the post is trying to solving is a case where a specific user needs to update a record which is not shared with him.
 
The insecure way to implement this would be to declare your class “without sharing”. However, the post recommends you set your classes to “with sharing”, then use some secure logic to decide if the current user should be allowed to update this record, and then use the ESAPI functions to handle sharing enforcement.
In a simple example you would have a field on the same record the user is trying to access, or another object that defines this permission. (Obviously, this field must not be accessible by regular users and the current user – you can define the logic for this as well). Then at run time, your class should check this logic, and if the user should be allowed to update the record, you use the ESAPI sharing mode set to WITHOUT, and if not you use the WITH mode.
 
Here are some answers to the specific questions:
1.       The field defining the logic of whether or not to allow the current user to update this record can be in any object, including the one we are attempting to check if the user can modify a record in. However, this field should only be modified by an Administrator. You may also create a new object where an admin can allow your package to entirely bypassing his org's sharing rules.

2.       Usually you should not modify the ESAPI classes. That will break the desired functionality of allowing some function calls to be executed without sharing when you need (like in the case this post is talking about).

3.       You don’t need to remove any with sharing from your classes. In fact it is recommended that all your classes have “with sharing” and if you have a legitimate reason for without sharing, you should use the ESAPI library to do that operation as described in the post. (In general you probably want
all db operations to be using the ESAPI calls and enforcing the logic for you).

4.       Sharing is enforced based on the execution path. It is examined when executing a db statement that has sharing enforced on it. What happens is, the run time system traverses the path backwards until it finds an explicit with/without sharing declaration. As soon as it finds that, it stops traversing and uses that mode. When you call the ESAPI functions, you have three sharing modes, WITH, WITHOUT, and INHERIT. If you use WITH and WITHOUT, it does not matter what your classes are defined with, because you call into the ESAPI functions that are defined in classes with the matching modes declared on them, and therefore they will take priority over whatever is declared on your calling class. Only when you call ESAPI with inherit it would use your calling class hierarchy to decide which mode to operate in.


Hope this help, and feel free to ask if anything needs more clarification.

This was selected as the best answer
OnDem DevOnDem Dev

Thanks Varun for your reply. It helped a lot in understanding ESAPi as well as how with sharing and without sharing works when CRUD operation occurs.

 

I have a question here.

Can you please elaborate little bit on the below statement you mentioned?

"(Obviously, this field must not be accessible by regular users and the current user – you can define the logic for this as well)."

 

Do you mean that the FLS setting for the custom field be set to hidden for non-admin users?

And then in Apex code, the check should be made if the custom field for the current User can bypass Sharing or not and then act accordingly.

 

Thanks in advance

vbadhwarvbadhwar

That is correct. The whole point of this solution is to ensure that only an Admin can override his own sharing rules.. An easier option might be to setup a config object with this field that only the Admin has access to. Alternatively, you can make it even more granular by adding this field to each object you want to allow the admin to bypass. But in this second case, CRUD access should be taken away from everyone but the Admin. 

OnDem DevOnDem Dev

Thanks Varun,

 

If i go with config object setup, it means that the customfield that stores the specific value will be irrespective of the Users in org.

Suppose in the org, if only 100 users are required to bypass the sharing and the others should not. How should the custom field in the config object be setup ?

 

 

 

 

 

vbadhwarvbadhwar

Then I would suggest adding this custom field on the User object. 

OnDem DevOnDem Dev

Thanks Varun for your answers.

But i will request you to answer the question i posted, because there can be such scenarios where in only few users in the org will be using a particular app that has to bypass sharing.

 

Should the config object have two fields, one field that will be a lookup to User object and the other custom field will store the bypass sharing preference against each User in the org?

 

Once again, thanks for all the help.

vbadhwarvbadhwar

There are a number of ways you can do this, so it really depends upon the context of the app and the approach you are comfortable with. You could do what you are proposing - create a user lookup, and then the associated sharing preference. In this case your class would run with sharing, lookup that permission and if allowed, use the ESAPI to bypass sharing.