cancel
Showing results for 
Search instead for 
Did you mean: 

In Memory Ldap Store Question

Paul
New Member
0 Kudos

In Memory Ldap Store Question

Is it possible to configure the unique attribute plug-in for the In Memory LDAP Store through an ldif or java and not from a command line? I've been looking for a few days and can't seem to find a way. Thanks!

6 REPLIES
UnboundID PhilipP
UnboundID
0 Kudos

Re: In Memory Ldap Store Question

Paul,

     there are three supported ways of managing/changing configuration (and setting up the uniquness plugin is configuration):

 

1) Via the dsconfig (command-line) utility.

2) Via the HTTP console (web interface).

3) Via the config API (REST interface), only available in later releases (5.x onwards).

 

It is possible to directly manipulate the configuration via cn=config (given appropriate access permission and privileges). However, that is totally unsupported and there is no guarantee that things under cn=config will not change without notice in future releases.

UnboundID NeilW
UnboundID
0 Kudos

Re: In Memory Ldap Store Question

 

Are you talking about the in-memory directory server that comes with the LDAP SDK? If so, then it doesn't have any out-of-the-box feature that requires values of a specified attribute to be unique. You could certainly write implement this for yourself using an InMemoryOperationInterceptor, and if you did then you could, of course, configure it programmatically. For example, here's a completely untested interceptor that I just threw together that might work:

 

package com.unboundid.example;



import com.unboundid.ldap.listener.InMemoryRequestHandler;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedAddRequest;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedModifyRequest;
import com.unboundid.ldap.listener.interceptor.
            InMemoryInterceptedModifyDNRequest;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModificationType;
import com.unboundid.ldap.sdk.ReadOnlyAddRequest;
import com.unboundid.ldap.sdk.ReadOnlyEntry;
import com.unboundid.ldap.sdk.ReadOnlyModifyRequest;
import com.unboundid.ldap.sdk.ReadOnlyModifyDNRequest;
import com.unboundid.ldap.sdk.RDN;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.util.Debug;



/**
 * This class provides an implementation of an in-memory operation interceptor
 * that can ensure that all values of a specified attribute are required to be
 * unique.  It will reject any add, modify, or modify DN operation that would
 * result in an entry having an attribute value that is already in use in a
 * different entry.
 */
public final class UniqueValueInterceptor
       extends InMemoryOperationInterceptor
{
  // The in-memory request handler that will be used to perform searches needed
  // to guarantee uniqueness.
  private final InMemoryRequestHandler requestHandler;

  // The name of the attribute for which to enforce uniqueness.
  private final String uniqueAttribute;



  /**
   * Creates a new unique value interceptor for the specified attribute.
   *
   * @param  requestHandler   The in-memory request handler that will be used to
   *                          perform searches needed to guarantee uniqueness.
   * @param  uniqueAttribute  The name of the attribute for which to require
   *                          unique values.
   */
  public UniqueValueInterceptor(final InMemoryRequestHandler requestHandler,
                                final String uniqueAttribute)
  {
    this.requestHandler  = requestHandler;
    this.uniqueAttribute = uniqueAttribute;
  }



  /**
   * {@inheritDoc}
   */
  @Override()
  public void processAddRequest(final InMemoryInterceptedAddRequest request)
         throws LDAPException
  {
    // Get the add request and see if it includes the target attribute.  If not,
    // then we don't need to do anything.
    final ReadOnlyAddRequest addRequest = request.getRequest();
    final Attribute a = addRequest.getAttribute(uniqueAttribute);
    if (a == null)
    {
      return;
    }

    // Make sure that none of the values is already in use in the server.
    for (final byte[] value : a.getValueByteArrays())
    {
      ensureNoConflict(addRequest.getDN(), value);
    }
  }



   /**
   * {@inheritDoc}
   */
  @Override()
  public void processModifyRequest(
                   final InMemoryInterceptedModifyRequest request)
         throws LDAPException
  {
    // Iterate through the modifications in the request.
    final ReadOnlyModifyRequest modifyRequest = request.getRequest();
    for (final Modification m : modifyRequest.getModifications())
    {
      // If the modification doesn't target the unique attribute, then we don't
      // care.
      if (! m.getAttributeName().equalsIgnoreCase(uniqueAttribute))
      {
        continue;
      }

      // If the modification doesn't have any values, then we don't care.
      final byte[][] values = m.getValueByteArrays();
      if (values.length == 0)
      {
        continue;
      }

      // If the modification doesn't have a modification type of ADD or REPLACE,
      // then we don't care.
      if (! ((m.getModificationType() == ModificationType.ADD) ||
             (m.getModificationType() == ModificationType.REPLACE)))
      {
        continue;
      }

      // Make sure that none of the values is already in use in the server.
      for (final byte[] value : values)
      {
        ensureNoConflict(modifyRequest.getDN(), value);
      }
    }
  }



  /**
   * {@inheritDoc}
   */
  @Override()
  public void processModifyDNRequest(
                   final InMemoryInterceptedModifyDNRequest request)
         throws LDAPException
  {
    // Get the new RDN from the modify request.
    final ReadOnlyModifyDNRequest modifyDNRequest = request.getRequest();

    final RDN newRDN;
    try
    {
      newRDN = new RDN(modifyDNRequest.getNewRDN());
    }
    catch (final Exception e)
    {
      Debug.debugException(e);
      return;
    }


    // Iterate through the attributes in the new RDN and see if any of them has
    // a conflict.
    for (final Attribute a : newRDN.getAttributes())
    {
      if (a.getName().equalsIgnoreCase(uniqueAttribute))
      {
        ensureNoConflict(modifyDNRequest.getDN(), a.getValueByteArray());
      }
    }
  }



  /**
   * Ensures that the specified value is not already in use in some other entry.
   *
   * @param  requestEntryDN  The DN of the entry being targeted by the request.
   *                         If the value is already in use in the same entry,
   *                         then we won't consider it a conflict.
   * @param  value           The value for which to ensure uniqueness.
   *
   * @throws  LDAPException  If a uniqueness conflict is found.
   */
  private void ensureNoConflict(final String requestEntryDN,
                                final byte[] value)
          throws LDAPException
  {
    final Filter filter = Filter.createEqualityFilter(uniqueAttribute, value);

    for (final ReadOnlyEntry e :
         requestHandler.search("", SearchScope.SUB, filter))
    {
      // If the target value is already in use in the entry being modified or
      // renamed, then we don't care.
      if (DN.equals(e.getDN(), requestEntryDN))
      {
        continue;
      }

      throw new LDAPException(ResultCode.CONSTRAINT_VIOLATION,
           "Unique attribute conflict:  Entry " + e.getDN() +
                "' already matches filter " + filter);
    }
  }
}

 

 

 

Paul
New Member
0 Kudos

Re: In Memory Ldap Store Question

Could you possibly give an example of configuring a plug-in using the cn=config approach?

Paul
New Member
0 Kudos

Re: In Memory Ldap Store Question

Wow awesome, I will take a look at this and see if it might work. Thanks!

Paul
New Member
0 Kudos

Re: In Memory Ldap Store Question

UnboundID PhilipP
UnboundID
0 Kudos

Re: In Memory Ldap Store Question

As I mentioned before, this is really not supported (at all!).

But, since you ask...

 

As an example of activating the existing plugin (for uid uniquness, globally):

 

$ ldapmodify -p 1389 -D "cn=directory manager" -w password <<EOF
dn: cn=UID Unique Attribute,cn=Plugins,cn=config
changetype: modify
replace: ds-cfg-enabled
ds-cfg-enabled: true
EOF

 

Just like any other ldapmodify. The directory manager account has appropriate privileges allowing it to access cn=config.

 

Another example is creating a new plugin instance to enure uniquness of the CN attribute, but limited to the subtree ou=people,dc=example,dc=com:

 

$ ldapmodify -a -p 1389 -D "cn=directory manager" -w password <<EOF
dn: cn=cn Uniquness,cn=Plugins,cn=config
changetype: add
objectClass: ds-cfg-unique-attribute-plugin
objectClass: top
objectClass: ds-cfg-plugin
ds-cfg-type: cn
ds-cfg-prevent-conflicts-with-soft-deleted-entries: false
cn: cn Uniquness
ds-cfg-base-dn: ou=people,dc=example,dc=com
ds-cfg-multiple-attribute-behavior: unique-within-each-attribute
ds-cfg-invoke-for-internal-operations: true
ds-cfg-java-class: com.unboundid.directory.server.plugins.UniqueAttributePlugin
ds-cfg-enabled: true
ds-cfg-plugin-type: postsynchronizationadd
ds-cfg-plugin-type: postsynchronizationmodify
ds-cfg-plugin-type: postsynchronizationmodifydn
ds-cfg-plugin-type: preoperationadd
ds-cfg-plugin-type: preoperationmodify
ds-cfg-plugin-type: preoperationmodifydn
EOF

 

You can find documentation on the attribute values for these plugins in the configuration guide.

Alternatively, on a test instance, enable the audit log, then make whatever changes you want via dsconfig. The audit log will contain a trace of all the operations carried out.

 

If you do this for anything important, it is ESSENTIAL to validate the operations on any new release before deploying it. Support is likely to be unsympathetic to any problems you may have.