blob: 54591cfa2da7eb840f4dedcb8c0a22048deaab79 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2015 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:
* Otto von Wesendonk - initial API and implementation
******************************************************************************/
package org.eclipse.emf.emfstore.internal.server.accesscontrol.authentication.verifiers;
import java.text.MessageFormat;
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.apache.commons.lang.StringUtils;
import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.internal.server.ServerConfiguration;
import org.eclipse.emf.emfstore.internal.server.connection.ServerKeyStoreManager;
import org.eclipse.emf.emfstore.internal.server.core.MonitorProvider;
import org.eclipse.emf.emfstore.internal.server.exceptions.AccessControlException;
import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.ACUser;
import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.AccesscontrolFactory;
import org.eclipse.emf.emfstore.server.model.ESOrgUnitProvider;
import com.google.common.base.Optional;
/**
* Verifies user name/password using LDAP.
*
* @author Wesendonk
*/
public class LDAPUserVerifier extends UserVerifier {
private final String ldapUrl;
private final String ldapBase;
private final String searchDn;
private boolean useSSL;
private static final String DEFAULT_CTX = "com.sun.jndi.ldap.LdapCtxFactory"; //$NON-NLS-1$
private final String authUser;
private final String authPassword;
private final ESOrgUnitProvider orgUnitProvider;
/**
* Default constructor.
*
* @param orgUnitProvider
* provides access to users and groups
* @param ldapUrl
* URL, if the URL starts with {@code 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
*/
// TODO: recheck orgUnitProvider
public LDAPUserVerifier(ESOrgUnitProvider orgUnitProvider,
String ldapUrl, String ldapBase, String searchDn, String authUser, String authPassword) {
super(orgUnitProvider);
this.orgUnitProvider = orgUnitProvider;
this.ldapUrl = ldapUrl;
this.ldapBase = ldapBase;
this.searchDn = searchDn;
this.authUser = authUser;
this.authPassword = authPassword;
if (ldapUrl.startsWith("ldaps://")) { //$NON-NLS-1$
useSSL = true;
ServerKeyStoreManager.getInstance().setJavaSSLProperties();
}
}
/**
* This method must be implemented by subclasses in order to verify a pair of username and password.
* When using authentication you should use {@link org.eclipse.emf.emfstore.server.auth.ESUserVerifier
* ESUserVerifier#verifyUser(String, String, ESClientVersionInfo)} in order to gain a session id.
*
* @param username
* the user name as entered by the client; may differ from the user name of the {@code resolvedUser}
* @param password
* the password as entered by the client
* @return boolean {@code true} if authentication was successful, {@code false} if not
* @throws AccessControlException
* if an exception occurs during the verification process
*/
@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
final 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 (final NamingException e) {
ModelUtil.logWarning(MessageFormat.format(
Messages.LDAPVerifier_LDAPDirectoryNotFound, ldapUrl), e);
return false;
}
final 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 (final NamingException e) {
ModelUtil.logWarning(
MessageFormat.format(Messages.LDAPVerifier_LoginFailed, ldapBase), e);
return false;
}
return true;
}
private Properties anonymousBind() {
final Properties props = new Properties();
props.put("java.naming.ldap.version", "3"); //$NON-NLS-1$ //$NON-NLS-2$
props.put(Context.INITIAL_CONTEXT_FACTORY, DEFAULT_CTX);
props.put(Context.PROVIDER_URL, ldapUrl);
if (useSSL()) {
props.put("java.naming.ldap.factory.socket", //$NON-NLS-1$
LDAPSSLSocketFactory.class.getCanonicalName());
props.put(Context.SECURITY_PROTOCOL, "ssl"); //$NON-NLS-1$
}
return props;
}
private boolean useSSL() {
return useSSL;
}
private Properties authenticatedBind(String principal, String credentials) {
final Properties bind = anonymousBind();
bind.put(Context.SECURITY_AUTHENTICATION, "simple"); //$NON-NLS-1$
bind.put(Context.SECURITY_PRINCIPAL, principal + "," + ldapBase); //$NON-NLS-1$
bind.put(Context.SECURITY_CREDENTIALS, credentials);
return bind;
}
private String resolveUser(String username, DirContext dirContext) {
final SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> results = null;
try {
results = dirContext.search(ldapBase, "(& (" + //$NON-NLS-1$
searchDn + "=" + username //$NON-NLS-1$
+ ") (objectclass=*))", //$NON-NLS-1$
constraints);
} catch (final NamingException e) {
ModelUtil.logWarning(MessageFormat.format(
Messages.LDAPVerifier_SearchFailed, ldapBase), e);
return null;
}
if (results == null) {
return null;
}
String resolvedName = null;
try {
while (results.hasMoreElements()) {
final SearchResult sr = results.next();
if (sr != null) {
resolvedName = sr.getName();
}
break;
}
} catch (final NamingException e) {
ModelUtil.logException(MessageFormat.format(
Messages.LDAPVerifier_InvalidResults, ldapBase), e);
return null;
}
if (resolvedName == null) {
ModelUtil.logWarning(MessageFormat.format(Messages.LDAPVerifier_DistinguishedNameNotFound, ldapBase));
return null;
}
return resolvedName;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.server.auth.ESUserVerifier#init(org.eclipse.emf.emfstore.server.model.ESOrgUnitProvider)
*/
public void init(ESOrgUnitProvider orgUnitProvider) {
}
@Override
protected ACUser findUser(String username) throws AccessControlException {
final Boolean ignoreCase = Boolean.parseBoolean(ServerConfiguration.getProperties().getProperty(
ServerConfiguration.AUTHENTICATION_MATCH_USERS_IGNORE_CASE, Boolean.FALSE.toString()));
final Boolean createAuthenticatedUsers = Boolean.parseBoolean(ServerConfiguration.getProperties().getProperty(
ServerConfiguration.AUTHENTICATION_CREATE_AUTHENTICATED_USERS, Boolean.FALSE.toString()));
synchronized (MonitorProvider.getInstance().getMonitor()) {
final Optional<ACUser> user = findExistingUser(orgUnitProvider, username, ignoreCase);
if (user.isPresent()) {
return user.get();
}
if (createAuthenticatedUsers) {
final ACUser acUser = AccesscontrolFactory.eINSTANCE.createACUser();
acUser.setName(username);
acUser.setDescription(StringUtils.EMPTY);
orgUnitProvider.addUser(acUser.toAPI());
return acUser;
}
throw new AccessControlException();
}
}
}