package org.freezo.admin.service;
import java.util.Date;
import org.freezo.domain.Account;
import org.freezo.domain.AccountRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class FailedAuthHandler implements ApplicationListener<AuthenticationFailureBadCredentialsEvent>
{
private static final Logger LOG = LoggerFactory.getLogger(FailedAuthHandler.class);
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private AccountRepository repository;
@Value("${freezo.security.authentiation.account.maxBadCredentials:-1}")
private int badCredentialsToLockAccount;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void onApplicationEvent(final AuthenticationFailureBadCredentialsEvent event)
{
repository.findByUsername((String) event.getAuthentication().getPrincipal())
.ifPresent(account -> updateAccount(account));
}
/**
* Updates account with failed login details such as: last failed login date
* and time, users IP address, increases failed logins counter etc.
*
* @param account
* current account
* @see #checkIfAccountLocked(Account)
*/
private void updateAccount(final Account account)
{
LOG.info("Updating details of ACCOUNT:[{}] upon bad credentials", account.getUsername());
account.setLastFailedAuth(new Date());
account.setLastFailedAuthIp(currentRequestRemoteAddr());
account.setFailedAuthCounter(account.getFailedAuthCounter() + 1);
checkIfAccountLocked(account);
repository.save(account);
}
/**
* Locks the given account if the maximum number of login failures (bad
* credentials) has been reached.
* <p>
* This feature can be disabled by setting
* {@code freezo.security.authentiation.maxBadCredentialsToLockAccount}
* system property to value lower than 1;
* <p>
* Note that account will be locked after the first failed login attempt is
* {@code freezo.security.authentiation.maxBadCredentialsToLockAccount} is
* set to 1.
*
* @param account
* current account
* @see UserDetails#isAccountNonLocked()
*/
private void checkIfAccountLocked(final Account account)
{
if (badCredentialsToLockAccount > 0 && account.getFailedAuthCounter() >= badCredentialsToLockAccount)
{
LOG.info("Locking ACCOUNT:[{}] due to {} invalid authentication attempts",
account.getUsername(), account.getFailedAuthCounter());
account.setLocked(true);
}
}
private String currentRequestRemoteAddr()
{
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest().getRemoteAddr();
}
}