1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.jndi;
18
19
20 import java.io.Serializable;
21 import java.util.HashSet;
22 import java.util.Hashtable;
23 import java.util.Iterator;
24 import java.util.Set;
25
26 import javax.naming.ConfigurationException;
27 import javax.naming.Context;
28 import javax.naming.InvalidNameException;
29 import javax.naming.Name;
30 import javax.naming.NameNotFoundException;
31 import javax.naming.NameParser;
32 import javax.naming.NamingEnumeration;
33 import javax.naming.NamingException;
34 import javax.naming.Reference;
35 import javax.naming.Referenceable;
36 import javax.naming.directory.Attribute;
37 import javax.naming.directory.Attributes;
38 import javax.naming.directory.DirContext;
39 import javax.naming.directory.SearchControls;
40 import javax.naming.event.EventContext;
41 import javax.naming.event.NamingListener;
42 import javax.naming.ldap.Control;
43 import javax.naming.spi.DirStateFactory;
44 import javax.naming.spi.DirectoryManager;
45
46 import org.apache.ldap.common.exception.LdapNoPermissionException;
47 import org.apache.ldap.common.filter.ExprNode;
48 import org.apache.ldap.common.filter.PresenceNode;
49 import org.apache.ldap.common.message.LockableAttributesImpl;
50 import org.apache.ldap.common.name.LdapName;
51 import org.apache.ldap.common.util.NamespaceTools;
52 import org.apache.ldap.server.DirectoryServiceConfiguration;
53 import org.apache.ldap.server.DirectoryService;
54 import org.apache.ldap.server.authn.AuthenticationService;
55 import org.apache.ldap.server.authn.LdapPrincipal;
56 import org.apache.ldap.server.partition.DirectoryPartitionNexus;
57 import org.apache.ldap.server.partition.DirectoryPartitionNexusProxy;
58
59
60 /***
61 * A non-federated abstract Context implementation.
62 *
63 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
64 * @version $Rev: 328280 $
65 */
66 public abstract class ServerContext implements EventContext
67 {
68 /*** property key used for deleting the old RDN on a rename */
69 public static final String DELETE_OLD_RDN_PROP = "java.naming.ldap.deleteRDN";
70
71 /*** The interceptor proxy to the backend nexus */
72 private final DirectoryPartitionNexus nexusProxy;
73
74 /*** The cloned environment used by this Context */
75 private final Hashtable env;
76
77 /*** The distinguished name of this Context */
78 private final LdapName dn;
79
80 /*** The set of registered NamingListeners */
81 private final Set listeners = new HashSet();
82
83 /*** The Principal associated with this context */
84 private LdapPrincipal principal;
85
86
87
88
89
90
91 /***
92 * Must be called by all subclasses to initialize the nexus proxy and the
93 * environment settings to be used by this Context implementation. This
94 * specific contstructor relies on the presence of the {@link
95 * Context#PROVIDER_URL} key and value to determine the distinguished name
96 * of the newly created context. It also checks to make sure the
97 * referenced name actually exists within the system. This constructor
98 * is used for all InitialContext requests.
99 *
100 * @param service the parent service that manages this context
101 * @param env the environment properties used by this context.
102 * @throws NamingException if the environment parameters are not set
103 * correctly.
104 */
105 protected ServerContext( DirectoryService service, Hashtable env ) throws NamingException
106 {
107
108 this.nexusProxy = new DirectoryPartitionNexusProxy( this, service );
109
110 DirectoryServiceConfiguration cfg = service.getConfiguration();
111
112 this.env = ( Hashtable ) cfg.getEnvironment().clone();
113 this.env.putAll( env );
114
115
116
117
118
119 if ( ! env.containsKey( Context.PROVIDER_URL ) )
120 {
121 String msg = "Expected property " + Context.PROVIDER_URL;
122 msg += " but could not find it in env!";
123
124 throw new ConfigurationException( msg );
125 }
126
127 String url = ( String ) env.get( Context.PROVIDER_URL );
128
129 if ( url == null )
130 {
131 String msg = "Expected value for property " + Context.PROVIDER_URL;
132 msg += " but it was set to null in env!";
133
134 throw new ConfigurationException( msg );
135 }
136
137 dn = new LdapName( url );
138
139 if ( ! nexusProxy.hasEntry( dn ) )
140 {
141 throw new NameNotFoundException( dn + " does not exist" );
142 }
143 }
144
145
146 /***
147 * Must be called by all subclasses to initialize the nexus proxy and the
148 * environment settings to be used by this Context implementation. This
149 * constructor is used to propagate new contexts from existing contexts.
150 *
151 * @param principal the directory user principal that is propagated
152 * @param nexusProxy the intercepting proxy to the nexus
153 * @param env the environment properties used by this context
154 * @param dn the distinguished name of this context
155 */
156 protected ServerContext( LdapPrincipal principal, DirectoryPartitionNexus nexusProxy, Hashtable env, Name dn )
157 {
158 this.dn = ( LdapName ) dn.clone();
159
160 this.env = ( Hashtable ) env.clone();
161
162 this.env.put( PROVIDER_URL, dn.toString() );
163
164 this.nexusProxy = nexusProxy;
165
166 this.principal = principal;
167 }
168
169
170
171
172
173
174
175 /***
176 * Gets the principal of the authenticated user which also happens to own
177 */
178 public LdapPrincipal getPrincipal()
179 {
180 return principal;
181 }
182
183
184 /***
185 * Sets the principal of the authenticated user which also happens to own.
186 * This method can be invoked only once to keep this property safe. This
187 * method has been changed to be public but it can only be set by the
188 * AuthenticationService to prevent malicious code from changing the
189 * effective principal.
190 */
191 public void setPrincipal( AuthenticationService.TrustedPrincipalWrapper wrapper )
192 {
193 this.principal = wrapper.getPrincipal();
194 }
195
196
197
198
199
200
201
202 /***
203 * Gets the RootNexus proxy.
204 *
205 * @return the proxy to the backend nexus.
206 */
207 protected DirectoryPartitionNexus getNexusProxy()
208 {
209 return nexusProxy ;
210 }
211
212
213 /***
214 * Gets the distinguished name of the entry associated with this Context.
215 *
216 * @return the distinguished name of this Context's entry.
217 */
218 protected Name getDn()
219 {
220 return dn;
221 }
222
223
224
225
226
227
228
229 /***
230 * @see javax.naming.Context#close()
231 */
232 public void close() throws NamingException
233 {
234 Iterator list = listeners.iterator();
235 while ( list.hasNext() )
236 {
237 ( ( DirectoryPartitionNexusProxy ) this.nexusProxy )
238 .removeNamingListener( this, ( NamingListener ) list.next() );
239 }
240 }
241
242
243 /***
244 * @see javax.naming.Context#getNameInNamespace()
245 */
246 public String getNameInNamespace() throws NamingException
247 {
248 return dn.toString();
249 }
250
251
252 /***
253 * @see javax.naming.Context#getEnvironment()
254 */
255 public Hashtable getEnvironment()
256 {
257 return env;
258 }
259
260
261 /***
262 * @see javax.naming.Context#addToEnvironment(java.lang.String,
263 * java.lang.Object)
264 */
265 public Object addToEnvironment( String propName, Object propVal ) throws NamingException
266 {
267 return env.put( propName, propVal );
268 }
269
270
271 /***
272 * @see javax.naming.Context#removeFromEnvironment(java.lang.String)
273 */
274 public Object removeFromEnvironment( String propName ) throws NamingException
275 {
276 return env.remove( propName );
277 }
278
279
280 /***
281 * @see javax.naming.Context#createSubcontext(java.lang.String)
282 */
283 public Context createSubcontext( String name ) throws NamingException
284 {
285 return createSubcontext( new LdapName( name ) );
286 }
287
288
289 /***
290 * @see javax.naming.Context#createSubcontext(javax.naming.Name)
291 */
292 public Context createSubcontext( Name name ) throws NamingException
293 {
294 Attributes attributes = new LockableAttributesImpl();
295
296 LdapName target = buildTarget( name );
297
298 String rdn = name.get( name.size() - 1 );
299
300 String rdnAttribute = NamespaceTools.getRdnAttribute( rdn );
301
302 String rdnValue = NamespaceTools.getRdnValue( rdn );
303
304 attributes.put( rdnAttribute, rdnValue );
305
306 attributes.put( JavaLdapSupport.OBJECTCLASS_ATTR, JavaLdapSupport.JCONTAINER_ATTR );
307
308 attributes.put( JavaLdapSupport.OBJECTCLASS_ATTR, JavaLdapSupport.TOP_ATTR );
309
310
311
312
313
314
315
316
317 nexusProxy.add( target.toString(), target, attributes );
318
319 ServerLdapContext ctx = new ServerLdapContext( principal, nexusProxy, env, target );
320
321 Control [] controls = ( Control [] ) ( ( ServerLdapContext ) this ).getRequestControls().clone();
322
323 ctx.setRequestControls( controls );
324
325 return ctx;
326 }
327
328
329 /***
330 * @see javax.naming.Context#destroySubcontext(java.lang.String)
331 */
332 public void destroySubcontext( String name ) throws NamingException
333 {
334 destroySubcontext( new LdapName( name ) );
335 }
336
337
338 /***
339 * @see javax.naming.Context#destroySubcontext(javax.naming.Name)
340 */
341 public void destroySubcontext( Name name ) throws NamingException
342 {
343 Name target = buildTarget( name );
344
345 if ( target.size() == 0 )
346 {
347 throw new LdapNoPermissionException( "can't delete the rootDSE" );
348 }
349
350 nexusProxy.delete( target );
351 }
352
353
354 /***
355 * @see javax.naming.Context#bind(java.lang.String, java.lang.Object)
356 */
357 public void bind( String name, Object obj ) throws NamingException
358 {
359 bind( new LdapName( name ), obj );
360 }
361
362
363 /***
364 * @see javax.naming.Context#bind(javax.naming.Name, java.lang.Object)
365 */
366 public void bind( Name name, Object obj ) throws NamingException
367 {
368
369 DirStateFactory.Result res = DirectoryManager.getStateToBind( obj, name, this, env, null );
370
371 Attributes outAttrs = res.getAttributes();
372
373 if ( outAttrs != null )
374 {
375 Name target = buildTarget( name );
376
377 nexusProxy.add( target.toString(), target, outAttrs );
378
379 return;
380 }
381
382
383 if ( obj instanceof Referenceable )
384 {
385 throw new NamingException( "Do not know how to store Referenceables yet!" );
386 }
387
388
389 if ( obj instanceof Reference )
390 {
391
392
393 throw new NamingException( "Do not know how to store References yet!" );
394 }
395 else if ( obj instanceof Serializable )
396 {
397
398
399 Attributes attributes = new LockableAttributesImpl();
400
401 if ( outAttrs != null && outAttrs.size() > 0 )
402 {
403 NamingEnumeration list = outAttrs.getAll();
404
405 while ( list.hasMore() )
406 {
407 attributes.put( ( Attribute ) list.next() );
408 }
409 }
410
411 Name target = buildTarget( name );
412
413
414
415 JavaLdapSupport.serialize( attributes, obj );
416
417 nexusProxy.add( target.toString(), target, attributes );
418 }
419 else if ( obj instanceof DirContext )
420 {
421
422
423 Attributes attributes = ( ( DirContext ) obj ).getAttributes( "" );
424
425 if ( outAttrs != null && outAttrs.size() > 0 )
426 {
427 NamingEnumeration list = outAttrs.getAll();
428
429 while ( list.hasMore() )
430 {
431 attributes.put( ( Attribute ) list.next() );
432 }
433 }
434
435 Name target = buildTarget( name );
436
437 nexusProxy.add( target.toString(), target, attributes );
438 }
439 else
440 {
441 throw new NamingException( "Can't find a way to bind: " + obj );
442 }
443 }
444
445
446 /***
447 * @see javax.naming.Context#rename(java.lang.String, java.lang.String)
448 */
449 public void rename( String oldName, String newName ) throws NamingException
450 {
451 rename( new LdapName( oldName ), new LdapName( newName ) );
452 }
453
454
455 /***
456 * @see javax.naming.Context#rename(javax.naming.Name, javax.naming.Name)
457 */
458 public void rename( Name oldName, Name newName ) throws NamingException
459 {
460 Name oldDn = buildTarget( oldName );
461
462 Name newDn = buildTarget( newName );
463
464 if ( oldDn.size() == 0 )
465 {
466 throw new LdapNoPermissionException( "can't rename the rootDSE" );
467 }
468
469 Name oldBase = oldName.getPrefix( 1 );
470
471 Name newBase = newName.getPrefix( 1 );
472
473 String newRdn = newName.get( newName.size() - 1 );
474
475 String oldRdn = oldName.get( oldName.size() - 1 );
476
477 boolean delOldRdn = true;
478
479
480
481
482
483 if ( null != env.get( DELETE_OLD_RDN_PROP ) )
484 {
485 String delOldRdnStr = ( String ) env.get( DELETE_OLD_RDN_PROP );
486
487 delOldRdn = ! delOldRdnStr.equals( "false" );
488
489 delOldRdn = delOldRdn || delOldRdnStr.equals( "no" );
490
491 delOldRdn = delOldRdn || delOldRdnStr.equals( "0" );
492 }
493
494
495
496
497
498
499
500
501
502 if ( oldName.size() == newName.size() && oldBase.equals( newBase ) )
503 {
504 nexusProxy.modifyRn( oldDn, newRdn, delOldRdn );
505 }
506 else
507 {
508 Name parent = newDn.getPrefix( 1 );
509
510 if ( newRdn.equalsIgnoreCase( oldRdn ) )
511 {
512 nexusProxy.move( oldDn, parent );
513 }
514 else
515 {
516 nexusProxy.move( oldDn, parent, newRdn, delOldRdn );
517 }
518 }
519 }
520
521
522 /***
523 * @see javax.naming.Context#rebind(java.lang.String, java.lang.Object)
524 */
525 public void rebind( String name, Object obj ) throws NamingException
526 {
527 rebind( new LdapName( name ), obj );
528 }
529
530
531 /***
532 * @see javax.naming.Context#rebind(javax.naming.Name, java.lang.Object)
533 */
534 public void rebind( Name name, Object obj ) throws NamingException
535 {
536 Name target = buildTarget( name );
537
538 if ( nexusProxy.hasEntry( target ) )
539 {
540 nexusProxy.delete( target );
541 }
542
543 bind( name, obj );
544 }
545
546
547 /***
548 * @see javax.naming.Context#unbind(java.lang.String)
549 */
550 public void unbind( String name ) throws NamingException
551 {
552 unbind( new LdapName( name ) );
553 }
554
555
556 /***
557 * @see javax.naming.Context#unbind(javax.naming.Name)
558 */
559 public void unbind( Name name ) throws NamingException
560 {
561 nexusProxy.delete( buildTarget( name ) );
562 }
563
564
565 /***
566 * @see javax.naming.Context#lookup(java.lang.String)
567 */
568 public Object lookup( String name ) throws NamingException
569 {
570 return lookup( new LdapName( name ) );
571 }
572
573
574 /***
575 * @see javax.naming.Context#lookup(javax.naming.Name)
576 */
577 public Object lookup( Name name ) throws NamingException
578 {
579 Object obj;
580 LdapName target = buildTarget( name );
581 Attributes attributes = nexusProxy.lookup( target );
582
583 try
584 {
585 obj = DirectoryManager.getObjectInstance( null, name, this, env, attributes );
586 }
587 catch ( Exception e )
588 {
589 String msg = "Failed to create an object for " + target;
590 msg += " using object factories within the context's environment.";
591 NamingException ne = new NamingException( msg );
592 ne.setRootCause( e );
593 throw ne;
594 }
595
596 if ( obj != null )
597 {
598 return obj;
599 }
600
601
602 if ( attributes.get( JavaLdapSupport.JCLASSNAME_ATTR ) != null )
603 {
604
605 return JavaLdapSupport.deserialize( attributes );
606 }
607
608
609 ServerLdapContext ctx = new ServerLdapContext( principal, nexusProxy, env, target );
610
611
612 Control [] controls = ( ( ServerLdapContext ) this ).getRequestControls();
613
614 if ( null != controls )
615 {
616 ctx.setRequestControls( ( Control [] ) controls.clone() );
617 }
618
619 return ctx;
620 }
621
622
623 /***
624 * @see javax.naming.Context#lookupLink(java.lang.String)
625 */
626 public Object lookupLink( String name ) throws NamingException
627 {
628 throw new UnsupportedOperationException();
629 }
630
631
632 /***
633 * @see javax.naming.Context#lookupLink(javax.naming.Name)
634 */
635 public Object lookupLink( Name name ) throws NamingException
636 {
637 throw new UnsupportedOperationException();
638 }
639
640
641 /***
642 * Non-federated implementation presuming the name argument is not a
643 * composite name spanning multiple namespaces but a compound name in
644 * the same LDAP namespace. Hence the parser returned is always the
645 * same as calling this method with the empty String.
646 *
647 * @see javax.naming.Context#getNameParser(java.lang.String)
648 */
649 public NameParser getNameParser( String name ) throws NamingException
650 {
651 return LdapName.getNameParser();
652 }
653
654
655 /***
656 * Non-federated implementation presuming the name argument is not a
657 * composite name spanning multiple namespaces but a compound name in
658 * the same LDAP namespace. Hence the parser returned is always the
659 * same as calling this method with the empty String Name.
660 *
661 * @see javax.naming.Context#getNameParser(javax.naming.Name)
662 */
663 public NameParser getNameParser( Name name ) throws NamingException
664 {
665 return LdapName.getNameParser();
666 }
667
668
669 /***
670 * @see javax.naming.Context#list(java.lang.String)
671 */
672 public NamingEnumeration list( String name ) throws NamingException
673 {
674 return list( new LdapName( name ) );
675 }
676
677
678 /***
679 * @see javax.naming.Context#list(javax.naming.Name)
680 */
681 public NamingEnumeration list( Name name ) throws NamingException
682 {
683 return nexusProxy.list( buildTarget( name ) );
684 }
685
686
687 /***
688 * @see javax.naming.Context#listBindings(java.lang.String)
689 */
690 public NamingEnumeration listBindings( String name ) throws NamingException
691 {
692 return listBindings( new LdapName( name ) );
693 }
694
695
696 /***
697 * @see javax.naming.Context#listBindings(javax.naming.Name)
698 */
699 public NamingEnumeration listBindings( Name name ) throws NamingException
700 {
701
702 Name base = buildTarget( name );
703
704 PresenceNode filter = new PresenceNode( "objectClass" );
705
706 SearchControls ctls = new SearchControls();
707
708 ctls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
709
710 return nexusProxy.search( base , getEnvironment(), filter, ctls );
711 }
712
713
714 /***
715 * @see javax.naming.Context#composeName(java.lang.String, java.lang.String)
716 */
717 public String composeName( String name, String prefix ) throws NamingException
718 {
719 return composeName( new LdapName( name ), new LdapName( prefix ) ).toString();
720 }
721
722
723 /***
724 * @see javax.naming.Context#composeName(javax.naming.Name,
725 * javax.naming.Name)
726 */
727 public Name composeName( Name name, Name prefix ) throws NamingException
728 {
729
730 if ( prefix == null || prefix.size() == 0 )
731 {
732 return name;
733 }
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751 Name fqn = buildTarget( name );
752
753 String head = prefix.get( 0 );
754
755
756 while ( fqn.size() > 0 )
757 {
758
759 if ( fqn.get( 0 ).equalsIgnoreCase( head ) )
760 {
761 return fqn;
762 }
763 else
764 {
765 fqn.remove( 0 );
766 }
767 }
768
769 String msg = "The prefix '" + prefix + "' is not an ancestor of this ";
770
771 msg += "entry '" + dn + "'";
772
773 throw new NamingException( msg );
774 }
775
776
777
778
779
780
781
782 public void addNamingListener( Name name, int scope, NamingListener namingListener ) throws NamingException
783 {
784 ExprNode filter = new PresenceNode( "objectClass" );
785 SearchControls controls = new SearchControls();
786 controls.setSearchScope( scope );
787 ( ( DirectoryPartitionNexusProxy ) this.nexusProxy )
788 .addNamingListener( this, buildTarget( name ), filter, controls, namingListener );
789 listeners.add( namingListener );
790 }
791
792
793 public void addNamingListener( String name, int scope, NamingListener namingListener ) throws NamingException
794 {
795 addNamingListener( new LdapName( name ), scope, namingListener );
796 }
797
798
799 public void removeNamingListener( NamingListener namingListener ) throws NamingException
800 {
801 ( ( DirectoryPartitionNexusProxy ) this.nexusProxy ).removeNamingListener( this, namingListener );
802 listeners.remove( namingListener );
803 }
804
805
806 public boolean targetMustExist() throws NamingException
807 {
808 return false;
809 }
810
811
812 /***
813 * Allows subclasses to register and unregister listeners.
814 *
815 * @return the set of listeners used for tracking registered name listeners.
816 */
817 protected Set getListeners()
818 {
819 return listeners;
820 }
821
822
823
824
825
826
827
828 /***
829 * Clones this context's DN and adds the components of the name relative to
830 * this context to the left hand side of this context's cloned DN.
831 *
832 * @param relativeName a name relative to this context.
833 * @return the name of the target
834 * @throws InvalidNameException if relativeName is not a valid name in
835 * the LDAP namespace.
836 */
837 LdapName buildTarget( Name relativeName ) throws InvalidNameException
838 {
839
840 LdapName target = ( LdapName ) dn.clone();
841
842
843 target.addAll( target.size(), relativeName );
844
845 return target;
846 }
847 }