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