Help creating a subcontext

Mar
2014-05-14
2014-05-15
  • Mar
    Mar
    2014-05-14

    I am writing test cases and in the actual service code during the add operation a subcontext is created from the context to verify the newly created user.

    I create a context
    contextSource.getReadWriteContext();

    I have Spring setting the contextSources and then actual code retrieves the context from the contextSource.

    But

    When the code attempts to create a subContext an exception is thrown:

    [LDAP: error code 32 - Unable to add entry 'uid=123456' because its parent entry 'null' does not exist in the server.]

    In the debugger I see the context is defaultContext.

    How can I get the subcontext? Can I not get a subcontext with a default context? I am running in memory - does that have something to do with it? Maybe the new user was not added or recognized as such inmemory?

    Some code:

    private ContextSource contextSource;
    public void setContextSource(ContextSource ctxSrc){
        this.contextSource = ctxSrc;
    }
    
    ...
    DirContext ctx =contextSource.getReadWriteContext();
    ...
    DirContext ctxNew = null;
    
    try {
        ctxNew = ctx.createSubcontext(strDN, attrs);
    }
    catch ( NamingException e ) {
        log.error("ldapCreate: failure creating subcontext: " + strDN + "\n" + e);
    }
    ...
    

    my service config xml file:

    <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
        <property name="urls" value="${ldap.connect.url}"/>
        <property name="userDn" value="${ldap.connect.user}"/>  
        <property name="password" value="${ldap.connect.password}"/>
    </bean>
    
    <bean id="myDao" class="com.company.module.dao.myDaoImpl">
       <property name="contextSource" ref="${beans.session.context}"/>
    </bean>
    

    The setup of unbounded:

    @BeforeClass
    public static void setup() {
        try{
        loadProperties();
    
        LOG.info("inMemoryDB = " + inMemoryDB);
        LOG.info("inMemoryLDAP = " + inMemoryLDAP);
    
        createDatabases();
        createLdapServers();
        createService();
        }
        catch(Exception e){
            LOG.error("Exception occurred during test setup: ", e);
        }
    }
    
    private static ContextSource cs;    
    protected static InMemoryDirectoryServer inMemoryDirSvr;
    
    createLdapServers():
        LDAPServer ldapServer = new LDAPServer(
            inMemoryServer, 
            inMemoryLdapPort, 
            ldapUserDn, 
            ldapPwd,
            inMemoryListenerName, 
            dataFile, 
            schemaFile, 
            strSearchDN);
       cs= createContextSource(ldapServer);//class variable
       inMemoryDirSvr = embedUnboundId(ldapServer);
    
    private static ContextSource createContextSource(LDAPServer ldapServer) throws Exception{
        LdapContextSource cs = new LdapContextSource();
        cs.setUrl(ldapServer.getLongUrl());
        cs.setUserDn(ldapServer.getUserDn());
        cs.setPassword(ldapServer.getLdapPwd());
    
        /**
         * afterPropertiesSet():
         * Checks that all necessary data is set and that there is no compatibility
         * issues, after which the instance is initialized. Note that you need to
         * call this method explicitly after setting all desired properties if using
         * the class outside of a Spring Context.
         */
        cs.afterPropertiesSet();
        return cs;
    }
    
    private static InMemoryDirectoryServer embedUnboundId(LDAPServer ldapServer)throws Exception{
        InMemoryDirectoryServerConfig config = configureInMemLdapServer(ldapServer);
        // Create the directory server instance, populate it with data from the
        // "test-data.ldif" file, and start listening for client connections.
        InMemoryDirectoryServer dirSvr;
        dirSvr = new InMemoryDirectoryServer(config);
        LOG.info("schema: "+ dirSvr.getSchema().toString());
        URL fileUrl = BaseTestSetup.class.getResource(ldapServer.getDataFileName());
        String path = fileUrl.getPath();
        int status = dirSvr.importFromLDIF(true, path);
    
        dirSvr.startListening();
    
        return dirSvr;
    }
    
    private static InMemoryDirectoryServerConfig configureInMemLdapServer(LDAPServer ldapServer)throws Exception{
       // Create the configuration to use for the server.
        InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(
                "dc=company,dc=com", "o=company.com");
        config.addAdditionalBindCredentials(ldapServer.getUserDn(), ldapServer.getLdapPwd());
    
        URL fileUrl = BaseTestSetup.class.getResource(ldapServer.getSchemaFileName());
        File file = new File(fileUrl.toURI());
        Schema schema = Schema.getSchema(file);
        LOG.info("schema : "+ schema .toString());
        config.setSchema(schema);
    
        InMemoryListenerConfig listenerConfig = new InMemoryListenerConfig(ldapServer.getListenerName(), null, ldapServer.getPort(), null, null, null);
        config.setListenerConfigs(listenerConfig);
    
        /*
        https://sourceforge.net/p/ldap-sdk/discussion/1001257/thread/0c3a2bec/
    
        The actual Directory Server is not LDAPv3-compliant.
        The setEnforceSingleStructuralObjectClass and methods setEnforceAttributeSyntaxCompliance methods
        can be used to allow the in-memory directory server to behave more like some of the less-compliant servers.
    
        Without this the data will fail while loading and validating against the schema:
        ...
        */
    
        config.setEnforceSingleStructuralObjectClass(false);
        config.setEnforceAttributeSyntaxCompliance(false);
        /*
            setGenerateOperationalAttributes to false else on modify got error:
                LDAP: error code 65 - Unable to modify...would have violated the provided schema:
        */
        config.setGenerateOperationalAttributes(false);
    
        return config;
    }
    

    Any ideas why I get an error creating the subcontext?

    Thanks

     
    • Bertold Kolics
      Bertold Kolics
      2014-05-14

      It looks like that you are missing a base DN when trying to create sub context. Shouldn't you provide a base in the Spring configuration?

      E.g.
      <property name="base" value="dc=company,dc=com" />

       
  • Pali Haneul
    Pali Haneul
    2014-05-14

    I was thinking the same direction, but then I saw:

    URL fileUrl = BaseTestSetup.class.getResource(ldapServer.getDataFileName());
    String path = fileUrl.getPath();
    int status = dirSvr.importFromLDIF(true, path);
    

    So it looks like a rootDn is being loaded into the server.

    What are the actual values for 'ldapUserDn' and 'strSearchDN'? Is ldapUserDn a full bind (eg uid=username,dc=company,cd=com). Are you maybe mixing baseDn with strSearchDn?

     
    Last edit: Pali Haneul 2014-05-14
  • Pali Haneul
    Pali Haneul
    2014-05-14

    I'm also a bit thrown off by this:

    config.setEnforceSingleStructuralObjectClass(false);
    config.setEnforceAttributeSyntaxCompliance(false);
    config.setGenerateOperationalAttributes(false);
    

    Earlier today I also started InMemoryDirectoryServer from scratch but I haven't touched any of those settings.

     
  • Bertold Kolics
    Bertold Kolics
    2014-05-14

    I think that the InMemoryDirectoryServer has all the entries, but I don't think that the

    DirContext ctx =contextSource.getReadWriteContext()
    

    creates a context with the proper base DN.

     
  • Mar
    Mar
    2014-05-14

    Thanks for looking into it.

    Spring configuration:
    I used the minimal settings where the same file could be used in production against live ldap servers as well as testing utilizing UnboundID

    The baseDN I believe I am setting here:

    InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(/ldapServer.getLogOnBaseDNs()/ "dc=companyName,dc=com", "o=companyName.com");

    Then sign on credentials here:

    config.addAdditionalBindCredentials(ldapServer.getUserDn(), ldapServer.getLdapPwd());

    ldapUserDn is:
    cn=pgmuser_globalsearch,ou=appid,o=company.com

    strSearchDn is:
    "ou=NonEmployees, ou=People, o=company.com"

    The following were added because I was importing an LDAP data file from an LDAP server and it appears to not follow the expected standards...so I have to set the flags to make unboundId more lenient:

    config.setEnforceSingleStructuralObjectClass(false);
    config.setEnforceAttributeSyntaxCompliance(false);

    On Modify had to add this - also because of the server data structure not being compliant:
    config.setGenerateOperationalAttributes(false);

    If DirContext ctx =contextSource.getReadWriteContext() does not create a context with a proper base dn - what do I need to do / what am I doing wrong? The current tests have been doing search/lookup/etc but I have not attempted getting a subcontext before... used to verify user just added works.

    ctxNew = ctx.createSubcontext(strDN, attrs);

    strDn="uid=123abc"

    ctx in the debugger has:

    myProps (Hashtable size 7)
    gotDefault=true
    defaultInitCtx with many values some that may help:
    currentDn=""
    currentParsedDn=""
    parentIsLdapCtx=false
    _contextType=2
    bindCtls=null
    hasLdapsScheme=false

    So it looks like maybe the context does not have the proper baseDn....
    Any ideas why/how to fix?

     
  • Mar
    Mar
    2014-05-14

    Ok it looks like the baseDn is set on the inmemory server but not on the contextsource.

    So it looks like I need to probably do contextSource.setBase("dc=companyName,dc=com,o=companyName.com") or some other value?...

    However once I put any value in setBase other areas start throwing exceptions like:

    javax.naming.NameNotFoundException: [LDAP: error code 32 -

    Unable to perform the search because base entry 'ou=NonEmployees, ou=People, o=companyName.com,dc=companyName,dc=com,o=companyName.com'
    does not exist in the server.];
    remaining name 'ou=NonEmployees, ou=People, o=companyName.com'

     
    • Bertold Kolics
      Bertold Kolics
      2014-05-15

      I am guessing that your base should be either dc=companyName,dc=com or o=companyName.com but not both.

      Since you confirmed that the base DN is properly set in the in-memory directory server, I would check out the Spring LDAP samples at https://github.com/spring-projects/spring-ldap/tree/master/samples