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
26package com.sun.jndi.url.ldap;
27
28import javax.naming.spi.ResolveResult;
29import javax.naming.*;
30import javax.naming.directory.*;
31import java.util.Hashtable;
32import java.util.StringTokenizer;
33import com.sun.jndi.ldap.LdapURL;
34
35/**
36 * An LDAP URL context.
37 *
38 * @author Rosanna Lee
39 * @author Scott Seligman
40 */
41
42final 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}
634