Friday, June 20, 2025

User Mode in Apex – The New Way to Respect Permissions

 In Salesforce, Apex code runs in system mode by default. That means it can access all data, even if the current user doesn’t have permission. This can lead to security issues if you're not careful.

To fix this, Salesforce introduced User Mode – a cleaner and safer way to run SOQL and DML operations as the user.

What is User Mode?

User Mode lets your code automatically respect:

  • Field-level security (FLS)
  • Object-level security (CRUD)

This means your queries and operations behave just like the user is running them.

How to Use User Mode

You can use User Mode in these operations:

Database.query(new Query("SELECT Name FROM Account").setUserMode(UserMode.CURRENT));


Database.getQueryLocator(new Query("SELECT Name FROM Account").setUserMode(UserMode.CURRENT));

You can also use it with:

  • Database.countQuery()
  • Database.insert(), update(), delete() – by setting user mode through Database.DMLOptions


Benefits

  • No need to manually check FLS or CRUD
  • Prevents accidental access to restricted data
  • Works across async processes and batch jobs
  • Safer and more user-friendly than WITH SECURITY_ENFORCED

Why It Matters

User Mode is the recommended approach by Salesforce. It makes your Apex code more secure, easier to maintain, and aligned with user permissions—automatically.

Here are real-life examples of using User Mode in Apex code to make sure your logic respects user permissions (FLS & CRUD):

Example 1: Querying Accounts Securely in a Controller

Without User Mode (runs in system mode – not secure):

List<Account> accounts = [SELECT Id, Name, Industry FROM Account];


With User Mode (recommended):

Query query = new Query('SELECT Id, Name, Industry FROM Account')

    .setUserMode(UserMode.CURRENT);


List<Account> accounts = Database.query(query);


Now the query will return only fields the current user is allowed to access.

Example 2: Batch Apex – Get Records with Respect to User Access

Batch class using User Mode in start method:

global class AccountBatch implements Database.Batchable<SObject> {

    global Database.QueryLocator start(Database.BatchableContext BC) {

        Query query = new Query('SELECT Id, Name FROM Account')

            .setUserMode(UserMode.CURRENT);

        return Database.getQueryLocator(query);

    }


    global void execute(Database.BatchableContext BC, List<SObject> scope) {

        // Process records here

    }


    global void finish(Database.BatchableContext BC) {}

}


Example 3: Secure DML Operation (Insert)

Without User Mode (may insert data user isn't allowed to):

insert new Account(Name='Test');

With User Mode:

Account acct = new Account(Name='Test');


Database.DMLOptions dmlOpts = new Database.DMLOptions();

dmlOpts.setUserMode(UserMode.CURRENT);


Database.insert(acct, dmlOpts);


If the user doesn’t have create access to the Account object, this insert will fail safely.

Example 4: Reporting Component – Avoid Showing Restricted Fields

Say you’re building a custom report viewer in LWC or Aura and calling Apex:

Query query = new Query('SELECT Name, AnnualRevenue, Industry FROM Account')

    .setUserMode(UserMode.CURRENT);


List<Account> result = Database.query(query);


If the user can't see AnnualRevenue, it will automatically be excluded from results — no error, no extra code!

No comments:

Post a Comment