1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.core.jndi;
21  
22  
23  import org.apache.directory.server.core.DirectoryService;
24  import org.apache.directory.server.core.entry.DefaultServerEntry;
25  import org.apache.directory.server.core.integ.CiRunner;
26  import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
27  import static org.apache.directory.server.core.integ.IntegrationUtils.getUserAddLdif;
28  import org.apache.directory.shared.ldap.constants.SchemaConstants;
29  import org.apache.directory.shared.ldap.exception.LdapNamingException;
30  import org.apache.directory.shared.ldap.ldif.LdifEntry;
31  import org.apache.directory.shared.ldap.message.ResultCodeEnum;
32  import org.apache.directory.shared.ldap.name.LdapDN;
33  import static org.junit.Assert.assertEquals;
34  import static org.junit.Assert.assertTrue;
35  import static org.junit.Assert.assertFalse;
36  import static org.junit.Assert.assertNull;
37  import static org.junit.Assert.assertNotNull;
38  import static org.junit.Assert.fail;
39  import org.junit.Test;
40  import org.junit.runner.RunWith;
41  
42  import javax.naming.Context;
43  import javax.naming.Name;
44  import javax.naming.NameAlreadyBoundException;
45  import javax.naming.NameNotFoundException;
46  import javax.naming.NamingEnumeration;
47  import javax.naming.NamingException;
48  import javax.naming.ReferralException;
49  import javax.naming.directory.Attribute;
50  import javax.naming.directory.Attributes;
51  import javax.naming.directory.BasicAttribute;
52  import javax.naming.directory.BasicAttributes;
53  import javax.naming.directory.DirContext;
54  import javax.naming.directory.ModificationItem;
55  import javax.naming.directory.SearchControls;
56  import javax.naming.directory.SearchResult;
57  import javax.naming.ldap.LdapContext;
58  import java.util.HashMap;
59  import java.util.Map;
60  
61  
62  /**
63   * Tests the referral handling functionality within the server's core.
64   * 
65   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
66   * @version $Rev: 691179 $
67   */
68  @RunWith ( CiRunner.class )
69  public class ReferralIT
70  {
71      public static DirectoryService service;
72      private TestData td = new TestData();
73  
74  
75      /*
76       * NOTE: We may encounter conflicting circumstances where the ManageDsaIT control
77       * is included in the request controls yet the Context.REFERRAL is set to 
78       * something other than ignore: throw or follow.  We have to figure out what to
79       * do in these cases.
80       * 
81       * Simply throw an illegal state exception when this happens?
82       * 
83       * NOTE: Need to figure out the behavoir of referral handling during search
84       * when aliases are being dereferenced.
85       */
86  
87      private class TestData
88      {
89          LdapContext rootCtx;
90          Name ctxDn;
91          LdapContext refCtx;
92      }
93  
94  
95      private void addReferralEntry() throws Exception
96      {
97          String ref0 = "ldap://fermi:10389/ou=users,ou=system";
98          String ref1 = "ldap://hertz:10389/ou=users,dc=example,dc=com";
99          String ref2 = "ldap://maxwell:10389/ou=users,ou=system";
100         td.rootCtx = getSystemContext( service );
101 
102         LdifEntry akarasulu = getUserAddLdif();
103         service.getAdminSession().add( 
104             new DefaultServerEntry( service.getRegistries(), akarasulu.getEntry() ) ); 
105 
106         // -------------------------------------------------------------------
107         // Adds a referral entry regardless of referral handling settings
108         // -------------------------------------------------------------------
109 
110         // Add a referral entry ( should be fine with or without the control )
111         Attributes referral = new BasicAttributes( "objectClass", "top", true );
112         referral.get( "objectClass" ).add( "referral" );
113         referral.get( "objectClass" ).add( "extensibleObject" );
114         referral.put( "ref", ref0 );
115         referral.get( "ref" ).add( ref1 );
116         referral.get( "ref" ).add( ref2 );
117         referral.put( "ou", "users" );
118 
119         // Just in case if server is a remote server destroy remaing referral
120         td.rootCtx.addToEnvironment( Context.REFERRAL, "ignore" );
121         try
122         {
123             td.rootCtx.destroySubcontext( "uid=akarasulu,ou=users" );
124         }
125         catch ( NameNotFoundException e )
126         {
127             e.printStackTrace();
128         }
129         try
130         {
131             td.rootCtx.destroySubcontext( "ou=users" );
132         }
133         catch ( NameNotFoundException e )
134         {
135             e.printStackTrace();
136         }
137         try
138         {
139             td.refCtx = ( LdapContext ) td.rootCtx.createSubcontext( "ou=users", referral );
140         }
141         catch ( NameAlreadyBoundException e )
142         {
143             td.refCtx = ( LdapContext ) td.rootCtx.lookup( "ou=users" );
144         }
145         referral = td.refCtx.getAttributes( "" );
146         assertTrue( referral.get( "ou" ).contains( "users" ) );
147         assertTrue( referral.get( "objectClass" ).contains( "referral" ) );
148     }
149 
150 
151     private void checkAncestorReferrals( ReferralException e ) throws Exception
152     {
153         assertEquals( "ldap://fermi:10389", e.getReferralInfo() );
154         assertTrue( e.skipReferral() );
155         assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=apache,ou=users,dc=example,dc=com", e.getReferralInfo() );
156         assertTrue( e.skipReferral() );
157         assertEquals( "ldap://maxwell:10389", e.getReferralInfo() );
158         assertFalse( e.skipReferral() );
159     }
160 
161 
162     private void checkParentReferrals( ReferralException e ) throws Exception
163     {
164         assertEquals( "ldap://fermi:10389", e.getReferralInfo() );
165         assertTrue( e.skipReferral() );
166         assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=users,dc=example,dc=com", e.getReferralInfo() );
167         assertTrue( e.skipReferral() );
168         assertEquals( "ldap://maxwell:10389", e.getReferralInfo() );
169         assertFalse( e.skipReferral() );
170     }
171 
172 
173     /**
174      * Checks for correct core behavior when Context.REFERRAL is set to <b>throw</b>
175      * for an add operation with the parent context being a referral.
176      * 
177      * @throws Exception if something goes wrong.
178      */
179     @Test
180     public void testAddWithReferralParent() throws Exception
181     {
182         addReferralEntry();
183 
184         // -------------------------------------------------------------------
185         // Attempt to add a normal entry below the referral parent. We should
186         // encounter referral errors with referral setting set to throw.
187         // -------------------------------------------------------------------
188 
189         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
190         Attributes userEntry = new BasicAttributes( "objectClass", "top", true );
191         userEntry.get( "objectClass" ).add( "person" );
192         userEntry.put( "sn", "karasulu" );
193         userEntry.put( "cn", "alex karasulu" );
194 
195         try
196         {
197             td.refCtx.createSubcontext( "cn=alex karasulu", userEntry );
198             fail( "Should fail here throwing a ReferralException" );
199         }
200         catch ( ReferralException e )
201         {
202             checkParentReferrals( e );
203         }
204     }
205 
206 
207     /**
208      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
209      * for an add operation with an ancestor context being a referral.
210      * 
211      * @throws Exception if something goes wrong.
212      */
213     @Test
214     public void testAddWithReferralAncestor() throws Exception
215     {
216         addReferralEntry();
217 
218         // -------------------------------------------------------------------
219         // Attempt to add a normal entry below the referral ancestor. We should
220         // encounter referral errors with referral setting set to throw.
221         // -------------------------------------------------------------------
222 
223         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
224         Attributes userEntry = new BasicAttributes( "objectClass", "top", true );
225         userEntry.get( "objectClass" ).add( "person" );
226         userEntry.put( "sn", "karasulu" );
227         userEntry.put( "cn", "alex karasulu" );
228 
229         try
230         {
231             td.refCtx.createSubcontext( "cn=alex karasulu,ou=apache", userEntry );
232             fail( "Should fail here throwing a ReferralException" );
233         }
234         catch ( ReferralException e )
235         {
236             checkAncestorReferrals( e );
237         }
238     }
239 
240 
241     /**
242      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
243      * for an delete operation with the parent context being a referral.
244      * 
245      * @throws Exception if something goes wrong.
246      */
247     @Test
248     public void testDeleteWithReferralParent() throws Exception
249     {
250         addReferralEntry();
251 
252         // -------------------------------------------------------------------
253         // Attempt to delete a non-existent entry below the referral parent. 
254         // We should encounter referral errors with referral setting set to 
255         // throw.
256         // -------------------------------------------------------------------
257 
258         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
259         try
260         {
261             td.refCtx.destroySubcontext( "cn=alex karasulu" );
262             fail( "Should fail here throwing a ReferralException" );
263         }
264         catch ( ReferralException e )
265         {
266             checkParentReferrals( e );
267         }
268     }
269 
270 
271     /**
272      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
273      * for a delete operation with an ancestor context being a referral.
274      * 
275      * @throws Exception if something goes wrong.
276      */
277     @Test
278     public void testDeleteWithReferralAncestor() throws Exception
279     {
280         addReferralEntry();
281 
282         // -------------------------------------------------------------------
283         // Attempt to delete a non-existent entry below the referral ancestor. 
284         // We should encounter referral errors when referral setting is set to 
285         // throw.
286         // -------------------------------------------------------------------
287 
288         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
289         try
290         {
291             td.refCtx.destroySubcontext( "cn=alex karasulu,ou=apache" );
292             fail( "Should fail here throwing a ReferralException" );
293         }
294         catch ( ReferralException e )
295         {
296             checkAncestorReferrals( e );
297         }
298     }
299 
300 
301     /**
302      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
303      * for an delete operation with the parent context being a referral.
304      * 
305      * @throws Exception if something goes wrong.
306      */
307     @Test
308     public void testCompareWithReferralParent() throws Exception
309     {
310         addReferralEntry();
311 
312         // -------------------------------------------------------------------
313         // Attempt to compare attributes in an entry below the referral parent. 
314         // We should encounter referral errors with referral setting set to 
315         // throw.
316         // -------------------------------------------------------------------
317 
318         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
319         try
320         {
321             if ( td.refCtx instanceof ServerLdapContext )
322             {
323                 LdapDN dn = new LdapDN( "cn=alex karasulu,ou=users,ou=system" );
324                 ( ( ServerLdapContext ) td.refCtx ).compare( dn, "sn", "karasulu" );
325             }
326             else
327             {
328                 // abort the test because we're using the sun jdni provider
329                 return;
330             }
331             fail( "Should fail here throwing a ReferralException" );
332         }
333         catch ( ReferralException e )
334         {
335             checkParentReferrals( e );
336         }
337     }
338 
339 
340     /**
341      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
342      * for a compare operation with an ancestor context being a referral.
343      * 
344      * @throws Exception if something goes wrong.
345      */
346     @Test
347     public void testCompareWithReferralAncestor() throws Exception
348     {
349         addReferralEntry();
350 
351         // -------------------------------------------------------------------
352         // Attempt to compare attributes in an entry below the referral ancestor. 
353         // We should encounter referral errors when referral setting is set to 
354         // throw.
355         // -------------------------------------------------------------------
356 
357         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
358         try
359         {
360             if ( td.refCtx instanceof ServerLdapContext )
361             {
362                 LdapDN dn = new LdapDN( "cn=alex karasulu,ou=apache,ou=users,ou=system" );
363                 ( ( ServerLdapContext ) td.refCtx ).compare( dn, "sn", "karasulu" );
364             }
365             else
366             {
367                 // abort the test because we're using the sun jdni provider
368                 return;
369             }
370             fail( "Should fail here throwing a ReferralException" );
371         }
372         catch ( ReferralException e )
373         {
374             checkAncestorReferrals( e );
375         }
376     }
377 
378 
379     /**
380      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
381      * for a modify operation with the parent context being a referral.
382      * 
383      * @throws Exception if something goes wrong.
384      */
385     @Test
386     public void testModifyWithReferralParent() throws Exception
387     {
388         addReferralEntry();
389 
390         // -------------------------------------------------------------------
391         // Attempt to modify the attributes of an entry below the referral 
392         // parent.  We should encounter referral errors with referral setting 
393         // set to throw.
394         // -------------------------------------------------------------------
395 
396         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
397         try
398         {
399             td.refCtx.modifyAttributes( "cn=alex karasulu", DirContext.ADD_ATTRIBUTE, new BasicAttributes(
400                 "description", "just some text", true ) );
401             fail( "Should fail here throwing a ReferralException" );
402         }
403         catch ( ReferralException e )
404         {
405             checkParentReferrals( e );
406         }
407     }
408 
409 
410     /**
411      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
412      * for a modify operation with an ancestor context being a referral.
413      * 
414      * @throws Exception if something goes wrong.
415      */
416     @Test
417     public void testModifyWithReferralAncestor() throws Exception
418     {
419         addReferralEntry();
420 
421         // -------------------------------------------------------------------
422         // Attempt to modify the attributes of an entry below the referral 
423         // ancestor. We should encounter referral errors when referral setting 
424         // is set to throw.
425         // -------------------------------------------------------------------
426 
427         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
428         try
429         {
430             td.refCtx.modifyAttributes( "cn=alex karasulu,ou=apache", DirContext.ADD_ATTRIBUTE, new BasicAttributes(
431                 "description", "just some text", true ) );
432             fail( "Should fail here throwing a ReferralException" );
433         }
434         catch ( ReferralException e )
435         {
436             checkAncestorReferrals( e );
437         }
438     }
439 
440 
441     /**
442      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
443      * for a modify operation with the parent context being a referral.
444      * 
445      * @throws Exception if something goes wrong.
446      */
447     @Test
448     public void testModifyWithReferralParent2() throws Exception
449     {
450         addReferralEntry();
451 
452         // -------------------------------------------------------------------
453         // Attempt to modify the attributes of an entry below the referral 
454         // parent.  We should encounter referral errors with referral setting 
455         // set to throw.
456         // -------------------------------------------------------------------
457 
458         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
459         try
460         {
461             ModificationItem[] mods = new ModificationItem[]
462                 { new ModificationItem( DirContext.ADD_ATTRIBUTE, new BasicAttribute( "description", "just some text" ) ) };
463             td.refCtx.modifyAttributes( "cn=alex karasulu", mods );
464             fail( "Should fail here throwing a ReferralException" );
465         }
466         catch ( ReferralException e )
467         {
468             checkParentReferrals( e );
469         }
470     }
471 
472 
473     /**
474      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
475      * for a modify operation with an ancestor context being a referral.
476      * 
477      * @throws Exception if something goes wrong.
478      */
479     @Test
480     public void testModifyWithReferralAncestor2() throws Exception
481     {
482         addReferralEntry();
483 
484         // -------------------------------------------------------------------
485         // Attempt to modify the attributes of an entry below the referral 
486         // ancestor. We should encounter referral errors when referral setting 
487         // is set to throw.
488         // -------------------------------------------------------------------
489 
490         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
491         try
492         {
493             ModificationItem[] mods = new ModificationItem[]
494                 { new ModificationItem( DirContext.ADD_ATTRIBUTE, new BasicAttribute( "description", "just some text" ) ) };
495             td.refCtx.modifyAttributes( "cn=alex karasulu,ou=apache", mods );
496             fail( "Should fail here throwing a ReferralException" );
497         }
498         catch ( ReferralException e )
499         {
500             checkAncestorReferrals( e );
501         }
502     }
503 
504 
505     /**
506      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
507      * for a modify rdn interceptor operation (corresponds to a subset of the modify 
508      * dn operation) with the parent context being a referral.
509      * 
510      * @throws Exception if something goes wrong.
511      */
512     @Test
513     public void testModifyRdnWithReferralParent() throws Exception
514     {
515         addReferralEntry();
516 
517         // -------------------------------------------------------------------
518         // Attempt to modify the last component of the entry's name which 
519         // resides below an parent which is a referral. We should encounter 
520         // referral errors when referral setting is set to throw.
521         // -------------------------------------------------------------------
522 
523         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
524         try
525         {
526             td.refCtx.rename( "cn=alex karasulu", "cn=aok" );
527             fail( "Should fail here throwing a ReferralException" );
528         }
529         catch ( ReferralException e )
530         {
531             checkParentReferrals( e );
532         }
533     }
534 
535 
536     /**
537      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
538      * for a modify rdn interceptor operation (corresponds to a subset of the modify 
539      * dn operation) with an ancestor context being a referral.
540      * 
541      * @throws Exception if something goes wrong.
542      */
543     @Test
544     public void testModifyRdnWithReferralAncestor() throws Exception
545     {
546         addReferralEntry();
547 
548         // -------------------------------------------------------------------
549         // Attempt to modify the last component of the entry's name which 
550         // resides below an ancestor which is a referral. We should encounter 
551         // referral errors when referral setting is set to throw.
552         // -------------------------------------------------------------------
553 
554         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
555         try
556         {
557             td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=aok,ou=apache" );
558             fail( "Should fail here throwing a ReferralException" );
559         }
560         catch ( ReferralException e )
561         {
562             checkAncestorReferrals( e );
563         }
564     }
565 
566 
567     /**
568      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
569      * for a move interceptor operation (corresponds to a subset of the modify 
570      * dn operation) with the parent context being a referral.
571      * 
572      * @throws Exception if something goes wrong.
573      */
574     @Test
575     public void testMoveWithReferralParent() throws Exception
576     {
577         addReferralEntry();
578 
579         // -------------------------------------------------------------------
580         // Attempt to modify the last component of the entry's name which 
581         // resides below an parent which is a referral. We should encounter 
582         // referral errors when referral setting is set to throw.
583         // -------------------------------------------------------------------
584 
585         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
586         try
587         {
588             td.refCtx.rename( "cn=alex karasulu", "cn=alex karasulu,ou=groups" );
589             fail( "Should fail here throwing a ReferralException" );
590         }
591         catch ( ReferralException e )
592         {
593             checkParentReferrals( e );
594         }
595     }
596 
597 
598     /**
599      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
600      * for a move interceptor operation (corresponds to a subset of the modify 
601      * dn operation) with an ancestor context being a referral.
602      * 
603      * @throws Exception if something goes wrong.
604      */
605     @Test
606     public void testMoveWithReferralAncestor() throws Exception
607     {
608         addReferralEntry();
609 
610         // -------------------------------------------------------------------
611         // Attempt to modify the last component of the entry's name which 
612         // resides below an ancestor which is a referral. We should encounter 
613         // referral errors when referral setting is set to throw.
614         // -------------------------------------------------------------------
615 
616         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
617         try
618         {
619             td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=alex karasulu,ou=groups" );
620             fail( "Should fail here throwing a ReferralException" );
621         }
622         catch ( ReferralException e )
623         {
624             checkAncestorReferrals( e );
625         }
626     }
627 
628 
629     /**
630      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
631      * for a move interceptor operation (corresponds to a subset of the modify 
632      * dn operation) with the parent context being a referral.
633      * 
634      * @throws Exception if something goes wrong.
635      */
636     @Test
637     public void testMoveWithReferralParent2() throws Exception
638     {
639         addReferralEntry();
640 
641         // -------------------------------------------------------------------
642         // Attempt to modify the last component of the entry's name which 
643         // resides below an parent which is a referral. We should encounter 
644         // referral errors when referral setting is set to throw.
645         // -------------------------------------------------------------------
646 
647         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
648         try
649         {
650             td.refCtx.rename( "cn=alex karasulu", "cn=aok,ou=groups" );
651             fail( "Should fail here throwing a ReferralException" );
652         }
653         catch ( ReferralException e )
654         {
655             checkParentReferrals( e );
656         }
657     }
658 
659 
660     /**
661      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
662      * for a move interceptor operation (corresponds to a subset of the modify 
663      * dn operation) with an ancestor context being a referral.
664      * 
665      * @throws Exception if something goes wrong.
666      */
667     @Test
668     public void testMoveWithReferralAncestor2() throws Exception
669     {
670         addReferralEntry();
671 
672         // -------------------------------------------------------------------
673         // Attempt to modify the last component of the entry's name which 
674         // resides below an ancestor which is a referral. We should encounter 
675         // referral errors when referral setting is set to throw.
676         // -------------------------------------------------------------------
677 
678         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
679         try
680         {
681             td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=aok,ou=groups" );
682             fail( "Should fail here throwing a ReferralException" );
683         }
684         catch ( ReferralException e )
685         {
686             checkAncestorReferrals( e );
687         }
688     }
689 
690 
691     /**
692      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
693      * for a move interceptor operation (corresponds to a subset of the modify 
694      * dn operation) with the parent context being a referral.
695      * 
696      * @throws Exception if something goes wrong.
697      */
698     @Test
699     public void testMoveWithReferralParentDest() throws Exception
700     {
701         addReferralEntry();
702 
703         // -------------------------------------------------------------------
704         // Attempt to modify the last component of the entry's name which 
705         // resides below an parent which is a referral. We should encounter 
706         // referral errors when referral setting is set to throw.
707         // -------------------------------------------------------------------
708 
709         createLocalUser();
710         td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
711         try
712         {
713             td.rootCtx.rename( "cn=akarasulu", "cn=akarasulu,ou=users" );
714             fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
715         }
716         catch ( LdapNamingException e )
717         {
718             assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
719         }
720     }
721 
722 
723     /**
724      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
725      * for a move interceptor operation (corresponds to a subset of the modify 
726      * dn operation) with an ancestor context being a referral.
727      * 
728      * @throws Exception if something goes wrong.
729      */
730     @Test
731     public void testMoveWithReferralAncestorDest() throws Exception
732     {
733         addReferralEntry();
734 
735         // -------------------------------------------------------------------
736         // Attempt to modify the last component of the entry's name which 
737         // resides below an ancestor which is a referral. We should encounter 
738         // referral errors when referral setting is set to throw.
739         // -------------------------------------------------------------------
740 
741         createDeepLocalUser();
742         td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
743         try
744         {
745             td.rootCtx.rename( "cn=akarasulu,ou=deep", "cn=akarasulu,ou=users" );
746             fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
747         }
748         catch ( LdapNamingException e )
749         {
750             assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
751         }
752     }
753 
754 
755     /**
756      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
757      * for a move interceptor operation (corresponds to a subset of the modify 
758      * dn operation) with the parent context being a referral.
759      * 
760      * @throws Exception if something goes wrong.
761      */
762     @Test
763     public void testMoveWithReferralParent2Dest() throws Exception
764     {
765         addReferralEntry();
766 
767         // -------------------------------------------------------------------
768         // Attempt to modify the last component of the entry's name which 
769         // resides below an parent which is a referral. We should encounter 
770         // referral errors when referral setting is set to throw.
771         // -------------------------------------------------------------------
772 
773         createLocalUser();
774         td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
775         try
776         {
777             td.rootCtx.rename( "cn=akarasulu", "cn=aok,ou=users" );
778             fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
779         }
780         catch ( LdapNamingException e )
781         {
782             assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
783         }
784     }
785 
786 
787     /**
788      * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
789      * for a move interceptor operation (corresponds to a subset of the modify 
790      * dn operation) with an ancestor context being a referral.
791      * 
792      * @throws Exception if something goes wrong.
793      */
794     @Test
795     public void testMoveWithReferralAncestor2Dest() throws Exception
796     {
797         addReferralEntry();
798 
799         // -------------------------------------------------------------------
800         // Attempt to modify the last component of the entry's name which 
801         // resides below an ancestor which is a referral. We should encounter 
802         // referral errors when referral setting is set to throw.
803         // -------------------------------------------------------------------
804 
805         createDeepLocalUser();
806         td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
807         try
808         {
809             td.rootCtx.rename( "cn=akarasulu,ou=deep", "cn=aok,ou=users" );
810             fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
811         }
812         catch ( LdapNamingException e )
813         {
814             assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
815         }
816     }
817 
818 
819     private void createLocalUser() throws Exception
820     {
821         addReferralEntry();
822 
823         LdapContext userCtx;
824         Attributes referral = new BasicAttributes( "objectClass", "top", true );
825         referral.get( "objectClass" ).add( "person" );
826         referral.put( "cn", "akarasulu" );
827         referral.put( "sn", "karasulu" );
828 
829         //noinspection EmptyCatchBlock
830         try
831         {
832             td.rootCtx.destroySubcontext( "uid=akarasulu" );
833         }
834         catch ( NameNotFoundException e )
835         {
836         }
837         
838         try
839         {
840             userCtx = ( LdapContext ) td.rootCtx.createSubcontext( "cn=akarasulu", referral );
841         }
842         catch ( NameAlreadyBoundException e )
843         {
844             userCtx = ( LdapContext ) td.rootCtx.lookup( "cn=akarasulu" );
845         }
846         
847         referral = userCtx.getAttributes( "" );
848         assertTrue( referral.get( "cn" ).contains( "akarasulu" ) );
849         assertTrue( referral.get( "sn" ).contains( "karasulu" ) );
850     }
851 
852 
853     private void createDeepLocalUser() throws Exception
854     {
855         addReferralEntry();
856 
857         LdapContext userCtx = null;
858         Attributes referral = new BasicAttributes( "objectClass", "top", true );
859         referral.get( "objectClass" ).add( "person" );
860         referral.get( "objectClass" ).add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
861         referral.put( "cn", "akarasulu" );
862         referral.put( "sn", "karasulu" );
863         referral.put( "ou", "deep" );
864 
865         //noinspection EmptyCatchBlock
866         try
867         {
868             td.rootCtx.destroySubcontext( "uid=akarasulu,ou=deep" );
869         }
870         catch ( NameNotFoundException e )
871         {
872         }
873         //noinspection EmptyCatchBlock
874         try
875         {
876             td.rootCtx.destroySubcontext( "ou=deep" );
877         }
878         catch ( NameNotFoundException e )
879         {
880         }
881         try
882         {
883             Attributes attrs = new BasicAttributes( "ou", "deep", true );
884             Attribute oc = new BasicAttribute( "ObjectClass" );
885             oc.add( "top" );
886             oc.add( "organizationalUnit" );
887             attrs.put( oc );
888 
889             td.rootCtx.createSubcontext( "ou=deep", attrs );
890             userCtx = ( LdapContext ) td.rootCtx.createSubcontext( "cn=akarasulu,ou=deep", referral );
891         }
892         catch ( NameAlreadyBoundException e )
893         {
894             td.refCtx = ( LdapContext ) td.rootCtx.lookup( "cn=akarasulu,ou=deep" );
895         }
896         referral = userCtx.getAttributes( "" );
897         assertTrue( referral.get( "cn" ).contains( "akarasulu" ) );
898         assertTrue( referral.get( "sn" ).contains( "karasulu" ) );
899     }
900 
901 
902     @Test
903     public void testSearchBaseIsReferral() throws Exception
904     {
905         addReferralEntry();
906 
907         SearchControls controls = new SearchControls();
908         controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
909         td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
910         try
911         {
912             td.rootCtx.search( "ou=users", "(objectClass=*)", controls );
913             fail( "should never get here" );
914         }
915         catch ( ReferralException e )
916         {
917             assertEquals( "ldap://fermi:10389/ou=users,ou=system??sub", e.getReferralInfo() );
918             assertTrue( e.skipReferral() );
919             assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??sub", e.getReferralInfo() );
920             assertTrue( e.skipReferral() );
921             assertEquals( "ldap://maxwell:10389/ou=users,ou=system??sub", e.getReferralInfo() );
922             assertFalse( e.skipReferral() );
923         }
924     }
925 
926 
927     @Test
928     public void testSearchBaseParentIsReferral() throws Exception
929     {
930         addReferralEntry();
931 
932         SearchControls controls = new SearchControls();
933         controls.setSearchScope( SearchControls.OBJECT_SCOPE );
934         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
935         try
936         {
937             td.refCtx.search( "cn=alex karasulu", "(objectClass=*)", controls );
938         }
939         catch ( ReferralException e )
940         {
941             assertEquals( "ldap://fermi:10389/cn=alex%20karasulu,ou=users,ou=system??base", e.getReferralInfo() );
942             assertTrue( e.skipReferral() );
943             assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=users,dc=example,dc=com??base", e.getReferralInfo() );
944             assertTrue( e.skipReferral() );
945             assertEquals( "ldap://maxwell:10389/cn=alex%20karasulu,ou=users,ou=system??base", e.getReferralInfo() );
946             assertFalse( e.skipReferral() );
947         }
948     }
949 
950 
951     @Test
952     public void testSearchBaseAncestorIsReferral() throws Exception
953     {
954         addReferralEntry();
955 
956         SearchControls controls = new SearchControls();
957         controls.setSearchScope( SearchControls.OBJECT_SCOPE );
958         td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
959         try
960         {
961             td.refCtx.search( "cn=alex karasulu,ou=apache", "(objectClass=*)", controls );
962         }
963         catch ( ReferralException e )
964         {
965             assertEquals( "ldap://fermi:10389/cn=alex%20karasulu,ou=apache,ou=users,ou=system??base", e.getReferralInfo() );
966             assertTrue( e.skipReferral() );
967             assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=apache,ou=users,dc=example,dc=com??base", e
968                 .getReferralInfo() );
969             assertTrue( e.skipReferral() );
970             assertEquals( "ldap://maxwell:10389/cn=alex%20karasulu,ou=apache,ou=users,ou=system??base", e
971                 .getReferralInfo() );
972             assertFalse( e.skipReferral() );
973         }
974     }
975 
976 
977     @Test
978     public void testSearchContinuations() throws Exception
979     {
980         addReferralEntry();
981 
982         SearchControls controls = new SearchControls();
983         controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
984         NamingEnumeration<SearchResult> list = td.rootCtx.search( "", "(objectClass=*)", controls );
985         Map<String, SearchResult> results = new HashMap<String, SearchResult>();
986         
987         while ( list.hasMore() )
988         {
989             SearchResult result = list.next();
990             results.put( result.getName(), result );
991         }
992 
993         assertNotNull( results.get( "ou=users,ou=system" ) );
994 
995         // -------------------------------------------------------------------
996         // Now we will throw exceptions when searching for referrals 
997         // -------------------------------------------------------------------
998 
999         td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
1000         list = td.rootCtx.search( "", "(objectClass=*)", controls );
1001         results = new HashMap<String, SearchResult>();
1002 
1003         try
1004         {
1005             while ( list.hasMore() )
1006             {
1007                 SearchResult result = ( SearchResult ) list.next();
1008                 results.put( result.getName(), result );
1009             }
1010         }
1011         catch ( ReferralException e )
1012         {
1013             assertEquals( "ldap://fermi:10389/ou=users,ou=system??sub", e.getReferralInfo() );
1014             assertTrue( e.skipReferral() );
1015             assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??sub", e.getReferralInfo() );
1016             assertTrue( e.skipReferral() );
1017             assertEquals( "ldap://maxwell:10389/ou=users,ou=system??sub", e.getReferralInfo() );
1018             assertFalse( e.skipReferral() );
1019         }
1020 
1021         assertNull( results.get( "ou=users" ) );
1022 
1023         // try again but this time with single level scope
1024 
1025         controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
1026         list = td.rootCtx.search( "", "(objectClass=*)", controls );
1027         results = new HashMap<String, SearchResult>();
1028 
1029         try
1030         {
1031             while ( list.hasMore() )
1032             {
1033                 SearchResult result = ( SearchResult ) list.next();
1034                 results.put( result.getName(), result );
1035             }
1036         }
1037         catch ( ReferralException e )
1038         {
1039             assertEquals( "ldap://fermi:10389/ou=users,ou=system??base", e.getReferralInfo() );
1040             assertTrue( e.skipReferral() );
1041             assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??base", e.getReferralInfo() );
1042             assertTrue( e.skipReferral() );
1043             assertEquals( "ldap://maxwell:10389/ou=users,ou=system??base", e.getReferralInfo() );
1044             assertFalse( e.skipReferral() );
1045         }
1046 
1047         assertNull( results.get( "ou=users" ) );
1048     }
1049 
1050 
1051     /**
1052      * Checks that when injecting LDAP ref with an empty DN we get an exception
1053      *
1054      * @throws Exception on error
1055      */
1056     @Test
1057     public void testAddWReferralWithEmptyDN() throws Exception
1058     {
1059         addReferralEntry();
1060 
1061         Attributes attrs = new BasicAttributes( true );
1062 
1063         Attribute oc = new BasicAttribute( "ObjectClass", "top" );
1064         oc.add( "extensibleObject" );
1065         oc.add( "referral" );
1066         attrs.put( oc );
1067         
1068         Attribute ref = new BasicAttribute( "ref", "ldap://" );
1069         attrs.put( ref );
1070 
1071         attrs.put( "cn", "refWithEmptyDN" );
1072 
1073         String base = "cn=refWithEmptyDN";
1074 
1075         //create subcontext
1076         try
1077         {
1078             getSystemContext( service ).createSubcontext( base, attrs );
1079             fail( "Should not reach this state" );
1080         }
1081         catch ( NamingException ne )
1082         {
1083             assertTrue( true );
1084         }
1085     }
1086 
1087 
1088     /**
1089      * Checks that when injecting LDAP ref with attribuutes we get an exception
1090      *
1091      * @throws Exception on error
1092      */
1093     @Test
1094     public void testAddWReferralWithAttrs() throws Exception
1095     {
1096         addReferralEntry();
1097 
1098         Attributes attrs = new BasicAttributes( true );
1099 
1100         Attribute oc = new BasicAttribute( "ObjectClass", "top" );
1101         oc.add( "extensibleObject" );
1102         oc.add( "referral" );
1103         attrs.put( oc );
1104         
1105         Attribute ref = new BasicAttribute( "ref", "ldap://localhost/cn=RefWithAttributes?cn" );
1106         attrs.put( ref );
1107 
1108         attrs.put( "cn", "RefWithAttributes" );
1109 
1110         String base = "cn=RefWithAttributes";
1111 
1112         //create subcontext
1113         try
1114         {
1115             getSystemContext( service ).createSubcontext( base, attrs );
1116             fail( "Should not reach this state" );
1117         }
1118         catch ( NamingException ne )
1119         {
1120             assertTrue( true );
1121         }
1122     }
1123 
1124 
1125     /**
1126      * Checks that when injecting LDAP ref with a scope we get an exception
1127      *
1128      * @throws Exception on error
1129      */
1130     @Test
1131     public void testAddWReferralWithScope() throws Exception
1132     {
1133         addReferralEntry();
1134 
1135         Attributes attrs = new BasicAttributes( true );
1136         Attribute oc = new BasicAttribute( "ObjectClass", "top" );
1137         oc.add( "extensibleObject" );
1138         oc.add( "referral" );
1139         attrs.put( oc );
1140         
1141         Attribute ref = new BasicAttribute( "ref", "ldap://localhost/cn=RefWithScope??sub" );
1142         attrs.put( ref );
1143 
1144         attrs.put( "cn", "RefWithScope" );
1145 
1146         String base = "cn=RefWithScope";
1147 
1148         //create subcontext
1149         try
1150         {
1151             getSystemContext( service ).createSubcontext( base, attrs );
1152             fail( "Should not reach this state" );
1153         }
1154         catch ( NamingException ne )
1155         {
1156             assertTrue( true );
1157         }
1158     }
1159 
1160 
1161     /**
1162      * Checks that when injecting LDAP ref with a filter we get an exception
1163      *
1164      * @throws Exception on error
1165      */
1166     @Test
1167     public void testAddWReferralWithFilters() throws Exception
1168     {
1169         addReferralEntry();
1170 
1171         Attributes attrs = new BasicAttributes( true );
1172         Attribute oc = new BasicAttribute( "ObjectClass", "top" );
1173         oc.add( "extensibleObject" );
1174         oc.add( "referral" );
1175         attrs.put( oc );
1176         
1177         Attribute ref = new BasicAttribute( "ref", "ldap://localhost/cn=RefWithFilter???(cn=*)" );
1178         attrs.put( ref );
1179 
1180         attrs.put( "cn", "RefWithFilter" );
1181 
1182         String base = "cn=RefWithFilter";
1183 
1184         //create subcontext
1185         try
1186         {
1187             getSystemContext( service ).createSubcontext( base, attrs );
1188             fail( "Should not reach this state" );
1189         }
1190         catch ( NamingException ne )
1191         {
1192             assertTrue( true );
1193         }
1194     }
1195 
1196 
1197     /**
1198      * Checks that when injecting LDAP ref with an extension we get an exception
1199      *
1200      * @throws Exception on error
1201      */
1202     @Test
1203     public void testAddWReferralWithExtension() throws Exception
1204     {
1205         addReferralEntry();
1206 
1207         Attributes attrs = new BasicAttributes( true );
1208         Attribute oc = new BasicAttribute( "ObjectClass", "top" );
1209         oc.add( "extensibleObject" );
1210         oc.add( "referral" );
1211         attrs.put( oc );
1212         
1213         Attribute ref = new BasicAttribute( "ref", "ldap://localhost/cn=RefWithExtension????x-extension=1.2.3.4" );
1214         attrs.put( ref );
1215 
1216         attrs.put( "cn", "RefWithExtension" );
1217 
1218         String base = "cn=RefWithExtension";
1219 
1220         //create subcontext
1221         try
1222         {
1223             getSystemContext( service ).createSubcontext( base, attrs );
1224             fail( "Should not reach this state" );
1225         }
1226         catch ( NamingException ne )
1227         {
1228             assertTrue( true );
1229         }
1230     }
1231 
1232 
1233     /**
1234      * Checks that when injecting LDAP ref with a critical extension we get an exception
1235      *
1236      * @throws Exception on error
1237      */
1238     @Test
1239     public void testAddWReferralWithCriticalExtension() throws Exception
1240     {
1241         addReferralEntry();
1242 
1243         Attributes attrs = new BasicAttributes( true );
1244         Attribute oc = new BasicAttribute( "ObjectClass", "top" );
1245         oc.add( "extensibleObject" );
1246         oc.add( "referral" );
1247         attrs.put( oc );
1248         
1249         Attribute ref = new BasicAttribute( "ref", "ldap://localhost/cn=RefWithCriticalExtension????!x-extension=1.2.3.4" );
1250         attrs.put( ref );
1251 
1252         attrs.put( "cn", "RefWithCriticalExtension" );
1253 
1254         String base = "cn=RefWithCriticalExtension";
1255 
1256         //create subcontext
1257         try
1258         {
1259             getSystemContext( service ).createSubcontext( base, attrs );
1260             fail( "Should not reach this state" );
1261         }
1262         catch ( NamingException ne )
1263         {
1264             assertTrue( true );
1265         }
1266     }
1267 }