czwartek, stycznia 15, 2015

How to add support for StartTls in Spring LDAP

import com.sun.jndi.ldap.LdapClient;

public class Binding extends LdapContextSource {

private SearchControls sc = new SearchControls();
private String base = null;
private static boolean requireServerCertInTrustedStore = false;
private static boolean traceAttributesModification = !true;
private boolean useStartTlsAuth = false;

private final static String[] CLEAR_PARAMS_FOR_STARTTLS = { ".security",
"java.naming.ldap.factory.socket", "com.sun.jndi.ldap.connect.pool"
};

private Hashtable<String,Object> prepareParamsForStartTls(@SuppressWarnings("rawtypes") Hashtable env) {
Hashtable<String,Object> props = new Hashtable<String, Object>();
for (Object k : env.keySet()) {
String key = k.toString();
int hitCount = 0;
for (int i = 0; i < CLEAR_PARAMS_FOR_STARTTLS.length; i++) {
if (key.contains(CLEAR_PARAMS_FOR_STARTTLS[i])) {
hitCount++;
break;
}
}
if (hitCount == 0)
props.put(key, env.get(key));
}
props.put("java.naming.ldap.version", "3");
props.put(Context.SECURITY_PROTOCOL, "plain");
return props;
}

@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected DirContext getDirContextInstance(Hashtable env)
throws NamingException {

Hashtable<String,Object> params = prepareParamsForStartTls(env); /* strip pooling and security */
params.put("java.naming.ldap.attributes.binary", "objectSid objectGUID");
final LdapContext ctx = (LdapContext) NamingManager.getInitialContext(params);

StartTlsResponse tlsResp = null;
if (useStartTlsAuth) {
tlsResp = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
tlsResp.setHostnameVerifier(TolerantSSLSocketFactory.TOLERANT_HOSTNAME_VERIFIER);
try {
tlsResp.negotiate(TolerantSSLSocketFactory.INSTANCE);
}
catch (Throwable t) {
throw new NamingException("SSL negotiation for LDAP StartTLS failed: "+t);
}
}
ctx.getEnvironment().putAll(env); /* do not use addToEnvironment because it would reconnect without StartTls */
try {
Field fClient = ctx.getClass().getDeclaredField("clnt");
fClient.setAccessible(true);
LdapClient cli = (LdapClient) fClient.get(ctx);
/* We are over secure channel, need to authenticate now */
Method mAuthenticate = cli.getClass().getDeclaredMethod("authenticate", new Class[] {
boolean.class, String.class, Object.class, int.class, String.class, Control[].class, Hashtable.class
});
mAuthenticate.setAccessible(true);
mAuthenticate.invoke(cli, false, env.get(Context.SECURITY_PRINCIPAL).toString(),
env.get(Context.SECURITY_CREDENTIALS), 3, "simple", null, env);
}
catch (Exception e) {
Throwable t = e;
while (t.getCause() != null)
t = t.getCause();
throw new NamingException("Cannot authenticate due to error: "+t.getMessage());
}
return new InitialLdapContext(env, null) {
@Override
protected Context getDefaultInitCtx() throws NamingException {
/* cache context */
return ctx;
}
};
}

public static boolean isRequiredServerCertInTrustedStore() {
return requireServerCertInTrustedStore;
}

public static void setRequireServerCertInTrustedStore(
boolean requireServerCertInTrustedStore) {
Binding.requireServerCertInTrustedStore = requireServerCertInTrustedStore;
}

public static void setTraceAttributesModification(
boolean traceAttributesModification) {
Binding.traceAttributesModification = traceAttributesModification;
}

public static boolean getTraceAttributesModification() {
return traceAttributesModification;
}

public Binding(String userLogin, String userPassword, String serverUrl, String searchBase) throws Exception {

useStartTlsAuth = serverUrl.startsWith("ldap(s)");
if (useStartTlsAuth) {
serverUrl = "ldap"+serverUrl.substring(7);
setPooled(false);
}
setUserDn(userLogin);
setPassword(userPassword);
setBase(searchBase);
setReferral("follow");
setUrl(serverUrl);
setPooled(true);

HashMap<String,String> props = new HashMap<String, String>();
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
props.put(Context.SECURITY_AUTHENTICATION, "none");
props.put(Context.SECURITY_AUTHENTICATION, "simple");
props.put(Context.SECURITY_CREDENTIALS, userLogin);
props.put(Context.SECURITY_PRINCIPAL, userPassword);

if (!requireServerCertInTrustedStore) {
props.put("java.naming.ldap.factory.socket", TolerantSSLSocketFactory.class.getName());
}
setCacheEnvironmentProperties(true);
setBaseEnvironmentProperties(props);
setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy());
afterPropertiesSet();
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
sc.setCountLimit(0);
sc.setReturningAttributes(null);
sc.setReturningObjFlag(true);
}

public SearchControls getSearchControls() {
return sc;
}

public String getSearchBase() {
return this.base != null ? base : "";
}
}

0 komentarze: