1 /*
   2  * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.jndi.url.ldap;
  27 
  28 import javax.naming.spi.ResolveResult;
  29 import javax.naming.*;
  30 import javax.naming.directory.*;
  31 import java.util.Hashtable;
  32 import java.util.StringTokenizer;
  33 import com.sun.jndi.ldap.LdapURL;
  34 
  35 /**
  36  * An LDAP URL context.
  37  *
  38  * @author Rosanna Lee
  39  * @author Scott Seligman
  40  */
  41 
  42 final public class ldapURLContext
  43         extends com.sun.jndi.toolkit.url.GenericURLDirContext {
  44 
  45     ldapURLContext(Hashtable<?,?> env) {
  46         super(env);
  47     }
  48 
  49     /**
  50       * Resolves 'name' into a target context with remaining name.
  51       * It only resolves the hostname/port number. The remaining name
  52       * contains the root DN.
  53       *
  54       * For example, with a LDAP URL "ldap://localhost:389/o=widget,c=us",
  55       * this method resolves "ldap://localhost:389/" to the root LDAP
  56       * context on the server 'localhost' on port 389,
  57       * and returns as the remaining name "o=widget, c=us".
  58       */
  59     protected ResolveResult getRootURLContext(String name, Hashtable<?,?> env)
  60     throws NamingException {
  61         return ldapURLContextFactory.getUsingURLIgnoreRootDN(name, env);
  62     }
  63 
  64     /**
  65      * Return the suffix of an ldap url.
  66      * prefix parameter is ignored.
  67      */
  68     protected Name getURLSuffix(String prefix, String url)
  69         throws NamingException {
  70 
  71         LdapURL ldapUrl = new LdapURL(url);
  72         String dn = (ldapUrl.getDN() != null? ldapUrl.getDN() : "");
  73 
  74         // Represent DN as empty or single-component composite name.
  75         CompositeName remaining = new CompositeName();
  76         if (!"".equals(dn)) {
  77             // if nonempty, add component
  78             remaining.add(dn);
  79         }
  80         return remaining;
  81     }
  82 
  83     /*
  84      * Override context operations.
  85      * Test for presence of LDAP URL query components in the name argument.
  86      * Query components are permitted only for search operations and only
  87      * when the name has a single component.
  88      */
  89 
  90     public Object lookup(String name) throws NamingException {
  91         if (LdapURL.hasQueryComponents(name)) {
  92             throw new InvalidNameException(name);
  93         } else {
  94             return super.lookup(name);
  95         }
  96     }
  97 
  98     public Object lookup(Name name) throws NamingException {
  99         if (LdapURL.hasQueryComponents(name.get(0))) {
 100             throw new InvalidNameException(name.toString());
 101         } else {
 102             return super.lookup(name);
 103         }
 104     }
 105 
 106     public void bind(String name, Object obj) throws NamingException {
 107         if (LdapURL.hasQueryComponents(name)) {
 108             throw new InvalidNameException(name);
 109         } else {
 110             super.bind(name, obj);
 111         }
 112     }
 113 
 114     public void bind(Name name, Object obj) throws NamingException {
 115         if (LdapURL.hasQueryComponents(name.get(0))) {
 116             throw new InvalidNameException(name.toString());
 117         } else {
 118             super.bind(name, obj);
 119         }
 120     }
 121 
 122     public void rebind(String name, Object obj) throws NamingException {
 123         if (LdapURL.hasQueryComponents(name)) {
 124             throw new InvalidNameException(name);
 125         } else {
 126             super.rebind(name, obj);
 127         }
 128     }
 129 
 130     public void rebind(Name name, Object obj) throws NamingException {
 131         if (LdapURL.hasQueryComponents(name.get(0))) {
 132             throw new InvalidNameException(name.toString());
 133         } else {
 134             super.rebind(name, obj);
 135         }
 136     }
 137 
 138     public void unbind(String name) throws NamingException {
 139         if (LdapURL.hasQueryComponents(name)) {
 140             throw new InvalidNameException(name);
 141         } else {
 142             super.unbind(name);
 143         }
 144     }
 145 
 146     public void unbind(Name name) throws NamingException {
 147         if (LdapURL.hasQueryComponents(name.get(0))) {
 148             throw new InvalidNameException(name.toString());
 149         } else {
 150             super.unbind(name);
 151         }
 152     }
 153 
 154     public void rename(String oldName, String newName) throws NamingException {
 155         if (LdapURL.hasQueryComponents(oldName)) {
 156             throw new InvalidNameException(oldName);
 157         } else if (LdapURL.hasQueryComponents(newName)) {
 158             throw new InvalidNameException(newName);
 159         } else {
 160             super.rename(oldName, newName);
 161         }
 162     }
 163 
 164     public void rename(Name oldName, Name newName) throws NamingException {
 165         if (LdapURL.hasQueryComponents(oldName.get(0))) {
 166             throw new InvalidNameException(oldName.toString());
 167         } else if (LdapURL.hasQueryComponents(newName.get(0))) {
 168             throw new InvalidNameException(newName.toString());
 169         } else {
 170             super.rename(oldName, newName);
 171         }
 172     }
 173 
 174     public NamingEnumeration<NameClassPair> list(String name)
 175             throws NamingException {
 176         if (LdapURL.hasQueryComponents(name)) {
 177             throw new InvalidNameException(name);
 178         } else {
 179             return super.list(name);
 180         }
 181     }
 182 
 183     public NamingEnumeration<NameClassPair> list(Name name)
 184             throws NamingException {
 185         if (LdapURL.hasQueryComponents(name.get(0))) {
 186             throw new InvalidNameException(name.toString());
 187         } else {
 188             return super.list(name);
 189         }
 190     }
 191 
 192     public NamingEnumeration<Binding> listBindings(String name)
 193             throws NamingException {
 194         if (LdapURL.hasQueryComponents(name)) {
 195             throw new InvalidNameException(name);
 196         } else {
 197             return super.listBindings(name);
 198         }
 199     }
 200 
 201     public NamingEnumeration<Binding> listBindings(Name name)
 202             throws NamingException {
 203         if (LdapURL.hasQueryComponents(name.get(0))) {
 204             throw new InvalidNameException(name.toString());
 205         } else {
 206             return super.listBindings(name);
 207         }
 208     }
 209 
 210     public void destroySubcontext(String name) throws NamingException {
 211         if (LdapURL.hasQueryComponents(name)) {
 212             throw new InvalidNameException(name);
 213         } else {
 214             super.destroySubcontext(name);
 215         }
 216     }
 217 
 218     public void destroySubcontext(Name name) throws NamingException {
 219         if (LdapURL.hasQueryComponents(name.get(0))) {
 220             throw new InvalidNameException(name.toString());
 221         } else {
 222             super.destroySubcontext(name);
 223         }
 224     }
 225 
 226     public Context createSubcontext(String name) throws NamingException {
 227         if (LdapURL.hasQueryComponents(name)) {
 228             throw new InvalidNameException(name);
 229         } else {
 230             return super.createSubcontext(name);
 231         }
 232     }
 233 
 234     public Context createSubcontext(Name name) throws NamingException {
 235         if (LdapURL.hasQueryComponents(name.get(0))) {
 236             throw new InvalidNameException(name.toString());
 237         } else {
 238             return super.createSubcontext(name);
 239         }
 240     }
 241 
 242     public Object lookupLink(String name) throws NamingException {
 243         if (LdapURL.hasQueryComponents(name)) {
 244             throw new InvalidNameException(name);
 245         } else {
 246             return super.lookupLink(name);
 247         }
 248     }
 249 
 250     public Object lookupLink(Name name) throws NamingException {
 251         if (LdapURL.hasQueryComponents(name.get(0))) {
 252             throw new InvalidNameException(name.toString());
 253         } else {
 254             return super.lookupLink(name);
 255         }
 256     }
 257 
 258     public NameParser getNameParser(String name) throws NamingException {
 259         if (LdapURL.hasQueryComponents(name)) {
 260             throw new InvalidNameException(name);
 261         } else {
 262             return super.getNameParser(name);
 263         }
 264     }
 265 
 266     public NameParser getNameParser(Name name) throws NamingException {
 267         if (LdapURL.hasQueryComponents(name.get(0))) {
 268             throw new InvalidNameException(name.toString());
 269         } else {
 270             return super.getNameParser(name);
 271         }
 272     }
 273 
 274     public String composeName(String name, String prefix)
 275         throws NamingException {
 276         if (LdapURL.hasQueryComponents(name)) {
 277             throw new InvalidNameException(name);
 278         } else if (LdapURL.hasQueryComponents(prefix)) {
 279             throw new InvalidNameException(prefix);
 280         } else {
 281             return super.composeName(name, prefix);
 282         }
 283     }
 284 
 285     public Name composeName(Name name, Name prefix) throws NamingException {
 286         if (LdapURL.hasQueryComponents(name.get(0))) {
 287             throw new InvalidNameException(name.toString());
 288         } else if (LdapURL.hasQueryComponents(prefix.get(0))) {
 289             throw new InvalidNameException(prefix.toString());
 290         } else {
 291             return super.composeName(name, prefix);
 292         }
 293     }
 294 
 295     public Attributes getAttributes(String name) throws NamingException {
 296         if (LdapURL.hasQueryComponents(name)) {
 297             throw new InvalidNameException(name);
 298         } else {
 299             return super.getAttributes(name);
 300         }
 301     }
 302 
 303     public Attributes getAttributes(Name name) throws NamingException  {
 304         if (LdapURL.hasQueryComponents(name.get(0))) {
 305             throw new InvalidNameException(name.toString());
 306         } else {
 307             return super.getAttributes(name);
 308         }
 309     }
 310 
 311     public Attributes getAttributes(String name, String[] attrIds)
 312         throws NamingException {
 313         if (LdapURL.hasQueryComponents(name)) {
 314             throw new InvalidNameException(name);
 315         } else {
 316             return super.getAttributes(name, attrIds);
 317         }
 318     }
 319 
 320     public Attributes getAttributes(Name name, String[] attrIds)
 321         throws NamingException {
 322         if (LdapURL.hasQueryComponents(name.get(0))) {
 323             throw new InvalidNameException(name.toString());
 324         } else {
 325             return super.getAttributes(name, attrIds);
 326         }
 327     }
 328 
 329     public void modifyAttributes(String name, int mod_op, Attributes attrs)
 330         throws NamingException {
 331         if (LdapURL.hasQueryComponents(name)) {
 332             throw new InvalidNameException(name);
 333         } else {
 334             super.modifyAttributes(name, mod_op, attrs);
 335         }
 336     }
 337 
 338     public void modifyAttributes(Name name, int mod_op, Attributes attrs)
 339         throws NamingException {
 340         if (LdapURL.hasQueryComponents(name.get(0))) {
 341             throw new InvalidNameException(name.toString());
 342         } else {
 343             super.modifyAttributes(name, mod_op, attrs);
 344         }
 345     }
 346 
 347     public void modifyAttributes(String name, ModificationItem[] mods)
 348         throws NamingException {
 349         if (LdapURL.hasQueryComponents(name)) {
 350             throw new InvalidNameException(name);
 351         } else {
 352             super.modifyAttributes(name, mods);
 353         }
 354     }
 355 
 356     public void modifyAttributes(Name name, ModificationItem[] mods)
 357         throws NamingException  {
 358         if (LdapURL.hasQueryComponents(name.get(0))) {
 359             throw new InvalidNameException(name.toString());
 360         } else {
 361             super.modifyAttributes(name, mods);
 362         }
 363     }
 364 
 365     public void bind(String name, Object obj, Attributes attrs)
 366         throws NamingException {
 367         if (LdapURL.hasQueryComponents(name)) {
 368             throw new InvalidNameException(name);
 369         } else {
 370             super.bind(name, obj, attrs);
 371         }
 372     }
 373 
 374     public void bind(Name name, Object obj, Attributes attrs)
 375         throws NamingException {
 376         if (LdapURL.hasQueryComponents(name.get(0))) {
 377             throw new InvalidNameException(name.toString());
 378         } else {
 379             super.bind(name, obj, attrs);
 380         }
 381     }
 382 
 383     public void rebind(String name, Object obj, Attributes attrs)
 384         throws NamingException {
 385         if (LdapURL.hasQueryComponents(name)) {
 386             throw new InvalidNameException(name);
 387         } else {
 388             super.rebind(name, obj, attrs);
 389         }
 390     }
 391 
 392     public void rebind(Name name, Object obj, Attributes attrs)
 393         throws NamingException {
 394         if (LdapURL.hasQueryComponents(name.get(0))) {
 395             throw new InvalidNameException(name.toString());
 396         } else {
 397             super.rebind(name, obj, attrs);
 398         }
 399     }
 400 
 401     public DirContext createSubcontext(String name, Attributes attrs)
 402         throws NamingException {
 403         if (LdapURL.hasQueryComponents(name)) {
 404             throw new InvalidNameException(name);
 405         } else {
 406             return super.createSubcontext(name, attrs);
 407         }
 408     }
 409 
 410     public DirContext createSubcontext(Name name, Attributes attrs)
 411         throws NamingException {
 412         if (LdapURL.hasQueryComponents(name.get(0))) {
 413             throw new InvalidNameException(name.toString());
 414         } else {
 415             return super.createSubcontext(name, attrs);
 416         }
 417     }
 418 
 419     public DirContext getSchema(String name) throws NamingException {
 420         if (LdapURL.hasQueryComponents(name)) {
 421             throw new InvalidNameException(name);
 422         } else {
 423             return super.getSchema(name);
 424         }
 425     }
 426 
 427     public DirContext getSchema(Name name) throws NamingException {
 428         if (LdapURL.hasQueryComponents(name.get(0))) {
 429             throw new InvalidNameException(name.toString());
 430         } else {
 431             return super.getSchema(name);
 432         }
 433     }
 434 
 435     public DirContext getSchemaClassDefinition(String name)
 436         throws NamingException {
 437         if (LdapURL.hasQueryComponents(name)) {
 438             throw new InvalidNameException(name);
 439         } else {
 440             return super.getSchemaClassDefinition(name);
 441         }
 442     }
 443 
 444     public DirContext getSchemaClassDefinition(Name name)
 445         throws NamingException {
 446         if (LdapURL.hasQueryComponents(name.get(0))) {
 447             throw new InvalidNameException(name.toString());
 448         } else {
 449             return super.getSchemaClassDefinition(name);
 450         }
 451     }
 452 
 453     // divert the search operation when the LDAP URL has query components
 454     public NamingEnumeration<SearchResult> search(String name,
 455         Attributes matchingAttributes)
 456         throws NamingException {
 457 
 458         if (LdapURL.hasQueryComponents(name)) {
 459             return searchUsingURL(name);
 460         } else {
 461             return super.search(name, matchingAttributes);
 462         }
 463     }
 464 
 465     // divert the search operation when name has a single component
 466     public NamingEnumeration<SearchResult> search(Name name,
 467         Attributes matchingAttributes)
 468         throws NamingException {
 469         if (name.size() == 1) {
 470             return search(name.get(0), matchingAttributes);
 471         } else if (LdapURL.hasQueryComponents(name.get(0))) {
 472             throw new InvalidNameException(name.toString());
 473         } else {
 474             return super.search(name, matchingAttributes);
 475         }
 476     }
 477 
 478     // divert the search operation when the LDAP URL has query components
 479     public NamingEnumeration<SearchResult> search(String name,
 480         Attributes matchingAttributes,
 481         String[] attributesToReturn)
 482         throws NamingException {
 483 
 484         if (LdapURL.hasQueryComponents(name)) {
 485             return searchUsingURL(name);
 486         } else {
 487             return super.search(name, matchingAttributes, attributesToReturn);
 488         }
 489     }
 490 
 491     // divert the search operation when name has a single component
 492     public NamingEnumeration<SearchResult> search(Name name,
 493         Attributes matchingAttributes,
 494         String[] attributesToReturn)
 495         throws NamingException {
 496 
 497         if (name.size() == 1) {
 498             return search(name.get(0), matchingAttributes, attributesToReturn);
 499         } else if (LdapURL.hasQueryComponents(name.get(0))) {
 500             throw new InvalidNameException(name.toString());
 501         } else {
 502             return super.search(name, matchingAttributes, attributesToReturn);
 503         }
 504     }
 505 
 506     // divert the search operation when the LDAP URL has query components
 507     public NamingEnumeration<SearchResult> search(String name,
 508         String filter,
 509         SearchControls cons)
 510         throws NamingException {
 511 
 512         if (LdapURL.hasQueryComponents(name)) {
 513             return searchUsingURL(name);
 514         } else {
 515             return super.search(name, filter, cons);
 516         }
 517     }
 518 
 519     // divert the search operation when name has a single component
 520     public NamingEnumeration<SearchResult> search(Name name,
 521         String filter,
 522         SearchControls cons)
 523         throws NamingException {
 524 
 525         if (name.size() == 1) {
 526             return search(name.get(0), filter, cons);
 527         } else if (LdapURL.hasQueryComponents(name.get(0))) {
 528             throw new InvalidNameException(name.toString());
 529         } else {
 530             return super.search(name, filter, cons);
 531         }
 532     }
 533 
 534     // divert the search operation when the LDAP URL has query components
 535     public NamingEnumeration<SearchResult> search(String name,
 536         String filterExpr,
 537         Object[] filterArgs,
 538         SearchControls cons)
 539         throws NamingException {
 540 
 541         if (LdapURL.hasQueryComponents(name)) {
 542             return searchUsingURL(name);
 543         } else {
 544             return super.search(name, filterExpr, filterArgs, cons);
 545         }
 546     }
 547 
 548     // divert the search operation when name has a single component
 549     public NamingEnumeration<SearchResult> search(Name name,
 550         String filterExpr,
 551         Object[] filterArgs,
 552         SearchControls cons)
 553         throws NamingException {
 554 
 555         if (name.size() == 1) {
 556             return search(name.get(0), filterExpr, filterArgs, cons);
 557         } else if (LdapURL.hasQueryComponents(name.get(0))) {
 558             throw new InvalidNameException(name.toString());
 559         } else {
 560             return super.search(name, filterExpr, filterArgs, cons);
 561         }
 562     }
 563 
 564     // Search using the LDAP URL in name.
 565     // LDAP URL query components override the search arguments.
 566     private NamingEnumeration<SearchResult> searchUsingURL(String name)
 567         throws NamingException {
 568 
 569         LdapURL url = new LdapURL(name);
 570 
 571         ResolveResult res = getRootURLContext(name, myEnv);
 572         DirContext ctx = (DirContext)res.getResolvedObj();
 573         try {
 574             return ctx.search(res.getRemainingName(),
 575                               setFilterUsingURL(url),
 576                               setSearchControlsUsingURL(url));
 577         } finally {
 578             ctx.close();
 579         }
 580     }
 581 
 582     /*
 583      * Initialize a String filter using the LDAP URL filter component.
 584      * If filter is not present in the URL it is initialized to its default
 585      * value as specified in RFC-2255.
 586      */
 587     private static String setFilterUsingURL(LdapURL url) {
 588 
 589         String filter = url.getFilter();
 590 
 591         if (filter == null) {
 592             filter = "(objectClass=*)"; //default value
 593         }
 594         return filter;
 595     }
 596 
 597     /*
 598      * Initialize a SearchControls object using LDAP URL query components.
 599      * Components not present in the URL are initialized to their default
 600      * values as specified in RFC-2255.
 601      */
 602     private static SearchControls setSearchControlsUsingURL(LdapURL url) {
 603 
 604         SearchControls cons = new SearchControls();
 605         String scope = url.getScope();
 606         String attributes = url.getAttributes();
 607 
 608         if (scope == null) {
 609             cons.setSearchScope(SearchControls.OBJECT_SCOPE); //default value
 610         } else {
 611             if (scope.equals("sub")) {
 612                 cons.setSearchScope(SearchControls.SUBTREE_SCOPE);
 613             } else if (scope.equals("one")) {
 614                 cons.setSearchScope(SearchControls.ONELEVEL_SCOPE);
 615             } else if (scope.equals("base")) {
 616                 cons.setSearchScope(SearchControls.OBJECT_SCOPE);
 617             }
 618         }
 619 
 620         if (attributes == null) {
 621             cons.setReturningAttributes(null); //default value
 622         } else {
 623             StringTokenizer tokens = new StringTokenizer(attributes, ",");
 624             int count = tokens.countTokens();
 625             String[] attrs = new String[count];
 626             for (int i = 0; i < count; i ++) {
 627                 attrs[i] = tokens.nextToken();
 628             }
 629             cons.setReturningAttributes(attrs);
 630         }
 631         return cons;
 632     }
 633 }