/*
* Copyright 2012 PRODYNA AG
*
* Licensed under the Eclipse Public License (EPL), Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php or
* http://www.nabucco.org/License.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.nabucco.framework.common.authorization.impl.service.login.ldap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import org.nabucco.common.extension.ExtensionException;
import org.nabucco.framework.base.facade.datatype.NabuccoSystem;
import org.nabucco.framework.base.facade.datatype.Tenant;
import org.nabucco.framework.base.facade.datatype.extension.ExtensionPointType;
import org.nabucco.framework.base.facade.datatype.extension.ExtensionResolver;
import org.nabucco.framework.base.facade.datatype.extension.NabuccoExtensionComposite;
import org.nabucco.framework.base.facade.datatype.extension.property.PropertyLoader;
import org.nabucco.framework.base.facade.datatype.extension.property.StringProperty;
import org.nabucco.framework.base.facade.datatype.extension.schema.authorization.authentication.LdapAuthenticationExtension;
import org.nabucco.framework.base.facade.datatype.logger.NabuccoLogger;
import org.nabucco.framework.base.facade.datatype.logger.NabuccoLoggingFactory;
import org.nabucco.framework.base.facade.datatype.security.UserId;
import org.nabucco.framework.base.facade.datatype.security.credential.Password;
import org.nabucco.framework.base.impl.service.maintain.PersistenceManager;
import org.nabucco.framework.common.authorization.facade.exception.login.LoginException;
/**
* LdapAuthenticationImpl
*
* @author Nicolas Moser, PRODYNA AG
*/
class LdapAuthenticationImpl implements LdapAuthentication {
private static final long serialVersionUID = 1L;
/** The default base DN */
private static final String DEFAULT_BASE_DN = "";
/** The default object filter value. */
private static final String DEFAULT_OBJECT_FILTER = "";
/** The logger */
private static NabuccoLogger logger = NabuccoLoggingFactory.getInstance().getLogger(LdapAuthentication.class);
@Override
public void authenticate(UserId username, Password password, Tenant tenant) throws LoginException {
try {
LdapAuthenticationExtension ldapConfiguration = this.loadConfiguration(tenant);
LdapContext context = this.createContext(ldapConfiguration);
String userDn = this.distinguishName(username.getValue(), context, ldapConfiguration);
context.addToEnvironment(Context.SECURITY_PRINCIPAL, userDn);
context.addToEnvironment(Context.SECURITY_CREDENTIALS, password.getValue());
try {
// Authentication
context.lookup(userDn);
} catch (NamingException ne) {
logger.warning("Cannot authenticate '", username.getValue(), "' against LDAP.");
throw new LoginException("Login failed. Cannot login user with username '" + username + "'.", ne);
}
} catch (ExtensionException ee) {
logger.error(ee, "Error resolving LDAP Authentication Extension.");
throw new LoginException("Error resolving LDAP Authentication Extension.", ee);
} catch (NamingException ne) {
logger.error(ne, "Cannot establish LDAP connection.");
throw new LoginException("Cannot establish LDAP connection.", ne);
} catch (Exception e) {
logger.error(e, "Error authenticating '" + username.getValue() + "' against LDAP.");
throw new LoginException("Login failed. Cannot login user with username '" + username + "'.");
}
}
/**
* Loads the LDAP authentication configuration from extension point
* <tt>org.nabucco.framework.authorization.authentication</tt>.
*
* @param tenant
* the users tenant
*
* @return the LDAP authentication configuration
*
* @throws IOException
* if the ldap.properties file cannot be found
*/
private LdapAuthenticationExtension loadConfiguration(Tenant tenant) throws ExtensionException {
ExtensionResolver resolver = NabuccoSystem.getExtensionResolver();
ExtensionPointType extensionPoint = ExtensionPointType.ORG_NABUCCO_FRAMEWORK_AUTHORIZATION_AUTHENTICATION;
String extensionName = ExtensionResolver.DEFAULT_EXTENSION;
NabuccoExtensionComposite extension = resolver.resolveExtension(extensionPoint, extensionName, tenant);
return (LdapAuthenticationExtension) extension;
}
/**
* Create context for LDAP connection depending on the ldap.properties file.
*
* @param extension
* the LDAP connection properties
*
* @throws NamingException
* if the LDAP authentification failed
*/
private LdapContext createContext(LdapAuthenticationExtension configuration) throws NamingException {
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, this.getFactoryName(configuration));
properties.put(Context.PROVIDER_URL, this.getUrl(configuration));
properties.put(Context.SECURITY_AUTHENTICATION, PropertyLoader.loadProperty(configuration.getSecurityType()));
properties.put(Context.SECURITY_PROTOCOL, PropertyLoader.loadProperty(configuration.getSecurityProtocol()));
properties.put(Context.SECURITY_PRINCIPAL, PropertyLoader.loadProperty(configuration.getSecurityPrincipal()));
properties.put(Context.SECURITY_CREDENTIALS,
PropertyLoader.loadProperty(configuration.getSecurityCredentials()));
return new InitialLdapContext(properties, null);
}
/**
* Resolve the LDAP factory name.
*
* @param configuration
* the LDAP authentication configuration
*
* @return the factory class name
*/
private String getFactoryName(LdapAuthenticationExtension configuration) {
LdapFactoryType type = PropertyLoader.loadProperty(LdapFactoryType.class, configuration.getFactory());
if (type != null) {
return type.getImplName();
}
return LdapFactoryType.SUN.getImplName();
}
/**
* Resolve the LDAP url.
*
* @param configuration
* the LDAP authentication configuration
*
* @return the directory url
*/
private String getUrl(LdapAuthenticationExtension configuration) {
String url = PropertyLoader.loadProperty(configuration.getUrl());
if (url == null || url.isEmpty()) {
logger.warning("No valid LDAP URL configured 'null'.");
} else {
logger.info("Authenticating against LDAP '", url, "'.");
}
return url;
}
/**
* Distinguishes the name from the LDAP.
*
* @param username
* the username
* @param context
* the LDAP directory context
* @param configuration
* the LDAP configuration
*
* @return the distuinguished name
*
* @throws NamingException
*/
private String distinguishName(String username, LdapContext context, LdapAuthenticationExtension configuration)
throws NamingException {
String filter = this.getObjectFilter(configuration);
String baseDn = this.getBaseDn(configuration);
String[] returnAttributes = this.getReturnAttributes(configuration);
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchControls.setReturningAttributes(returnAttributes);
NamingEnumeration<SearchResult> resultList = context.search(baseDn, filter, new String[] { username },
searchControls);
if (resultList.hasMore()) {
SearchResult result = resultList.next();
return result.getNameInNamespace();
}
return null;
}
/**
* Extracts the base distinguished name from the LDAP configuration.
*
* @param configuration
* the LDAP configuration
*
* @return the Base DN
*/
private String getBaseDn(LdapAuthenticationExtension configuration) {
String baseDn = PropertyLoader.loadProperty(configuration.getBaseDn());
if (baseDn == null) {
return DEFAULT_BASE_DN;
}
return baseDn;
}
/**
* Extracts the object filter from the LDAP configuration.
*
* @param configuration
* the LDAP configuration
*
* @return the object filter
*/
private String getObjectFilter(LdapAuthenticationExtension configuration) {
String objectFilter = PropertyLoader.loadProperty(configuration.getObjectFilter());
if (objectFilter == null) {
return DEFAULT_OBJECT_FILTER;
}
return objectFilter;
}
/**
* Extracts the return attributes from the LDAP configuration.
*
* @param configuration
* the LDAP configuration
*
* @return the return attributes
*/
private String[] getReturnAttributes(LdapAuthenticationExtension configuration) {
List<String> returnAttributes = new ArrayList<String>();
for (StringProperty attribute : configuration.getReturnAttributes()) {
String returnAttribute = PropertyLoader.loadProperty(attribute);
returnAttributes.add(returnAttribute);
}
return returnAttributes.toArray(new String[returnAttributes.size()]);
}
@Override
public void setPersistenceManager(PersistenceManager persistenceManager) {
// No persistence manager necessary!
}
}