diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AbstractTlsDirContextAuthenticationStrategy.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AbstractTlsDirContextAuthenticationStrategy.java new file mode 100755 index 00000000..2d1df9cf --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/AbstractTlsDirContextAuthenticationStrategy.java @@ -0,0 +1,119 @@ +package org.springframework.ldap.core.support; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Hashtable; + +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; +import javax.naming.ldap.StartTlsRequest; +import javax.naming.ldap.StartTlsResponse; +import javax.net.ssl.HostnameVerifier; + +import org.springframework.ldap.UncategorizedLdapException; +import org.springframework.ldap.core.DirContextProxy; +import org.springframework.ldap.support.LdapUtils; + +public abstract class AbstractTlsDirContextAuthenticationStrategy implements DirContextAuthenticationStrategy { + + private HostnameVerifier hostnameVerifier; + + private boolean shutdownTlsGracefully = false; + + public void setShutdownTlsGracefully(boolean shutdownTlsGracefully) { + this.shutdownTlsGracefully = shutdownTlsGracefully; + } + + public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + } + + public final void setupEnvironment(Hashtable env, String userDn, String password) { + // Nothing to do in this implementation - authentication should take + // place after TLS has been negotiated. + } + + public final DirContext processContextAfterCreation(DirContext ctx, String userDn, String password) + throws NamingException { + + if (ctx instanceof LdapContext) { + LdapContext ldapCtx = (LdapContext) ctx; + StartTlsResponse tlsResponse = (StartTlsResponse) ldapCtx.extendedOperation(new StartTlsRequest()); + try { + if (hostnameVerifier != null) { + tlsResponse.setHostnameVerifier(hostnameVerifier); + } + tlsResponse.negotiate(); + applyAuthentication(ldapCtx, userDn, password); + + if (shutdownTlsGracefully) { + // Wrap the target context in a proxy to intercept any calls + // to 'close', so that we can shut down the TLS connection + // gracefully first. + return (DirContext) Proxy.newProxyInstance(DirContextProxy.class.getClassLoader(), new Class[] { + LdapContext.class, DirContextProxy.class }, new TlsAwareDirContextProxy(ldapCtx, + tlsResponse)); + } + else { + return ctx; + } + } + catch (IOException e) { + LdapUtils.closeContext(ctx); + throw new UncategorizedLdapException("Failed to negitiate tls session", e); + } + } + else { + throw new IllegalArgumentException( + "Processed Context must be an LDAPv3 context, i.e. an LdapContext implementation"); + } + + } + + /** + * Apply the actual authentication to the specified LdapContext. + * Typically, this will involve adding stuff to the environment. + * + * @param ctx the LdapContext instance. + * @param userDn the user dn of the user to authenticate. + * @param password the password of the user to authenticate. + * @throws NamingException if any error occurs. + */ + protected abstract void applyAuthentication(LdapContext ctx, String userDn, String password) throws NamingException; + + private static final class TlsAwareDirContextProxy implements DirContextProxy, InvocationHandler { + + private static final String GET_TARGET_CONTEXT_METHOD_NAME = "getTargetContext"; + + private static final String CLOSE_METHOD_NAME = "close"; + + private final LdapContext target; + + private final StartTlsResponse tlsResponse; + + public TlsAwareDirContextProxy(LdapContext target, StartTlsResponse tlsResponse) { + this.target = target; + this.tlsResponse = tlsResponse; + } + + public DirContext getTargetContext() { + return target; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals(CLOSE_METHOD_NAME)) { + tlsResponse.close(); + return method.invoke(target, args); + } + else if (method.getName().equals(GET_TARGET_CONTEXT_METHOD_NAME)) { + return target; + } + else { + return method.invoke(target, args); + } + } + } +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DefaultTlsDirContextAuthenticationStrategy.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DefaultTlsDirContextAuthenticationStrategy.java new file mode 100755 index 00000000..164f75ce --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DefaultTlsDirContextAuthenticationStrategy.java @@ -0,0 +1,16 @@ +package org.springframework.ldap.core.support; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.ldap.LdapContext; + +public class DefaultTlsDirContextAuthenticationStrategy extends AbstractTlsDirContextAuthenticationStrategy { + private static final String SIMPLE_AUTHENTICATION = "simple"; + + protected void applyAuthentication(LdapContext ctx, String userDn, String password) throws NamingException { + ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, SIMPLE_AUTHENTICATION); + ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDn); + ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password); + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DirContextAuthenticationStrategy.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DirContextAuthenticationStrategy.java index 467b52b4..5fab9473 100755 --- a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DirContextAuthenticationStrategy.java +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/DirContextAuthenticationStrategy.java @@ -62,7 +62,11 @@ public interface DirContextAuthenticationStrategy { /** * This method is responsible for post-processing the * DirContext instance after it has been created. It will be - * called immediately after the instance has been created. + * called immediately after the instance has been created. Some + * authentication mechanisms, e.g. TLS, require particular stuff to happen + * before the actual target Context is closed. This method provides the + * possibility to replace or wrap the actual DirContext with a proxy so that + * any calls on it may be intercepted. * * @param ctx the freshly created DirContext instance. The * actual implementation class (e.g. InitialLdapContext) @@ -71,10 +75,12 @@ public interface DirContextAuthenticationStrategy { * {@link AuthenticationSource} of the {@link ContextSource}. * @param password the password to authenticate with, as received from the * {@link AuthenticationSource} of the {@link ContextSource}. + * @return the DirContext, possibly modified, replaced or wrapped. * @throws NamingException if anything goes wrong. This will cause the * DirContext creation to be aborted and the exception to be * translated and rethrown. */ - public void processContextAfterCreation(DirContext ctx, String userDn, String password) throws NamingException; + public DirContext processContextAfterCreation(DirContext ctx, String userDn, String password) + throws NamingException; } diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/ExternalTlsDirContextAuthenticationStrategy.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/ExternalTlsDirContextAuthenticationStrategy.java new file mode 100755 index 00000000..68dedd04 --- /dev/null +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/ExternalTlsDirContextAuthenticationStrategy.java @@ -0,0 +1,21 @@ +package org.springframework.ldap.core.support; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.ldap.LdapContext; + +/** + * {@link DirContextAuthenticationStrategy} for using TLS and external (SASL) + * authentication. + * + * @author Mattias Hellborg Arthursson + */ +public class ExternalTlsDirContextAuthenticationStrategy extends AbstractTlsDirContextAuthenticationStrategy { + + private static final String EXTERNAL_AUTHENTICATION = "EXTERNAL"; + + protected void applyAuthentication(LdapContext ctx, String userDn, String password) throws NamingException { + ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, EXTERNAL_AUTHENTICATION); + } + +} diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/SimpleDirContextAuthenticationStrategy.java b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/SimpleDirContextAuthenticationStrategy.java index 9770b15b..9e74fa70 100755 --- a/mvn-build/core/src/main/java/org/springframework/ldap/core/support/SimpleDirContextAuthenticationStrategy.java +++ b/mvn-build/core/src/main/java/org/springframework/ldap/core/support/SimpleDirContextAuthenticationStrategy.java @@ -49,8 +49,8 @@ public class SimpleDirContextAuthenticationStrategy implements DirContextAuthent * @see org.springframework.ldap.core.support.DirContextAuthenticationStrategy#processContextAfterCreation(javax.naming.directory.DirContext, * java.lang.String, java.lang.String) */ - public void processContextAfterCreation(DirContext ctx, String userDn, String password) { - // Nothing to do here + public DirContext processContextAfterCreation(DirContext ctx, String userDn, String password) { + return ctx; } }