package org.synyx.urlaubsverwaltung.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.ldap.core.AttributesMapper; import org.springframework.ldap.core.DirContextOperations; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; /** * Maps LDAP attributes to {@link LdapUser} class. * * @author Aljona Murygina - murygina@synyx.de */ @Component @ConditionalOnExpression("'${auth}'=='activeDirectory' or '${auth}'=='ldap'") public class LdapUserMapper implements AttributesMapper<LdapUser> { private static final String MEMBER_OF_ATTRIBUTE = "memberOf"; private final String identifierAttribute; private final String firstNameAttribute; private final String lastNameAttribute; private final String mailAddressAttribute; private final String memberOfFilter; @Autowired public LdapUserMapper(@Value("${uv.security.identifier}") String identifierAttribute, @Value("${uv.security.firstName}") String firstNameAttribute, @Value("${uv.security.lastName}") String lastNameAttribute, @Value("${uv.security.mailAddress}") String mailAddressAttribute, @Value("${uv.security.filter.memberOf}") String memberOfFilter) { this.identifierAttribute = identifierAttribute; this.firstNameAttribute = firstNameAttribute; this.lastNameAttribute = lastNameAttribute; this.mailAddressAttribute = mailAddressAttribute; this.memberOfFilter = memberOfFilter; } @Override public LdapUser mapFromAttributes(Attributes attributes) throws NamingException { Optional<Attribute> userNameAttribute = Optional.ofNullable(attributes.get(identifierAttribute)); if (!userNameAttribute.isPresent()) { throw new InvalidSecurityConfigurationException("User identifier is configured incorrectly"); } String username = (String) userNameAttribute.get().get(); Optional<String> firstName = getAttributeValue(attributes, firstNameAttribute); Optional<String> lastName = getAttributeValue(attributes, lastNameAttribute); Optional<String> email = getAttributeValue(attributes, mailAddressAttribute); List<String> groups = new ArrayList<>(); Optional<Attribute> memberOfAttribute = Optional.ofNullable(attributes.get(MEMBER_OF_ATTRIBUTE)); if (memberOfAttribute.isPresent()) { NamingEnumeration<?> groupNames = memberOfAttribute.get().getAll(); while (groupNames.hasMoreElements()) { groups.add((String) groupNames.nextElement()); } } return new LdapUser(username, firstName, lastName, email, groups.stream().toArray(String[]::new)); } private Optional<String> getAttributeValue(Attributes attributes, String attributeName) throws NamingException { Optional<Attribute> attribute = Optional.ofNullable(attributes.get(attributeName)); Optional<String> attributeValue = Optional.empty(); if (attribute.isPresent()) { attributeValue = Optional.ofNullable((String) attribute.get().get()); } return attributeValue; } public LdapUser mapFromContext(DirContextOperations ctx) throws NamingException, UnsupportedMemberAffiliationException { Optional.ofNullable(ctx.getStringAttribute(identifierAttribute)).orElseThrow(() -> new InvalidSecurityConfigurationException( "Can not get a username using '" + identifierAttribute + "' attribute to identify the user.")); String username = ctx.getStringAttribute(identifierAttribute); Optional<String> firstName = Optional.ofNullable(ctx.getStringAttribute(firstNameAttribute)); Optional<String> lastName = Optional.ofNullable(ctx.getStringAttribute(lastNameAttribute)); Optional<String> email = Optional.ofNullable(ctx.getStringAttribute(mailAddressAttribute)); if (StringUtils.hasText(memberOfFilter)) { String[] memberOf = ctx.getStringAttributes(MEMBER_OF_ATTRIBUTE); if (!Arrays.asList(memberOf).contains(memberOfFilter)) { throw new UnsupportedMemberAffiliationException("User '" + username + "' is not a member of '" + memberOfFilter + "'"); } return new LdapUser(username, firstName, lastName, email, memberOf); } return new LdapUser(username, firstName, lastName, email); } }