Enforce User Mode for Database Operations
Apex code runs in system mode by default, which means that it runs with substantially elevated permissions over the user running the code. To enhance the security context of Apex, you can specify user-mode access for database operations. Field-level security (FLS) and object permissions of the running user are respected in user mode, unlike in system mode. User mode always applies sharing rules, but in system mode they’re controlled by sharing keywords on the class. See Use the with sharing, without sharing, and inherited sharing Keywords.
List<Account> acc = [SELECT Id FROM Account WITH USER_MODE];
Salesforce recommends that you enforce Field Level Security (FLS) by using WITH USER_MODE rather than WITH SECURITY_ENFORCED because of these additional advantages.
- WITH USER_MODE accounts for polymorphic fields like Owner and Task.whatId.
- WITH USER_MODE processes all clauses in the SOQL SELECT statement including the WHERE clause.
- WITH USER_MODE finds all FLS errors in your SOQL query, while WITH SECURITY_ENFORCED finds only the first error. Further, in user mode, you can use the getInaccessibleFields() method on QueryException to examine the full set of access errors.
Account acc = new Account(Name='test');
insert as user acc;
The AccessLevel class represents the two modes in which Apex runs database operations. Use this class to define the execution mode as user mode or system mode. An optional accessLevel parameter in Database and Search methods specifies whether the method runs in system mode (AccessLevel.SYSTEM_MODE) or user mode (AccessLevel.USER_MODE).
Use these overloaded methods to perform DML and query operations.
- Database.query method. See Dynamic SOQL.
- Database.getQueryLocator methods
- Database.countQuery method
- Search.query method
- Database DML methods (insert, update, upsert, merge, delete, undelete, and convertLead). Includes the *Immediate and *Async methods, such as insertImmediate and deleteAsync.
These methods require the accessLevel parameter.
Example
try {
List<Account> accts = new Account[] {new Account(name ='foo', AnnualRevenue=2000)};
Database.insert(accts, AccessLevel.USER_MODE); // throws an exception
Assert.fail('DmlException expected');
} catch (DmlException dex) {
Assert.isTrue(dex.getMessage()
.contains(
'Operation failed due to fields being inaccessible on Sobject <object name>, check errors on Exception or Result!'
)
);
Assert.isTrue(dex.getDmlFieldNames(0).contains('AnnualRevenue'));
}
Using Permission Sets to Enforce Security in DML and Search Operations (Developer Preview)
In Developer Preview, you can specify a permission set that is used to augment the field-level and object-level security for database and search operations. Run the AccessLevel.withPermissionSetId() method with a specified permission set ID. Specific user mode DML operations that are performed with that AccessLevel, respect the permissions in the specified permission set, in addition to the running user’s permissions.
@isTest
public with sharing class ElevateUserModeOperations_Test {
@isTest
static void objectCreatePermViaPermissionSet() {
Profile p = [SELECT Id FROM Profile WHERE Name='Minimum Access - Salesforce'];
User u = new User(Alias = 'standt', Email='[email protected]',
EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',
LocaleSidKey='en_US', ProfileId = p.Id,
TimeZoneSidKey='America/Los_Angeles',
UserName='standarduser' + DateTime.now().getTime() + '@testorg.com');
System.runAs(u) {
try {
Database.insert(new Account(name='foo'), AccessLevel.User_mode);
Assert.fail();
} catch (SecurityException ex) {
Assert.isTrue(ex.getMessage().contains('Account'));
}
//Get ID of previously created permission set named 'AllowCreateToAccount'
Id permissionSetId = [Select Id from PermissionSet
where Name = 'AllowCreateToAccount' limit 1].Id;
Database.insert(new Account(name='foo'), AccessLevel.User_mode.withPermissionSetId(permissionSetId));
// The elevated access level is not persisted to subsequent operations
try {
Database.insert(new Account(name='foo2'), AccessLevel.User_mode);
Assert.fail();
} catch (SecurityException ex) {
Assert.isTrue(ex.getMessage().contains('Account'));
}
}
}
}