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    }