Saturday, October 1, 2016

Dynamic Apex - Playing with CRUD and FLS


Security is one of the important things in today's world. And when we talk about cloud, Security is the top most priority items. No doubt. Salesforce is not an exception and they are also considering Security really seriously.

In Salesforce, we have different types of Security controls - starting from org level security to object level, followed by record level and field level. This post focuses on methods for controlling data at the object and field level.

As always I always believe explaining concept with examples is the best and effective way. Today also I will do the same thing.

For this post, I have prepared a small object called Book and below is the detail -


Important Note -

In Visualforce, when you use any sObject or sObject field, the platform will enforce CRUD and FLS automatically. If the user is not having FLS to a field, Salesforce will automatically remove the field from the Visualforce page. For example, apex:inputField tag will be rendered as read-only for those fields having FLS set as read-only. But the exception here are the tags apex:inputText, apex:inputTextArea. Using these tags tells Visualforce to not treat them as sObject field, thus platform will not automatically enforce FLS for those  fields.

Apex Code to check CRUD manually:
Requirement:
Write a Visualforce page with controller behind which should display all Books present in the org in the form of Picklist. When we will execute the page with user having access to Book object, it should work fine, but when we will execute the same page with user having no access to Book object, we want the page to display an error message - "SORRY!!! USER IS NOT HAVING ACCESS.."(Instead of some Salesforce error message).

Implementation:
To implement this one, in my developer org, I have two profiles. One having CRUD access to Book object and another having no access at all. Below are the details -


VisualForce Page:
<apex:page controller="BookSelector">
    <apex:form >
        <apex:outputLabel rendered="{IF(hasAccess==false,true,false)}" value="{!errorMessage}"/> 
        <apex:pageBlock rendered="{IF(hasAccess==true,true,false)}">
            <apex:pageBlockSection title="Book Selector" collapsible="true" columns="1">
                <apex:pageBlockSectionItem >
                    <apex:outputLabel value="Select the book here: "/>
                    <apex:selectList value="{!selectedBook}" size="1" multiselect="false">
                        <apex:selectOptions value="{!allBooks}"/>
                    </apex:selectList>
                </apex:pageBlockSectionItem>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>
Controller:
public with sharing class BookSelector {
    public String selectedBook{get;set;}
    public String errorMessage{get;set;}
    public Boolean hasAccess{get;set;}
    
    public BookSelector(){
        //Check if user is having access to the object
        if(!sudipta__Book__c.sObjectType.getDescribe().isAccessible()){
            errorMessage = 'SORRY!!! USER IS NOT HAVING ACCESS..';
            hasAccess=false;
        }else{
            hasAccess=true;
        }
    }
    
    public List<SelectOption> getAllBooks(){
        List<SelectOption> options = new List<SelectOption>();
        if(hasAccess){
            List<sudipta__Book__c> allBooks = [SELECT 
                                                    ID,
                                                    NAME,
                                                    sudipta__Author__c,
                                                    sudipta__Price__c,
                                                    sudipta__Type__c
                                               FROM sudipta__Book__c ];
            for(sudipta__Book__c singleBook : allBooks){
                options.add(new SelectOption(singleBook.Name, singleBook.Name));
            }
        }
        return options;
    }
}

Explanation:
In the controller, I am using sudipta__Book__c.sObjectType.getDescribe().isAccessible() method to find out whether the current user is having read access to the Book object. If not, I am setting up the custom error message (Ideally this message should come from Custom Label).

Take away:
The method sObjectName.sObjecType.getDescribe().isAccessible() will return whether the user is having read access to this object.
Similarly we can use isCreateable(), isDeletable(), isUpdateable() methods to find out create, delete and edit permission.
You can find the all the methods @
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_sobject_describe.htm

CRUD and FLS check manually in Visualforce Page:
You can check whether you have CRUD permission for sObject or FLS permission for sObjectField
in Visualforce page as well. You can do that with the help of $ObjectType element.

For example, below Visualforce statement will not execute if the running user is not having View permission to see the Contact Name field.

<apex:outputText value="{!contactName}" rendered="{!$ObjectType.Contact.fields.Name.Accessible}" />
Similarly, the below command button will not even render if  the running user is not having Updateable permission on custom book object.

<apex:commandButton action="{!CustomUpdate}" rendered="{!$ObjectType.sudipta__Book__c.Updateable}" />

Please provide your feedback. Thanks.

Reference:

0 comments:

Post a Comment