Menu

Help creating a subcontext

Mar
2014-05-14
2014-09-08
  • 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

       
  • Mar

    Mar - 2014-09-08

    Update: calls were occasionally being made to determine the ldap query string utilizing javax.naming.directory.SearchResult.getNameInNamespace(). When this method was called the baseDn was present twice.

    To get the subcontext from the Spring context a baseDn needed to be defined in the Spring configuration file. Defining the baseDn in the Spring config, caused ", o=companyName.com" aka the baseDn to be appended to the strDn used to search the ldap server. The LDAPConstants strDn values were fixed to not include the baseDn - since it would be appended to the string behind the scenes. However portions of the code do not use LDAPCConstants, rather they use this method to get the full ldap search path...and then the baseDn is appended again causing an error in certain methods. An example exception message:
    06/13/2014 16:12:26 [DEBUG] org.springframework.ldap.core.support.AbstractContextSource
    Got Ldap context on server 'ldap://localhost:port123/o=companyName.com'
    javax.naming.NameNotFoundException:
    [LDAP: error code 32 - Unable to modify entry
    'uid=someName,ou=NonEmployees,ou=People,o=companyName.com,o=companyName.com'
    because it does not exist in the server.]
    ;
    remaining name 'uid=someName,ou=NonEmployees,ou=People,o=companyName.com'

    I just created a local service method for a quick fix.

    / kludge /
    public String getNameInNamespace(SearchResult userResult{
    String fullName = userResult.getNameInNamespace();
    int endIndex = fullName.lastIndexOf(",");
    String newValue = fullName.substring(0, endIndex);
    return newValue;
    }

     

Log in to post a comment.