package alien4cloud.security.spring; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import javax.annotation.PostConstruct; import javax.annotation.Resource; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.security.authentication.AnonymousAuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCrypt; import alien4cloud.security.AuthorizationUtil; import alien4cloud.security.model.User; import alien4cloud.security.users.IAlienUserDao; import lombok.extern.slf4j.Slf4j; @Slf4j public class Alien4CloudAuthenticationProvider implements AuthenticationProvider { @Resource private IAlienUserDao alienUserDao; protected AuthenticationProvider wrappedProvider = null; @Resource private ListableBeanFactory beanFactory; /** * Configure the bean from the list of {@link AuthenticationProvider} defined in the spring context. */ @PostConstruct public void configure() { Map<String, AuthenticationProvider> providers = beanFactory.getBeansOfType(AuthenticationProvider.class); for (Entry<String, AuthenticationProvider> provider : providers.entrySet()) { if (provider.getValue() != this && !(provider.getValue() instanceof AnonymousAuthenticationProvider)) { wrappedProvider = provider.getValue(); } } } @Override public Authentication authenticate(Authentication authentication) { String login = authentication.getName(); String password = authentication.getCredentials().toString(); // get user from internal store User user; if (login == null || login.isEmpty()) { user = null; } else { user = alienUserDao.find(login); } if (user == null) { return authenticateNewUser(authentication, password); } if (user.isInternalDirectory()) { return internalAuthentication(user, password); } else if (wrappedProvider == null) { log.error("The user <" + login + "> is not internal but no wrapped provider has been defined. Unable to authenticate."); return null; } return delegateAuthenticate(authentication, user, password); } private Authentication internalAuthentication(User user, String password) { if (BCrypt.checkpw(password, user.getPassword())) { return AuthorizationUtil.createAuthenticationToken(user, password); } else { log.debug("Wrong password for user <" + user.getUsername() + ">"); throw new BadCredentialsException("Incorrect password for user <" + user.getUsername() + ">"); } } private Authentication delegateAuthenticate(Authentication authentication, User user, String password) { Authentication auth = wrappedProvider.authenticate(authentication); // refresh the user in case the wrapped provider changed some roles User updatedUser = alienUserDao.find(user.getUsername()); if (auth.isAuthenticated()) { return AuthorizationUtil.createAuthenticationToken(updatedUser, password); } else { return auth; } } private Authentication authenticateNewUser(Authentication authentication, String password) { if (wrappedProvider == null) { throw new UsernameNotFoundException("Cannot find user <" + authentication.getName() + ">"); } else { Authentication providerAuth = wrappedProvider.authenticate(authentication); // create a user in local store User user; if (providerAuth.getPrincipal() != null && providerAuth.getPrincipal() instanceof User) { user = (User) providerAuth.getPrincipal(); } else { user = new User(); } user.setUsername(providerAuth.getName()); userFromPrincipal(user, providerAuth.getPrincipal()); setUserAuthorities(user, providerAuth.getAuthorities()); alienUserDao.save(user); return AuthorizationUtil.createAuthenticationToken(user, password); } } /** * Set the list of roles from the authorities collection. * * @param user The user to populate. * @param authorities The collection of authorities. */ private void setUserAuthorities(User user, Collection<? extends GrantedAuthority> authorities) { if (authorities != null && authorities.size() > 0) { String[] roles = new String[authorities.size()]; int i = 0; for (GrantedAuthority authority : authorities) { roles[i] = authority.getAuthority(); i++; } user.setRoles(roles); } } /** * Initialize user's fields from the principal object if it is an instance of {@link UserDetails}. * * @param user The user to populate. * @param principal The principal object from {@link Authentication}. */ private void userFromPrincipal(User user, Object principal) { if (principal instanceof UserDetails) { UserDetails userDetails = (UserDetails) principal; user.setAccountNonExpired(userDetails.isAccountNonExpired()); user.setAccountNonLocked(userDetails.isAccountNonLocked()); user.setCredentialsNonExpired(userDetails.isCredentialsNonExpired()); user.setEnabled(userDetails.isEnabled()); } else { log.info("Principal from configured provider is not a UserDetails, do not populate user."); } } public void setWrappedProvider(AuthenticationProvider wrappedProvider) { this.wrappedProvider = wrappedProvider; } public void setAlienUserDao(IAlienUserDao alienUserDao) { this.alienUserDao = alienUserDao; } @Override public boolean supports(Class<?> authenticationClass) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authenticationClass); } }