Securing Data in Apex Controllers
Set Sharing Rules
An @AuraEnabled Apex class that doesn’t explicitly set with sharing or without sharing, or is defined with inherited sharing, uses a default or implicit value of with sharing. However, Salesforce recommends that you use keyword declarations on all your classes to make your code easier to maintain across API versions.
1public with sharing class SharingClass {
2 // Code here
3}To bypass sharing rules, you must explicitly declare the class as without sharing.
1public without sharing class WithoutSharingClass {
2 // Code here
3}The with sharing and with sharing keywords set record-level security. They don’t enforce object-level and field-level security. You must manually enforce object-level and field-level security separately in your Apex classes.
Set Object and Field Permissions (CRUD and FLS)
Apex database operations run in user mode by default. Database operations only ignore FLS and object permissions if you explicitly set them to run in system mode. However, Salesforce recommends setting an access mode on all database operations so that your code is easier to maintain across API versions.
Set an Access Mode for SOQL and SOSL Queries
Use the WITH USER_MODE or WITH SYSTEM_MODE clauses for SOQL SELECT queries in Apex code, including subqueries and cross-object relationships.
For example, getExpenses() uses the WITH USER_MODE clause to enforce object-level and field-level permissions.
1public with sharing class ExpenseController {
2 @AuraEnabled
3 public static List<ns__Expense__c> getExpenses() {
4 return [SELECT Id, Name, ns__Amount__c,
5 ns__Client__c, ns__Date__c,
6 ns__Reimbursed__c, CreatedDate
7 FROM ns__Expense__c
8 WITH USER_MODE];
9 }
10}To bypass the current user’s FLS and object permissions, you must explicitly add the WITH SYSTEM_MODE clause to the SOQL query.
1List<Account> acc = [SELECT Id FROM Account WITH SYSTEM_MODE];For more details, see Set an Access Mode for Database Operations in the Apex Developer Guide.
Graceful Degradation with stripInaccessible()
For more graceful degradation on permissions errors, use the stripInaccessible() method to enforce field- and object-level data protection. This method strips the fields and relationship fields from query and subquery results that the user can’t access. You can find out if any fields were stripped and throw an AuraHandledException with a custom error message, if desired.
You can also use the method to remove inaccessible sObject fields before DML operations to avoid exceptions and to sanitize sObjects that have been deserialized from an untrusted source.
This example updates ExpenseController to use stripInaccessible() instead of the WITH USER_MODE SOQL clause. The results are the same but stripInaccessible() allows you to gracefully degrade instead of failing on an access violation when by using WITH USER_MODE.
1public with sharing class ExpenseControllerStripped {
2
3 @AuraEnabled
4 public static List<ns__Expense__c> getExpenses() {
5 // Query the object but don't use WITH USER_MODE
6 List<ns__Expense__c> expenses =
7 [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c,
8 ns__Reimbursed__c, CreatedDate
9 FROM ns__Expense__c];
10
11 // Strip fields that are not readable
12 SObjectAccessDecision decision = Security.stripInaccessible(
13 AccessType.READABLE,
14 expenses);
15
16 // Throw an exception if any data was stripped
17 if (!decision.getModifiedIndexes().isEmpty()) {
18 throw new AuraHandledException('Data was stripped');
19 }
20
21 return expenses;
22 }
23}For more details and examples, see Enforce Security with the stripInaccessible Method in the Apex Developer Guide.
Update Legacy Code by Using the DescribeSObjectResult and DescribeFieldResult Methods
Before the WITH USER_MODE clause and stripInaccessible() method were available, the only way to enforce object and field permissions was to check the current user’s access permission levels by calling the Schema.DescribeSObjectResult and Schema.DescribeFieldResult methods. Then, if a user has the necessary permissions, perform a specific DML operation or a query.
For example, you can call the isAccessible, isCreateable, or isUpdateable methods of Schema.DescribeSObjectResult to verify whether the current user has read, create, or update access to an sObject, respectively. Similarly, Schema.DescribeFieldResult exposes access control methods that you can call to check the current user’s read, create, or update access for a field.
This example uses the describe result methods.
1public with sharing class ExpenseControllerLegacy {
2 @AuraEnabled
3 public static List<ns__Expense__c> getExpenses() {
4 String [] expenseAccessFields = new String [] {'Id',
5 'Name',
6 'ns__Amount__c',
7 'ns__Client__c',
8 'ns__Date__c',
9 'ns__Reimbursed__c',
10 'CreatedDate'
11 };
12
13
14 // Obtain the field name/token map for the Expense object
15 Map<String,Schema.SObjectField> m = Schema.SObjectType.ns__Expense__c.fields.getMap();
16
17 for (String fieldToCheck : expenseAccessFields) {
18
19 // Call getDescribe to check if the user has access to view field
20 if (!m.get(fieldToCheck).getDescribe().isAccessible()) {
21
22 // Pass error to client
23 throw new System.NoAccessException();
24 }
25 }
26
27 // Query the object safely
28 return [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c,
29 ns__Reimbursed__c, CreatedDate FROM ns__Expense__c];
30 }
31}For more details and examples, see Enforce Security with Field and SObject Describe Methods in the Apex Developer Guide.