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.operations.add;
21  
22  
23  import javax.naming.NamingEnumeration;
24  import javax.naming.NamingException;
25  import javax.naming.ReferralException;
26  import javax.naming.directory.Attribute;
27  import javax.naming.directory.Attributes;
28  import javax.naming.directory.BasicAttribute;
29  import javax.naming.directory.BasicAttributes;
30  import javax.naming.directory.DirContext;
31  import javax.naming.directory.InvalidAttributeValueException;
32  import javax.naming.directory.SchemaViolationException;
33  import javax.naming.directory.SearchControls;
34  import javax.naming.directory.SearchResult;
35  import javax.naming.ldap.LdapContext;
36  
37  import netscape.ldap.LDAPAttribute;
38  import netscape.ldap.LDAPAttributeSet;
39  import netscape.ldap.LDAPConnection;
40  import netscape.ldap.LDAPConstraints;
41  import netscape.ldap.LDAPControl;
42  import netscape.ldap.LDAPEntry;
43  import netscape.ldap.LDAPException;
44  import netscape.ldap.LDAPResponse;
45  import netscape.ldap.LDAPResponseListener;
46  import netscape.ldap.LDAPSearchConstraints;
47  
48  import org.apache.directory.server.core.integ.Level;
49  import org.apache.directory.server.core.integ.annotations.ApplyLdifs;
50  import org.apache.directory.server.core.integ.annotations.CleanupLevel;
51  
52  import org.apache.directory.server.integ.SiRunner;
53  import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredConnection;
54  import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredContext;
55  import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredContextThrowOnRefferal;
56  
57  import org.apache.directory.server.ldap.LdapService;
58  import org.apache.directory.shared.ldap.constants.SchemaConstants;
59  import org.apache.directory.shared.ldap.message.ResultCodeEnum;
60  import org.junit.Test;
61  import org.junit.runner.RunWith;
62  import org.slf4j.Logger;
63  import org.slf4j.LoggerFactory;
64  
65  import static org.junit.Assert.fail;
66  import static org.junit.Assert.assertTrue;
67  import static org.junit.Assert.assertFalse;
68  import static org.junit.Assert.assertEquals;
69  import static org.junit.Assert.assertNotNull;
70  
71  
72  /**
73   * Various add scenario tests.
74   * 
75   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
76   * @version $Rev: 674593 $
77   */
78  @RunWith ( SiRunner.class ) 
79  @CleanupLevel ( Level.SUITE )
80  @ApplyLdifs( {
81      // Entry # 0
82      "dn: cn=The Person,ou=system\n" +
83      "objectClass: person\n" +
84      "objectClass: top\n" +
85      "cn: The Person\n" +
86      "description: this is a person\n" +
87      "sn: Person\n\n" + 
88      // Entry # 1
89      "dn: uid=akarasulu,ou=users,ou=system\n" +
90      "objectClass: uidObject\n" +
91      "objectClass: person\n" +
92      "objectClass: top\n" +
93      "uid: akarasulu\n" +
94      "cn: Alex Karasulu\n" +
95      "sn: karasulu\n\n" + 
96      // Entry # 2
97      "dn: ou=Computers,uid=akarasulu,ou=users,ou=system\n" +
98      "objectClass: organizationalUnit\n" +
99      "objectClass: top\n" +
100     "ou: computers\n" +
101     "description: Computers for Alex\n" +
102     "seeAlso: ou=Machines,uid=akarasulu,ou=users,ou=system\n\n" + 
103     // Entry # 3
104     "dn: uid=akarasuluref,ou=users,ou=system\n" +
105     "objectClass: uidObject\n" +
106     "objectClass: referral\n" +
107     "objectClass: top\n" +
108     "uid: akarasuluref\n" +
109     "ref: ldap://localhost:10389/uid=akarasulu,ou=users,ou=system\n" + 
110     "ref: ldap://foo:10389/uid=akarasulu,ou=users,ou=system\n" +
111     "ref: ldap://bar:10389/uid=akarasulu,ou=users,ou=system\n\n"
112     }
113 )
114 public class AddIT
115 {
116     private static final Logger LOG = LoggerFactory.getLogger( AddIT.class );
117     private static final String RDN = "cn=The Person";
118 
119     private static final String BASE = "ou=system";
120 
121 
122     public static LdapService ldapService;
123 
124 
125     /**
126      * This is the original defect as in JIRA DIREVE-216.
127      * 
128      * @throws NamingException if we cannot connect and perform add operations
129      */
130     @Test
131     public void testAddObjectClasses() throws Exception
132     {
133         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
134 
135         // modify object classes, add two more
136         Attributes attributes = new BasicAttributes( true );
137         Attribute ocls = new BasicAttribute( "objectClass" );
138         ocls.add( "organizationalPerson" );
139         ocls.add( "inetOrgPerson" );
140         attributes.put( ocls );
141 
142         DirContext person = ( DirContext ) ctx.lookup( RDN );
143         person.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, attributes );
144 
145         // Read again from directory
146         person = ( DirContext ) ctx.lookup( RDN );
147         attributes = person.getAttributes( "" );
148         Attribute newOcls = attributes.get( "objectClass" );
149 
150         String[] expectedOcls = { "top", "person", "organizationalPerson", "inetOrgPerson" };
151 
152         for ( String name : expectedOcls )
153         {
154             assertTrue( "object class " + name + " is present", newOcls.contains( name ) );
155         }
156     }
157 
158 
159     /**
160      * This changes a single attribute value. Just as a reference.
161      * 
162      * @throws NamingException if we cannot connect and modify the description
163      */
164     @Test
165     public void testModifyDescription() throws Exception
166     {
167         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
168 
169         String newDescription = "More info on the user ...";
170 
171         // modify object classes, add two more
172         Attributes attributes = new BasicAttributes( true );
173         Attribute desc = new BasicAttribute( "description", newDescription );
174         attributes.put( desc );
175 
176         DirContext person = ( DirContext ) ctx.lookup( RDN );
177         person.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, attributes );
178 
179         // Read again from directory
180         person = ( DirContext ) ctx.lookup( RDN );
181         attributes = person.getAttributes( "" );
182         Attribute newDesc = attributes.get( "description" );
183 
184         assertTrue( "new Description", newDesc.contains( newDescription ) );
185     }
186 
187 
188     /**
189      * Try to add entry with required attribute missing.
190      * 
191      * @throws NamingException if we fail to connect
192      */
193     @Test
194     public void testAddWithMissingRequiredAttributes() throws Exception
195     {
196         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
197 
198         // person without sn
199         Attributes attrs = new BasicAttributes( true );
200         Attribute ocls = new BasicAttribute( "objectClass" );
201         ocls.add( "top" );
202         ocls.add( "person" );
203         attrs.put( ocls );
204         attrs.put( "cn", "Fiona Apple" );
205 
206         try
207         {
208             ctx.createSubcontext( "cn=Fiona Apple", attrs );
209             fail( "creation of entry should fail" );
210         }
211         catch ( SchemaViolationException e )
212         {
213             // expected
214         }
215     }
216     
217     
218     /**
219      * Test case to demonstrate DIRSERVER-643 ("Netscape SDK: Adding an entry with
220      * two description attributes does not combine values."). Uses Sun ONE Directory
221      * SDK for Java 4.1 , or comparable (Netscape, Mozilla).
222      * 
223      * @throws LDAPException if we fail to connect and add entries
224      */
225     @Test
226     public void testAddEntryWithTwoDescriptions() throws Exception
227     {
228         LDAPConnection con = getWiredConnection( ldapService );
229         LDAPAttributeSet attrs = new LDAPAttributeSet();
230         LDAPAttribute ocls = new LDAPAttribute( "objectclass", new String[]
231             { "top", "person" } );
232         attrs.add( ocls );
233         attrs.add( new LDAPAttribute( "sn", "Bush" ) );
234         attrs.add( new LDAPAttribute( "cn", "Kate Bush" ) );
235 
236         String descr[] =
237             { "a British singer-songwriter with an expressive four-octave voice",
238                 "one of the most influential female artists of the twentieth century" };
239 
240         attrs.add( new LDAPAttribute( "description", descr ) );
241 
242         String dn = "cn=Kate Bush," + BASE;
243         LDAPEntry kate = new LDAPEntry( dn, attrs );
244 
245         con.add( kate );
246 
247         // Analyze entry and description attribute
248         LDAPEntry kateReloaded = con.read( dn );
249         assertNotNull( kateReloaded );
250         LDAPAttribute attr = kateReloaded.getAttribute( "description" );
251         assertNotNull( attr );
252         assertEquals( 2, attr.getStringValueArray().length );
253 
254         // Remove entry
255         con.delete( dn );
256         con.disconnect();
257     }
258 
259 
260     /**
261      * Testcase to demonstrate DIRSERVER-643 ("Netscape SDK: Adding an entry with
262      * two description attributes does not combine values."). Uses Sun ONE Directory
263      * SDK for Java 4.1 , or comparable (Netscape, Mozilla).
264      * 
265      * @throws LDAPException if we fail to connect and add entries
266      */
267     @Test
268     public void testAddEntryWithTwoDescriptionsVariant() throws Exception
269     {
270         LDAPConnection con = getWiredConnection( ldapService );
271         LDAPAttributeSet attrs = new LDAPAttributeSet();
272         LDAPAttribute ocls = new LDAPAttribute( "objectclass", new String[]
273             { "top", "person" } );
274         attrs.add( ocls );
275         attrs.add( new LDAPAttribute( "sn", "Bush" ) );
276         attrs.add( new LDAPAttribute( "cn", "Kate Bush" ) );
277 
278         String descr[] =
279             { "a British singer-songwriter with an expressive four-octave voice",
280                 "one of the most influential female artists of the twentieth century" };
281 
282         attrs.add( new LDAPAttribute( "description", descr[0] ) );
283         attrs.add( new LDAPAttribute( "description", descr[1] ) );
284 
285         String dn = "cn=Kate Bush," + BASE;
286         LDAPEntry kate = new LDAPEntry( dn, attrs );
287 
288         con.add( kate );
289 
290         // Analyze entry and description attribute
291         LDAPEntry kateReloaded = con.read( dn );
292         assertNotNull( kateReloaded );
293         LDAPAttribute attr = kateReloaded.getAttribute( "description" );
294         assertNotNull( attr );
295         assertEquals( 2, attr.getStringValueArray().length );
296 
297         // Remove entry
298         con.delete( dn );
299         con.disconnect();
300     }
301 
302 
303     /**
304      * Testcase to demonstrate DIRSERVER-643 ("Netscape SDK: Adding an entry with
305      * two description attributes does not combine values."). Uses Sun ONE Directory
306      * SDK for Java 4.1 , or comparable (Netscape, Mozilla).
307      * 
308      * @throws LDAPException if we fail to connect and add entries
309      */
310     @Test
311     public void testAddEntryWithTwoDescriptionsSecondVariant() throws Exception
312     {
313         LDAPConnection con = getWiredConnection( ldapService );
314         LDAPAttributeSet attrs = new LDAPAttributeSet();
315         LDAPAttribute ocls = new LDAPAttribute( "objectclass", new String[]
316             { "top", "person" } );
317         attrs.add( ocls );
318         attrs.add( new LDAPAttribute( "sn", "Bush" ) );
319 
320         String descr[] =
321             { "a British singer-songwriter with an expressive four-octave voice",
322                 "one of the most influential female artists of the twentieth century" };
323 
324         attrs.add( new LDAPAttribute( "description", descr[0] ) );
325         attrs.add( new LDAPAttribute( "cn", "Kate Bush" ) );
326         attrs.add( new LDAPAttribute( "description", descr[1] ) );
327 
328         String dn = "cn=Kate Bush," + BASE;
329         LDAPEntry kate = new LDAPEntry( dn, attrs );
330 
331         con.add( kate );
332 
333         // Analyze entry and description attribute
334         LDAPEntry kateReloaded = con.read( dn );
335         assertNotNull( kateReloaded );
336         LDAPAttribute attr = kateReloaded.getAttribute( "description" );
337         assertNotNull( attr );
338         assertEquals( 2, attr.getStringValueArray().length );
339 
340         // Remove entry
341         con.delete( dn );
342         con.disconnect();
343     }
344 
345     
346     /**
347      * Try to add entry with invalid number of values for a single-valued attribute
348      * 
349      * @throws NamingException if we fail to connect and add entries
350      * @see <a href="http://issues.apache.org/jira/browse/DIRSERVER-614">DIRSERVER-614</a>
351      */
352     @Test
353     public void testAddWithInvalidNumberOfAttributeValues() throws Exception
354     {
355         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
356         
357         // add inetOrgPerson with two displayNames
358         Attributes attrs = new BasicAttributes( true );
359         Attribute ocls = new BasicAttribute( "objectClass" );
360         ocls.add( "top" );
361         ocls.add( "inetOrgPerson" );
362         attrs.put( ocls );
363         attrs.put( "cn", "Fiona Apple" );
364         attrs.put( "sn", "Apple" );
365         Attribute displayName = new BasicAttribute( "displayName" );
366         displayName.add( "Fiona" );
367         displayName.add( "Fiona A." );
368         attrs.put( displayName );
369 
370         try
371         {
372             ctx.createSubcontext( "cn=Fiona Apple", attrs );
373             fail( "creation of entry should fail" );
374         }
375         catch ( InvalidAttributeValueException e )
376         {
377         }
378     }
379 
380 
381     /**
382      * Try to add entry and an alias to it. Afterwards, remove it.
383      * 
384      * @throws NamingException if we fail to connect and add entries
385      */
386     @Test
387     public void testAddAlias() throws Exception
388     {
389         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
390 
391         // Create entry
392         Attributes entry = new BasicAttributes( true );
393         Attribute entryOcls = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT );
394         entryOcls.add( SchemaConstants.TOP_OC );
395         entryOcls.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
396         entry.put( entryOcls );
397         entry.put( SchemaConstants.OU_AT, "favorite" );
398         String entryRdn = "ou=favorite";
399         ctx.createSubcontext( entryRdn, entry );
400 
401         // Create Alias
402         String aliasedObjectName = entryRdn + "," + ctx.getNameInNamespace();
403         Attributes alias = new BasicAttributes( true );
404         Attribute aliasOcls = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT );
405         aliasOcls.add( SchemaConstants.TOP_OC );
406         aliasOcls.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
407         aliasOcls.add( SchemaConstants.ALIAS_OC );
408         alias.put( aliasOcls );
409         alias.put( SchemaConstants.OU_AT, "bestFruit" );
410         alias.put( SchemaConstants.ALIASED_OBJECT_NAME_AT, aliasedObjectName );
411         String rdnAlias = "ou=bestFruit";
412         ctx.createSubcontext( rdnAlias, alias );
413 
414         // Remove alias and entry
415         ctx.destroySubcontext( rdnAlias );
416         ctx.destroySubcontext( entryRdn );
417     }
418 
419 
420     /**
421      * Try to add entry and an alias to it. Afterwards, remove it. This version
422      * cretes a container entry before the operations.
423      * 
424      * @throws NamingException if we fail to connect and add entries
425      */
426     @Test
427     public void testAddAliasInContainer() throws Exception
428     {
429         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
430 
431         // Create container
432         Attributes container = new BasicAttributes( true );
433         Attribute containerOcls = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT );
434         containerOcls.add( SchemaConstants.TOP_OC );
435         containerOcls.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
436         container.put( containerOcls );
437         container.put( SchemaConstants.OU_AT, "Fruits" );
438         String containerRdn = "ou=Fruits";
439         DirContext containerCtx = ctx.createSubcontext( containerRdn, container );
440 
441         // Create entry
442         Attributes entry = new BasicAttributes( true );
443         Attribute entryOcls = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT );
444         entryOcls.add( SchemaConstants.TOP_OC );
445         entryOcls.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
446         entry.put( entryOcls );
447         entry.put( SchemaConstants.OU_AT, "favorite" );
448         String entryRdn = "ou=favorite";
449         containerCtx.createSubcontext( entryRdn, entry );
450 
451         // Create alias ou=bestFruit,ou=Fruits to entry ou=favorite,ou=Fruits
452         String aliasedObjectName = entryRdn + "," + containerCtx.getNameInNamespace();
453         Attributes alias = new BasicAttributes( true );
454         Attribute aliasOcls = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT );
455         aliasOcls.add( SchemaConstants.TOP_OC );
456         aliasOcls.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
457         aliasOcls.add( SchemaConstants.ALIAS_OC );
458         alias.put( aliasOcls );
459         alias.put( SchemaConstants.OU_AT, "bestFruit" );
460         alias.put( SchemaConstants.ALIASED_OBJECT_NAME_AT, aliasedObjectName );
461         String rdnAlias = "ou=bestFruit";
462         containerCtx.createSubcontext( rdnAlias, alias );
463 
464         // search one level scope for alias 
465         SearchControls controls = new SearchControls();
466         controls.setDerefLinkFlag( true );
467         controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
468         containerCtx.addToEnvironment( "java.naming.ldap.derefAliases", "never" );
469         NamingEnumeration<SearchResult> ne = containerCtx.search( "", "(objectClass=*)", controls );
470         assertTrue( ne.hasMore() );
471         SearchResult sr = ne.next();
472         assertEquals( "ou=favorite", sr.getName() );
473         assertTrue( ne.hasMore() );
474         sr = ne.next();
475         assertEquals( "ou=bestFruit", sr.getName() );
476         
477         // search one level with dereferencing turned on
478         controls = new SearchControls();
479         controls.setDerefLinkFlag( true );
480         controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
481         containerCtx.addToEnvironment( "java.naming.ldap.derefAliases", "always" );
482         ne = containerCtx.search( "", "(objectClass=*)", controls );
483         assertTrue( ne.hasMore() );
484         sr = ne.next();
485         assertEquals( "ou=favorite", sr.getName() );
486         assertFalse( ne.hasMore() );
487         
488         // search with base set to alias and dereferencing turned on
489         controls = new SearchControls();
490         controls.setDerefLinkFlag( false );
491         controls.setSearchScope( SearchControls.OBJECT_SCOPE );
492         containerCtx.addToEnvironment( "java.naming.ldap.derefAliases", "always" );
493         ne = containerCtx.search( "ou=bestFruit", "(objectClass=*)", controls );
494         assertTrue( ne.hasMore() );
495         sr = ne.next();
496         assertEquals( "ldap://localhost:"+ ldapService.getIpPort() +"/ou=favorite,ou=Fruits,ou=system", sr.getName() );
497         assertFalse( ne.hasMore() );
498         
499         // Remove alias and entry
500         containerCtx.destroySubcontext( rdnAlias );
501         containerCtx.destroySubcontext( entryRdn );
502 
503         // Remove container
504         ctx.destroySubcontext( containerRdn );
505     }
506     
507     
508     /**
509      * Try to add entry and an alias to it. Afterwards, remove it.  Taken from
510      * DIRSERVER-1157 test contribution.
511      * 
512      * @see https://issues.apache.org/jira/browse/DIRSERVER-1157
513      * @throws Exception
514      */
515     @Test
516     public void testAddDeleteAlias() throws Exception
517     {
518         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
519 
520         // Create entry ou=favorite,dc=example,dc=com
521         Attributes entry = new BasicAttributes( true );
522         Attribute entryOcls = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT );
523         entryOcls.add( SchemaConstants.TOP_OC );
524         entryOcls.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
525         entry.put( entryOcls );
526         entry.put( SchemaConstants.OU_AT, "favorite" );
527         String entryRdn = "ou=favorite";
528         ctx.createSubcontext( entryRdn, entry );
529 
530         // Create Alias ou=bestFruit,dc=example,dc=com to ou=favorite
531         String aliasedObjectName = entryRdn + "," + ctx.getNameInNamespace();
532         Attributes alias = new BasicAttributes( true );
533         Attribute aliasOcls = new BasicAttribute( SchemaConstants.OBJECT_CLASS_AT );
534         aliasOcls.add( SchemaConstants.TOP_OC );
535         aliasOcls.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
536         aliasOcls.add( SchemaConstants.ALIAS_OC );
537         alias.put( aliasOcls );
538         alias.put( SchemaConstants.OU_AT, "bestFruit" );
539         alias.put( SchemaConstants.ALIASED_OBJECT_NAME_AT, aliasedObjectName );
540         String rdnAlias = "ou=bestFruit";
541         ctx.createSubcontext( rdnAlias, alias );
542 
543         // Remove alias and entry
544         ctx.destroySubcontext( rdnAlias ); //Waiting for Connection.reply()
545         ctx.destroySubcontext( entryRdn );
546     }
547     
548     
549     /**
550      * Tests add operation on referral entry with the ManageDsaIT control.
551      */
552     @Test
553     public void testOnReferralWithManageDsaITControl() throws Exception
554     {
555         LDAPConnection conn = getWiredConnection( ldapService );
556         LDAPConstraints constraints = new LDAPSearchConstraints();
557         constraints.setClientControls( new LDAPControl( LDAPControl.MANAGEDSAIT, true, new byte[0] ) );
558         constraints.setServerControls( new LDAPControl( LDAPControl.MANAGEDSAIT, true, new byte[0] ) );
559         conn.setConstraints( constraints );
560         
561         // add success
562         LDAPAttributeSet attrSet = new LDAPAttributeSet();
563         attrSet.add( new LDAPAttribute( "objectClass", "organizationalUnit" ) );
564         attrSet.add( new LDAPAttribute( "ou", "UnderReferral" ) );
565         LDAPEntry entry = new LDAPEntry( "ou=UnderReferral,uid=akarasuluref,ou=users,ou=system", attrSet );
566         
567         conn.add( entry, constraints );
568         
569         LDAPEntry reread = conn.read( "ou=UnderReferral,uid=akarasuluref,ou=users,ou=system", 
570             ( LDAPSearchConstraints ) constraints );
571         assertEquals( "ou=UnderReferral,uid=akarasuluref,ou=users,ou=system", reread.getDN() );
572         
573         conn.disconnect();
574     }
575     
576     
577     /**
578      * Tests referral handling when an ancestor is a referral.
579      */
580     @Test 
581     public void testAncestorReferral() throws Exception
582     {
583         LOG.debug( "" );
584 
585         LDAPConnection conn = getWiredConnection( ldapService );
586         LDAPConstraints constraints = new LDAPConstraints();
587         conn.setConstraints( constraints );
588 
589         // referrals failure
590         LDAPAttributeSet attrSet = new LDAPAttributeSet();
591         attrSet.add( new LDAPAttribute( "objectClass", "organizationalUnit" ) );
592         attrSet.add( new LDAPAttribute( "ou", "UnderReferral" ) );
593         LDAPEntry entry = new LDAPEntry( "ou=UnderReferral,ou=Computers,uid=akarasuluref,ou=users,ou=system", attrSet );
594         
595         LDAPResponseListener listener = conn.add( entry, null, constraints );
596         LDAPResponse response = listener.getResponse();
597         assertEquals( ResultCodeEnum.REFERRAL.getValue(), response.getResultCode() );
598 
599         assertEquals( "ldap://localhost:10389/ou=UnderReferral,ou=Computers,uid=akarasulu,ou=users,ou=system", 
600             response.getReferrals()[0] );
601         assertEquals( "ldap://foo:10389/ou=UnderReferral,ou=Computers,uid=akarasulu,ou=users,ou=system", 
602             response.getReferrals()[1] );
603         assertEquals( "ldap://bar:10389/ou=UnderReferral,ou=Computers,uid=akarasulu,ou=users,ou=system", 
604             response.getReferrals()[2] );
605 
606         conn.disconnect();
607     }
608 
609     
610     /**
611      * Tests add operation on normal and referral entries without the 
612      * ManageDsaIT control. Referrals are sent back to the client with a
613      * non-success result code.
614      */
615     @Test
616     public void testOnReferral() throws Exception
617     {
618         LDAPConnection conn = getWiredConnection( ldapService );
619         LDAPConstraints constraints = new LDAPConstraints();
620         constraints.setReferrals( false );
621         conn.setConstraints( constraints );
622         
623         // referrals failure
624 
625         LDAPAttributeSet attrSet = new LDAPAttributeSet();
626         attrSet.add( new LDAPAttribute( "objectClass", "organizationalUnit" ) );
627         attrSet.add( new LDAPAttribute( "ou", "UnderReferral" ) );
628         LDAPEntry entry = new LDAPEntry( "ou=UnderReferral,uid=akarasuluref,ou=users,ou=system", attrSet );
629         
630         LDAPResponseListener listener = null;
631         LDAPResponse response = null;
632         listener = conn.add( entry, null, constraints );
633         response = listener.getResponse();
634 
635         assertEquals( ResultCodeEnum.REFERRAL.getValue(), response.getResultCode() );
636 
637         assertEquals( "ldap://localhost:10389/ou=UnderReferral,uid=akarasulu,ou=users,ou=system", response.getReferrals()[0] );
638         assertEquals( "ldap://foo:10389/ou=UnderReferral,uid=akarasulu,ou=users,ou=system", response.getReferrals()[1] );
639         assertEquals( "ldap://bar:10389/ou=UnderReferral,uid=akarasulu,ou=users,ou=system", response.getReferrals()[2] );
640 
641         conn.disconnect();
642     }
643     
644     
645     /**
646      * Tests add operation on normal and referral entries without the 
647      * ManageDsaIT control using JNDI instead of the Netscape API. Referrals 
648      * are sent back to the client with a non-success result code.
649      */
650     @Test
651     public void testThrowOnReferralWithJndi() throws Exception
652     {
653         LdapContext ctx = getWiredContextThrowOnRefferal( ldapService );
654         SearchControls controls = new SearchControls();
655         controls.setReturningAttributes( new String[0] );
656         controls.setSearchScope( SearchControls.OBJECT_SCOPE );
657         
658         // add failure
659         Attributes attrs = new BasicAttributes( "objectClass", "organizationalUnit", true );
660         attrs.put( "ou", "UnderReferral" );
661         
662         try
663         {
664             ctx.createSubcontext( "ou=UnderReferral,uid=akarasuluref,ou=users,ou=system", attrs );
665             fail( "Should never get here: add should fail with ReferralExcpetion" );
666         }
667         catch( ReferralException e )
668         {
669             assertEquals( "ldap://localhost:10389/ou=UnderReferral,uid=akarasulu,ou=users,ou=system", e.getReferralInfo() );
670         }
671 
672         ctx.close();
673     }
674 
675 
676     /**
677      * Test for DIRSERVER-1183.
678      * 
679      * @see https://issues.apache.org/jira/browse/DIRSERVER-1183
680      * @throws Exception
681      */
682     @Test
683     public void testDIRSERVER_1183() throws Exception
684     {
685         LdapContext ctx = ( LdapContext ) getWiredContext( ldapService ).lookup( BASE );
686     	Attributes attrs = new BasicAttributes( "objectClass", "inetOrgPerson", true );
687     	attrs.get( "objectClass" ).add( "organizationalPerson" );
688     	attrs.get( "objectClass" ).add( "person" );
689     	attrs.put( "givenName", "Jim" );
690     	attrs.put( "sn", "Bean" );
691     	attrs.put( "cn", "\"Jim, Bean\"" );
692     	
693     	ctx.createSubcontext( "cn=\"Jim, Bean\"", attrs );
694     }
695 
696 
697     /**
698      * Create an entry a RDN which is not present in the entry
699      */
700     @Test
701     public void testAddEntryNoRDNInEntry() throws Exception
702     {
703         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
704         
705         // Create a person
706         Attributes person = new BasicAttributes( "objectClass", "inetOrgPerson", true );
707         person.get( "objectClass" ).add( "top" );
708         person.get( "objectClass" ).add( "person" );
709         person.get( "objectClass" ).add( "organizationalperson" );
710         person.put( "sn", "Michael Jackson" );
711         person.put( "cn", "Jackson" );
712 
713         DirContext michaelCtx = ctx.createSubcontext( "givenname=Michael", person );
714         
715         assertNotNull( michaelCtx );
716         
717         DirContext jackson = ( DirContext ) ctx.lookup( "givenname=Michael" );
718         person = jackson.getAttributes( "" );
719         Attribute newOcls = person.get( "objectClass" );
720 
721         String[] expectedOcls = { "top", "person", "organizationalPerson", "inetOrgPerson" };
722 
723         for ( String name : expectedOcls )
724         {
725             assertTrue( "object class " + name + " is present", newOcls.contains( name ) );
726         }
727         
728         Attribute givenName = person.get( "givenname" );
729         
730         assertEquals( "Michael", givenName.get() );
731     }
732 
733 
734     /**
735      * Create an entry a RDN which is not present in the entry, but
736      * with another attribute's value
737      */
738     @Test
739     public void testAddEntryDifferentRDNInEntry() throws Exception
740     {
741         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
742         
743         // Create a person
744         Attributes person = new BasicAttributes( "objectClass", "inetOrgPerson", true );
745         person.get( "objectClass" ).add( "top" );
746         person.get( "objectClass" ).add( "person" );
747         person.get( "objectClass" ).add( "organizationalperson" );
748         person.put( "givenName", "Michael" );
749         person.put( "sn", "Michael Jackson" );
750         person.put( "cn", "Jackson" );
751 
752         DirContext michaelCtx = ctx.createSubcontext( "cn=Michael", person );
753         
754         assertNotNull( michaelCtx );
755         
756         DirContext jackson = ( DirContext ) ctx.lookup( "cn=Michael" );
757         person = jackson.getAttributes( "" );
758         Attribute newOcls = person.get( "objectClass" );
759 
760         String[] expectedOcls = { "top", "person", "organizationalPerson", "inetOrgPerson" };
761 
762         for ( String name : expectedOcls )
763         {
764             assertTrue( "object class " + name + " is present", newOcls.contains( name ) );
765         }
766         
767         Attribute cn = person.get( "cn" );
768         
769         assertEquals( 2, cn.size() );
770         String[] expectedCns = { "Jackson", "Michael" };
771 
772         for ( String name : expectedCns )
773         {
774             assertTrue( "CN " + name + " is present", cn.contains( name ) );
775         }
776     }
777 
778 
779     /**
780      * Create an entry a RDN which is not present in the entry, 
781      * with another attribute's value, and on a SingleValued attribute
782      */
783     @Test
784     public void testAddEntryDifferentRDNSingleValuedInEntry() throws Exception
785     {
786         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
787         
788         // Create a person
789         Attributes person = new BasicAttributes( "objectClass", "inetOrgPerson", true );
790         person.get( "objectClass" ).add( "top" );
791         person.get( "objectClass" ).add( "person" );
792         person.get( "objectClass" ).add( "organizationalperson" );
793         person.put( "displayName", "Michael" );
794         person.put( "sn", "Michael Jackson" );
795         person.put( "cn", "Jackson" );
796 
797         DirContext michaelCtx = ctx.createSubcontext( "displayName=test", person );
798         
799         assertNotNull( michaelCtx );
800         
801         DirContext jackson = ( DirContext ) ctx.lookup( "displayName=test" );
802         person = jackson.getAttributes( "" );
803         Attribute newOcls = person.get( "objectClass" );
804 
805         String[] expectedOcls = { "top", "person", "organizationalPerson", "inetOrgPerson" };
806 
807         for ( String name : expectedOcls )
808         {
809             assertTrue( "object class " + name + " is present", newOcls.contains( name ) );
810         }
811         
812         // Check that the displayName attribute has been replaced
813         Attribute displayName = person.get( "displayName" );
814         
815         assertEquals( 1, displayName.size() );
816         assertTrue( displayName.contains( "test" ) );
817     }
818 
819 
820     /**
821      * Create an entry a composed RDN which is not present in the entry, 
822      * with another attribute's value, and on a SingleValued attribute
823      */
824     @Test
825     public void testAddEntryComposedRDN() throws Exception
826     {
827         DirContext ctx = ( DirContext ) getWiredContext( ldapService ).lookup( BASE );
828         
829         // Create a person
830         Attributes person = new BasicAttributes( "objectClass", "inetOrgPerson", true );
831         person.get( "objectClass" ).add( "top" );
832         person.get( "objectClass" ).add( "person" );
833         person.get( "objectClass" ).add( "organizationalperson" );
834         person.put( "sn", "Michael Jackson" );
835         person.put( "cn", "Jackson" );
836 
837         DirContext michaelCtx = ctx.createSubcontext( "displayName=test+cn=Michael", person );
838         
839         assertNotNull( michaelCtx );
840         
841         DirContext jackson = ( DirContext ) ctx.lookup( "displayName=test+cn=Michael" );
842         person = jackson.getAttributes( "" );
843         Attribute newOcls = person.get( "objectClass" );
844 
845         String[] expectedOcls = { "top", "person", "organizationalPerson", "inetOrgPerson" };
846 
847         for ( String name : expectedOcls )
848         {
849             assertTrue( "object class " + name + " is present", newOcls.contains( name ) );
850         }
851         
852         // Check that the DIsplayName attribute has been added
853         Attribute displayName = person.get( "displayName" );
854         
855         assertEquals( 1, displayName.size() );
856         assertTrue( displayName.contains( "test" ) );
857 
858         // Check that the cn attribute value has been added
859         Attribute cn = person.get( "cn" );
860         
861         assertEquals( 2, cn.size() );
862         assertTrue( cn.contains( "Jackson" ) );
863         assertTrue( cn.contains( "Michael" ) );
864     }
865 }