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