Skip to content

Instantly share code, notes, and snippets.

@pgajek2
Last active March 28, 2026 18:46
Show Gist options
  • Select an option

  • Save pgajek2/e0f00f0614df86ea7698153cb77bbea3 to your computer and use it in GitHub Desktop.

Select an option

Save pgajek2/e0f00f0614df86ea7698153cb77bbea3 to your computer and use it in GitHub Desktop.
FLS + Sharing in Apex SOQL
/**
* Tests covering all FLS, OLS, and Sharing Mode combinations from the SOQL security matrix:
*
* SOQL Keyword | Class Sharing | OLS+FLS | Sharing Mode
* -----------------------|-------------------|---------|-------------
* WITH USER_MODE | with sharing | ✅ | ✅
* WITH USER_MODE | inherited sharing | ✅ | ✅
* WITH USER_MODE | without sharing | ✅ | ✅
* WITH SYSTEM_MODE | with sharing | ❌ | ✅
* WITH SYSTEM_MODE | inherited sharing | ❌ | 🔄
* WITH SYSTEM_MODE | without sharing | ❌ | ❌
* WITH SECURITY_ENFORCED | with sharing | ✅ | ✅
* WITH SECURITY_ENFORCED | inherited sharing | ✅ | 🔄
* WITH SECURITY_ENFORCED | without sharing | ✅ | ❌
*
* ✅ Enforced ❌ Not enforced 🔄 Inherited from calling context
*
* Minimum Access User object access:
* - HAS access to Task → *WhenHasAccessToObject methods
* - Does NOT have access to Case → *WhenDoesntHaveAccessToObject methods
* Organization-Wide Default:
* - Case: Private
* - Task (Activity): Private
*/
@IsTest
private without sharing class FlsSharingTest {
// ─── WITH SHARING ─────────────────────────────────────────────────────────
@IsTest
static void withSharingUserModeHasAccessToObject() {
insertTasks();
System.runAs(minimumAccessUser()) {
List<Task> tasks;
Test.startTest();
tasks = new WithSharing().withUserModeWhenHasAccessToObject();
Test.stopTest();
Assert.areEqual(0, tasks.size(), 'WITH USER_MODE in with sharing class enforces sharing - records not visible.');
}
}
@IsTest
static void withSharingUserModeDoesntHaveAccessToObject() {
insertCases();
System.runAs(minimumAccessUser()) {
Exception queryException;
Test.startTest();
try {
new WithSharing().withUserModeWhenDoesntHaveAccessToObject();
} catch (Exception e) {
queryException = e;
}
Test.stopTest();
Assert.isNotNull(queryException, 'WITH USER_MODE should enforce OLS - querying inaccessible object must throw.');
Assert.isTrue(queryException.getMessage().contains('sObject type \'Case\' is not supported.'), 'The exception message should indicate that the object is not accessible.');
}
}
@IsTest
static void withSharingSystemModeHasAccessToObject() {
insertTasks();
System.runAs(minimumAccessUser()) {
List<Task> tasks;
Test.startTest();
tasks = new WithSharing().withSystemModeWhenHasAccessToObject();
Test.stopTest();
Assert.areEqual(0, tasks.size(), 'WITH SYSTEM_MODE in with sharing class enforces sharing - records not visible.');
}
}
@IsTest
static void withSharingSystemModeDoesntHaveAccessToObject() {
insertCases();
System.runAs(minimumAccessUser()) {
List<Case> cases;
Test.startTest();
cases = new WithSharing().withSystemModeWhenDoesntHaveAccessToObject();
Test.stopTest();
Assert.areEqual(0, cases.size(), 'WITH SYSTEM_MODE bypasses OLS - query succeeds but with sharing enforces record visibility.');
}
}
@IsTest
static void withSharingSecurityEnforcedHasAccessToObject() {
insertTasks();
System.runAs(minimumAccessUser()) {
List<Task> tasks;
Test.startTest();
tasks = new WithSharing().withSecurityEnforcedWhenHasAccessToObject();
Test.stopTest();
Assert.areEqual(0, tasks.size(), 'WITH SECURITY_ENFORCED in with sharing class enforces sharing - records not visible.');
}
}
@IsTest
static void withSharingSecurityEnforcedDoesntHaveAccessToObject() {
insertCases();
System.runAs(minimumAccessUser()) {
Exception queryException;
Test.startTest();
try {
new WithSharing().withSecurityEnforcedWhenDoesntHaveAccessToObject();
} catch (Exception e) {
queryException = e;
}
Test.stopTest();
Assert.isNotNull(queryException, 'WITH SECURITY_ENFORCED should enforce OLS - querying inaccessible object must throw.');
Assert.isTrue(queryException.getMessage().contains('Insufficient permissions: secure query included inaccessible field'), 'The exception message should indicate insufficient permissions.');
}
}
// ─── INHERITED SHARING ────────────────────────────────────────────────────
@IsTest
static void inheritedSharingUserModeHasAccessToObject() {
insertTasks();
System.runAs(minimumAccessUser()) {
List<Task> tasks;
Test.startTest();
tasks = new InheritedSharing().withUserModeWhenHasAccessToObject();
Test.stopTest();
Assert.areEqual(0, tasks.size(), 'WITH USER_MODE always enforces sharing regardless of class keyword.');
}
}
@IsTest
static void inheritedSharingUserModeDoesntHaveAccessToObject() {
insertCases();
System.runAs(minimumAccessUser()) {
Exception queryException;
Test.startTest();
try {
new InheritedSharing().withUserModeWhenDoesntHaveAccessToObject();
} catch (Exception e) {
queryException = e;
}
Test.stopTest();
Assert.isNotNull(queryException, 'WITH USER_MODE should enforce OLS - querying inaccessible object must throw.');
Assert.isTrue(queryException.getMessage().contains('sObject type \'Case\' is not supported.'), 'The exception message should indicate that the object is not accessible.');
}
}
@IsTest
static void inheritedSharingSystemModeHasAccessToObject() {
insertTasks();
System.runAs(minimumAccessUser()) {
List<Task> tasks;
Test.startTest();
tasks = new InheritedSharing().withSystemModeWhenHasAccessToObject();
Test.stopTest();
Assert.areEqual(0, tasks.size(), 'WITH SYSTEM_MODE in inherited sharing class inherits with sharing from calling context - records not visible.');
}
}
@IsTest
static void inheritedSharingSystemModeDoesntHaveAccessToObject() {
insertCases();
System.runAs(minimumAccessUser()) {
List<Case> cases;
Test.startTest();
cases = new InheritedSharing().withSystemModeWhenDoesntHaveAccessToObject();
Test.stopTest();
Assert.areEqual(2, cases.size(), 'WITH SYSTEM_MODE bypasses OLS but inherited sharing inherits with sharing from calling context - records not visible.');
}
}
@IsTest
static void inheritedSharingSecurityEnforcedHasAccessToObject() {
insertTasks();
System.runAs(minimumAccessUser()) {
List<Task> tasks;
Test.startTest();
tasks = new InheritedSharing().withSecurityEnforcedWhenHasAccessToObject();
Test.stopTest();
Assert.areEqual(0, tasks.size(), 'WITH SECURITY_ENFORCED in inherited sharing class inherits with sharing from calling context - records not visible.');
}
}
@IsTest
static void inheritedSharingSecurityEnforcedDoesntHaveAccessToObject() {
insertCases();
System.runAs(minimumAccessUser()) {
Exception queryException;
Test.startTest();
try {
new InheritedSharing().withSecurityEnforcedWhenDoesntHaveAccessToObject();
} catch (Exception e) {
queryException = e;
}
Test.stopTest();
Assert.isNotNull(queryException, 'WITH SECURITY_ENFORCED should enforce OLS - querying inaccessible object must throw.');
Assert.isTrue(queryException.getMessage().contains('Insufficient permissions: secure query included inaccessible field'), 'The exception message should indicate insufficient permissions.');
}
}
// ─── WITHOUT SHARING ──────────────────────────────────────────────────────
@IsTest
static void withoutSharingUserModeHasAccessToObject() {
insertTasks();
System.runAs(minimumAccessUser()) {
List<Task> tasks;
Test.startTest();
tasks = new WithoutSharing().withUserModeWhenHasAccessToObject();
Test.stopTest();
Assert.areEqual(0, tasks.size(), 'WITH USER_MODE always enforces sharing even inside a without sharing class.');
}
}
@IsTest
static void withoutSharingUserModeDoesntHaveAccessToObject() {
insertCases();
System.runAs(minimumAccessUser()) {
Exception queryException;
Test.startTest();
try {
new WithoutSharing().withUserModeWhenDoesntHaveAccessToObject();
} catch (Exception e) {
queryException = e;
}
Test.stopTest();
Assert.isNotNull(queryException, 'WITH USER_MODE should enforce OLS even inside a without sharing class.');
Assert.isTrue(queryException.getMessage().contains('sObject type \'Case\' is not supported.'), 'The exception message should indicate that the object is not accessible.');
}
}
@IsTest
static void withoutSharingSystemModeHasAccessToObject() {
insertTasks();
System.runAs(minimumAccessUser()) {
List<Task> tasks;
Test.startTest();
tasks = new WithoutSharing().withSystemModeWhenHasAccessToObject();
Test.stopTest();
Assert.areEqual(2, tasks.size(), 'WITH SYSTEM_MODE in without sharing class does not enforce sharing - all records visible.');
}
}
@IsTest
static void withoutSharingSystemModeDoesntHaveAccessToObject() {
insertCases();
System.runAs(minimumAccessUser()) {
List<Case> cases;
Test.startTest();
cases = new WithoutSharing().withSystemModeWhenDoesntHaveAccessToObject();
Test.stopTest();
Assert.areEqual(2, cases.size(), 'WITH SYSTEM_MODE bypasses OLS and without sharing does not enforce sharing - all records visible.');
}
}
@IsTest
static void withoutSharingSecurityEnforcedHasAccessToObject() {
insertTasks();
System.runAs(minimumAccessUser()) {
List<Task> tasks;
Test.startTest();
tasks = new WithoutSharing().withSecurityEnforcedWhenHasAccessToObject();
Test.stopTest();
Assert.areEqual(2, tasks.size(), 'WITH SECURITY_ENFORCED in without sharing class does not enforce sharing - all records visible.');
}
}
@IsTest
static void withoutSharingSecurityEnforcedDoesntHaveAccessToObject() {
insertCases();
System.runAs(minimumAccessUser()) {
Exception queryException;
Test.startTest();
try {
new WithoutSharing().withSecurityEnforcedWhenDoesntHaveAccessToObject();
} catch (Exception e) {
queryException = e;
}
Test.stopTest();
Assert.isNotNull(queryException, 'WITH SECURITY_ENFORCED should enforce OLS even inside a without sharing class.');
Assert.isTrue(queryException.getMessage().contains('Insufficient permissions: secure query included inaccessible field'), 'The exception message should indicate insufficient permissions.');
}
}
// ─── INNER CLASSES ────────────────────────────────────────────────────────
private with sharing class WithSharing {
public List<Task> withUserModeWhenHasAccessToObject() {
return [SELECT Id FROM Task WITH USER_MODE];
}
public List<Case> withUserModeWhenDoesntHaveAccessToObject() {
return [SELECT Id, Status, Origin FROM Case WITH USER_MODE];
}
public List<Task> withSystemModeWhenHasAccessToObject() {
return [SELECT Id, Subject, Type FROM Task WITH SYSTEM_MODE];
}
public List<Case> withSystemModeWhenDoesntHaveAccessToObject() {
return [SELECT Id, Status, Origin FROM Case WITH SYSTEM_MODE];
}
public List<Task> withSecurityEnforcedWhenHasAccessToObject() {
return [SELECT Id FROM Task WITH SECURITY_ENFORCED];
}
public List<Case> withSecurityEnforcedWhenDoesntHaveAccessToObject() {
return [SELECT Id, Status, Origin FROM Case WITH SECURITY_ENFORCED];
}
}
private inherited sharing class InheritedSharing {
public List<Task> withUserModeWhenHasAccessToObject() {
return [SELECT Id FROM Task WITH USER_MODE];
}
public List<Case> withUserModeWhenDoesntHaveAccessToObject() {
return [SELECT Id, Status, Origin FROM Case WITH USER_MODE];
}
public List<Task> withSystemModeWhenHasAccessToObject() {
return [SELECT Id, Subject, Type FROM Task WITH SYSTEM_MODE];
}
public List<Case> withSystemModeWhenDoesntHaveAccessToObject() {
return [SELECT Id, Status, Origin FROM Case WITH SYSTEM_MODE];
}
public List<Task> withSecurityEnforcedWhenHasAccessToObject() {
return [SELECT Id FROM Task WITH SECURITY_ENFORCED];
}
public List<Case> withSecurityEnforcedWhenDoesntHaveAccessToObject() {
return [SELECT Id, Status, Origin FROM Case WITH SECURITY_ENFORCED];
}
}
private without sharing class WithoutSharing {
public List<Task> withUserModeWhenHasAccessToObject() {
return [SELECT Id FROM Task WITH USER_MODE];
}
public List<Case> withUserModeWhenDoesntHaveAccessToObject() {
return [SELECT Id, Status, Origin FROM Case WITH USER_MODE];
}
public List<Task> withSystemModeWhenHasAccessToObject() {
return [SELECT Id, Subject, Type FROM Task WITH SYSTEM_MODE];
}
public List<Case> withSystemModeWhenDoesntHaveAccessToObject() {
return [SELECT Id, Status, Origin FROM Case WITH SYSTEM_MODE];
}
public List<Task> withSecurityEnforcedWhenHasAccessToObject() {
return [SELECT Id FROM Task WITH SECURITY_ENFORCED];
}
public List<Case> withSecurityEnforcedWhenDoesntHaveAccessToObject() {
return [SELECT Id, Status, Origin FROM Case WITH SECURITY_ENFORCED];
}
}
// ─── HELPERS ──────────────────────────────────────────────────────────────
static void insertTasks() {
insert new List<Task>{
new Task(Subject = 'Test', Type = 'Other'),
new Task(Subject = 'Test', Type = 'Other')
};
}
static void insertCases() {
insert new List<Case>{
new Case(Status = 'New', Origin = 'Web'),
new Case(Status = 'New', Origin = 'Web')
};
}
static User minimumAccessUser() {
return new User(
Alias = 'minUser',
Email = 'flssharingtest@testorg.com',
EmailEncodingKey = 'UTF-8',
LastName = 'FlsSharingTest',
LanguageLocaleKey = 'en_US',
LocaleSidKey = 'en_US',
Profile = new Profile(Name = 'Minimum Access - Salesforce'),
TimeZoneSidKey = 'America/Los_Angeles',
Username = 'flssharingtest@testorg.com'
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment