Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
java
java
package org.apache.directory.samples.interceptor.pwdhash;

import static org.apache.directory.samples.interceptor.pwdhash.HashTools.applyHashAlgorithm;
import static org.apache.directory.samples.interceptor.pwdhash.HashTools.isAlreadyHashed;

import java.util.List;

import org.apache.directory.server.core.entry.ClonedServerEntry;
import org.apache.directory.server.core.interceptor.BaseInterceptor;
import org.apache.directory.server.core.interceptor.NextInterceptor;
import org.apache.directory.server.core.interceptor.context.AddOperationContext;
import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
import org.apache.directory.shared.ldap.entry.EntryAttribute;
import org.apache.directory.shared.ldap.entry.Modification;
import org.apache.directory.shared.ldap.entry.ModificationOperation;

public class PasswordHashInterceptor extends BaseInterceptor {

    private String hashAlgorithm = "MD5";

    private String passwordAttributeName = "userPassword";

    public void setHashAlgorithm(String hashAlgorithm) {
        this.hashAlgorithm = hashAlgorithm;
    }

    public void setPasswordAttributeName(String passwordAttributeName) {
        this.passwordAttributeName = passwordAttributeName;
    }

    /**
     * Intercepts the add operation in order to replace plain password values
     * with hashed ones.
     */
    @Override
    public void add(NextInterceptor next, AddOperationContext opContext)
            throws Exception {

        ClonedServerEntry entry = opContext.getEntry();
        EntryAttribute attribute = entry.get(passwordAttributeName);
        if (attribute != null) {
            hashPasswordIfNeccessary(attribute);
        }

        super.add(next, opContext);
    }

    /**
     * Intercepts the modify operation in order to replace plain password values
     * with hashed ones.
     */
    @Override
    public void modify(NextInterceptor next, ModifyOperationContext opContext)
            throws Exception {

        List<Modification> items = opContext.getModItems();
        for (Modification modification : items) {
            ModificationOperation operation = modification.getOperation();
            if (operation == ModificationOperation.ADD_ATTRIBUTE
                    || operation == ModificationOperation.REPLACE_ATTRIBUTE) {
                EntryAttribute attribute = modification.getAttribute();
                if (attribute.getId().equalsIgnoreCase(passwordAttributeName)) {
                    hashPasswordIfNeccessary(attribute);
                }
            }
        }
        super.modify(next, opContext);
    }

    protected void hashPasswordIfNeccessary(EntryAttribute attribute) {
        try {
            byte[] password = attribute.getBytes();
            if (!isAlreadyHashed(password)) {
                byte[] hashed = applyHashAlgorithm(hashAlgorithm, password);
                attribute.clear();
                attribute.add(hashed);
            }
        } catch (Exception e) {
            throw new RuntimeException("Password hash failed", e);
        }
    }
}

Using the interceptor

You may use a custom interceptor both in a standard ApacheDS installation and in a server started embedded.

Adding it to a standard server installation (server.xml)

In order to get the interceptor installed in a default installation of ApacheDS 1.5.5., just copy the jar-File resulting from the Maven build, which contains the custom classes, to APACHEDS_INSTALLDIR/lib/ext.

After that, add the interceptor to the server.xml file in APACHEDS_INSTALLDIR/conf/. Make sure to backup the file before your modifications. Within server.xml find the XML elements which list the interceptors. The easiest way to add a custom interceptor is to ass a spring bean (namespace "s"). You my set configuration properties to the interceptor as well, if it supports some.

The following fragment shows the interceptor list with the example interceptor added just behind normalization. For demonstration, the hash algorithm is set to "MD5" (which is the default of our interceptor anyway).

Code Block
xml
xml

...
<interceptors>
  <normalizationInterceptor/>
  <s:bean class="org.apache.directory.samples.interceptor.pwdhash.PasswordHashInterceptor"> 
     <s:property name="hashAlgorithm" value="MD5" /> 
  </s:bean>
  <authenticationInterceptor/>
  <referralInterceptor/>
  <aciAuthorizationInterceptor/>
  <defaultAuthorizationInterceptor/>
  <exceptionInterceptor/>
  <operationalAttributeInterceptor/>
  ...
</interceptors>
...

Embedded mode

The following Java code starts an ApacheDS embedded in a main method. The list of interceptors is complemented with the example interceptor. We insert it exactly behind the NormalizingInterceptor (the position is a little bit tricky to determine).

Code Block
java
java
package org.apache.directory.samples.interceptor.pwdhash;

import java.util.List;

import org.apache.directory.server.core.DefaultDirectoryService;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.core.interceptor.Interceptor;
import org.apache.directory.server.core.normalization.NormalizationInterceptor;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;

/**
 * Main class which starts an embedded server with the interceptor inserted into
 * the chain.
 */
public class Main {

    public static void main(String[] args) throws Exception {

        DirectoryService directoryService = new DefaultDirectoryService();
        directoryService.setShutdownHookEnabled(true);

        List<Interceptor> interceptors = directoryService.getInterceptors();

        // Find Normalization interceptor in chain
        int insertionPosition = -1;
        for (int pos = 0; pos < interceptors.size(); ++pos) {
            Interceptor interceptor = interceptors.get(pos);
            if (interceptor instanceof NormalizationInterceptor) {
                insertionPosition = pos;
            }
        }

        // insert our new interceptor just behind
        interceptors.add(insertionPosition + 1, new PasswordHashInterceptor());
        directoryService.setInterceptors(interceptors);

        LdapServer ldapServer = new LdapServer();
        ldapServer.setDirectoryService(directoryService);
        ldapServer.setAllowAnonymousAccess(true);

        TcpTransport ldapTransport = new TcpTransport(10389);
        ldapServer.setTransports(ldapTransport);

        directoryService.startup();
        ldapServer.start();
    }
}

Adding it to a server.xml file

...

Verification

Let's check whether our new interceptor does its job! In order to do so, we use Apache Directory Studio and connect to the server with the interceptor enabled (see above).

...