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