I am trying to create a Transaction Security Policy with apex that aims to notify by email a technical lead when a user tries to log in with a wrong password too many times (like a brute force scenario). I created custom metadata called TSP_LoginEvent_Setting__mdt to set the threshold from how many wrong logins should be considered to send an email notification. Here's my code :
global class LoginEventConditionTooManyFailedLogins implements TxnSecurity.EventCondition {
public boolean evaluate(SObject event) {
switch on event{
when LoginEvent loginEvent {
return evaluate_multiplefailedlogins(loginEvent);
}
when null {
return false;
}
when else{
return false;
}
}
}
private boolean evaluate_multiplefailedlogins(LoginEvent loginEvent) {
/*LoginHistory last_login = [SELECT LoginTime,Status
FROM LoginHistory
WHERE UserId = :loginEvent.UserId
ORDER BY LoginTime DESC
LIMIT 1][0];*/
//system.debug(results[0].Status);
AggregateResult[] results = [SELECT UserId,Status,count(Id) c
FROM LoginHistory
WHERE UserId = :loginEvent.UserId
AND LoginTime = LAST_N_DAYS:1
GROUP BY UserId,Status];
Integer size_res = results.size();
Integer i = 0;
TSP_LoginEvent_Setting__mdt tsp_le = [SELECT Number_failed_logins__c FROM TSP_LoginEvent_Setting__mdt WHERE QualifiedApiName = 'Login_Event_Settings'][0];
boolean res = false;
while (i < size_res){
String status = (String)results[i].get('Status');
Integer count = (Integer)results[i].get('c');
if ( !(status == 'Success') && count> tsp_le.Number_failed_logins__c /*&& !(last_login.Status == 'Success')*/ ){
system.debug( 'Statuuus ' + results[i].get('Status') + 'Couuunt' + results[i].get('c'));
res = true;
break;
}
i++;
}
return res;
}
}
Problem is, the policy was being triggered as follow : suppose I want to notify by email everytime a user tries to log in at least three times with a wrong password. User A types his email and a wrong password four times (I want the email to be sent at the fourth wrong login attempt), but the email gets sent only once the user logs in successfully with the right password immediately after the serie of wrong passwords. Then, everytime my user logs in after that, the email gets sent again, because the count is still greater than my parameter.
So in order to avoid this, I added a part where I fetch the last login, and if it is successful I don't want to trigger the policy (this is the commented part of my code) :
/*LoginHistory last_login = [SELECT LoginTime,Status
FROM LoginHistory
WHERE UserId = :loginEvent.UserId
ORDER BY LoginTime DESC
LIMIT 1][0];*/
AND :
if ( !(status == 'Success') && count> tsp_le.Number_failed_logins__c /*&& !(last_login.Status == 'Success')*/ ){
If I uncomment it, the Policy doesn't get triggered at all and I don't understand why. I feel like the triggering event is really the successful login.
Sorry for the long message, would be great if anyone can help! Thanks