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 static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
24  import static org.junit.Assert.assertEquals;
25  import static org.junit.Assert.assertFalse;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import java.util.Hashtable;
30  
31  import javax.naming.Context;
32  import javax.naming.NamingException;
33  import javax.naming.directory.Attribute;
34  import javax.naming.directory.Attributes;
35  import javax.naming.directory.BasicAttribute;
36  import javax.naming.directory.BasicAttributes;
37  import javax.naming.directory.DirContext;
38  import javax.naming.directory.InitialDirContext;
39  import javax.naming.directory.InvalidAttributeIdentifierException;
40  import javax.naming.directory.InvalidAttributeValueException;
41  import javax.naming.directory.ModificationItem;
42  import javax.naming.directory.SchemaViolationException;
43  
44  import org.apache.directory.server.core.DirectoryService;
45  import org.apache.directory.server.core.integ.CiRunner;
46  import org.junit.Test;
47  import org.junit.runner.RunWith;
48  
49  
50  /**
51   * A test case which demonstrates the three defects described in DIRSERVER-791.
52   * 
53   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
54   */
55  @RunWith(CiRunner.class)
56  public class DIRSERVER791IT
57  {
58      public static DirectoryService service;
59  
60  
61      /**
62       * Returns the attributes as depicted as test data in DIRSERVER-791
63       * @return attributes for the test entry
64       */
65      protected Attributes getTestEntryAttributes()
66      {
67          Attributes attrs = new BasicAttributes( true );
68          Attribute ocls = new BasicAttribute( "objectClass" );
69          ocls.add( "top" );
70          ocls.add( "person" );
71          ocls.add( "organizationalPerson" );
72          ocls.add( "inetOrgPerson" );
73          attrs.put( ocls );
74  
75          Attribute cn = new BasicAttribute( "cn" );
76          cn.add( "test" );
77          cn.add( "aaa" );
78          attrs.put( cn );
79  
80          attrs.put( "sn", "test" );
81  
82          return attrs;
83      }
84  
85  
86      /**
87       * @todo  replace this with an ldif annotation
88       *
89       * @throws NamingException on error
90       */
91      protected void createData() throws Exception
92      {
93          Attributes entry = this.getTestEntryAttributes();
94          getSystemContext( service ).createSubcontext( "cn=test", entry );
95      }
96  
97  
98      /**
99       * Tests that it is possible to remove a value (in this case "cn=aaa") 
100      * from the RDN attribute which is not part of the RDN
101      * 
102      * The defect was:
103      * Removal of a value from RDN attribute which is not part
104      * of the RDN is not possible.
105      *
106      * @throws NamingException on error
107      */
108     @Test
109     public void testDefect1a() throws Exception
110     {
111         createData();
112         Hashtable<String, Object> env = new Hashtable<String, Object>();
113         env.put( DirectoryService.JNDI_KEY, service );
114         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
115         env.put( Context.PROVIDER_URL, "ou=system" );
116         DirContext ctx = new InitialDirContext( env );
117 
118         // remove "cn=aaa", which is not part of the RDN
119         Attribute attr = new BasicAttribute( "cn", "aaa" );
120         ModificationItem modification = new ModificationItem( DirContext.REMOVE_ATTRIBUTE, attr );
121         ctx.modifyAttributes( "cn=test", new ModificationItem[]
122             { modification } );
123 
124         Attributes attrs = ctx.getAttributes( "cn=test", new String[]
125             { "cn" } );
126         Attribute cn = attrs.get( "cn" );
127 
128         // cn=aaa must be removed, cn=test must exist
129         assertEquals( "number of cn values", 1, cn.size() );
130         assertTrue( cn.contains( "test" ) );
131         assertFalse( cn.contains( "aaa" ) );
132     }
133 
134 
135     /**
136      * Tests that it is possible to replace the RDN attribute with 
137      * 
138      * Checks whether it is possible to replace the cn attribute with a single
139      * value. The JIRA issue states that this one works.
140      *
141      * @throws NamingException on error
142      */
143     @Test
144     public void testDefect1b() throws Exception
145     {
146         createData();
147         Hashtable<String, Object> env = new Hashtable<String, Object>();
148         env.put( DirectoryService.JNDI_KEY, service );
149         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
150         env.put( Context.PROVIDER_URL, "ou=system" );
151         DirContext ctx = new InitialDirContext( env );
152 
153         // replace cn attribute with "cn=test", must remove the previous "cn=aaa"
154         Attribute attr = new BasicAttribute( "cn", "test" );
155         ModificationItem modification = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr );
156         ctx.modifyAttributes( "cn=test", new ModificationItem[]
157             { modification } );
158 
159         Attributes attrs = ctx.getAttributes( "cn=test", new String[]
160             { "cn" } );
161         Attribute cn = attrs.get( "cn" );
162 
163         // cn=aaa must be removed, cn=test must exist
164         assertEquals( "number of cn values", 1, cn.size() );
165         assertTrue( cn.contains( "test" ) );
166         assertFalse( cn.contains( "aaa" ) );
167     }
168 
169 
170     /**
171      * Tests that the server rejects the addition of an non-existing objectClass.
172      * Also checks that the non-existing isn't stored in the entry.
173      * 
174      * The defect was:
175      * It is possible to add an value to objectclass, which isn't a valid
176      * objectclass. The server returns an error, but nevertheless the invalid
177      * value is stored. I think this should be rejected from server.
178      *
179      * @throws NamingException on error
180      */
181     @Test
182     public void testDefect2() throws Exception
183     {
184         createData();
185         Hashtable<String, Object> env = new Hashtable<String, Object>();
186         env.put( DirectoryService.JNDI_KEY, service );
187         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
188         env.put( Context.PROVIDER_URL, "ou=system" );
189         DirContext ctx = new InitialDirContext( env );
190 
191         // try to add an non-existing objectClass "test", must be rejected
192         Attribute attr = new BasicAttribute( "objectclass", "test" );
193         ModificationItem modification = new ModificationItem( DirContext.ADD_ATTRIBUTE, attr );
194         try
195         {
196             ctx.modifyAttributes( "cn=test", new ModificationItem[]
197                 { modification } );
198             fail( "Exception expected" );
199         }
200         catch ( SchemaViolationException sve )
201         {
202             // Valid behavior
203         }
204         catch ( InvalidAttributeValueException iave )
205         {
206             // Valid behavior
207         }
208         catch ( NamingException ne )
209         {
210             // Valid behavior
211         }
212 
213         // re-read the entry, the non-existing objectClass "test" must not be present
214         Attributes attrs = ctx.getAttributes( "cn=test", new String[]
215             { "objectClass" } );
216         Attribute ocls = attrs.get( "objectClass" );
217         assertEquals( "number of objectClasses", 4, ocls.size() );
218         assertTrue( ocls.contains( "top" ) );
219         assertTrue( ocls.contains( "person" ) );
220         assertTrue( ocls.contains( "organizationalPerson" ) );
221         assertTrue( ocls.contains( "inetOrgPerson" ) );
222         assertFalse( ocls.contains( "test" ) );
223     }
224 
225 
226     /**
227      * Tests that no unallowed attribute could be added to the entry.
228      * 
229      * The defect was:
230      * It is possible to add an attribute to the entry that is not allowed
231      * according the objectclasses. The server should reject this.
232      *
233      * @throws NamingException on error
234      */
235     @Test
236     public void testDefect3() throws Exception
237     {
238         createData();
239         Hashtable<String, Object> env = new Hashtable<String, Object>();
240         env.put( DirectoryService.JNDI_KEY, service );
241         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
242         env.put( Context.PROVIDER_URL, "ou=system" );
243         DirContext ctx = new InitialDirContext( env );
244 
245         // try to add an unallowed attribute, must be rejected
246         Attribute attr = new BasicAttribute( "javaDoc", "test" );
247         ModificationItem modification = new ModificationItem( DirContext.ADD_ATTRIBUTE, attr );
248         try
249         {
250             ctx.modifyAttributes( "cn=test", new ModificationItem[]
251                 { modification } );
252             fail( "Exception expected" );
253         }
254         catch ( SchemaViolationException sve )
255         {
256             // Valid behavior
257         }
258         catch ( InvalidAttributeIdentifierException iaie )
259         {
260             // Valid behavior
261         }
262     }
263 }