Apex security model next generation is almost here. Now you can now declare when Apex runs database operations in user mode or system mode. In this post you will learn about the dramatically simplified ways of writing Secure Apex Code with User Mode Database Operations ( Summer ’22 release ). This feature currently in Beta.
The new Database methods support an AccessLevel
parameter that enables you to run database operations in user mode, instead of in the default system mode. To enhance the security context of Apex, you can specify user-mode access for database operations by invoking Apex in user mode. The field-level security (FLS), sharing rules, and CRUD permissions of the running user are respected in user mode, unlike in system mode.
What is System Mode?
System mode means running apex code by ignoring user’s permissions. User mode means running apex code by respecting user’s permissions and sharing of records. System mode is trusted, admin-vetted logic run with extra rights.
• For trusted business logic
• For data consistency
Image is picked from our Security in Salesforce session.
What is With Sharing and Without sharing in Apex?
Now question is what is With sharing & Without sharing keywords in APEX ? Will that help us to execute code in system mode to user mode?
Use the with sharing or without sharing keywords specify whether or not to enforce sharing rules, records being updated via Role, Sales Teams – any sort of sharing really.
With Sharing Keyword
If you use this keyword, then the Apex code will enforce the Sharing settings of current user to Apex code. This does not enforce the Profile permission, only the data level sharing settings.
Without Sharing Keyword
Class declared with this keyword executes in System mode.
Enforcing the current user’s sharing rules can impact:
- SOQL and SOSL queries. A query may return fewer rows than it would operating in system context.
- DML operations. An operation may fail because the current user doesn’t have the correct permissions.
Now again what all option we have to Enforcing Object & FLS Permissions in Apex? Let see that in details.
Enforcing Object & FLS Permissions in Apex
Apex doesn’t enforce object-level and field-level permissions by default. Let see how we can enforce the CRUD & FLS in Apex.
Read data (SOQL) | Modify data (DML) | |
Schema methods | Yes | Yes |
WITH SECURITY_ENFORCED | Yes | No |
Security.stripInaccessible() | Yes | Yes |
Database operations in user mode (beta) | Yes | Yes |
In this post will talk about Secure Apex Code with User Mode Database Operations. If you want to learn about other option then check our old blog post Apex security in Salesforce.
Using WITH USER_MODE or WITH SYSTEM_MODE
If you need to fetch data from SOQL query then How we handle user mode today?
return [SELECT Name, Owner.Name FROM Account];
Let an example of Describe-based checks. For that we handle like below code
if (Schema.SObjectType.Account.isAccessible() &&
Schema.SObjectType.Account.fields.Name.isAccessible() &&
Schema.SObjectType.Account.fields.OwnerId.isAccessible() &&
Schema.SObjectType.Account.fields.Support__c.isAccessible() &&
Schema.SObjectType.Account.fields.OwnerId.getReferenceTo()[0].getDescribe().isAccessible()) {
return [SELECT Name, Owner.Name FROM Account WHERE Support__C = 'Premier'];
} else {
throw new AuraHandledException('Access Denied');
}
Now we have a better way to handle it using USER_MODE.
return [SELECT Name, Owner.Name FROM Account WHERE Support__C = 'Premier' WITH USER_MODE];
Database operations can specify user or system mode
Database operations can specify user or system mode. This example inserts a new account in user mode.
insert as user new Account(Name = 'ApexHours');
Account a = [SELECT Id, Name FROM Account WHERE Support__c = 'FREE TRAINING' WITH USER_MODE];
a.Rating = 'Hot';
update as system a;
Dynamic at runtime syntax.
The new AccessLevel
class represents the two modes in which Apex runs database operations. Use this new class to define the execution mode as user mode or system mode. Use these new overloaded methods to perform DML and query operations.
Database.query
methodsSearch.query
methods- Database DML methods (
insert
,update
,upsert
,merge
,delete
,undelete
, andconvertLead
)
Example of Database DML methods
Database.insert( new Account(Name = 'ApexHours'),
AccessLevel.USER_MODE
);
Here is example of Database.query
Account a = Database.query('SELECT Id, Name FROM Account WHERE Rating = \'Hot\'',
AccessLevel.USER_MODE
);
Now what about if any error will come FLS? How to handle it.
List<Database.SaveResult> srList = Database.insert(
new SObject[] {
new Account(Name='ApexHours', AnnualRevenue=1), // no FLS edit on AnnualRevenue
new Contact(LastName='Chaudhary', Email='foo'), // no FLS view on Email
},
false, // allOrNone
AccessLevel.USER_MODE
);
System.assert(!srList.get(0).getErrors()[0].getFields().contains('AnnualRevenue’),
'Missing Account.AnnualRevenue FLS');
System.assert(!srList.get(1).getErrors()[0].getFields().contains('Email’),
'Missing Contact.Email FLS')
Full Error Reporting – not just the first error!
try {
integer count = Database.countQuery(
'select count(email) cnt from Contact where Account.website =\'foo\'',
AccessLevel.USER_MODE
);
} catch (QueryException qe) {
Map<String, Set<String>> inaccessibleFields = qe.getInaccessibleFields();
System.assert(!inaccessibleFields.containsKey('Contact'), 'Missing Contact CRUD');
System.assert(!inaccessibleFields.get('Contact').contains('Email'), 'Missing Contact.Email
FLS');
System.assert(!inaccessibleFields.get('Account').contains('Website'), 'Missing Account.Website
FLS');
}
Let take a look for Search.query
methods. SOSL returns no results if matched on inaccessible field
String searchquery = 'Find \'Apex\' IN ALL FIELDS RETURNING Account(id,name)';
List<List<SObject>> searchList = Search.query(searchquery, AccessLevel.USER_MODE);
Search.SearchResults sr = Search.find(searchquery, AccessLevel.USER_MODE);
Search.SuggestionResults sgr = Search.suggest('all', 'Account', null,
AccessLevel.USER_MODE);
List<List<SObject>> searchList = [FIND 'website123' RETURNING Account(id,name)
with user_mode];
Why this is Different
- Historical focus on easier CRUD/FLS checks
- Describes method don’t scale well
This means that as Salesforce launches new features, like Restriction Rules, that operate on unique security models we can support them fully.
Edge cases work right
- WITH SECURITY_ENFORCED doesn’t work with Task.WhatId
- User-mode Database Operations returns all errors, not just the first
- SOSL is a great example of a place where CRUD/FLS wasn’t sufficient.
Do I need to refactor all my security checks? Share your finding and thoughts with us.
Thanks for the session
What is the difference of WITH USER_MODE vs WITH SECURITY_ENFORCED ?
That WITH SECURITY_ENFORCED doesn’t enforce sharing, while WITH USER_MODE does
Thanks, Alba for helping us.
For more detail check https://www.youtube.com/watch?v=RMTA06yWNms
Hi Amit,
This is wrong statement: “Class declared with this keyword executes in System mode.” under section ‘Without Sharing Keyword’.
Correct one: Use the without sharing keyword when declaring a class to ensure that the sharing rules for the current user are not enforced.
Thanks
Hi Amit,
I am using Portal guest User
In My use case guest user insert Content Version and ContentDistribution record for public url.
But it showing error for guest user to insert ContentDistribution record.
Can you please suggest me solution?
while publishing platform events via trigger can we enforce AccessLevel.USER_MODE