/*******************************************************************************
* Copyright (c) 2008-2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
******************************************************************************/
package org.eclipse.emf.emfstore.server.accesscontrol.authentication;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.eclipse.emf.emfstore.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.server.connection.ServerKeyStoreManager;
import org.eclipse.emf.emfstore.server.exceptions.AccessControlException;
/**
* Verifies username/password using LDAP.
*
* @author Wesendonk
*/
public class LDAPVerifier extends AbstractAuthenticationControl {
private String ldapUrl;
private String ldapBase;
private String searchDn;
private boolean useSSL;
private static final String DEFAULT_CTX = "com.sun.jndi.ldap.LdapCtxFactory";
private final String authUser;
private final String authPassword;
/**
* Default constructor.
*
* @param ldapUrl url, if url starts with ldaps:// SSL is used.
* @param ldapBase base
* @param searchDn dn
* @param authUser user to allow access to server
* @param authPassword password of user to allow access to server
*/
public LDAPVerifier(String ldapUrl, String ldapBase, String searchDn, String authUser, String authPassword) {
this.ldapUrl = ldapUrl;
this.ldapBase = ldapBase;
this.searchDn = searchDn;
this.authUser = authUser;
this.authPassword = authPassword;
if (ldapUrl.startsWith("ldaps://")) {
useSSL = true;
ServerKeyStoreManager.getInstance().setJavaSSLProperties();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean verifyPassword(String username, String password) throws AccessControlException {
DirContext dirContext = null;
// anonymous bind and resolve user
try {
if (authUser != null && authPassword != null) {
// authenticated bind and resolve user
Properties authenticatedBind = authenticatedBind(authUser, authPassword);
authenticatedBind.put(Context.SECURITY_PRINCIPAL, authUser);
dirContext = new InitialDirContext(authenticatedBind);
} else {
// anonymous bind and resolve user
dirContext = new InitialDirContext(anonymousBind());
}
} catch (NamingException e) {
ModelUtil.logWarning("LDAP Directory " + ldapUrl + " not found.", e);
return false;
}
String resolvedName = resolveUser(username, dirContext);
if (resolvedName == null) {
return false;
}
// Authenticated bind and check user's password
try {
dirContext = new InitialDirContext(authenticatedBind(resolvedName, password));
} catch (NamingException e) {
e.printStackTrace();
ModelUtil.logWarning("Login failed on " + ldapBase + " .", e);
return false;
}
return true;
}
private Properties anonymousBind() {
Properties props = new Properties();
props.put("java.naming.ldap.version", "3");
props.put(Context.INITIAL_CONTEXT_FACTORY, DEFAULT_CTX);
props.put(Context.PROVIDER_URL, ldapUrl);
if (useSSL()) {
props.put(Context.SECURITY_PROTOCOL, "ssl");
}
return props;
}
private boolean useSSL() {
return useSSL;
}
private Properties authenticatedBind(String principal, String credentials) {
Properties bind = anonymousBind();
bind.put(Context.SECURITY_AUTHENTICATION, "simple");
bind.put(Context.SECURITY_PRINCIPAL, principal + ", " + ldapBase);
bind.put(Context.SECURITY_CREDENTIALS, credentials);
return bind;
}
private String resolveUser(String username, DirContext dirContext) {
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> results = null;
try {
results = dirContext.search(ldapBase, "(& (" + searchDn + "=" + username + ") (objectclass=*))",
constraints);
} catch (NamingException e) {
ModelUtil.logWarning("Search failed, base = " + ldapBase, e);
return null;
}
if (results == null) {
return null;
}
String resolvedName = null;
try {
while (results.hasMoreElements()) {
SearchResult sr = results.next();
if (sr != null) {
resolvedName = sr.getName();
}
break;
}
} catch (NamingException e) {
ModelUtil.logException("Search returned invalid results, base = " + ldapBase, e);
return null;
}
if (resolvedName == null) {
ModelUtil.logWarning("Distinguished name not found on " + ldapBase);
return null;
}
return resolvedName;
}
}