/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.resource.adapter.ldap;
import java.util.Hashtable;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.ExtendedRequest;
import javax.naming.ldap.ExtendedResponse;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.resource.ResourceException;
import javax.security.auth.Subject;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.resource.spi.BasicConnection;
import org.teiid.resource.spi.ConnectionContext;
/**
* Represents a connection to an LDAP data source.
*/
public class LDAPConnectionImpl extends BasicConnection implements LdapContext {
static SearchControls VALIDATION_SEARCH_CONTROLS = new SearchControls();
static {
VALIDATION_SEARCH_CONTROLS.setSearchScope(SearchControls.OBJECT_SCOPE);
VALIDATION_SEARCH_CONTROLS.setCountLimit(1);
VALIDATION_SEARCH_CONTROLS.setReturningAttributes(new String[] { "objectclass" }); //$NON-NLS-1$
VALIDATION_SEARCH_CONTROLS.setTimeLimit(500);
}
private LDAPManagedConnectionFactory config;
// Standard Connection data members
private InitialLdapContext initCtx;
// LDAP-specific properties
public static final String LDAP_AUTH_TYPE = "simple"; //$NON-NLS-1$
public static final String LDAP_USER_OBJECT_TYPE = "person"; //$NON-NLS-1$
public static final String LDAP_REFERRAL_MODE = "follow"; //$NON-NLS-1$
public LDAPConnectionImpl(LDAPManagedConnectionFactory config) throws ResourceException {
this.config = config;
checkProperties();
// Create initial LDAP connection.
this.initCtx = initializeLDAPContext();
LogManager.logDetail(LogConstants.CTX_CONNECTOR, "LDAP Connection has been newly created."); //$NON-NLS-1$
}
/**
* Helper method to retrieve the LDAP Connector properties. If any properties are in error,
* a ConnectorException is thrown.
* @param props
*/
private void checkProperties() throws ResourceException {
// LDAP URL
if(this.config.getLdapUrl() == null) {
final String msg = LDAPPlugin.Util.getString("LDAPConnection.urlPropNotFound"); //$NON-NLS-1$
throw new ResourceException(msg);
}
}
/**
* Setup a standard initial LDAP context using JNDI's context factory.
* This method may be extended to support Sun-specific and AD-specific
* contexts, in order to support the different paging implementations they provide.
* @return the initial LDAP Context
*/
private InitialLdapContext initializeLDAPContext() throws ResourceException {
// Create the root context.
InitialLdapContext initContext;
Hashtable connenv = new Hashtable();
connenv.put(Context.INITIAL_CONTEXT_FACTORY, this.config.getLdapContextFactory());
connenv.put(Context.PROVIDER_URL, this.config.getLdapUrl());
connenv.put(Context.REFERRAL, LDAP_REFERRAL_MODE);
String userName = this.config.getLdapAdminUserDN();
String password = this.config.getLdapAdminUserPassword();
// if security-domain is specified and caller identity is used; then use
// credentials from subject
Subject subject = ConnectionContext.getSubject();
if (subject != null) {
userName = ConnectionContext.getUserName(subject, this.config, userName);
password = ConnectionContext.getPassword(subject, this.config, userName, password);
}
if (userName == null) {
final String msg = LDAPPlugin.Util.getString("LDAPConnection.adminUserDNPropNotFound"); //$NON-NLS-1$
throw new ResourceException(msg);
}
if (password == null) {
final String msg = LDAPPlugin.Util.getString("LDAPConnection.adminUserPassPropNotFound"); //$NON-NLS-1$
throw new ResourceException(msg);
}
// If username is blank, we will perform an anonymous bind.
// Note: This is not supported when using Sun's VLVs, so remove this if VLVs are used.
if(!userName.equals("")) { //$NON-NLS-1$
connenv.put(Context.SECURITY_AUTHENTICATION, LDAP_AUTH_TYPE);
connenv.put(Context.SECURITY_PRINCIPAL, userName);
connenv.put(Context.SECURITY_CREDENTIALS, password);
} else {
LogManager.logDetail(LogConstants.CTX_CONNECTOR, "LDAP Username DN was blank; performing anonymous bind."); //$NON-NLS-1$
connenv.put(Context.SECURITY_AUTHENTICATION, "none"); //$NON-NLS-1$
}
if(this.config.getLdapTxnTimeoutInMillis() != null && this.config.getLdapTxnTimeoutInMillis() != -1) {
connenv.put("com.sun.jndi.ldap.connect.timeout", this.config.getLdapTxnTimeoutInMillis().toString()); //$NON-NLS-1$
}
// Enable connection pooling for the Initial context.
connenv.put("com.sun.jndi.ldap.connect.pool", "true"); //$NON-NLS-1$ //$NON-NLS-2$
connenv.put("com.sun.jndi.ldap.connect.pool.debug", "fine"); //$NON-NLS-1$ //$NON-NLS-2$
try {
initContext = new InitialLdapContext(connenv, null);
} catch(NamingException ne){
final String msg = LDAPPlugin.Util.getString("LDAPConnection.directoryNamingError",ne.getExplanation()); //$NON-NLS-1$
throw new ResourceException(msg, ne);
}
LogManager.logDetail(LogConstants.CTX_CONNECTOR, "Successfully obtained initial LDAP context."); //$NON-NLS-1$
return initContext;
}
/**
* Closes LDAP context, effectively closing the connection to LDAP.
* (non-Javadoc)
*/
@Override
public void close() {
if(initCtx != null) {
try {
initCtx.close();
} catch(NamingException e) {
LogManager.logDetail(LogConstants.CTX_CONNECTOR, LDAPPlugin.Util.getString("LDAPConnection.contextCloseError",e.getExplanation())); //$NON-NLS-1$
}
}
LogManager.logDetail(LogConstants.CTX_CONNECTOR,"LDAP context has been closed."); //$NON-NLS-1$
}
public boolean isAlive() {
try {
NamingEnumeration<SearchResult> result = this.initCtx.search("", "objectclass=*", VALIDATION_SEARCH_CONTROLS); //$NON-NLS-1$ //$NON-NLS-2$
if (result.hasMore()) {
result.next();
}
} catch (NamingException e) {
return false;
}
LogManager.logTrace(LogConstants.CTX_CONNECTOR, "LDAP Connection is alive."); //$NON-NLS-1$
return true;
}
public Object lookup(String context) throws NamingException {
return this.initCtx.lookup(context);
}
public Object addToEnvironment(String propName, Object propVal)
throws NamingException {
return initCtx.addToEnvironment(propName, propVal);
}
public void bind(Name name, Object obj, Attributes attrs)
throws NamingException {
initCtx.bind(name, obj, attrs);
}
public void bind(Name name, Object obj) throws NamingException {
initCtx.bind(name, obj);
}
public void bind(String name, Object obj, Attributes attrs)
throws NamingException {
initCtx.bind(name, obj, attrs);
}
public void bind(String name, Object obj) throws NamingException {
initCtx.bind(name, obj);
}
public Name composeName(Name name, Name prefix) throws NamingException {
return initCtx.composeName(name, prefix);
}
public String composeName(String name, String prefix)
throws NamingException {
return initCtx.composeName(name, prefix);
}
public DirContext createSubcontext(Name name, Attributes attrs)
throws NamingException {
return initCtx.createSubcontext(name, attrs);
}
public Context createSubcontext(Name name) throws NamingException {
return initCtx.createSubcontext(name);
}
public DirContext createSubcontext(String name, Attributes attrs)
throws NamingException {
return initCtx.createSubcontext(name, attrs);
}
public Context createSubcontext(String name) throws NamingException {
return initCtx.createSubcontext(name);
}
public void destroySubcontext(Name name) throws NamingException {
initCtx.destroySubcontext(name);
}
public void destroySubcontext(String name) throws NamingException {
initCtx.destroySubcontext(name);
}
public boolean equals(Object obj) {
return initCtx.equals(obj);
}
public ExtendedResponse extendedOperation(ExtendedRequest request)
throws NamingException {
return initCtx.extendedOperation(request);
}
public Attributes getAttributes(Name name, String[] attrIds)
throws NamingException {
return initCtx.getAttributes(name, attrIds);
}
public Attributes getAttributes(Name name) throws NamingException {
return initCtx.getAttributes(name);
}
public Attributes getAttributes(String name, String[] attrIds)
throws NamingException {
return initCtx.getAttributes(name, attrIds);
}
public Attributes getAttributes(String name) throws NamingException {
return initCtx.getAttributes(name);
}
public Control[] getConnectControls() throws NamingException {
return initCtx.getConnectControls();
}
public Hashtable<?, ?> getEnvironment() throws NamingException {
return initCtx.getEnvironment();
}
public String getNameInNamespace() throws NamingException {
return initCtx.getNameInNamespace();
}
public NameParser getNameParser(Name name) throws NamingException {
return initCtx.getNameParser(name);
}
public NameParser getNameParser(String name) throws NamingException {
return initCtx.getNameParser(name);
}
public Control[] getRequestControls() throws NamingException {
return initCtx.getRequestControls();
}
public Control[] getResponseControls() throws NamingException {
return initCtx.getResponseControls();
}
public DirContext getSchema(Name name) throws NamingException {
return initCtx.getSchema(name);
}
public DirContext getSchema(String name) throws NamingException {
return initCtx.getSchema(name);
}
public DirContext getSchemaClassDefinition(Name name)
throws NamingException {
return initCtx.getSchemaClassDefinition(name);
}
public DirContext getSchemaClassDefinition(String name)
throws NamingException {
return initCtx.getSchemaClassDefinition(name);
}
public int hashCode() {
return initCtx.hashCode();
}
public NamingEnumeration<NameClassPair> list(Name name)
throws NamingException {
return initCtx.list(name);
}
public NamingEnumeration<NameClassPair> list(String name)
throws NamingException {
return initCtx.list(name);
}
public NamingEnumeration<Binding> listBindings(Name name)
throws NamingException {
return initCtx.listBindings(name);
}
public NamingEnumeration<Binding> listBindings(String name)
throws NamingException {
return initCtx.listBindings(name);
}
public Object lookup(Name name) throws NamingException {
return initCtx.lookup(name);
}
public Object lookupLink(Name name) throws NamingException {
return initCtx.lookupLink(name);
}
public Object lookupLink(String name) throws NamingException {
return initCtx.lookupLink(name);
}
public void modifyAttributes(Name name, int modOp, Attributes attrs)
throws NamingException {
initCtx.modifyAttributes(name, modOp, attrs);
}
public void modifyAttributes(Name name, ModificationItem[] mods)
throws NamingException {
initCtx.modifyAttributes(name, mods);
}
public void modifyAttributes(String name, int modOp, Attributes attrs)
throws NamingException {
initCtx.modifyAttributes(name, modOp, attrs);
}
public void modifyAttributes(String name, ModificationItem[] mods)
throws NamingException {
initCtx.modifyAttributes(name, mods);
}
public LdapContext newInstance(Control[] reqCtls) throws NamingException {
return initCtx.newInstance(reqCtls);
}
public void rebind(Name name, Object obj, Attributes attrs)
throws NamingException {
initCtx.rebind(name, obj, attrs);
}
public void rebind(Name name, Object obj) throws NamingException {
initCtx.rebind(name, obj);
}
public void rebind(String name, Object obj, Attributes attrs)
throws NamingException {
initCtx.rebind(name, obj, attrs);
}
public void rebind(String name, Object obj) throws NamingException {
initCtx.rebind(name, obj);
}
public void reconnect(Control[] connCtls) throws NamingException {
initCtx.reconnect(connCtls);
}
public Object removeFromEnvironment(String propName) throws NamingException {
return initCtx.removeFromEnvironment(propName);
}
public void rename(Name oldName, Name newName) throws NamingException {
initCtx.rename(oldName, newName);
}
public void rename(String oldName, String newName) throws NamingException {
initCtx.rename(oldName, newName);
}
public NamingEnumeration<SearchResult> search(Name name,
Attributes matchingAttributes, String[] attributesToReturn)
throws NamingException {
return initCtx.search(name, matchingAttributes, attributesToReturn);
}
public NamingEnumeration<SearchResult> search(Name name,
Attributes matchingAttributes) throws NamingException {
return initCtx.search(name, matchingAttributes);
}
public NamingEnumeration<SearchResult> search(Name name, String filterExpr,
Object[] filterArgs, SearchControls cons) throws NamingException {
return initCtx.search(name, filterExpr, filterArgs, cons);
}
public NamingEnumeration<SearchResult> search(Name name, String filter,
SearchControls cons) throws NamingException {
return initCtx.search(name, filter, cons);
}
public NamingEnumeration<SearchResult> search(String name,
Attributes matchingAttributes, String[] attributesToReturn)
throws NamingException {
return initCtx.search(name, matchingAttributes, attributesToReturn);
}
public NamingEnumeration<SearchResult> search(String name,
Attributes matchingAttributes) throws NamingException {
return initCtx.search(name, matchingAttributes);
}
public NamingEnumeration<SearchResult> search(String name,
String filterExpr, Object[] filterArgs, SearchControls cons)
throws NamingException {
return initCtx.search(name, filterExpr, filterArgs, cons);
}
public NamingEnumeration<SearchResult> search(String name, String filter,
SearchControls cons) throws NamingException {
return initCtx.search(name, filter, cons);
}
public void setRequestControls(Control[] requestControls)
throws NamingException {
initCtx.setRequestControls(requestControls);
}
public void unbind(Name name) throws NamingException {
initCtx.unbind(name);
}
public void unbind(String name) throws NamingException {
initCtx.unbind(name);
}
}