0
votes

I'm creating an extension to a user management application that maintains users in an OpenLDAP server for authentication (in addition to an internal database) using the Spring Data Ldap repositories as the application is already using Spring 5 and Spring Data repositories.

The basic User and Group repositories work for finding, creating and updating, with one exception: group membership, because the member attribute requires a full DN (distinguished name) and unfortunately the user repository's @Id key is a relative DN.

My Ldap base is              "dc=example,dc=com"
The UserRepository returns   "cn=user1,ou=users"
but I need the full DN:      "cn=user1,ou=users,dc=example,dc=com"

The base name is loaded in a ContextSource from ApplicationConfig.java, but since the repositories are generated dynamically and I have defined only an interface, I don't know how to inject that or the property and write code using it.

I have not found any method that returns the full DN. I have found what looks like a solution through this question and the user admin example from Spring LDAP. However this involves falling back on the Spring LDAP LdapTemplate, which is a step back from the Spring Data LDAP repositories to a more generic query model. Also, I can't get the example to build or run by itself due to dependency conflicts, so I'm worried about the longevity of a solution that mixes Spring Data LDAP and Spring Ldap Templates.

What I'm looking for is any way to retrieve the full DN or the base name from within the User, Group or their repository classes, so that I can supply these when adding members to a group or looking for groups for a member.

1

1 Answers

1
votes

After some trials and many errors, I've found a decently clean way to obtain the Ldap base path, injecting the ContextSource as defined in ApplicationConfig also in the Service class. For anyone else looking for this:

ApplicationConfig.java

@Configuration
@PropertySource("classpath:ldap.properties")

...

@Value("${ldap.url}") String ldapUrl;
@Value("${ldap.base}") String ldapBase;
@Value("${ldap.user}") String ldapUser;
@Value("${ldap.password}") String ldapPassword;

@Bean
public LdapContextSource contextSource() {
    LdapContextSource contextSource = new LdapContextSource();
    contextSource.setUrl(ldapUrl);
    contextSource.setBase(ldapBase);
    contextSource.setUserDn(ldapUser);
    contextSource.setPassword(ldapPassword);
    return contextSource;
}

@Bean
LdapTemplate ldapTemplate(ContextSource contextSource) {
    return new LdapTemplate(contextSource);
}

MyServiceImpl.java

@Repository
@Transactional
public class MyServiceImpl implements MyService {

    @Autowired
    private LdapUserRepository userRepository;

    @Autowired
    private LdapGroupRepository groupRepository;

    @Autowired
    LdapContextSource contextSource;

    ...

    @Override
    public Collection<LdapGroup> findGroupByMember(Name name) {
        LdapName dName = LdapUtils.prepend(LdapUtils.removeFirst(name,
            contextSource.getBaseLdapName()), contextSource.getBaseLdapName());

        return groupRepository.findByMember(dName);
    }
}

With the help of LdapUtils methods to remove and prepend the base path, I can clean up any incoming name to the full DN. This keeps the LdapGroupRepository simple, with only an interface class and the rest done by Spring Data LDAP:

LdapGroupRepository.java

public interface LdapGroupRepository extends LdapRepository<LdapGroup> {

    Optional<LdapGroup> findByName(String groupName);

    @Query("(member={0})")
    Collection<LdapGroup> findByMember(Name member);
}