I am trying to setup embedded ldap for unit test with Spring Ldap. But I need to use a custom schema for custom objectClasses/attributes definitions. How can I configure it with Spring Ldap test (LdapTestUtils?)
Actually if I run test, it fail saying that my custom objectClass "myOb" is not defined in the schema with the following message :
org.springframework.ldap.UncategorizedLdapException: Failed to populate LDIF; nested exception is javax.naming.directory.NoSuchAttributeException: [LDAP: error code 16 - NO_SUCH_ATTRIBUTE: failed for Add Request :
...
: OID for name 'myOb' was not found within the OID registry]; remaining name 'cn=123456, ou=MyUser, o=company.com'
If I comment objectClass: myOb
from ldif, the test fail with a null value (attribute is not read).
Here is my test class :
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = LdapConfiguration.class, loader = AnnotationConfigContextLoader.class)
public class LdapTest {
// Ldap port
private static final int LDAP_PORT = 18880;
// Base DN for test data
private static final LdapName baseName = LdapUtils.newLdapName("o=company.com");
@Autowired
LdapTemplate ldapTemplate;
@BeforeClass
public static void setupBeforeClass() {
LdapTestUtils.startEmbeddedServer(LDAP_PORT, baseName.toString(), "ldaptest");
// How to load schema definition ?
}
@AfterClass
public static void teardownAfterClass() throws Exception {
LdapTestUtils.shutdownEmbeddedServer();
}
@Before
public void setup() throws Exception {
LdapTestUtils.cleanAndSetup(ldapTemplate.getContextSource(), baseName, new ClassPathResource("ldap/test-users.ldif"));
}
@Test
public void testSearchLdap() throws Exception {
String myObId = ldapTemplate.lookup(LdapNameBuilder.newInstance("ou=MyUser, o=company.com").add("cn", "123456").build(), new AbstractContextMapper<String>() {
@Override
protected String doMapFromContext(DirContextOperations ctx) {
return ctx.getStringAttribute("myObId"); // custom type
}
});
Assert.assertNotNull(myObId); // myObId is null if I comment `objectClass: myOb` !
}
}
and my ldif :
dn: ou=MyUser, o=company.com
ou: User
description: MyUser
objectClass: top
objectClass: organizationalunit
dn: cn=123456, ou=MyUser, o=company.com
objectClass: top
objectClass: person
objectClass: myOb
cn: 123456
sn: 823456
myObId: TEST
I don't know how to do it with Spring Ldap... But I use Unboundid InMemoryDirectoryServer in my unit tests. This implementation of the server doesn't restrict any custom objectClasses/attributes definitions. If you want, I can share my JUnitRule here. The rule starts InMemory server and loads a ldiff into it
UPDATED:
public class LdapServerRule extends ExternalResource {
private static final Log LOG = LogFactory
.getLog(LdapServerRule.class);
public static final String DefaultDn = "cn=Directory Manager";
public static final String DefaultPassword = "password";
private String baseDn;
private String dn;
private String password;
private String lDiffPath;
private InMemoryDirectoryServer server;
private int listenPort;
public LdapServerRule(String baseDn, String lDiffPath) {
this(baseDn, lDiffPath, 0);
}
public LdapServerRule(String baseDn, String lDiffPath, int listenPort) {
this.lDiffPath = lDiffPath;
this.baseDn = baseDn;
this.dn = DefaultDn;
this.password = DefaultPassword;
this.listenPort = listenPort;
}
@Override
protected void before() {
start();
}
@Override
protected void after() {
stop();
}
public int getRunningPort() {
return getServer().getListenPort();
}
private void start() {
InMemoryDirectoryServerConfig config;
try {
LOG.info("LDAP server " + toString() + " starting...");
config = new InMemoryDirectoryServerConfig(getBaseDn());
config.addAdditionalBindCredentials(getDn(),
getPassword());
config.setSchema(null);
config.setListenerConfigs(
InMemoryListenerConfig.createLDAPConfig("LDAP", getListenPort()));
setServer(new InMemoryDirectoryServer(config));
getServer().importFromLDIF(true, getLDiffPath());
getServer().startListening();
LOG.info("LDAP server " + toString() + " started. Listen on port " + getServer().getListenPort());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void stop() {
server.shutDown(true);
LOG.info("LDAP server " + toString() + " stopped");
}
public String getBaseDn() {
return baseDn;
}
public String getDn() {
return dn;
}
public String getPassword() {
return password;
}
public InMemoryDirectoryServer getServer() {
return server;
}
public void setServer(InMemoryDirectoryServer server) {
this.server = server;
}
public String getLDiffPath() {
return lDiffPath;
}
public int getListenPort() {
return listenPort;
}
@Override
public String toString() {
return com.google.common.base.Objects.toStringHelper(this)
.add("baseDn", baseDn)
.add("listenPort", listenPort)
.toString();
}
}
You can use this rule like this
@ClassRule
public static final LdapServerRule LDAP_RULE =
new LdapServerRule("dc=mmkauth", resourceFilePath("data.ldiff"));
LDAP_RULE.getListenPort() returns actual port for connection, or you can pass the port directly into the constructor