MyAuthProvider.java

package org.flasby.security;

import java.security.Principal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.transaction.Transactional;

import org.flasby.entity.Authority;
import org.flasby.entity.ChristmasUser;
import org.flasby.entity.Users;
import org.flasby.entity.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import lombok.extern.log4j.Log4j2;

@Log4j2
// @Configuration
@Component
public class MyAuthProvider implements AuthenticationProvider {
    
    public MyAuthProvider(@Autowired UserRepository<ChristmasUser> users /*, @Autowired Environment env,  @Autowired Config config */ ) {
        // this.env = env;
        this.users = users;
        // this.config = config;
        System.err.println("Instantiating MyAuthProvider with "+users);
    }
    private final UserRepository<ChristmasUser> users;
    // private final Environment env;
    // private final Config config;

    public static interface AuthHandler<T extends Authentication> {
        Authentication handle(T authentication);
    }

    Set<Class<? extends Principal>> handlers = new HashSet<>();
    {
        handlers.add(RememberMeAuthenticationToken.class);
        handlers.add(UsernamePasswordAuthenticationToken.class);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        log.warn(" supports - - - - - - - - " + authentication + "  is OK: " + handlers.contains(authentication));
        return handlers.contains(authentication);
    }

    /**
     * checks the userId and password.
     * In addition it will invoke the IP Block checks
     */
    @Override
    @Transactional(dontRollbackOn = AuthenticationException.class)
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        AbstractAuthenticationToken token = (AbstractAuthenticationToken) authentication;
        log.info("authentication: " + authentication.getPrincipal()); // User Name

        if (authentication instanceof RememberMeAuthenticationToken) {
            RememberMeAuthenticationToken at = (RememberMeAuthenticationToken) authentication;
            log.warn("RememberMeAuthenticationToken");
            // Must be a remember me here so don't build a new user.
            return new UsernamePasswordAuthenticationToken(at.getPrincipal(), 
                    null,
                    // If the principal owned additional stuff like certs or passwords they
                    // can be put here to be made available to other systems. I don't need them
                    // so set to null.
                    at.getAuthorities());
        }
        
        Users<Authority> user = users.findByName((String) authentication.getPrincipal());
        if (user == null) {
            log.info("Can't find " + authentication.getPrincipal() + " in database");
            throw new UsernameNotFoundException("Incorrect credentials");
        } else {
            if (!user.checkPassword((String) authentication.getCredentials())) {
                log.info("Incorrect password for " + authentication.getPrincipal()
                        + " - " + user.getPassword()
                        + " - Passwords are now BCrypt encoded in the database: " + user.getFailedLoginAttempts());
                if (user.isDisabled()) {
                    log.warn("User {} presented an incorrect password but is additionally disabled - Login refused", () -> user.getName());
                    throw new DisabledException("User is disabled");
                }
                throw new UsernameNotFoundException("Incorrect credentials");
            } else if (user.isDisabled()) {
                log.warn("User {} is correctly authenticated but disabled - Login refused", () -> user.getName());
                throw new DisabledException("User is disabled");
            }
        }
        // Grrr, it seems that the Authority has to be ROLE_USER so that
        // .hasRole("USER") evaluates to true.
        // A bit crap in my view.
        // Compare with the roles used in SocketSecurityConfig

        List<GrantedAuthority> auths = new ArrayList<>();
        user.getRoles().forEach((a) -> auths.add(new SimpleGrantedAuthority(a.getAuthority())));

        // Dispose of any passwords lying around
        token.eraseCredentials();

        log.info("Initial Login all good: " + auths);
        return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), 
                null,
                // If the principal owned additional stuff like certs or passwords they
                // can be put here to be made available to other systems. I don't need them
                // so set to null.
                auths);
    }
}