001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.admin.client.ldap; 028 029 030 031 import java.util.Collection; 032 import java.util.Hashtable; 033 import java.util.LinkedList; 034 import java.util.List; 035 036 import javax.naming.Context; 037 import javax.naming.NameNotFoundException; 038 import javax.naming.NamingEnumeration; 039 import javax.naming.NamingException; 040 import javax.naming.directory.Attribute; 041 import javax.naming.directory.Attributes; 042 import javax.naming.directory.DirContext; 043 import javax.naming.directory.ModificationItem; 044 import javax.naming.directory.SearchControls; 045 import javax.naming.directory.SearchResult; 046 import javax.naming.ldap.InitialLdapContext; 047 import javax.naming.ldap.LdapName; 048 import javax.naming.ldap.Rdn; 049 050 import org.opends.admin.ads.util.ConnectionUtils; 051 import org.opends.server.admin.client.AuthenticationException; 052 import org.opends.server.admin.client.AuthenticationNotSupportedException; 053 import org.opends.server.admin.client.CommunicationException; 054 055 056 057 /** 058 * An LDAP connection adaptor which maps LDAP requests onto an 059 * underlying JNDI connection context. 060 */ 061 public final class JNDIDirContextAdaptor extends LDAPConnection { 062 063 /** 064 * Adapts the provided JNDI <code>DirContext</code>. 065 * 066 * @param dirContext 067 * The JNDI connection. 068 * @return Returns a new JNDI connection adaptor. 069 */ 070 public static JNDIDirContextAdaptor adapt(DirContext dirContext) { 071 return new JNDIDirContextAdaptor(dirContext); 072 } 073 074 075 076 /** 077 * Creates a new JNDI connection adaptor by performing a simple bind 078 * operation to the specified LDAP server. 079 * 080 * @param host 081 * The host. 082 * @param port 083 * The port. 084 * @param name 085 * The LDAP bind DN. 086 * @param password 087 * The LDAP bind password. 088 * @return Returns a new JNDI connection adaptor. 089 * @throws CommunicationException 090 * If the client cannot contact the server due to an 091 * underlying communication problem. 092 * @throws AuthenticationNotSupportedException 093 * If the server does not support simple authentication. 094 * @throws AuthenticationException 095 * If authentication failed for some reason, usually due 096 * to invalid credentials. 097 */ 098 public static JNDIDirContextAdaptor simpleBind(String host, int port, 099 String name, String password) throws CommunicationException, 100 AuthenticationNotSupportedException, AuthenticationException { 101 Hashtable<String, Object> env = new Hashtable<String, Object>(); 102 env 103 .put(Context.INITIAL_CONTEXT_FACTORY, 104 "com.sun.jndi.ldap.LdapCtxFactory"); 105 String hostname = ConnectionUtils.getHostNameForLdapUrl(host) ; 106 env.put(Context.PROVIDER_URL, "ldap://" + hostname + ":" + port); 107 env.put(Context.SECURITY_PRINCIPAL, name); 108 env.put(Context.SECURITY_CREDENTIALS, password); 109 110 DirContext ctx; 111 try { 112 ctx = new InitialLdapContext(env, null); 113 } catch (javax.naming.CommunicationException e) { 114 throw new CommunicationException(e); 115 } catch (javax.naming.AuthenticationException e) { 116 throw new AuthenticationException(e); 117 } catch (javax.naming.AuthenticationNotSupportedException e) { 118 throw new AuthenticationNotSupportedException(e); 119 } catch (NamingException e) { 120 // Assume some kind of communication problem. 121 throw new CommunicationException(e); 122 } 123 124 return new JNDIDirContextAdaptor(ctx); 125 } 126 127 // The JNDI connection context. 128 private final DirContext dirContext; 129 130 131 132 // Create a new JNDI connection adaptor using the provider JNDI 133 // DirContext. 134 private JNDIDirContextAdaptor(DirContext dirContext) { 135 this.dirContext = dirContext; 136 } 137 138 139 140 /** 141 * {@inheritDoc} 142 */ 143 @Override 144 public void createEntry(LdapName dn, Attributes attributes) 145 throws NamingException { 146 dirContext.createSubcontext(dn, attributes); 147 } 148 149 150 151 /** 152 * {@inheritDoc} 153 */ 154 @Override 155 public void deleteSubtree(LdapName dn) throws NamingException { 156 // Delete the children first. 157 for (LdapName child : listEntries(dn, null)) { 158 deleteSubtree(child); 159 } 160 161 // Delete the named entry. 162 dirContext.destroySubcontext(dn); 163 } 164 165 166 167 /** 168 * {@inheritDoc} 169 */ 170 @Override 171 public boolean entryExists(LdapName dn) throws NamingException { 172 String filter = "(objectClass=*)"; 173 SearchControls controls = new SearchControls(); 174 controls.setSearchScope(SearchControls.OBJECT_SCOPE); 175 176 try { 177 NamingEnumeration<SearchResult> results = dirContext.search(dn, filter, 178 controls); 179 if (results.hasMore()) { 180 return true; 181 } 182 } catch (NameNotFoundException e) { 183 // Fall through - entry not found. 184 } 185 return false; 186 } 187 188 189 190 /** 191 * {@inheritDoc} 192 */ 193 @Override 194 public Collection<LdapName> listEntries(LdapName dn, String filter) 195 throws NamingException { 196 if (filter == null) { 197 filter = "(objectClass=*)"; 198 } 199 200 SearchControls controls = new SearchControls(); 201 controls.setSearchScope(SearchControls.ONELEVEL_SCOPE); 202 203 List<LdapName> children = new LinkedList<LdapName>(); 204 NamingEnumeration<SearchResult> results = dirContext.search(dn, filter, 205 controls); 206 while (results.hasMore()) { 207 SearchResult sr = results.next(); 208 LdapName child = new LdapName(dn.getRdns()); 209 child.add(new Rdn(sr.getName())); 210 children.add(child); 211 } 212 213 return children; 214 } 215 216 217 218 /** 219 * {@inheritDoc} 220 */ 221 @Override 222 public void modifyEntry(LdapName dn, Attributes mods) throws NamingException { 223 ModificationItem[] modList = new ModificationItem[mods.size()]; 224 NamingEnumeration<? extends Attribute> ne = mods.getAll(); 225 for (int i = 0; ne.hasMore(); i++) { 226 ModificationItem modItem = new ModificationItem( 227 DirContext.REPLACE_ATTRIBUTE, ne.next()); 228 modList[i] = modItem; 229 } 230 dirContext.modifyAttributes(dn, modList); 231 } 232 233 234 235 /** 236 * {@inheritDoc} 237 */ 238 @Override 239 public Attributes readEntry(LdapName dn, Collection<String> attrIds) 240 throws NamingException { 241 String[] attrIdList = attrIds.toArray(new String[attrIds.size()]); 242 return dirContext.getAttributes(dn, attrIdList); 243 } 244 245 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override 251 public void unbind() { 252 try { 253 dirContext.close(); 254 } catch (NamingException e) { 255 // nothing to do 256 } 257 } 258 }