pingidentity / ldapsdk

UnboundID LDAP SDK for Java
Other
327 stars 79 forks source link

Get the entry's name without the base DN #139

Closed edu-de closed 1 year ago

edu-de commented 1 year ago

Hi,

we finished migration from LDAP JNDI to LdapSDK and it works like a charm.

I have one question regarding migration, maybe you know a better way to achieve this as the workaround solution we are using?

We need the name of a LDAP SDK entry excluding the base DN. For this purpose, we had to convert it to a JNDI search result and get the name with jndiSearchResult.getName(), which gets the needed information.

So we have this:

Entry asJndiSearchResult = JNDIConverter.convertSearchEntry(searchResultEntry, "myBaseDn"); String name = asJndiSearchResult.getName();

while this works, it seems only to be a workaround and it feels wrong. Any ideas ?

dirmgr commented 1 year ago

Are you asking about how to get the RDN for an entry DN?

For example, if you have "uid=jdoe,ou=People,dc=example,dc=com", are you trying to get just "uid=jdoe"? If that's the case, then you would use something like:

 String dnStr = "uid=jdoe,ou=People,dc=example,dc=com";
 DN dn = new DN(dnStr);
 RDN rdn = dn.getRDN();
 String rdnStr = rdn.toString();

Note that creating a DN object will fail if the provided string doesn't represent a valid LDAP DN, and that the DN.getRDN() method will return null if it's the zero-length DN (i.e., the DN that represents the server's root DSE), so RDN.toString would throw a NullPointerException in that case.

And if you're just trying to get the "jdoe" portion, then you could get that from the RDN.getAttributeValues() or RDN.getNameValuePairs() method. Note that it's possible for an RDN to have multiple components (e.g., "givenName=John+sn=Doe"), so those methods account for that.

edu-de commented 1 year ago

Hi @dirmgr

I made some test outputs but the RDN approach does not show the expected results. My base DN is: ou=developers,dc=ifwi,dc=infofabrik,dc=de

My DN is: CN=Douglas,OU=Users,OU=developers,DC=IFWI,DC=Infofabrik,DC=de so I need everything before the base DN: CN=Douglas,OU=Users but with the RDN approach I get: CN=Douglas while with the JNDI converter I get the correct result: CN=Douglas,OU=Users

I think the JNDI converter is doing a similar thing for converting, as the asJndiSearchResult.getName() is returning the correct string, but I have not found any method or similar for getting this directly from a SearchResultEntry without having to convert to JNDI first

edu-de commented 1 year ago

Hi @dirmgr

with some string manipulation, this seems also to be doable, but maybe there is something better (analogous to JNDI.getName() ) in the LdapSDK?

My groovy string manipulation (testable here):

def dn = 'CN=Douglas,OU=Users,OU=developers,DC=IFWI,DC=Infofabrik,DC=de'
def base = 'ou=developers,dc=ifwi,dc=infofabrik,dc=de'

def approach3 = (dn.toLowerCase(Locale.ROOT) - base.toLowerCase(Locale.ROOT)).trim()
if (approach3.endsWith(','))
  approach3 = approach3[0..approach3.size()-2]

println approach3

Output: cn=douglas,ou=users

But, again, this seems not the best way to do this

dirmgr commented 1 year ago

Sorry. I didn't realize you wanted to be able to retrieve the portion of a DN that is relative to an arbitrary base DN and not necessarily its immediate parent. This is also a pretty straightforward thing to accomplish. You just need to use the getRDNs method for both DNs and then create a new DN with the RDNs that are in the full DN that aren't in the base DN.

I went ahead and committed a change that adds a DN.getDNRelativeToBaseDN method that does this. It'll be included in the next release of the LDAP SDK. There's currently no specific time frame for that release, but it'll probably be at least a couple of months. In the meantime, you can either check out and build the LDAP SDK for yourself, or you can use the code below as a model for how to do it in your own code.

  /**
   * Retrieves a string that represents the portion of the provided full DN that
   * is relative to the given base DN (that is, the full DN with the base DN
   * stripped off).  For example, if the provided full DN is
   * "uid=jdoe,ou=People,dc=example,dc=com" and the base DN is
   * "dc=example,dc=com", then the returned DN will be "uid=jdoe,ou=People".
   *
   * @param  fullDN  The full DN for which to obtain the portion relative to the
   *                 base DN.  It must not be {@code null}, and it must
   *                 represent a valid DN that is a descendant of or equal to
   *                 the base DN.
   * @param  baseDN  The base DN to strip off of the provided full DN.  It must
   *                 not be {@code null}, and it must be an ancestor of or equal
   *                 to the full DN.
   *
   * @return  A string representation of the DN that represents the portion of
   *          the full DN that is relative to the base DN, an empty string if
   *          the full DN is equal to the base DN, or the provided full DN if
   *          the base DN represents the null DN.
   *
   * @throws  LDAPException  If either of the provided strings is not a valid
   *                         DN, or if the provided full DN is not an ancestor
   *                         of or equal to the given base DN.
   */
  @NotNull()
  public static String getDNRelativeToBaseDN(@NotNull final String fullDN,
                                             @NotNull final String baseDN)
         throws LDAPException
  {
    final DN parsedFullDN = new DN(fullDN);
    final DN parsedBaseDN = new DN(baseDN);
    return getDNRelativeToBaseDN(parsedFullDN, parsedBaseDN).toString();
  }

  /**
   * Retrieves a portion of the provided full DN that is relative to the given
   * base DN (that is, the full DN with the base DN stripped off).  For example,
   * if the provided full DN is "uid=jdoe,ou=People,dc=example,dc=com" and the
   * base DN is "dc=example,dc=com", then the returned DN will be
   * "uid=jdoe,ou=People".
   *
   * @param  fullDN  The full DN for which to obtain the portion relative to the
   *                 base DN.  It must not be {@code null}, and it must be a
   *                 descendant of or equal to the base DN.
   * @param  baseDN  The base DN to strip off of the provided full DN.  It must
   *                 not be {@code null}, and it must be an ancestor of or equal
   *                 to the full DN.
   *
   * @return  A DN that represents the portion of the full DN that is relative
   *          to the base DN, {@link #NULL_DN} if the full DN is equal to the
   *          base DN, or the provided full DN if the base DN is the null DN.
   *
   * @throws  LDAPException  If the provided full DN is not an ancestor of or
   *                         equal to the given base DN.
   */
  @NotNull()
  public static DN getDNRelativeToBaseDN(@NotNull final DN fullDN,
                                         @NotNull final DN baseDN)
         throws LDAPException
  {
    if (baseDN.isNullDN())
    {
      return fullDN;
    }

    if (! fullDN.isDescendantOf(baseDN, true))
    {
      throw new LDAPException(ResultCode.PARAM_ERROR,
           ERR_DN_FULL_DN_NOT_DESCENDANT_OF_BASE.get(String.valueOf(fullDN),
                String.valueOf(baseDN)));
    }

    final RDN[] fullRDNs = fullDN.getRDNs();
    final RDN[] baseRDNs = baseDN.getRDNs();
    if (fullRDNs.length == baseRDNs.length)
    {
      return NULL_DN;
    }

    final RDN[] relativeRDNs = new RDN[fullRDNs.length - baseRDNs.length];
    System.arraycopy(fullRDNs, 0, relativeRDNs, 0, relativeRDNs.length);
    return new DN(relativeRDNs);
  }
edu-de commented 1 year ago

Hi @dirmgr

thank you very much, I will implement this in our code and replace with the method call when the next LDAP SDK version is available.

Best regards, Eduardo