📢 Attention Salesforce Certified Trailblazers! Maintain your credentials and link your Trailhead and Webassessor accounts by April 19th. Learn more.
close

Prevent CRUD and FLS Violations

Learning Objectives

After completing this unit, you’ll be able to:

  • Prevent CRUD and FLS violations in Visualforce.
  • Prevent CRUD and FLS violations in Apex.

Protect Against CRUD and FLS Violations

As we saw in the previous unit, depending on how your custom applications render and process data, unauthorized users have the potential to access and modify data that they shouldn’t. Luckily the platform makes preventing unauthorized access easy!

The DescribeSObjectResult class includes a number of helper functions that you can use to verify a user’s level of access and prevent data from being inadvertently exposed or modified.

  • IsCreateable()
  • IsAccessible()
  • IsUpdateable()
  • IsDeleteable()

Let’s walk through each of these to show you how they work.

isCreateable()

Before your code inserts a record in the database, you have to check that the logged-in user has both “Edit” permission on the field and “Create” permission on the object. You can check both permissions by checking if the particular field isCreateable().

Suppose the user needs to create an opportunity with $500 in the Amount field. To ensure that the user calling the function has authorization to create opportunities and opportunity amounts, your Apex code should perform a check to see if the user has the isCreateable() permission on Opportunity.Amount.

  if (!Schema.sObjectType.Opportunity.fields.Amount.isCreateable()){
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Error: Insufficient Access'));
    return null;
  }
  Opportunity o = new Opportunity(Amount=500);
  insert o;
  

You may notice that in the example we’re explicitly verifying that the user can create the field, not the object. Luckily by checking access to the field, you're also implicitly checking that the user has create access on the object. So in this case, you check FLS and you also get CRUD verification for free.

isAccessible()

Before your code retrieves a field from an object, you want to verify that the logged-in user has permission to access the field by checking if the field isAccessible().

Suppose the user wants to access the Expected Revenue field in an opportunity. Your Apex code should check if the user has isAccessible permission on Opportunity.ExpectedRevenue.

    // Check if the user has read access on the Opportunity.ExpectedRevenue field
    if (!Schema.sObjectType.Opportunity.fields.ExpectedRevenue.isAccessible()){
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Error: Insufficient Access'));
      return null;
    }
    Opportunity [] myList = [SELECT ExpectedRevenue FROM Opportunity LIMIT 1000];
  

isUpdateable()

Similarly, before your code updates a record, you have to check if the logged-in user has “Edit” permission for the field and the object. You can check for both permissions by checking if the particular field isUpdateable().

Suppose the user wants to update an opportunity to mark the stage as Closed Won. Your Apex code should then check if the user has isUpdateable() permission on Opportunity.StageName.

      //Let’s assume we have fetched opportunity “o” from a SOQL query
      if (!Schema.sObjectType.Opportunity.fields.StageName.isUpdateable()){
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Error: Insufficient Access'));
        return null;
      }
      o.StageName=’Closed Won’;
      update o;
      

As we saw in the isCreateable example, here we check explicitly for FLS on the object field, and we get implicit verification of CRUD at the same time.

Let’s try this one out in our org.

  1. Log in to the Kingdom Management developer org.
  2. Select CRUD/FLS & Sharing from the app picker on the right.
  3. Click the isUpdateable Demo tab.

    Screenshot of the isUpdateable Demo App

    In this app, you see the scoreboard for a jousting tournament between two castles in the kingdom. The list shows each of the participants and their current standing in the tourney. The rankings are editable, but only users with proper permissions should edit ranks.

    The app includes a dropdown which allows us to simulate different users using the application.

  4. Select User with Read ONLY Access to the Jousters object.

    Below is the permission set applied to this user.
    Screenshot showing read only access granted to the score field

    As you can see, the user has read and edit access on the object and but doesn’t have edit access for the rank field.

  5. In the app, change the rank for Vinay the Chain Breaker to 1, sliding him into first place.
  6. Click Update.

    Remember, the logged-in user only has read access to the rank field. Even so, it looks like the rank update worked.
    Screenshot showing that the rank setting was updated without proper permissions

    Let’s fix this.

  7. Click Return to User Selection to return to the Admin user.
  8. Click the Apex Controller link at the bottom of the page.

    Here’s the code that controls the update portion of the app.
              public PageReference updateRequest(){
                try{
                  update jousters;
                  init();
                }
                catch(DmlException ex){
                  ApexPages.addMessages(ex);
                }
                return null;
              }
              

    Notice that the updateRequest function updates all the scoreboard records when the link is clicked. This action is done whether or not the user has the necessary FLS permissions. We can fix this by including some explicit authorization checks (that is, isUpdateable()) in the code.

  9. Press Edit at the top of the screen to modify the updateRequest Apex function.

    Add the following if statement before the app calls update.
    if (Schema.sObjectType.Jouster__c.fields.Rank__c.isUpdateable())

    The modified function looks something like this.
              public PageReference updateRequest(){
                try{
                  if(Schema.sObjectType.Jouster__c.fields.Rank__c.isUpdateable()){
                    update jousters;
                  }
                  else {
                    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Error: User is unauthorized to modify the scoreboard rankings'));
                  }
                  init();
                }
                catch(DmlException ex){
                  ApexPages.addMessages(ex);
                }
                return null;
              }
              
  10. Click Save and navigate back to the isUpdateable Demo tab.
  11. Select User with Only Read Access to Jousters.
  12. Change the rank of any participant and click the Update link.

    Screenshot of the updated isUpdateable Demo app showing that the app is no longer vulnerable

    This time you see an error message and the record isn’t updated. Perfect!

isDeleteable()

Lastly, to enforce “delete” access restrictions, use the isDeleteable() function before your code performs a delete database operation. 

      if (!Lead.sObjectType.getDescribe().isDeleteable()){
        delete l;
        return null;
      }
    

Notice that unlike update, create, and access, with delete we explicitly perform only a CRUD check, verifying that the user can delete the object. Since you delete entire records in SOQL and don’t delete fields, you need to check only the user’s CRUD access to the object.

Resources

Note

Note

Remember, this module is meant for Salesforce Classic. When you launch your hands-on org, switch to Salesforce Classic to complete this challenge.

retargeting