As we all know (Hopefully), Apex runs in system context which means it doesn’t run as per user’s access level. You might get confused this with “With Sharing” keyword we use for apex classes, but it allows to retrieve only records that are accessible by user, it doesn’t check if that user having access to particular field or not.
In Spring 20 release, some security enhancements are added to enforce field-level permissions. Let’s take a look with these updates:
WITH SECURITY_ENFORCED
WITH SECURITY_ENFORCED can be used with SOQL and query the records by enforcing Field and Object level permissions of currently running user.
Suppose User “A” is having access to fields Name, Website and Phone from Account but not a Status. If this user execute an apex which contains following query:
List<Account> Accounts = [select id, Name, Website, Status, MobilePhone from Account WITH SECURITY_ENFORCED]
The apex will throw a System.QueryException exception, and no results will be returned. Which then can be handled with the help of Try-Catch block to show user friendly error message.
Another consideration while using WITH SECURITY_ENFORCED in SOQL is that, its only applicable to field which is in select clause. If any field used in where clause then it will not check for that field.
Suppose same user, user “A” has run following apex code, he will not get any error:
List<Account> Accounts = [select id, Name, Website, Status, MobilePhone from Account where Status = 'Active' WITH SECURITY_ENFORCED]
Practically this should not happen, may be in future releases, Salesforce will cover this. Till then, to avoid this situation, its always good practice to check all field permissions in apex using Schema.DescribeFieldResult.
Schema.DescribeFieldResult
Using Schema.DescribeFieldResult, we can check if user is either having Read, Create or Update access for particular field.
To implement proper checks, before executing SOQL having WITH SECURITY_ENFORCED in where condition, make use of Schema.DescribeFieldResult. So the above code can be written as:
if(Schema.sObjectType.Account.fields.Status.isAccessible()){
List<Account> Accounts = [select id, Name, Website, Status, MobilePhone from Account where Status = 'Active' WITH SECURITY_ENFORCED]
}
Similarly you can use isCreatable() or isUpdatable() methods.
StripInaccessible
Using Schema.DescribeFieldResult, to check access of each field used while creating records, is really cumbersome, especially when you have bunch of fields to be checked. Using StripInaccessible method, this can directly be done without writing much of code.
StripInaccessible method will enforce field and object level security in Apex. This method will strip fields from sObject list for which current user does not have permission.
Our user “A” is not having create access to Status field of account object, suppose following record is being inserted in apex:
Account A = new Account(name = 'Text', Website='https://sfdcdevelopers.com', Status = 'Active');
As apex runs under system content, this will not throw any error and it will create an account having Status as Active, however if you want to enforce field level security, Status should not be updated as per user “A” context. So how to do this without use of DescribeFieldResult? We need to use StripInaccessible function.
SObjectAccessDecision decision = Security.stripInaccessible(AccessType.CREATABLE, new list<Account>{A});
StripInaccessible function must have two parameters:
- AccessType : Uses values from the AccessType enum. This parameter determines the type of field-level access check to be performed. To check the current user’s field-level access, use the Schema.DescribeFieldResult methods —isCreatable(), isAccessible(), or isUpdatable().
- List of sObject
Using StripInaccessible function, Account will get created without any value under Status field as user A is not having create access to Status field.
Leave a Reply