Secure Account Data
By the end of this lesson, you'll be able to:
- Apply Module 28's WITH SECURITY_ENFORCED and CRUD/FLS checks to the banking objects
- Prevent a withdrawal from making Balance__c negative
- Prevent transactions against a Frozen or Closed account
Prerequisites: "Schedule Nightly Reconciliation (Batch + Scheduled)"
Field-level security on Balance__c
public with sharing class BankAccountService {
public static List<Bank_Account__c> getAccountsFor(Id userId) {
return [
SELECT Id, Balance__c, Status__c
FROM Bank_Account__c
WHERE OwnerId = :userId
WITH SECURITY_ENFORCED
];
}
}
with sharing and WITH SECURITY_ENFORCED are Module 28's exact security mechanisms — for a banking system, enforcing that a user can only see accounts and balances they're actually permitted to see isn't optional polish, it's the whole point of the security lesson finally mattering in a domain where the stakes are obvious.
Rejecting an overdraft in a before-insert validation trigger
trigger TransactionValidation on Transaction__c (before insert) {
Set<Id> accountIds = new Set<Id>();
for (Transaction__c t : Trigger.new) accountIds.add(t.Bank_Account__c);
Map<Id, Bank_Account__c> accounts = new Map<Id, Bank_Account__c>(
[SELECT Id, Balance__c, Status__c FROM Bank_Account__c WHERE Id IN :accountIds]
);
for (Transaction__c t : Trigger.new) {
Bank_Account__c acc = accounts.get(t.Bank_Account__c);
if (acc.Status__c != 'Active') {
t.addError('This account is not active and cannot accept transactions.');
} else if (t.Type__c == 'Withdrawal' && t.Amount__c > acc.Balance__c) {
t.addError('Insufficient funds for this withdrawal.');
}
}
}
This is a separate before insert trigger, deliberately kept apart from Lesson 2's balance-update after insert trigger — Module 24's separation-of-concerns principle, applied here so that validation logic and balance-update logic can each be tested (Lesson 7) and reasoned about independently.
Why validation happens before, not after, the balance update
before insert runs, and can call addError() to block the record entirely, before Lesson 2's after insert balance-update trigger ever executes. This ordering guarantee means an invalid overdraft transaction never reaches the balance-update logic at all — it's rejected outright, exactly the fail-safe design a banking system requires.
Exercise
As a comment, explain why the overdraft check belongs in a before-insert trigger rather than the after-insert trigger from Lesson 2.
Show hint
Think about what addError() does and when a record needs to be rejected relative to the balance update.
Secure Account Data 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 layers Module 28's security discipline and genuine business-rule validation onto the banking objects — enforcing field-level security on sensitive financial data, and rejecting transactions that would violate the account's own rules.