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