Menu

LDAPConnection.getSchema returns null

2015-08-26
2015-09-01
  • Jason "JRSofty" Reed

    I'm trying to get the schema from the LDAPConnection with the getSchema() and getSchema(DN) methods but in both cases they are returning null.

    The DN I'm passing is the very first DN in the schema according to the browser "c=de" but that doesn't seem to help. Am I using the wrong method to access the schema, or is this only available for certain supported LDAP servers?

     
  • Neil Wilson

    Neil Wilson - 2015-08-26

    The LDAPConnection.getSchema methods work as follows:

    • If you use the getSchema method that doesn't take any arguments, then it will use the zero-length DN, which is the DN for the server root DSE.

    • The LDAP SDK will attempt to retrieve the entry specified by the target DN, requesting the subschemaSubentry attribute. If that entry doesn't exist, or if the connection can't be used to retrieve that entry (e.g., because it's not authenticated as a user with permission to access it), then the method will return null.

    • If it can retrieve the target entry, then the LDAP SDK will take look at the value of the subschemaSubentry operational attribute in that entry. If the entry that was returned doesn't include a subschemaSubentry value (e.g., because the client doesn't have permission to access that attribute), then the method will return null.

    • If there is a subschemaSubentry value, the LDAP SDK will attempt to retrieve the entry whose DN is provided in the value of the subschemaSubentry attribute. If that entry doesn't exist, or if the client can't retrieve it, then the method will return null. Otherwise, it will create and return a Schema object parsed from that entry.

    My guess is that it's probably an access control problem. If your connection isn't authenticated, or if it's authenticated as a user with limited access rights, then you might not have permission to retrieve the root DSE entry (or the entry you're targeting if a specific DN is provided), you might not have permission to access the subschemaSubentry operational attribute in that entry, or you might not have permission to retrieve the schema subentry. If you can try it on a connection that is authenticated as an administrative user and that works, then that pretty much confirms it's an access control issue.

    If it's not an access control problem, then the best way to troubleshoot the issue would be to try to see at which point in the process listed above you aren't able to retrieve the expected entry or attribute.

     
  • Jason "JRSofty" Reed

    Hi Neil,

    As I'm making a simple bind using the administrative account for the directory service I would assume that I should have access to the entire schema. Using the directory service's client application using the same simple bind parameters and account, I'm able to see all parts of the directory service schema, and access all the entries.

    As for where I have problems is immediately after making a connection. My code at this point looks something like this:

    LDAPConnection connection = new LDAPConnection();
    connection.connect(host,port);
    connection.bind(bindDn, password);
    Schema schema = connection.getSchema();
    assert(schema != null); // fails here as the schema is null.
    

    [Edit]
    After contacting the directory service vendor (ISODE) they had me perform a ldapsearch(1) call that seems to fail on a bad filter for objectClass=subschema. I am in contact with them on why this is failing and hopefully that will fix the problem when using the SDK.
    [/Edit]

    [Edit again]
    The problem with the ldapsearch(1) parameters they gave me was a quote problem they used single quotes and it needed double quotes. With the correct quote characters in place it seems I can use the ldapsearch to get the schema. Using these calls:

    ldapsearch -H ldap://ldapserver:19389 -x -s base subschemaSubentry
    

    Which returns a DN cn=subschema-subentry,c=de

    I then use this dn to create this call which works:

    ldapsearch -H ldap://ldapserver:19389 -x -s base -b "cn=subschema-subentry,c=de" "(objectClass=subschema)" objectClasses
    

    Which returns the schema. Isode suggests this is what LDAP SDK should be doing as well. Tomorrow I'll be doing tests to see what the logging difference between LDAP SDK and ldapsearch(1) when trying to get the schema hopefully that will tell me where the problem lies.
    [/Edit again]

     

    Last edit: Jason "JRSofty" Reed 2015-08-31
  • Neil Wilson

    Neil Wilson - 2015-08-31

    I think that I figured out what the problem is, and it is likely a bug in the LDAP SDK.

    RFC 4512 section 4.4 states that the correct way for a client to retrieve the schema information from the server is to first read the subschemaSubentry operational attribute from the entry for which you wish to determine the governing schema, and then perform a base-level search to retrieve the entry whose DN is contained in the subschemaSubentry attribute with a filter of "(objectClass=subschema)". The LDAP SDK was doing all of this correctly, except that it was using a filter of "(objectClass=*)" instead of "(objectClass=subschema)". With most directory servers, this works just fine, but my guess is that the ISODE server (which I believe has its roots as an X.500 server) is more picky about this and only works with the precise filter specified in the standard.

    I have just committed a change in the LDAP SDK that makes it use the correct filter when trying to retrieve the schema entry. If you check out and build the LDAP SDK for yourself then you can see whether it fixes the problem.

    You can also use the following test code to confirm whether that is the problem, or to figure out where the problem does lie along the way:

    LDAPConnection connection = new LDAPConnection();
    connection.connect(host, port);
    connection.bind(bindDn, password);
    
    Entry rootDSEEntry = connection.getEntry("", "subschemaSubentry");
    if (rootDSEEntry == null)
    {
      System.err.println("Unable to retrieve the root DSE");
      return;
    }
    else
    {
      System.out.println("Successfully read the root DSE");
    }
    
    Attribute subschemaSubentryAttribute =
         rootDSEEntry.getAttribute("subschemaSubentry");
    if (subschemaSubentryAttribute == null)
    {
      System.err.println("Root DSE entry did not include the " +
           "subschemaSubentry attribute");
      return;
    }
    
    String schemaEntryDN = subschemaSubentryAttribute.getValue();
    System.out.println("Root DSE entry included a subschemaSubentry " +
         "attribute with value " + schemaEntryDN);
    
    Entry schemaEntry = connection.getEntry(schemaEntryDN, "ldapSyntaxes",
         "attributeTypes", "dITContentRules", "dITStructureRules",
         "matchingRules", "matchingRuleUse", "nameForms", "objectClasses");
    if (schemaEntry == null)
    {
      System.out.println("Unable to retrieve the schema entry with a filter " +
           "of '(objectClass=*)'");
    }
    else
    {
      System.out.println("Successfully retrieved the schema entry with a " +
           "filter of '(objectClass=*)'");
    }
    
    schemaEntry = connection.searchForEntry(schemaEntryDN, SearchScope.BASE,
         Filter.createEqualityFilter("objectClass", "subschema"),
         "ldapSyntaxes", "attributeTypes", "dITContentRules",
         "dITStructureRules", "matchingRules", "matchingRuleUse", "nameForms",
         "objectClasses");
    if (schemaEntry == null)
    {
      System.out.println("Unable to retrieve the schema entry with a filter " +
           "of '(objectClass=subschema)'");
    }
    else
    {
      System.out.println("Successfully retrieved the schema entry with a " +
           "filter of '(objectClass=subschema)'");
    }
    
     
  • Neil Wilson

    Neil Wilson - 2015-08-31

    I should also point out that, if the problem is what I think it is, then you could work around it in existing versions of the LDAP SDK by implementing the logic to retrieve the schema entry for yourself using the above code, and then just using the Schema constructor that takes an entry to create the Schema object. Implementing it yourself is obviously less desirable, but it is an alternative to updating to a version of the SDK that you built for yourself.

     
  • Jason "JRSofty" Reed

    Thanks Niel,

    Yesterday before I left for home I noticed that the LDAP SDK had some low level handling classes in the protocol package, and was thinking if it would be possible to use them to call out the schema, but it looks like you've given me a plausible workaround which to use.

    I'm going to hold off on a check out and self build for now because I'm getting the LDAP SDK via Maven Repository and I'm not sure the best way to handle this as a snapshot. However, if there is also the need for the newly implemented interface mentioned in my other discussion thread, then I can revisit that decision.

     

Log in to post a comment.