Newer Version Available

This content describes an older version of this product. View Latest

Enforce Field-Level Security With the stripInaccessible Method (Pilot)

Use the stripInaccessible method to enforce field-level data protection. This method can be used to strip the fields from query results that the user can’t access. The method can also be used to remove inaccessible fields from sObjects before a DML operation to avoid exceptions and to sanitize sObjects that have been deserialized from an untrusted source.

This pilot feature is available automatically in sandbox, developer, and scratch organizations as a pilot program. The functionality of this feature is subject to change, and is not available for production organizations while in pilot. Pilot programs are subject to change, and we can’t guarantee acceptance. This feature isn’t generally available unless or until Salesforce announces its general availability in documentation or in press releases or public statements. We can’t guarantee general availability within any particular time frame or at all. Make your purchase decisions only based on generally available products and features. You can provide feedback and suggestions for this feature in the Security.stripInaccessible group in the IdeaExchange.

Note

The field-level data protection is accessed through the Security and SObjectAccessDecision classes. The access check is based on the field-level permission of the current user in the context of the specified operation - create, read, or update. The stripInaccessible method checks the source records for fields that don’t meet field-level security check for the current user and creates a list of sObjects. The return list is identical to the source records, except that the fields that are inaccessible to the current user are removed. The sObjects returned by the getRecords method contain records in the same order as the sObjects in the sourceRecords parameter of the stripInaccessible method. Fields that aren’t queried are null in the return list, without causing an exception.

The ID field is never stripped by the stripInaccessible method to avoid issues when performing DML on the result.

Note

To identify inaccessible fields that were removed, you can use the isSet method. For example, the return list contains Contact object and the custom field social_security_number__c is inaccessible to the user. Because this custom field fails the field-level access check, the field is not set and isSet returns false.

1SObjectAccessDecision securityDecision = Security.stripFields(sourceRecords);
2Contact c = securityDecision.getRecords()[0];
3System.debug(c.isSet('social_security_number__c')); // prints "false"

The following are some examples where the stripInaccessible method can be used.

Example

This example removes inaccessible fields from the query result. A display table for campaign data must always show the BudgetedCost. The ActualCost must be shown only to users who have permission to read that field.
1Security.SObjectAccessDecision securityDecision = Security.stripInaccessible(
2AccessType.READABLE,
3[SELECT Name, BudgetedCost, ActualCost from Campaign];
4);
5
6// Construct the output table
7    if (securityDecision.getRemovedFields().get('Campaign').contains('ActualCost')) {
8        for (Campaign c : securityDecision.getRecords()) {
9        // Output: Name, BudgetedCost
10        }
11    } else {
12        for (Campaign c : securityDecision.getRecords()) {
13        // Output: Name, BudgetedCost, ActualCost
14        }
15}

Example

This example removes inaccessible fields from sObjects before DML operations. The user who doesn’t have permission to create an OwnerID for an Account can still create an Account. The method ensures that no Account owner is set and doesn’t throw an exception.
1List<Account> newAccounts = new List<Account>();
2Account a = new Account(Name='Acme Corporation');
3Account b = new Account(Name='Blaze Comics', OwnerId=UserInfo.getUserId());
4newAccounts.add(a);
5newAccounts.add(b);
6
7SObjectAccessDecision securityDecision = Security.stripInaccessible(
8AccessType.CREATABLE,
9newAccounts);
10
11// No exceptions are thrown and no Account Owners are set
12insert securityDecision.getRecords();
13
14System.debug(String.join(securityDecision.getRemovedFields().get('Account'), ', ')); // Prints "OwnerID"
15System.debug(String.join(securityDecision.getModifiedIndexes(), ', ')); // Prints "1"

Example

This example sanitizes sObjects that have been deserialized from an untrusted source. The user doesn’t have permission to update the AnnualRevenue of an Account.
1String jsonInput =
2'[' +
3'{' +
4'"Name": "InGen",' +
5'"AnnualRevenue": "100"' +
6'},' +
7'{' +
8'"Name": "Octan"' +
9'}' +
10']';
11
12List<Account> accounts = (List<Account>)JSON.deserializeStrict(jsonInput, List<Account>.class);
13SObjectAccessDecision securityDecision = Security.stripInaccessible(
14AccessType.UPDATABLE,
15accounts);
16
17// Secure update
18update securityDecision.getRecords(); // Doesn’t update AnnualRevenue field
19System.debug(String.join(securityDecision.getRemovedFields().get('Account'), ', ')); // Prints "AnnualRevenue"
20System.debug(String.join(securityDecision.getModifiedIndexes(), ', ')); // Prints "0”