content: package alien4cloud.security.spring.ldap;
  
  import java.util.List;
  import java.util.Map;
  
  import javax.annotation.PostConstruct;
  import javax.annotation.Resource;
  
  import lombok.extern.slf4j.Slf4j;
  
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.context.annotation.Conditional;
  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.stereotype.Component;
  
  import alien4cloud.security.users.IAlienUserDao;
  import alien4cloud.security.model.Role;
  import alien4cloud.security.model.User;
  
  import com.google.common.collect.Lists;
  import com.google.common.collect.Maps;
  
  /**
   * Provider responsible to authenticate agains LDAP.
   * Note: we don't use the native Spring security (3.x) provider it is not compatible with spring ldap 2.x
   */
  @Slf4j
  @Conditional(LdapCondition.class)
  @Component("ldap-provider")
  public class LdapAuthenticationProvider implements AuthenticationProvider {
  
      @Resource
      private LdapUserDao ldapUserDao;
  
      @Resource
      private IAlienUserDao alienUserDao;
  
      @Value("${ldap.mapping.roles.defaults}")
      private String[] defaultRoles;
      @Value("${ldap.mapping.roles.mapping:}")
      private String[] roleMappings;
      private Map<String, String> parsedRoleMappings;
  
      @PostConstruct
      public void importLdapUsers() {
          // parse role mappings
          for (String roleMapping : roleMappings) {
              String[] mapping = roleMapping.split("=");
              if (mapping.length != 2) {
                  throw new IllegalArgumentException(
                          "Check your alien configuration, every entry in ldap.roles.mapping must be matching the <LDAP_ROLE>=<ALIEN_ROLE> expression");
              }
              // check that the alien role is indeed an alien role.
              Role.valueOf(mapping[1]);
              if (parsedRoleMappings == null) {
                  parsedRoleMappings = Maps.newHashMap();
              }
              parsedRoleMappings.put(mapping[0], mapping[1]);
          }
  
          if (ldapUserDao.getLdapTemplate().getContextSource() != null) {
              List<User> users = ldapUserDao.getUsers();
              checkRoles();
              for (User user : users) {
                  // refresh roles based on ldap.
                  User alienUser = alienUserDao.find(user.getUsername());
                  if (alienUser == null) {
                      // eventually update if a mapping exists for this user.
                      mapLdapRoles(user, user);
                      if(user.getRoles() == null || user.getRoles().length == 0) {
                          // initialize the user with default roles.
                          user.setRoles(defaultRoles);
                      }
                      alienUserDao.save(user);
                  } else {
                      mapLdapRoles(user, alienUser);
                      alienUserDao.save(alienUser);
                  }
              }
          }
      }
  
      @Override
      public Authentication authenticate(Authentication authentication) {
          String login = authentication.getName();
          String password = authentication.getCredentials().toString();
  
          if (ldapUserDao.authenticate(login, password)) {
              List<? extends GrantedAuthority> emptyList = Lists.newArrayList();
              Authentication auth = new UsernamePasswordAuthenticationToken(login, password, emptyList);
              updateLdapUserRoles(login, auth);
              return auth;
          } else {
              log.debug("Wrong password for user <" + login + ">");
              throw new BadCredentialsException("Incorrect password for user <" + login + ">");
          }
      }
  
      private void updateLdapUserRoles(String login, Authentication auth) {
          if (auth.isAuthenticated() && parsedRoleMappings != null) {
              // refresh roles if loaded from mapping
              User ldapUser = ldapUserDao.getById(login);
              User user = alienUserDao.find(login);
  
              if (ldapUser != null) {
                  mapLdapRoles(ldapUser, user);
              }
              alienUserDao.save(user);
          }
      }
  
      private void mapLdapRoles(User ldapUser, User user) {
          if(ldapUser.getRoles() == null || parsedRoleMappings == null) {
              return;
          }
  
          List<String> userRoles = Lists.newArrayList();
          for (String role : ldapUser.getRoles()) {
              String alienRole = parsedRoleMappings.get(role);
              if (alienRole != null) {
                  userRoles.add(alienRole);
              }
          }
          user.setRoles(userRoles.toArray(new String[userRoles.size()]));
  
      }
  
      @Override
      public boolean supports(Class<?> authentication) {
          return authentication.equals(UsernamePasswordAuthenticationToken.class);
      }
  
      private void checkRoles() {
          for (String role : defaultRoles) {
              // should throw an exception and fail if the role doesn't exists.
              Role.valueOf(role);
          }
      }
  }
  