replace SECURITY_ENFORCED with USER_MODE across SOQL/Apex skill examples

This commit is contained in:
anatolii 2026-08-01 16:23:35 +03:00
parent fe3e2edad0
commit d4bfd686c2
No known key found for this signature in database
11 changed files with 52 additions and 52 deletions

View File

@ -37,7 +37,7 @@ public with sharing class LwcController {
SELECT Id, Name, Industry, AnnualRevenue, Phone, CreatedDate
FROM Account
WHERE Name LIKE :searchKey
WITH SECURITY_ENFORCED
WITH USER_MODE
ORDER BY Name
LIMIT :recordLimit
];
@ -57,7 +57,7 @@ public with sharing class LwcController {
(SELECT Id, FirstName, LastName, Email FROM Contacts LIMIT 5)
FROM Account
WHERE Id = :accountId
WITH SECURITY_ENFORCED
WITH USER_MODE
LIMIT 1
];
@ -146,7 +146,7 @@ public with sharing class LwcController {
List<Account> existing = [
SELECT Id FROM Account
WHERE Id = :accountId
WITH SECURITY_ENFORCED
WITH USER_MODE
LIMIT 1
];
@ -201,7 +201,7 @@ public with sharing class LwcController {
List<Account> accountsToDelete = [
SELECT Id FROM Account
WHERE Id IN :recordIds
WITH SECURITY_ENFORCED
WITH USER_MODE
];
if (accountsToDelete.size() != recordIds.size()) {
@ -280,14 +280,14 @@ public with sharing class LwcController {
// Build dynamic query
String query = 'SELECT Id, Name, Industry, AnnualRevenue, Phone, CreatedDate ' +
'FROM Account ' +
'WITH SECURITY_ENFORCED ' +
'WITH USER_MODE ' +
'ORDER BY ' + sortField + ' ' + sortDir + ' NULLS LAST ' +
'LIMIT :recordLimit OFFSET :recordOffset';
List<Account> records = Database.query(query);
// Get total count
Integer totalCount = [SELECT COUNT() FROM Account WITH SECURITY_ENFORCED];
Integer totalCount = [SELECT COUNT() FROM Account WITH USER_MODE];
return new PagedResult(records, totalCount);
} catch (Exception e) {

View File

@ -138,7 +138,7 @@ public static List<Account> getAccounts(String searchTerm) {
SELECT Id, Name, Industry
FROM Account
WHERE Name LIKE :searchKey
WITH SECURITY_ENFORCED
WITH USER_MODE
LIMIT 50
];
}
@ -1368,7 +1368,7 @@ public with sharing class LwcController {
SELECT Id, Name, Industry, AnnualRevenue
FROM Account
WHERE Name LIKE :searchKey
WITH SECURITY_ENFORCED
WITH USER_MODE
ORDER BY Name
LIMIT 50
];
@ -1425,7 +1425,7 @@ public static void deleteAccounts(List<Id> accountIds) {
List<Account> toDelete = [
SELECT Id FROM Account
WHERE Id IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
];
delete toDelete;
@ -1446,7 +1446,7 @@ public static List<Contact> getContactsWithErrorHandling(Id accountId) {
SELECT Id, Name, Email, Phone
FROM Contact
WHERE AccountId = :accountId
WITH SECURITY_ENFORCED
WITH USER_MODE
ORDER BY Name
LIMIT 100
];

View File

@ -980,10 +980,10 @@ it('displays data', async () => {
### FLS Enforcement
```apex
// Always use SECURITY_ENFORCED or stripInaccessible
// Always use USER_MODE or stripInaccessible
@AuraEnabled(cacheable=true)
public static List<Account> getAccounts() {
return [SELECT Id, Name FROM Account WITH SECURITY_ENFORCED];
return [SELECT Id, Name FROM Account WITH USER_MODE];
}
// For DML operations

View File

@ -435,7 +435,7 @@ public class AccountSelector {
SELECT Id, Name, Industry
FROM Account
WHERE IsActive__c = true
WITH SECURITY_ENFORCED
WITH USER_MODE
];
}

View File

@ -7,7 +7,7 @@ Salesforce SOQL query generation, optimization, and analysis skill with 100-poin
- **Natural Language to SOQL**: Convert requests into executable queries
- **Query Optimization**: Improve selectivity, LIMIT usage, and field selection
- **Relationship Queries**: Parent-child, child-parent, and polymorphic patterns
- **Security Guidance**: `WITH USER_MODE`, `WITH SECURITY_ENFORCED`, and Apex-safe usage
- **Security Guidance**: `WITH USER_MODE`, and Apex-safe usage
- **100-Point Scoring**: Performance, correctness, security, and readability checks
## Quick Start

View File

@ -47,7 +47,7 @@ public class BulkPatternExample {
SELECT Id, Name
FROM Account
WHERE Id IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
]);
// Step 3: O(1) Map lookups
@ -86,7 +86,7 @@ public class GroupedChildPattern {
SELECT Id, FirstName, LastName, Email, AccountId
FROM Contact
WHERE AccountId IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
]) {
contactsByAccount.get(c.AccountId).add(c);
}
@ -137,7 +137,7 @@ public class MultiLevelLookupPattern {
SELECT Id, Name, OwnerId, Owner.Name, Owner.Email
FROM Account
WHERE Id IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
]);
// Step 3: Use the data
@ -177,7 +177,7 @@ public class ConditionalQueryPattern {
SELECT Id, Name, AnnualRevenue, Industry
FROM Account
WHERE Id IN :highValueAccountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
]);
// Step 3: Process only the high-value opportunities
@ -212,7 +212,7 @@ public class SharedQueryPattern {
SELECT Id, Name, Industry, AnnualRevenue, OwnerId
FROM Account
WHERE Id IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
]);
}

View File

@ -184,19 +184,19 @@ SELECT Id, Status__c FROM Account WHERE Id = :accountId FOR UPDATE
-- Adds overhead and can cause lock contention
// ═══════════════════════════════════════════════════════════════════════════
// SECURITY ENFORCEMENT
// SECURITY
// ═══════════════════════════════════════════════════════════════════════════
-- ✅ Recommended: WITH SECURITY_ENFORCED
-- ✅ Recommended: WITH USER_MODE (API 54.0+)
SELECT Id, Name, Phone FROM Account
WITH SECURITY_ENFORCED
-- Throws exception if user lacks field access
-- Alternative: USER_MODE (API 54.0+)
SELECT Id, Name FROM Account
WITH USER_MODE
-- Respects sharing rules and FLS
-- Alternative: WITH SECURITY_ENFORCED (Removed in API version 67.0+, use USER_MODE instead)
SELECT Id, Name FROM Account
WITH SECURITY_ENFORCED
-- Throws exception if user lacks field access
-- For admin operations: SYSTEM_MODE
SELECT Id, Name FROM Account
WITH SYSTEM_MODE

View File

@ -83,7 +83,7 @@ public inherited sharing class ${OBJECT_NAME}Selector {
SELECT Id, Name, OwnerId, CreatedDate, LastModifiedDate
FROM ${OBJECT_NAME}
WHERE Id IN :recordIds
WITH SECURITY_ENFORCED
WITH USER_MODE
];
}
@ -114,7 +114,7 @@ public inherited sharing class ${OBJECT_NAME}Selector {
SELECT Id, Name, OwnerId, CreatedDate, LastModifiedDate
FROM ${OBJECT_NAME}
WHERE OwnerId = :ownerId
WITH SECURITY_ENFORCED
WITH USER_MODE
ORDER BY LastModifiedDate DESC
LIMIT 1000
];
@ -139,7 +139,7 @@ public inherited sharing class ${OBJECT_NAME}Selector {
FROM ${OBJECT_NAME}
WHERE CreatedDate >= :startDate
AND CreatedDate <= :endDate
WITH SECURITY_ENFORCED
WITH USER_MODE
ORDER BY CreatedDate DESC
LIMIT 10000
];
@ -171,7 +171,7 @@ public inherited sharing class ${OBJECT_NAME}Selector {
LIMIT 50)
FROM ${OBJECT_NAME}
WHERE Id IN :recordIds
WITH SECURITY_ENFORCED
WITH USER_MODE
];
}
@ -211,7 +211,7 @@ public inherited sharing class ${OBJECT_NAME}Selector {
SELECT Id
FROM ${OBJECT_NAME}
WHERE Id = :recordId
WITH SECURITY_ENFORCED
WITH USER_MODE
LIMIT 1
];
return !results.isEmpty();

View File

@ -111,7 +111,7 @@ for (List<Account> accs : [SELECT Id, Name FROM Account]) {
## Security Patterns
### WITH SECURITY_ENFORCED
### WITH SECURITY_ENFORCED (Removed in API version 67.0+, use USER_MODE instead)
```sql
-- Throws exception if user lacks FLS

View File

@ -41,7 +41,7 @@ public class AccountSelector {
SELECT Id, Name, Industry
FROM Account
WHERE Id IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
];
}
}
@ -89,7 +89,7 @@ public inherited sharing class AccountSelector {
SELECT Id, Name, Industry, AnnualRevenue, OwnerId
FROM Account
WHERE Id IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
];
}
@ -101,7 +101,7 @@ public inherited sharing class AccountSelector {
SELECT Id, Name, Industry, AnnualRevenue, OwnerId
FROM Account
WHERE OwnerId = :ownerId
WITH SECURITY_ENFORCED
WITH USER_MODE
LIMIT 1000
];
}
@ -118,7 +118,7 @@ public inherited sharing class AccountSelector {
LIMIT 50)
FROM Account
WHERE Id IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
];
}
}
@ -207,7 +207,7 @@ public inherited sharing class OpportunitySelector {
SELECT Id, Name, StageName, Amount, CloseDate, AccountId
FROM Opportunity
WHERE AccountId IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
];
}
@ -402,7 +402,7 @@ public inherited sharing class BulkQueryHelper {
SELECT Id, Name, Industry
FROM Account
WHERE Id IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
]);
}
@ -416,7 +416,7 @@ public inherited sharing class BulkQueryHelper {
SELECT Id, FirstName, LastName, Email, AccountId
FROM Contact
WHERE AccountId IN :accountIds
WITH SECURITY_ENFORCED
WITH USER_MODE
]) {
if (!contactsByAccount.containsKey(c.AccountId)) {
contactsByAccount.put(c.AccountId, new List<Contact>());
@ -456,15 +456,15 @@ for (Opportunity opp : Trigger.new) {
## Best Practices Summary
| Practice | Benefit |
|----------|---------|
| Practice | Benefit |
|--------------------------------|---------|
| Centralize in Selector classes | One place to update field lists |
| Use `WITH SECURITY_ENFORCED` | Automatic FLS enforcement |
| Return empty List, not null | Prevents NullPointerException |
| Use `inherited sharing` | Respects caller's sharing context |
| Make fields list a constant | Easy to update across queries |
| Add null/empty checks | Prevent unnecessary queries |
| Support mocking in tests | Faster tests, no database dependencies |
| Use `WITH USER_MODE` | Respects sharing rules and FLS |
| Return empty List, not null | Prevents NullPointerException |
| Use `inherited sharing` | Respects caller's sharing context |
| Make fields list a constant | Easy to update across queries |
| Add null/empty checks | Prevent unnecessary queries |
| Support mocking in tests | Faster tests, no database dependencies |
---

View File

@ -165,11 +165,11 @@ SELECT Id, (SELECT Id FROM Children__r) FROM Parent__c
## WITH Clauses
| Clause | Description |
|--------|-------------|
| `WITH SECURITY_ENFORCED` | Enforce FLS (throws exception if no access) |
| `WITH USER_MODE` | Respect sharing and FLS |
| `WITH SYSTEM_MODE` | Bypass sharing rules |
| Clause | Description |
|--------|-----------------------------------------------------------------------------|
| `WITH SECURITY_ENFORCED` | Enforce FLS (throws exception if no access; removed in API version 67.0+) |
| `WITH USER_MODE` | Respect sharing and FLS |
| `WITH SYSTEM_MODE` | Bypass sharing rules |
---