Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
Diagram illustrating how to enforce field and object level security checks within Apex code securely
Apex

Enforce Field and Object level security in Apex

The short answer

Apex runs in system context by default, and the with sharing keyword only controls record-level access, so field and object permissions are not checked for you. To enforce field-level security, use the WITH SECURITY_ENFORCED clause in SOQL, the Schema.DescribeFieldResult check methods, or Security.stripInaccessible.

Key takeaways Add WITH SECURITY_ENFORCED to a SOQL query and it enforces field and object permissions for every field in the SELECT clause. Wrap those queries in a try-catch so you can handle the System.QueryException a user without field access will hit. Check WHERE clause fields yourself with Schema.DescribeFieldResult methods such as isAccessible(), since WITH SECURITY_ENFORCED looks only at the SELECT clause. Use Security.stripInaccessible() with the right AccessType value to drop fields the user cannot touch before you create a record or run DML.

Most of us know this already (hopefully): Apex runs in system context, which means it doesn't run as per user's access level. You might confuse this with the "With Sharing" keyword we use for apex classes, but that only retrieves records which 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. Here they are:

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 context, 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.

Reference: https://releasenotes.docs.salesforce.com/en-us/summer19/release-notes/rn_apex_Security_stripInaccessible.htm

Frequently asked questions

Does with sharing enforce field-level security in Apex?

No. The with sharing keyword enforces record-level access through sharing rules only. It never checks whether the user has permission on a given field or object.

What happens when WITH SECURITY_ENFORCED fails in SOQL?

If the running user cannot read any object or field in the SELECT clause, Apex throws a System.QueryException and the query returns nothing.

Does WITH SECURITY_ENFORCED check fields in the WHERE clause?

No. It checks only the fields in the SELECT clause. Fields used in the WHERE clause need a separate check with Schema.DescribeFieldResult methods such as isAccessible().

How does Security.stripInaccessible work in Apex?

Security.stripInaccessible takes an AccessType enum and a list of sObjects, then removes any field the running user has no permission to access. Records can then be created or processed without writing a describe check for every field.

Newsletter

One email every Tuesday

New guides, tool updates, and the release-note changes that break things.

No spam. Unsubscribe in one click.

Comments

Loading comments...

Leave a Comment