Secure the Data Access
By the end of this lesson, you'll be able to:
- Apply with sharing to the service class, with explicit reasoning
- Check field-level access before reading the fields each metric needs
- Apply Module 28's full security toolkit to a real dashboard feature
Prerequisites: "Build the Service Layer"
Reasoning through the sharing choice
public with sharing class AccountHealthService {
// ...
}
public with sharing class AccountHealthService {—with sharingadded explicitly, not left to the default.// ...— the methods from the previous lesson, unchanged.}— closes the class.
Applying Module 30's "Secure Data Access" question directly: does this dashboard need to see Cases or Contacts the running user couldn't normally access? No — a support manager viewing a dashboard should only ever see health data for Accounts they already have access to. with sharing is the clear, deliberate choice.
Checking field-level access before reading
public Integer getOpenCaseCount(Id accountId) {
if (! Schema.sObjectType.Case.isAccessible()) {
return 0;
}
return [SELECT COUNT() FROM Case WHERE AccountId = :accountId AND IsClosed = false];
}
public Integer getOpenCaseCount(Id accountId) {— the method from Lesson 2.if (! Schema.sObjectType.Case.isAccessible()) {— Module 28's CRUD-check pattern, asked before touching the object at all.return 0;— a graceful fallback, not an error, when the running user lacksCaseread access.}— closes theif.return [SELECT COUNT() FROM Case WHERE AccountId = :accountId AND IsClosed = false];— unchanged from Lesson 2, now only reached once access is confirmed.}— closes the method.
This is Module 28's CRUD-check pattern, applied to a genuinely realistic scenario: a user with dashboard access but no Case object permission shouldn't see a broken component — returning 0 gracefully (rather than a permissions error) keeps the dashboard usable even for a user with narrower access.
Using WITH USER_MODE for automatic enforcement
public Decimal getTotalWonRevenue(Id accountId) {
List<Account> accounts = [
SELECT Total_Won_Revenue__c FROM Account WHERE Id = :accountId WITH USER_MODE
];
if (accounts.isEmpty()) {
return 0;
}
return accounts[0].Total_Won_Revenue__c == null ? 0 : accounts[0].Total_Won_Revenue__c;
}
public Decimal getTotalWonRevenue(Id accountId) {— the method signature.List<Account> accounts = [— aList, not a singleAccount, deliberately.SELECT Total_Won_Revenue__c FROM Account WHERE Id = :accountId WITH USER_MODE— Module 28'sWITH USER_MODEclause, enforcing field- and object-level security automatically for this query.];— closes the query.if (accounts.isEmpty()) {— covers both "no such Account" and "Account exists but the field/object isn't accessible to this user" —WITH USER_MODEsimply omits inaccessible results rather than throwing.return 0;— the same graceful zero used throughout this service.}— closes theif.return accounts[0].Total_Won_Revenue__c == null ? 0 : accounts[0].Total_Won_Revenue__c;— the field itself can still be blank on an existing Account, sonullis handled separately from "no access."}— closes the method.
Module 28's WITH USER_MODE handles field-level security automatically for this query, rather than manually checking Total_Won_Revenue__c.isAccessible() — and switching to a List query (instead of assuming exactly one record) means a user without access to any matching Account, or the field itself, simply gets an empty result instead of a runtime error.
Exercise
As a comment, write the reasoning for why AccountHealthService should be with sharing rather than without sharing.
Show hint
Follow Module 30's "does this genuinely need to bypass the running user's access?" question.
Secure the Data Access Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
This lesson secures AccountHealthService following Module 28's full toolkit — sharing, field-level access, and the reasoning behind each choice, not just the mechanical application.