content: 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);
      }
  }
  