IpAddressAuthProvider.java

package org.flasby.security;

import java.util.Optional;

import javax.transaction.Transactional;

import org.flasby.entity.BannedIp;
import org.flasby.entity.repository.BannedIpRepository;
import org.flasby.security.servlet.ServletAuthenticationDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;

import lombok.extern.log4j.Log4j2;

@Log4j2
// @Configuration
@Component
public class IpAddressAuthProvider implements AuthenticationProvider {

    /**
     * annoyingly, the Spring Auth Manager keeps iterating through its list of 
     * Auth Providers if one throws an exception unless it's a subclass of 
     * AccountStatusException. This is really quite crap, it's enough to be returned
     * a null Authentication object to try the next provider.
     */
    public static class BannedIpAddressException extends DisabledException {
        private static final long serialVersionUID = 1L;
        BannedIpAddressException(String msg) {
            super(msg);
        }
    }

    private final BannedIpRepository bannedIpRepository;

    public IpAddressAuthProvider(@Autowired BannedIpRepository bannedIpRepository) {
        this.bannedIpRepository = bannedIpRepository;
        log.info("Instantiating IpAddressAuthProvider");
    }
    @Override
    public boolean supports(Class<?> authentication) {
        boolean retval = UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication) ;
        System.err.println("= = = ==> "+authentication+" - is it supported here? "+retval);
        return retval;
    }

    /**
     * Invokes the IP Block checks
     */
    @Override
    @Transactional(dontRollbackOn = AuthenticationException.class)
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        System.err.println("= = = ==> "+authentication.getClass().getName());
        System.err.println("= = = ==> "+authentication.getDetails());

        if ( authentication.getDetails() instanceof ServletAuthenticationDetails ) {
            ServletAuthenticationDetails token = (ServletAuthenticationDetails)authentication.getDetails();
            String ipAddress = token.getRemoteAddress();
            if ( ipAddress != null ) {
                Optional<BannedIp> banned = bannedIpRepository.findById(ipAddress);
                banned.ifPresentOrElse(
                    (x) -> { 
                        if ( x.getFailedCount() > 5 ) throw new BannedIpAddressException("IP Address "+x.getIp()+" is still banned after at least "+x.getFailedCount()+" attempts");
                    }, 
                    () -> {
                        System.out.println("IP "+ipAddress+" not found in the BannedIp table");
                    }
                );
            } else {
                log.warn("Can't check if the remote client is banned as we don't know their IP address");
            }
        } else {
            log.error("Unhandled authentication type of "+authentication.getDetails().getClass()+" should be reject this login attempt?" );
        }
        return null; // Accept this IP Address as not banned and move on to the next Auth check
    }
    
}