View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.jndi;
18  
19  
20  import java.io.IOException;
21  import java.io.Serializable;
22  import java.text.ParseException;
23  import java.util.Hashtable;
24  
25  import javax.naming.Name;
26  import javax.naming.NamingEnumeration;
27  import javax.naming.NamingException;
28  import javax.naming.Reference;
29  import javax.naming.Referenceable;
30  import javax.naming.directory.Attribute;
31  import javax.naming.directory.Attributes;
32  import javax.naming.directory.DirContext;
33  import javax.naming.directory.InvalidSearchFilterException;
34  import javax.naming.directory.ModificationItem;
35  import javax.naming.directory.SearchControls;
36  import javax.naming.ldap.Control;
37  import javax.naming.spi.DirStateFactory;
38  import javax.naming.spi.DirectoryManager;
39  
40  import org.apache.ldap.common.filter.BranchNode;
41  import org.apache.ldap.common.filter.ExprNode;
42  import org.apache.ldap.common.filter.FilterParser;
43  import org.apache.ldap.common.filter.FilterParserImpl;
44  import org.apache.ldap.common.filter.PresenceNode;
45  import org.apache.ldap.common.filter.SimpleNode;
46  import org.apache.ldap.common.name.LdapName;
47  import org.apache.ldap.common.util.NamespaceTools;
48  import org.apache.ldap.server.authn.LdapPrincipal;
49  import org.apache.ldap.server.partition.ContextPartitionNexus;
50  
51  
52  /***
53   * The DirContext implementation for the Server Side JNDI LDAP provider.
54   *
55   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
56   * @version $Rev: 264732 $
57   */
58  public abstract class ServerDirContext extends ServerContext implements DirContext
59  {
60      
61      
62      // ------------------------------------------------------------------------
63      // Constructors
64      // ------------------------------------------------------------------------
65      
66      /***
67       * Creates a new ServerDirContext by reading the PROVIDER_URL to resolve the
68       * distinguished name for this context.
69       *
70       * @param service the parent service that manages this context
71       * @param env the environment used for this context
72       * @throws NamingException if something goes wrong
73       */
74      public ServerDirContext( ContextFactoryService service, Hashtable env ) throws NamingException
75      {
76          super( service, env );
77      }
78  
79  
80      /***
81       * Creates a new ServerDirContext with a distinguished name which is used to
82       * set the PROVIDER_URL to the distinguished name for this context.
83       *
84       * @param principal the principal which is propagated
85       * @param nexusProxy the intercepting proxy to the nexus
86       * @param env the environment properties used by this context
87       * @param dn the distinguished name of this context
88       */
89      protected ServerDirContext( LdapPrincipal principal, ContextPartitionNexus nexusProxy, Hashtable env, Name dn )
90      {
91          super( principal, nexusProxy, env, dn );
92      }
93  
94  
95      // ------------------------------------------------------------------------
96      // DirContext Implementations
97      // ------------------------------------------------------------------------
98  
99  
100     /***
101      * @see javax.naming.directory.DirContext#getAttributes(java.lang.String)
102      */
103     public Attributes getAttributes( String name ) throws NamingException
104     {
105         return getAttributes( new LdapName( name ) );
106     }
107     
108 
109     /***
110      * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name)
111      */
112     public Attributes getAttributes( Name name ) throws NamingException
113     {
114         return getNexusProxy().lookup( buildTarget( name ) );
115     }
116 
117 
118     /***
119      * @see javax.naming.directory.DirContext#getAttributes(java.lang.String,
120      *      java.lang.String[])
121      */
122     public Attributes getAttributes( String name, String[] attrIds ) throws NamingException
123     {
124         return getAttributes( new LdapName( name ), attrIds );
125     }
126 
127 
128     /***
129      * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name,
130      *      java.lang.String[])
131      */
132     public Attributes getAttributes( Name name, String[] attrIds ) throws NamingException
133     {
134         return getNexusProxy().lookup( buildTarget( name ), attrIds );
135     }
136     
137 
138     /***
139      * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
140      *      int, javax.naming.directory.Attributes)
141      */
142     public void modifyAttributes( String name, int modOp, Attributes attrs ) throws NamingException
143     {
144         modifyAttributes( new LdapName( name ), modOp, attrs );
145     }
146 
147 
148     /***
149      * @see javax.naming.directory.DirContext#modifyAttributes(
150      * javax.naming.Name,int, javax.naming.directory.Attributes)
151      */
152     public void modifyAttributes( Name name, int modOp, Attributes attrs ) throws NamingException
153     {
154         getNexusProxy().modify( buildTarget( name ), modOp, attrs );
155     }
156 
157 
158     /***
159      * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
160      *      javax.naming.directory.ModificationItem[])
161      */
162     public void modifyAttributes( String name, ModificationItem[] mods ) throws NamingException
163     {
164         modifyAttributes( new LdapName( name ), mods );
165     }
166 
167 
168     /***
169      * @see javax.naming.directory.DirContext#modifyAttributes(
170      * javax.naming.Name, javax.naming.directory.ModificationItem[])
171      */
172     public void modifyAttributes( Name name, ModificationItem[] mods ) throws NamingException
173     {
174         getNexusProxy().modify( buildTarget( name ), mods );
175     }
176     
177 
178     /***
179      * @see javax.naming.directory.DirContext#bind(java.lang.String,
180      *      java.lang.Object, javax.naming.directory.Attributes)
181      */
182     public void bind( String name, Object obj, Attributes attrs ) throws NamingException
183     {
184         bind( new LdapName( name ), obj, attrs );
185     }
186 
187 
188     /***
189      * @see javax.naming.directory.DirContext#bind(javax.naming.Name,
190      *      java.lang.Object, javax.naming.directory.Attributes)
191      */
192     public void bind( Name name, Object obj, Attributes attrs ) throws NamingException
193     {
194         if ( null == obj && null == attrs )
195         {
196             throw new NamingException( "Both obj and attrs args are null. "
197                 + "At least one of these parameters must not be null." );
198         }
199 
200         // A null attrs defaults this to the Context.bind() operation
201         if ( null == attrs )
202         {
203             super.bind( name, obj );
204 
205             return;
206         }
207 
208         // No object binding so we just add the attributes
209         if ( null == obj )
210         {
211             Attributes clone = ( Attributes ) attrs.clone();
212 
213             Name target = buildTarget( name );
214 
215             getNexusProxy().add( target.toString(), target, clone );
216 
217             return;
218         }
219 
220         // First, use state factories to do a transformation
221         DirStateFactory.Result res = DirectoryManager.getStateToBind( obj, name, this, getEnvironment(), attrs );
222 
223         Attributes outAttrs = res.getAttributes();
224 
225         if ( outAttrs != attrs )
226         {
227             Name target = buildTarget( name );
228 
229             Attributes attributes = ( Attributes ) attrs.clone();
230 
231             if ( outAttrs != null && outAttrs.size() > 0 )
232             {
233                 NamingEnumeration list = outAttrs.getAll();
234 
235                 while ( list.hasMore() )
236                 {
237                     attributes.put( ( Attribute ) list.next() );
238                 }
239             }
240 
241             getNexusProxy().add( target.toString(), target, attributes );
242 
243             return;
244         }
245 
246         // Check for Referenceable
247         if ( obj instanceof Referenceable )
248         {
249             obj = ( ( Referenceable ) obj ).getReference();
250 
251             throw new NamingException( "Do not know how to store Referenceables yet!" );
252         }
253 
254         // Store different formats
255         if ( obj instanceof Reference )
256         {
257             // Store as ref and add outAttrs
258 
259             throw new NamingException( "Do not know how to store References yet!" );
260         }
261         else if ( obj instanceof Serializable )
262         {
263             // Serialize and add outAttrs
264 
265             Attributes attributes = ( Attributes ) attrs.clone();
266 
267             if ( outAttrs != null && outAttrs.size() > 0 )
268             {
269                 NamingEnumeration list = outAttrs.getAll();
270 
271                 while ( list.hasMore() )
272                 {
273                     attributes.put( ( Attribute ) list.next() );
274                 }
275             }
276 
277             Name target = buildTarget( name );
278 
279             // Serialize object into entry attributes and add it.
280 
281             JavaLdapSupport.serialize( attributes, obj );
282 
283             getNexusProxy().add( target.toString(), target, attributes );
284         }
285         else if ( obj instanceof DirContext )
286         {
287             // Grab attributes and merge with outAttrs
288 
289             Attributes attributes = ( ( DirContext ) obj ).getAttributes( "" );
290 
291             if ( outAttrs != null && outAttrs.size() > 0 )
292             {
293                 NamingEnumeration list = outAttrs.getAll();
294 
295                 while ( list.hasMore() )
296                 {
297                     attributes.put( ( Attribute ) list.next() );
298                 }
299             }
300 
301             Name target = buildTarget( name );
302 
303             getNexusProxy().add( target.toString(), target, attributes );
304         }
305         else
306         {
307             throw new NamingException( "Can't find a way to bind: " + obj );
308         }
309     }
310 
311 
312     /***
313      * @see javax.naming.directory.DirContext#rebind(java.lang.String,
314      *      java.lang.Object, javax.naming.directory.Attributes)
315      */
316     public void rebind( String name, Object obj, Attributes attrs ) throws NamingException
317     {
318         rebind( new LdapName( name ), obj, attrs );
319     }
320 
321 
322     /***
323      * @see javax.naming.directory.DirContext#rebind(javax.naming.Name,
324      *      java.lang.Object, javax.naming.directory.Attributes)
325      */
326     public void rebind( Name name, Object obj, Attributes attrs ) throws NamingException
327     {
328         Name target = buildTarget( name );
329 
330         if ( getNexusProxy().hasEntry( target ) )
331         {
332             getNexusProxy().delete( target );
333         }
334 
335         bind( name, obj, attrs );
336     }
337 
338 
339     /***
340      * @see javax.naming.directory.DirContext#createSubcontext(java.lang.String,
341      *      javax.naming.directory.Attributes)
342      */
343     public DirContext createSubcontext( String name, Attributes attrs ) throws NamingException
344     {
345         return createSubcontext( new LdapName( name ), attrs );
346     }
347 
348 
349     /***
350      * @see javax.naming.directory.DirContext#createSubcontext(
351      * javax.naming.Name, javax.naming.directory.Attributes)
352      */
353     public DirContext createSubcontext( Name name, Attributes attrs ) throws NamingException
354     {
355         if ( null == attrs )
356         {
357             return ( DirContext ) super.createSubcontext( name );
358         }
359 
360         LdapName target = buildTarget( name );
361 
362         String rdn = name.get( name.size() - 1 );
363 
364         String rdnAttribute = NamespaceTools.getRdnAttribute( rdn );
365 
366         String rdnValue = NamespaceTools.getRdnValue( rdn );
367 
368         // Clone the attributes and add the Rdn attributes
369         Attributes attributes = ( Attributes ) attrs.clone();
370 
371         boolean doRdnPut = attributes.get( rdnAttribute ) == null;
372 
373         doRdnPut = doRdnPut || attributes.get( rdnAttribute ).size() == 0;
374 
375         doRdnPut = doRdnPut || ! attributes.get( rdnAttribute ).contains( rdnValue );
376 
377         if ( doRdnPut )
378         {
379             attributes.put( rdnAttribute, rdnValue );
380         }
381 
382         // Add the new context to the server which as a side effect adds
383         getNexusProxy().add( target.toString(), target, attributes );
384 
385         // Initialize the new context
386         ServerLdapContext ctx = new ServerLdapContext( getPrincipal(), getNexusProxy(), getEnvironment(), target );
387 
388         Control [] controls = ( ( ServerLdapContext ) this ).getRequestControls();
389 
390         if ( controls != null )
391         {
392         	controls = ( Control[] ) controls.clone();
393         }
394         else
395         {
396         	controls = new Control[0];
397         }
398 
399         ctx.setRequestControls( controls );
400 
401         return ctx;
402     }
403 
404 
405     /***
406      * Presently unsupported operation!
407      */
408     public DirContext getSchema( Name name ) throws NamingException
409     {
410         throw new UnsupportedOperationException();
411     }
412 
413 
414     /***
415      * Presently unsupported operation!
416      */
417     public DirContext getSchema( String name ) throws NamingException
418     {
419         throw new UnsupportedOperationException();
420     }
421 
422 
423     /***
424      * Presently unsupported operation!
425      */
426     public DirContext getSchemaClassDefinition( Name name ) throws NamingException
427     {
428         throw new UnsupportedOperationException();
429     }
430 
431 
432     /***
433      * Presently unsupported operation!
434      */
435     public DirContext getSchemaClassDefinition( String name ) throws NamingException
436     {
437         throw new UnsupportedOperationException();
438     }
439 
440 
441     // ------------------------------------------------------------------------
442     // Search Operation Implementations
443     // ------------------------------------------------------------------------
444 
445 
446     /***
447      * @see javax.naming.directory.DirContext#search(java.lang.String,
448      *      javax.naming.directory.Attributes)
449      */
450     public NamingEnumeration search( String name, Attributes matchingAttributes )
451             throws NamingException
452     {
453         return search( new LdapName( name ), matchingAttributes, null );
454     }
455 
456 
457     /***
458      * @see javax.naming.directory.DirContext#search(javax.naming.Name,
459      *      javax.naming.directory.Attributes)
460      */
461     public NamingEnumeration search( Name name, Attributes matchingAttributes )
462             throws NamingException
463     {
464         return search( name, matchingAttributes, null );
465     }
466 
467 
468     /***
469      * @see javax.naming.directory.DirContext#search(java.lang.String,
470      *      javax.naming.directory.Attributes, java.lang.String[])
471      */
472     public NamingEnumeration search( String name, Attributes matchingAttributes, String[] attributesToReturn ) throws NamingException
473     {
474         return search( new LdapName( name ), matchingAttributes, attributesToReturn );
475     }
476 
477 
478     /***
479      * @see javax.naming.directory.DirContext#search(javax.naming.Name,
480      *      javax.naming.directory.Attributes, java.lang.String[])
481      */
482     public NamingEnumeration search( Name name, Attributes matchingAttributes, String[] attributesToReturn ) throws NamingException
483     {
484         SearchControls ctls = new SearchControls();
485 
486         LdapName target = buildTarget( name );
487 
488         // If we need to return specific attributes add em to the SearchControls
489         if ( null != attributesToReturn )
490         {
491             ctls.setReturningAttributes( attributesToReturn );
492         } 
493 
494         // If matchingAttributes is null/empty use a match for everything filter
495         if ( null == matchingAttributes || matchingAttributes.size() <= 0 )
496         {
497             PresenceNode filter = new PresenceNode( "objectClass" );
498 
499             return getNexusProxy().search( target , getEnvironment(), filter, ctls );
500         }
501 
502         /*
503          * Go through the set of attributes using each attribute value pair as 
504          * an attribute value assertion within one big AND filter expression.
505          */
506         Attribute attr = null;
507 
508         SimpleNode node = null;
509 
510         BranchNode filter = new BranchNode( BranchNode.AND );
511 
512         NamingEnumeration list = matchingAttributes.getAll();
513 
514         // Loop through each attribute value pair
515         while ( list.hasMore() )
516         {
517             attr = ( Attribute ) list.next();
518             
519             /*
520              * According to JNDI if an attribute in the matchingAttributes
521              * list does not have any values then we match for just the presence
522              * of the attribute in the entry
523              */
524             if ( attr.size() == 0 )
525             {
526                 filter.addNode( new PresenceNode( attr.getID() ) );
527 
528                 continue;
529             }
530             
531             /*
532              * With 1 or more value we build a set of simple nodes and add them
533              * to the AND node - each attribute value pair is a simple AVA node.
534              */
535             for ( int ii = 0; ii < attr.size(); ii++ )
536             {
537                 Object val = attr.get( ii );
538                 
539                 // Add simpel AVA node if its value is a String 
540                 if ( val instanceof String )
541                 {
542                     node = new SimpleNode( attr.getID(), ( String ) val, SimpleNode.EQUALITY );
543 
544                     filter.addNode( node );
545                 }
546             }
547         }
548 
549         return getNexusProxy().search( target , getEnvironment(), filter, ctls );
550     }
551 
552 
553     /***
554      * @see javax.naming.directory.DirContext#search(java.lang.String,
555      *      java.lang.String, javax.naming.directory.SearchControls)
556      */
557     public NamingEnumeration search( String name, String filter, SearchControls cons )
558             throws NamingException
559     {
560         return search( new LdapName( name ), filter, cons );
561     }
562 
563 
564     /***
565      * @see javax.naming.directory.DirContext#search(javax.naming.Name,
566      *      java.lang.String, javax.naming.directory.SearchControls)
567      */
568     public NamingEnumeration search( Name name, String filter, SearchControls cons )
569             throws NamingException
570     {
571         ExprNode filterNode = null;
572 
573         LdapName target = buildTarget( name );
574 
575         if ( filter == null && getEnvironment().containsKey( "__filter__" ) )
576         {
577             filterNode = ( ExprNode ) getEnvironment().get( "__filter__" );
578         }
579         else
580         {
581             try
582             {
583                 FilterParser parser = new FilterParserImpl();
584 
585                 filterNode = parser.parse( filter );
586             }
587             catch ( ParseException pe )
588             {
589                 InvalidSearchFilterException isfe =
590                     new InvalidSearchFilterException (
591                     "Encountered parse exception while parsing the filter: '"
592                     + filter + "'" );
593 
594                 isfe.setRootCause( pe );
595 
596                 throw isfe;
597             }
598             catch ( IOException ioe )
599             {
600                 NamingException ne = new NamingException(
601                     "Parser failed with IO exception on filter: '"
602                     + filter + "'" );
603                 ne.setRootCause( ioe );
604                 throw ne;
605             }
606         }
607 
608         return getNexusProxy().search( target , getEnvironment(), filterNode, cons );
609     }
610 
611 
612     /***
613      * @see javax.naming.directory.DirContext#search(java.lang.String,
614      *      java.lang.String, java.lang.Object[],
615      *      javax.naming.directory.SearchControls)
616      */
617     public NamingEnumeration search( String name, String filterExpr,
618         Object[] filterArgs, SearchControls cons ) throws NamingException
619     {
620         return search( new LdapName( name ), filterExpr, filterArgs, cons );
621     }
622 
623 
624     /***
625      * @see javax.naming.directory.DirContext#search(javax.naming.Name,
626      *      java.lang.String, java.lang.Object[],
627      *      javax.naming.directory.SearchControls)
628      */
629     public NamingEnumeration search( Name name, String filterExpr, Object[] filterArgs, SearchControls cons ) throws NamingException
630     {
631         int start;
632 
633         StringBuffer buf = new StringBuffer( filterExpr );
634         
635         // Scan until we hit the end of the string buffer 
636         for ( int ii = 0; ii < buf.length(); ii++ )
637         {
638             // Advance until we hit the start of a variable
639             while ( '{' != buf.charAt( ii ) )
640             {
641                 ii++;
642             }
643             
644             // Record start of variable at '{'
645             start = ii;
646             
647             // Advance to the end of a variable at '}'
648             while ( '}' != buf.charAt( ii ) )
649             {
650                 ii++;
651             }
652             
653             /*
654              * Replace the '{ i }' with the string representation of the value
655              * held in the filterArgs array at index index.
656              */           
657             buf.replace( start, ii + 1, filterArgs[ii].toString() );
658         }
659         
660         return search( name, buf.toString(), cons );
661     }
662 }