Friday 24 January 2020

Enforce Security With the stripInaccessible Method


In this post we will talk about the new way to enforce the security in apex with stripInaccessible() method. From Winter 20, stripInaccessible() security feature for field-level data protection is available for beta in production. In winter 20 Salesforce extended the feature and added enum value UPSERTABLE to System.AccessType.

stripInaccessible() is useful to strip the field that current user don't have access from query and sub-query. We can use it to remove inaccessible field from sObjects before DML operation to avoid exceptions. This method also provides the option an option to enforce the Object level access check.

Syntax :- 


public static System.SObjectAccessDecision stripInaccessible(System.AccessType accessCheckType,
                                                             List<SObject> sourceRecords,
                                                             Boolean enforceRootObjectCRUD )
  • accessCheckType : This parameter determines the type of field-level access check to be performed
  • sourceRecords : A list of sObjects to be checked for fields that aren’t accessible in the context of the current user’s operation
  • enforceRootObjectCRUD : Indicates whether an object-level access check is performed
 

Use Case 1: No access on Field

In this example, User don't have access on field called accountNumber on Account Object.

        List<Account> accounts =[SELECT Id, Name,AccountNumber
                                 FROM Account limit 2];
         
        // Strip fields that are not readable
        SObjectAccessDecision decision = Security.stripInaccessible(
                                               AccessType.READABLE,
                                               accounts);
        
        // Print stripped records
        for (Integer i = 0; i < accounts.size(); i++) {
              System.debug('Insecure record access: '+accounts[i]);
              System.debug('Secure record access: '+decision.getRecords()[i]);
        }
        
        // Print modified indexes
        System.debug('Records modified by stripInaccessible: '+decision.getModifiedIndexes());
        
        // Print removed fields
        System.debug('Fields removed by stripInaccessible: '+decision.getRemovedFields());             
In above example user dont have access on accountNumber field. After using the Security.scripInaccessible method we can simply strip out the same field. Which all field are removed we can check with "getRemovedFields" method. Here is output of above code.



Use Case 2: No access on sObject

Let see another example when user don't have access on object itself. For demo I simply removed the access from Account object. Then we got the below exception.


 System.NoAccessException: No access to entity: Account
we have "enforceRootObjectCRUD" optional parameter which is true by default we can set that as false to get null value.


Use Case 3: SubQuery.

What about if you are try to access field from subquery ?

 List<Account> accountsWithContacts =
    [SELECT Name, AccountNumber,
        (SELECT LastName, Phone FROM Account.Contacts)
    FROM Account  limit 2];
 
// Strip fields that are not readable
   SObjectAccessDecision decision = Security.stripInaccessible(
                                       AccessType.READABLE,
                                       accountsWithContacts);

// Print stripped records
   for (Integer i = 0; i < accountsWithContacts.size(); i++)
  {
      System.debug('Insecure record access: '+accountsWithContacts[i]);
      System.debug('Secure record access: '+decision.getRecords()[i]);
   }

// Print modified indexes
   System.debug('Records modified by stripInaccessible: '+decision.getModifiedIndexes());

// Print removed fields
   System.debug('Fields removed by stripInaccessible: '+decision.getRemovedFields());
  This will remove the field on which user dont have access.



Use Case 4: DML.


What about if use dont have access on AccountNumber field and we will try to add value of AccountNumber by DML ?

public static void testDML(){
    Account acc = new Account(Name='Test', AccountNumber ='TestRating');
    insert acc ;
    System.debug('---->'+acc );
}   
This will insert the record with AccountNumber value in same user context even user dont have access on same field. How we can stop the same with Security.stripInaccessible. Let see

public static void testDML()
{  
    Account acc = new Account(Name='Test' ,AccountNumber ='Demo');
    System.debug('---->'+acc );
    List<Account> lstAcc = new List<Account>();
    lstAcc.add(acc);
   
    SObjectAccessDecision securityDecision = Security.stripInaccessible(
                                                AccessType.CREATABLE,
                                                lstAcc );
    System.debug('---ecurityDecision.getRecords()->'+securityDecision.getRecords());                                           
    insert securityDecision.getRecords();
    System.debug(securityDecision.getRemovedFields().get('Account'));
}      
here is output


You can also use the method to sanitize sObjects that have been deserialized from an untrusted source





Further Reading
  1. https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_with_security_stripInaccessible.htm
  2. https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_class_System_Security.htm#topic-title


Sunday 19 January 2020

Order of execution in Salesforce

We did one session in apex hours of How to become an Order of Execution Hero. In that session we did one deep drive in Salesforce Order of execution and covered some interview question on order of execution. Save Order of Execution is the most important factor in designing sustainable and scalable applications.
When you save a record with an insert, update, or upsert statement, Salesforce performs the following events in order. Here is cheat-sheet of Order of execution.

Order of Execution

  1. Loads Initial record.
  2. If the request came from a standard UI edit page, Salesforce runs system validation to check the record for page layout specific rules, field definition, Maximum field length.
  3. Executes  flows that make before-save update. (New Change in Winter 20)
  4. Executes all before triggers. 
  5. Runs most Custom validation.
  6. Executes duplicate rules. 
  7. Saves the record to the database, but doesn't commit yet. 
  8. Executes all after triggers. 
  9. Executes assignment rules. 
  10. Executes auto-response rules. 
  11. Executes workflow rules. 
  12. If there are workflow field updates, updates the record again.
  13. If workflow field updates introduced new duplicate field values, executes duplicate rules again. If the record was updated with workflow field updates, fires before update triggers and after update triggers one more time (and only one more time), in addition to standard validations. Custom validation rules are not run again.
  14. Executes processes and flow. 
  15. Executes escalation rules. 
  16. Executes entitlement rules. 
  17. If the record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the parent record. Parent record goes through save procedure. 
  18. If the parent record is updated, and a grandparent record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the grandparent record. Grandparent record goes through save procedure. 
  19. Executes Criteria Based Sharing evaluation. 
  20. Commits all DML operations to the database. 
  21. Executes all after-commit logic, such as sending email.

What is part of the after commit logic?

  1. All email sends
  2. Asynchronous Apex: @future methods
  3. Async Sharing Rule processing (for >25,000 records)
  4. Outbound Messages placed on queue
  5. Calculate Index, such as Search Index
  6. Render File Previews
  7. Publication of Platform Events (if configured)
NOTE: The order of execution isn’t guaranteed when having multiple triggers for the same object due to the same event. For example, if you have two before insert triggers for Case, and a new Case record is inserted that fires the two triggers, the order in which these triggers fire isn’t guaranteed.

Check this post for more detail Triggers and Order of Execution.

How deep is your knowledge of the Order of Execution ?

There's a lot more to know than triggers run before workflows. Dig deeper, understand the logic flow and your apps will scale better and run more reliably. Become an Order of Execution hero and walk away with an understanding of how to design robust apps optimized for scale and how to control recursion and remedy typical problems.  

What kind of issues did you experience ?

  • Unexpected Outcomes
    •  Missing or duplicate records
    •  Blank or “wrongly” populated fields
  • Hitting Limits
    •  CPU Time Limit Exception
    •  Too many SOQL Queries


Here is recording of our Apex Hour session.


 

Further Reading:

 https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_order_of_execution.htm 


Thanks,
Amit Chaudhary

Friday 3 January 2020

Lightning Datatable Sorting in Lightning Web Components

Lightning Datatable Sorting in Lightning Web Components
Last time we talk about Lightning Datatable in Lightning Web Components (LWC). In this post we will talk about lightning datatable example with sorting. We know lightning-datatable component displays in tabular data and each column can be displayed based on the data type. We can also achieve the column sorting with the help of onsort attribute in datatable. I hope VsCode is already setup on you machine and you know how to create Lightning Web Component. If not please check our Get started with Salesforce lightning web components post.

Lightning-datatable

Lightning datatable provides an onsort attribute which allow us to implement the sorting in lightning datatable. To enable the sorting on row you need to set sortable to true for the column and set sorted-By to match the fieldName attribute on the column. 

Use onsort event handler to update the table with the new column index and sort direction. The sort event returns the following parameter.
  1. fieldName    The fieldName that controls the sorting.
  2. sortDirection    The sorting direction. Valid options include 'asc' and 'desc'.
 You can implement sorting locally or via apex call.

Local Sorting:

We mostly implement this type of sorting when we know data elements in lightning datatable is small and limited.

1) Create Apex Class. 

To select certain contacts using SOQL, use an Apex method. Check this post to learn about how to Call Apex Methods in LWC.

LWCDataTableSortingExample:
public with sharing class LWCDataTableSortingExample {
    @AuraEnabled(Cacheable=true)
    public static List <Contact> getContacts() {
        List<Contact> contList = [ SELECT Id, FirstName, LastName, Phone, Email
                                   FROM Contact
                                   LIMIT 10 ];
        return contList;
    }     
}

2) Create Lightning web component.

dataTableSortingLWC.html
<template>
    <lightning-card title="Data Sorting in Lightning Datatable in LWC" icon-name="standard:contact" >
        <br/>
        <div style="width: auto;">
            <template if:true={data}>
                <lightning-datatable data={data}
                                     columns={columns}
                                     key-field="id"
                                     sorted-by={sortBy}
                                     sorted-direction={sortDirection}
                                     onsort={doSorting}
                                     hide-checkbox-column="true"></lightning-datatable>
            </template>
        </div>
    </lightning-card>
</template>


dataTableSortingLWC.js
import {LightningElement, wire, track} from 'lwc';
import getContacts from '@salesforce/apex/LWCDataTableSortingExample.getContacts';

// datatable columns with row actions. Set sortable = true
const columns = [ { label: 'FirstName', fieldName: 'FirstName', sortable: "true"},
                  { label: 'LastName', fieldName: 'LastName', sortable: "true"},
                  { label: 'Phone', fieldName: 'Phone', type: 'phone', sortable: "true"},
                  { label: 'Email', fieldName: 'Email', type: 'email', sortable: "true" },];

export default class DataTableSortingLWC extends LightningElement {
    @track data;
    @track columns = columns;
    @track sortBy;
    @track sortDirection;
  
    @wire(getContacts)
    contacts(result) {
        if (result.data) {
            this.data = result.data;
            this.error = undefined;
        } else if (result.error) {
            this.error = result.error;
            this.data = undefined;
        }
    }

    doSorting(event) {
        this.sortBy = event.detail.fieldName;
        this.sortDirection = event.detail.sortDirection;
        this.sortData(this.sortBy, this.sortDirection);
    }

    sortData(fieldname, direction) {
        let parseData = JSON.parse(JSON.stringify(this.data));
        // Return the value stored in the field
        let keyValue = (a) => {
            return a[fieldname];
        };
        // cheking reverse direction
        let isReverse = direction === 'asc' ? 1: -1;
        // sorting data
        parseData.sort((x, y) => {
            x = keyValue(x) ? keyValue(x) : ''; // handling null values
            y = keyValue(y) ? keyValue(y) : '';
            // sorting values based on direction
            return isReverse * ((x > y) - (y > x));
        });
        this.data = parseData;
    }      
}

dataTableSortingLWC.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="dataTableSortingLWC">
    <apiVersion>46.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

 

Sorting by Apex Call:

We also have another way of data sorting with Apex class.

1) Create Apex Class. 

LWCDataTableSortingExample:
public with sharing class LWCDataTableSortingExample {
    @AuraEnabled(Cacheable=true)
    public static List <Contact> getContacts(String field, String sortOrder) {
        String query;
        query  = 'SELECT Id, FirstName, LastName, Phone, Email FROM Contact';
        if(field != null && sortOrder !=null){
            query += ' ORDER BY '+field+' '+sortOrder;
        }
        return Database.query(query);
    }
}

2) Create Lightning web component.

dataTableSortingLWC.html
<template>
    <lightning-card title="Data Sorting by Apex" icon-name="standard:contact" >
        <br/>
        <div style="width: auto;">
            <template if:true={data}>
                <lightning-datatable data={data}
                                     columns={columns}
                                     key-field="id"
                                     sorted-by={sortBy}
                                     sorted-direction={sortDirection}
                                     onsort={doSorting}
                                     hide-checkbox-column="true"></lightning-datatable>
            </template>
        </div>
    </lightning-card>
</template>

dataTableSortingLWC.js
import {LightningElement, wire, track} from 'lwc';
import getContacts from '@salesforce/apex/LWCDataTableSortingExample.getContacts';

// datatable columns with row actions
const columns = [ { label: 'FirstName', fieldName: 'FirstName', sortable: "true"},
                  { label: 'LastName', fieldName: 'LastName', sortable: "true"},
                  { label: 'Phone', fieldName: 'Phone', type: 'phone', sortable: "true"},
                  { label: 'Email', fieldName: 'Email', type: 'email', sortable: "true" },];

export default class DataTableSortingLWC extends LightningElement {
    // reactive variable
    @track data;
    @track columns = columns;
    @track sortBy='FirstName'
    @track sortDirection='asc';
  
    // retrieving the data using wire service
    @wire(getContacts,{field : '$sortBy',sortOrder : '$sortDirection'})
    contacts(result) {
        if (result.data) {
            this.data = result.data;
            this.error = undefined;
        } else if (result.error) {
            this.error = result.error;
            this.data = undefined;
        }
    }

    doSorting(event) {
        // calling sortdata function to sort the data based on direction and selected field
        this.sortBy = event.detail.fieldName;
        this.sortDirection = event.detail.sortDirection;
        this.sortData(this.sortBy, this.sortDirection);
    }

    sortData(fieldname, direction) {
        let parseData = JSON.parse(JSON.stringify(this.data));
        // Return the value stored in the field
        let keyValue = (a) => {
            return a[fieldname];
        };
        // cheking reverse direction
        let isReverse = direction === 'asc' ? 1: -1;
        // sorting data
        parseData.sort((x, y) => {
            x = keyValue(x) ? keyValue(x) : ''; // handling null values
            y = keyValue(y) ? keyValue(y) : '';
            // sorting values based on direction
            return isReverse * ((x > y) - (y > x));
        });

        // set the sorted data to data table data
        this.data = parseData;
    }      
}


Please check below post on Lightning Web Components:-

Thanks
Amit Chaudhary