Trailhead – Practice simplifying object- and field-level security checks with new Spring ’19 in Apex code
Historically, fully vetting object- and field-level security compliance in Apex code has required using verbose and potentially complex checks on all fields that are included in a query. This can lead to code duplication and additional maintenance work to ensure code stays secure when queries are updated. Practice simplifying your code by making use of the WITH SECURITY_ENFORCED clause in SOQL queries run from Apex.
In this exercise, you’ll begin with code that contains manual field- and object-level security checks before a SOQL query. You will then refactor the code into a simplified implementation that relies on WITH SECURITY_ENFORCED to handle field- and object-level security checks.
- Create the Secret Key custom text field on the Contact object as specified in the Get Ready for the Hands-on Challenge section above.
- Create a new Apex class named SecureApexRest.
- Copy and paste the SecureApexRest code provided above. This code is already secured with conventional field- and object-level access checks.
- Add the WITH SECURITY_ENFORCED clause to the SOQL query on line 13 in the code provided. This will make the manual Schema.SObjectType checks redundant.
- Refactor the code to remove the redundant object and field level access checks.
- Maintain existing behavior by ensuring that failing results are checked in a SecurityException, rather than any other type of exception. This will require catching the System.QueryException that WITH SECURITY_ENFORCED throws and throwing a new SecurityException.
- Optional: If you would like to test the behavior of these changes, you can make use of workbench to invoke the Apex REST service.
@RestResource(urlMapping='/secureApexRest')
global with sharing class SecureApexRest {
@HttpGet
global static Contact doGet(){
Id recordId = RestContext.request.params.get('id');
Contact result;
if (recordId == null){
throw new FunctionalException('Id parameter is required');
}
try {
List<Contact> results = [SELECT id, Name, Secret_Key__c FROM Contact WHERE Id = :recordId WITH SECURITY_ENFORCED];
if (!results.isEmpty()) {
result = results[0];
}
}catch(System.QueryException e){
// Perform logic here
throw new SecurityException('You don\'t have access to all contact fields required to use this API');
}
return result;
}
public class FunctionalException extends Exception{}
public class SecurityException extends Exception{}
}