anusha(salesforce developer)

Tuesday, 5 July 2016

System mode & User mode and "With sharing & Without sharing" keywords

Salesforce - "System mode & User mode" and "With sharing & Without sharing" keywords

This topic has always been part of discussion where the answer is not satisfactory and convinced to others.

Using this article, I will try to explain how system mode & user mode works in Salesforce and how with sharing, without sharing keywords affect these modes.

Before moving ahead let’s have a summary on what these modes and keywords are..

System mode -
  • System mode is nothing but running apex code by ignoring user's permissions. For example, logged in user does not have create permission but he/she is able to create a record.
  • In system mode, Apex code has access to all objects and fields— object permissions, field-level security, sharing rules aren't applied for the current user. This is to ensure that code won’t fail to run because of hidden fields or objects for a user.
  • In Salesforce, all apex code run in system mode. It ignores user's permissions. Only exception is anonymous blocks like developer console and standard controllers. Even runAs() method doesn't enforce user permissions or field-level permissions, it only enforces record sharing.

User mode - 
  • User mode is nothing but running apex code by respecting user's permissions and sharing of records. For example, logged in user does not have create permission and so he/she is not able to create a record.
  • In Salesforce, only standard controllers and anonymous blocks like developer console run in user mode.

Without Sharing keyword - 
  • The 'without sharing' keyword is to ensure that the sharing rules (not permissions) for the current user are not enforced.
  • Example - Let's consider that the OWD for Account is private, no account records are owned by or shared with an user 'u' and a "without sharing" class called MyClass is fetching account records in a list. Now if class 'MyClass' is run by user 'u' then account records will be fetched. Remember that whether the user 'u' is having full CRUD or having no CRUD, records will be fetched.

With Sharing keyword - 
  • The with sharing keyword allows you to specify that the sharing rules (and not permissions) for the current user be taken into account for a class. You have to explicitly set this keyword for the class because Apex code runs in system mode.
  • Example - Let's consider that the OWD for Account is private, no account records are owned by or shared with an user 'u' and a "with sharing" class called MyClass is fetching account records in a list. Now if class 'MyClass' is run by user 'u' then no records will be fetched. Remember that whether the user 'u' is having full CRUD or having no CRUD record will not be fetched.


Some more things to note about sharing keywords:
  1. The sharing setting of the class where the method is defined is applied, not of the class where the method is called. For example, if a method is defined in a class declared with with sharing keyword is called by a class declared with without sharing keyword, the method will execute with sharing rules enforced.
  2. If a class isn’t declared as either with or without sharing, the current sharing rules remain in effect. This means that if the class is called by a class that has sharing enforced, then sharing is enforced for the called class.
  3. Both inner classes and outer classes can be declared as with sharing. The sharing setting applies to all code contained in the class, including initialization code, constructors, and methods.
  4. Inner classes do not inherit the sharing setting from their container class.
  5. Classes inherit this setting from a parent class when one class extends or implements another.

Illustration 1 -

Let's create 'Test' user with profile having no CRUD on Case object.






Build a trigger to insert a new case on creating a new account.
trigger InsertCase on Account (after insert) {
    list <case> caseList = new list <case>();
    for(Account acc : trigger.new) {
        caseList.add(new Case(AccountId = acc.Id, Status = 'New'));
    }
    insert caseList;
}
Let's check how many cases are created today. For this we will use system admin creds.
Let's login into Salesforce by 'Test' user having no CRUD and create a new Account. Now again let's check Today's cases.



Here we can see a new case got inserted into database on creating an account.
Writing above code in with sharing & without sharing classes and calling these classes from trigger, results the same.
Conclusion -
All apex code run into system mode and will never check user permissions.
Note - Above statement is true for both with and without sharing classes.
Illustration 2 -
Set OWD for Account and Case to private.
Create a new class

public with sharing class MyClass {

    public static void createCase() {
        list <Account> accList = [select Id from Account where name = 'My Account'];
        list <Case> caseList = new list <Case>();
        for(Account acc : accList) {
            caseList.add(new Case(AccountId = acc.Id, Status = 'New', Subject = 'Some Subject...'));
        }
        insert caseList;
    }
}



Create a new VF page.

<apex:page controller="MyClass" action="{!createCase}">
</apex:page>




Two accounts with name 'My Account' are present in database and are owned by system administrator.




No account with name 'My Account' is owned by a test user.




Delete all today's cases.

To avoid confusion, let's modify a profile so that no CRUD on case as well as account object.
Now let's login into Salesforce by test user, run above VF page and check Today's Cases. No cases are inserted.


Since no account is owned by or shared with test user, 'accList' is empty and hence no case are inserted.
Illustration 3 -
Now let's share one account named 'My Account' with test user using system admin creds by manual sharing then run above VF again using test user creds and check Today's Cases.




One new case got inserted into database as 'accList' contains one account.
Conclusion -
  1. Though there is no CRUD, an user can perform all DML operations as generally all apex run in system mode.
  2. As keywords 'With Sharing & Without Sharing' themselves suggests that they have nothing to do with permissions on object & fields. They only play role in selecting records on sharing basis.
============================================================
User Mode : As per this Salesforce post, all Profile level permissions, sharing rules and Field level security are enforced in Apex if it runs in User mode. Standard Controller and Anonymous Apex runs in User mode.
System Mode : Same post conforms that custom controller, trigger, Apex class, controller extension works in System mode. Means eventhough user does not have necessary profile level permission, record level permission or field level permission, but still they can perform any operation on it.
Creating test scenario
For testing purpose, I am going to use a user with “Chatter free” license and existing Opportunity record. Consider OWD for opportunity is “private”.
Apex class :
In this class, I am trying to update existing Opportunity
1public class  SystemMode_Scenario {
2    public static void perform(){
3        Opportunity Opp = [SELECT NAME,StageName FROM Opportunity WHERE Id = '006K000000CD1fZIAT'] ;
4        Opp.StageName = 'Negotiation/Review' ;
5        update Opp;
6    }
7}
Trigger on Feeditem :
Whenever chatter free user is posting any chatter comment, below trigger should run and execute method in Apex class
1trigger updateOpportunityFromChatterComment on FeedItem  (before insert) {
2    SystemMode_Scenario.perform();
3}
Chatter free user trying to update Standard Opportunity record – God mode in Action
As we know, Chatter free user cannot access any standard or custom objects. so as per documentation there is no way chatter user can access or update existing Opportunity.
However, if you try to add comment in chatter using chatter user, trigger will be able to update existing Opportunity eventhough user does not has CRUD permission at profile level. So, we can say Apex really works in GOD mode.
When Apex does not work in System or God mode – Gode mode failing because of “with sharing” keyword
Example 1 :
Update Apex class with below code, only difference you can spot is addition of “with sharing” keyword.
1public with sharing class  SystemMode_Scenario {
2    public static void perform(){
3        Opportunity Opp = [SELECT NAME,StageName FROM Opportunity WHERE Id = '006K000000CD1fZIAT'] ;
4        Opp.StageName = 'Negotiation/Review' ;
5        update Opp;
6    }
7}
If you try to add chatter comment, it will fail saying “List has no rows for assignment” because logged in user does not has access to existing Opportunity and Apex class is defined using keyword “with Sharing” and makes sense.
Salesforce System Mode fail- With Sharing keyword
Salesforce System Mode fail- “With Sharing” keyword
Example 2:
In this example, lets try to create new record for Lead object and therefore update Apex class with below code :
1public with sharing class  SystemMode_Scenario {
2    public static void perform(){
3        Lead l = new Lead(LastName='Zaa',Company='Cognizant');
4        insert l;
5    }
6}
In previous example, we have seen that Apex class failed because of “with sharing” keyword and it makes sense because chatter does not has record level permission.
In this example, we are not trying to access any existing record rather creating a new lead record. “with sharing” keyword only checks if user has access to existing record or not and therfore this code should work if Apex works in God mode.
However, code will still fail with below error saying “Invalid User Type”.
Salesforce God Mode failing
Salesforce God Mode failing
Conclusion:
So saying, Apex runs in God mode not realy true if class is defined using keyword “with sharing”. “with sharing” keyword not only checks record level permission however somehow it enforces profile level permission as well.
========================================================

No comments:

Post a Comment