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.authz;
21  
22  
23  import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
24  import org.apache.directory.shared.ldap.name.LdapDN;
25  import org.apache.directory.server.core.integ.CiRunner;
26  import org.apache.directory.server.core.integ.annotations.Factory;
27  import org.apache.directory.server.core.DirectoryService;
28  import org.junit.runner.RunWith;
29  
30  import javax.naming.NamingException;
31  import javax.naming.NamingEnumeration;
32  import javax.naming.Name;
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.ModificationItem;
39  
40  import java.util.List;
41  import java.util.ArrayList;
42  
43  import static org.junit.Assert.assertTrue;
44  import static org.junit.Assert.assertFalse;
45  import org.junit.Test;
46  
47  import static org.apache.directory.server.core.authz.AutzIntegUtils.createUser;
48  import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAs;
49  import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAsAdmin;
50  import static org.apache.directory.server.core.authz.AutzIntegUtils.createAccessControlSubentry;
51  import static org.apache.directory.server.core.authz.AutzIntegUtils.addUserToGroup;
52  import static org.apache.directory.server.core.authz.AutzIntegUtils.deleteAccessControlSubentry;
53  import static org.apache.directory.server.core.authz.AutzIntegUtils.createGroup;
54  import static org.apache.directory.server.core.authz.AutzIntegUtils.changePresciptiveACI;
55  
56  
57  /**
58   * Tests whether or not authorization around entry modify operations work properly.
59   *
60   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
61   * @version $Rev: 691179 $
62   */
63  @RunWith ( CiRunner.class )
64  @Factory ( AutzIntegUtils.ServiceFactory.class )
65  public class ModifyAuthorizationIT
66  {
67      public static DirectoryService service;
68  
69  
70      /**
71       * Checks if an attribute of a simple entry (an organizationalUnit) with an RDN
72       * relative to ou=system can be modified by a specific non-admin user.  If a
73       * permission exception is encountered it is caught and false is returned,
74       * otherwise true is returned.  The entry is deleted after being created just in case
75       * subsequent calls to this method are made in the same test case: the admin account
76       * is used to add and delete this test entry so permissions to add and delete are not
77       * required to test the modify operation by the user.
78       *
79       * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
80       * @param password the password of this user
81       * @param entryRdn the relative DN, relative to ou=system where entry is created
82       * for modification test
83       * @param mods the modifications to make to the entry
84       * @return true if the modifications can be made by the user at the specified location,
85       * false otherwise.
86       * @throws javax.naming.NamingException if there are problems conducting the test
87       */
88      public boolean checkCanModifyAs( String uid, String password, String entryRdn, ModificationItem[] mods )
89          throws Exception
90      {
91          // create the entry with the telephoneNumber attribute to modify
92          Attributes testEntry = new BasicAttributes( "ou", "testou", true );
93          Attribute objectClass = new BasicAttribute( "objectClass" );
94          testEntry.put( objectClass );
95          objectClass.add( "top" );
96          objectClass.add( "organizationalUnit" );
97          testEntry.put( "telephoneNumber", "867-5309" ); // jenny don't change your number
98  
99          DirContext adminContext = getContextAsAdmin();
100 
101         //noinspection EmptyCatchBlock
102         try
103         {
104             // create the entry as admin
105             LdapDN userName = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
106             adminContext.createSubcontext( entryRdn, testEntry );
107 
108             // modify the entry as the user
109             DirContext userContext = getContextAs( userName, password );
110             userContext.modifyAttributes( entryRdn, mods );
111 
112             return true;
113         }
114         catch ( LdapNoPermissionException e )
115         {
116         }
117         finally
118         {
119             // let's clean up
120             adminContext.destroySubcontext( entryRdn );
121         }
122         
123         return false;
124     }
125 
126 
127     /**
128      * Checks if an attribute of a simple entry (an organizationalUnit) with an RDN
129      * relative to ou=system can be modified by a specific non-admin user.  If a
130      * permission exception is encountered it is caught and false is returned,
131      * otherwise true is returned.  The entry is deleted after being created just in case
132      * subsequent calls to this method are made in the same test case: the admin account
133      * is used to add and delete this test entry so permissions to add and delete are not
134      * required to test the modify operation by the user.
135      *
136      * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
137      * @param password the password of this user
138      * @param entryRdn the relative DN, relative to ou=system where entry is created
139      * for modification test
140      * @param mods the attributes to modify in the entry
141      * @param modOp the modification operation to use for all attributes
142      * @return true if the modifications can be made by the user at the specified location,
143      * false otherwise.
144      * @throws javax.naming.NamingException if there are problems conducting the test
145      */
146     public boolean checkCanModifyAs( String uid, String password, String entryRdn, int modOp, Attributes mods )
147         throws Exception
148     {
149         // create the entry with the telephoneNumber attribute to modify
150         Attributes testEntry = new BasicAttributes( "ou", "testou", true );
151         Attribute objectClass = new BasicAttribute( "objectClass" );
152         testEntry.put( objectClass );
153         objectClass.add( "top" );
154         objectClass.add( "organizationalUnit" );
155         testEntry.put( "telephoneNumber", "867-5309" ); // jenny don't change your number
156 
157         DirContext adminContext = getContextAsAdmin();
158 
159         try
160         {
161             // create the entry as admin
162             LdapDN userName = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
163             adminContext.createSubcontext( entryRdn, testEntry );
164 
165             // modify the entry as the user
166             DirContext userContext = getContextAs( userName, password );
167             userContext.modifyAttributes( entryRdn, modOp, mods );
168 
169             return true;
170         }
171         catch ( LdapNoPermissionException e )
172         {
173             return false;
174         }
175         finally
176         {
177             // let's clean up
178             adminContext.destroySubcontext( entryRdn );
179         }
180     }
181 
182 
183     /**
184      * Checks if a user can modify an attribute of their own entry.  Users are
185      * presumed to reside under ou=users,ou=system.  If a permission exception is
186      * encountered it is caught and false is returned, otherwise true is returned.
187      *
188      * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
189      * @param password the password of this user
190      * @param mods the attributes to modify in the entry
191      * @param modOp the modification operation to use for all attributes
192      * @return true if the modifications can be made by the user his/her own entry,
193      * false otherwise.
194      * @throws javax.naming.NamingException if there are problems conducting the test
195      */
196     public boolean checkCanSelfModify( String uid, String password, int modOp, Attributes mods ) throws Exception
197     {
198         try
199         {
200             // modify the entry as the user
201             Name userEntry = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
202             DirContext userContext = getContextAs( userEntry, password, userEntry.toString() );
203             userContext.modifyAttributes( "", modOp, mods );
204             return true;
205         }
206         catch ( LdapNoPermissionException e )
207         {
208             return false;
209         }
210     }
211 
212 
213     /**
214      * Checks if a user can modify an attribute of their own entry.  Users are
215      * presumed to reside under ou=users,ou=system.  If a permission exception is
216      * encountered it is caught and false is returned, otherwise true is returned.
217      *
218      * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
219      * @param password the password of this user
220      * @param mods the attributes to modify in the entry
221      * @return true if the modifications can be made by the user his/her own entry,
222      * false otherwise.
223      * @throws javax.naming.NamingException if there are problems conducting the test
224      */
225     public boolean checkCanSelfModify( String uid, String password, ModificationItem[] mods ) throws Exception
226     {
227         try
228         {
229             // modify the entry as the user
230             Name userEntry = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
231             DirContext userContext = getContextAs( userEntry, password, userEntry.toString() );
232             userContext.modifyAttributes( "", mods );
233             return true;
234         }
235         catch ( LdapNoPermissionException e )
236         {
237             return false;
238         }
239     }
240 
241 
242     /**
243      * Converts a set of attributes and a modification operation type into a MoficationItem array.
244      *
245      * @param modOp the modification operation to perform
246      * @param changes the modifications to the attribute
247      * @return the array of modification items represting the changes
248      * @throws NamingException if there are problems accessing attributes
249      */
250     private ModificationItem[] toItems( int modOp, Attributes changes ) throws NamingException
251     {
252         List<ModificationItem> mods = new ArrayList<ModificationItem>();
253         NamingEnumeration<? extends Attribute> list = changes.getAll();
254         while ( list.hasMore() )
255         {
256             Attribute attr = list.next();
257             mods.add( new ModificationItem( modOp, attr ) );
258         }
259         ModificationItem[] modArray = new ModificationItem[mods.size()];
260         return mods.toArray( modArray );
261     }
262 
263 
264     @Test
265     public void testSelfModification() throws Exception
266     {
267         // ----------------------------------------------------------------------------------
268         // Modify with Attribute Addition
269         // ----------------------------------------------------------------------------------
270 
271         // create the non-admin user
272         createUser( "billyd", "billyd" );
273 
274         // create the password modification
275         ModificationItem[] mods = toItems( DirContext.REPLACE_ATTRIBUTE, new BasicAttributes( "userPassword",
276             "williams", true ) );
277 
278         // try a modify operation which should fail without any ACI
279         assertFalse( checkCanSelfModify( "billyd", "billyd", mods ) );
280 
281         // Gives grantModify, and grantRead perm to all users in the Administrators group for
282         // entries and all attribute types and values
283         createAccessControlSubentry( "selfModifyUserPassword", "{ " + "identificationTag \"addAci\", "
284             + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
285             + "userClasses { thisEntry }, " + "userPermissions { "
286             + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse, grantRead } }, "
287             + "{ protectedItems {allAttributeValues {userPassword}}, grantsAndDenials { grantAdd, grantRemove } } "
288             + "} } }" );
289 
290         // try a modify operation which should succeed with ACI
291         assertTrue( checkCanSelfModify( "billyd", "billyd", mods ) );
292         deleteAccessControlSubentry( "selfModifyUserPassword" );
293     }
294 
295 
296     /**
297      * Checks to make sure group membership based userClass works for modify operations.
298      *
299      * @throws javax.naming.NamingException if the test encounters an error
300      */
301     @Test
302     public void testGrantModifyByTestGroup() throws Exception
303     {
304         // ----------------------------------------------------------------------------------
305         // Modify with Attribute Addition
306         // ----------------------------------------------------------------------------------
307 
308         // create the add modifications
309         ModificationItem[] mods = toItems( DirContext.ADD_ATTRIBUTE, new BasicAttributes( "registeredAddress",
310             "100 Park Ave.", true ) );
311 
312         // create the non-admin user
313         createUser( "billyd", "billyd" );
314         
315         createGroup( "TestGroup" );
316 
317         // try a modify operation which should fail without any ACI
318         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
319 
320         // Gives grantModify, and grantRead perm to all users in the TestGroup group for
321         // entries and all attribute types and values
322         createAccessControlSubentry( "administratorModifyAdd", "{ " + "identificationTag \"addAci\", "
323             + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
324             + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
325             + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
326             + "{ protectedItems {attributeType {registeredAddress}, allAttributeValues {registeredAddress}}, grantsAndDenials { grantAdd } } " + "} } }" );
327 
328         // see if we can now add that test entry which we could not before
329         // add op should still fail since billd is not in the admin group
330         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
331 
332         // now add billyd to the TestGroup group and try again
333         addUserToGroup( "billyd", "TestGroup" );
334 
335         // try a modify operation which should succeed with ACI and group membership change
336         assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
337         deleteAccessControlSubentry( "administratorModifyAdd" );
338 
339         // ----------------------------------------------------------------------------------
340         // Modify with Attribute Removal
341         // ----------------------------------------------------------------------------------
342 
343         // now let's test to see if we can perform a modify with a delete op
344         mods = toItems( DirContext.REMOVE_ATTRIBUTE, new BasicAttributes( "telephoneNumber", "867-5309", true ) );
345 
346         // make sure we cannot remove the telephone number from the test entry
347         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
348 
349         // Gives grantModify, and grantRead perm to all users in the TestGroup group for
350         // entries and all attribute types and values
351         createAccessControlSubentry( "administratorModifyRemove", "{ " + "identificationTag \"addAci\", "
352             + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
353             + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
354             + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
355             + "{ protectedItems {attributeType {telephoneNumber}, allAttributeValues {telephoneNumber}}, grantsAndDenials { grantRemove } } " + "} } }" );
356 
357         // try a modify operation which should succeed with ACI and group membership change
358         assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
359         deleteAccessControlSubentry( "administratorModifyRemove" );
360 
361         // ----------------------------------------------------------------------------------
362         // Modify with Attribute Replace (requires both grantRemove and grantAdd on attrs)
363         // ----------------------------------------------------------------------------------
364 
365         // now let's test to see if we can perform a modify with a delete op
366         mods = toItems( DirContext.REPLACE_ATTRIBUTE, new BasicAttributes( "telephoneNumber", "867-5309", true ) );
367 
368         // make sure we cannot remove the telephone number from the test entry
369         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
370 
371         // Gives grantModify, and grantRead perm to all users in the TestGroup group for
372         // entries and all attribute types and values
373         createAccessControlSubentry( "administratorModifyReplace", "{ " + "identificationTag \"addAci\", "
374             + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
375             + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
376             + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
377             + "{ protectedItems {attributeType {registeredAddress}, allAttributeValues {telephoneNumber}}, grantsAndDenials { grantAdd, grantRemove } } "
378             + "} } }" );
379 
380         // try a modify operation which should succeed with ACI and group membership change
381         assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
382         deleteAccessControlSubentry( "administratorModifyReplace" );
383 
384         /* =================================================================================
385          *              DO IT ALL OVER AGAIN BUT USE THE OTHER MODIFY METHOD
386          * ================================================================================= */
387 
388         // ----------------------------------------------------------------------------------
389         // Modify with Attribute Addition
390         // ----------------------------------------------------------------------------------
391         // create the add modifications
392         Attributes changes = new BasicAttributes( "registeredAddress", "100 Park Ave.", true );
393 
394         // try a modify operation which should fail without any ACI
395         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.ADD_ATTRIBUTE, changes ) );
396 
397         // Gives grantModify, and grantRead perm to all users in the TestGroup group for
398         // entries and all attribute types and values
399         createAccessControlSubentry( "administratorModifyAdd", "{ " + "identificationTag \"addAci\", "
400             + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
401             + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
402             + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
403             + "{ protectedItems {attributeType {registeredAddress}, allAttributeValues {registeredAddress}}, grantsAndDenials { grantAdd } } " + "} } }" );
404 
405         // try a modify operation which should succeed with ACI and group membership change
406         assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.ADD_ATTRIBUTE, changes ) );
407         deleteAccessControlSubentry( "administratorModifyAdd" );
408 
409         // ----------------------------------------------------------------------------------
410         // Modify with Attribute Removal
411         // ----------------------------------------------------------------------------------
412 
413         // now let's test to see if we can perform a modify with a delete op
414         changes = new BasicAttributes( "telephoneNumber", "867-5309", true );
415 
416         // make sure we cannot remove the telephone number from the test entry
417         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REMOVE_ATTRIBUTE, changes ) );
418 
419         // Gives grantModify, and grantRead perm to all users in the TestGroup group for
420         // entries and all attribute types and values
421         createAccessControlSubentry( "administratorModifyRemove", "{ " + "identificationTag \"addAci\", "
422             + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
423             + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
424             + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
425             + "{ protectedItems {attributeType {telephoneNumber}, allAttributeValues {telephoneNumber}}, grantsAndDenials { grantRemove } } " + "} } }" );
426 
427         // try a modify operation which should succeed with ACI and group membership change
428         assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REMOVE_ATTRIBUTE, changes ) );
429         deleteAccessControlSubentry( "administratorModifyRemove" );
430 
431         // ----------------------------------------------------------------------------------
432         // Modify with Attribute Replace (requires both grantRemove and grantAdd on attrs)
433         // ----------------------------------------------------------------------------------
434 
435         // now let's test to see if we can perform a modify with a delete op
436         changes = new BasicAttributes( "telephoneNumber", "867-5309", true );
437 
438         // make sure we cannot remove the telephone number from the test entry
439         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REPLACE_ATTRIBUTE, changes ) );
440 
441         // Gives grantModify, and grantRead perm to all users in the TestGroup group for
442         // entries and all attribute types and values
443         createAccessControlSubentry( "administratorModifyReplace", "{ " + "identificationTag \"addAci\", "
444             + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
445             + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
446             + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
447             + "{ protectedItems {attributeType {registeredAddress}, allAttributeValues {telephoneNumber}}, grantsAndDenials { grantAdd, grantRemove } } "
448             + "} } }" );
449 
450         // try a modify operation which should succeed with ACI and group membership change
451         assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REPLACE_ATTRIBUTE, changes ) );
452         deleteAccessControlSubentry( "administratorModifyReplace" );
453     }
454 
455 
456     //    /**
457     //     * Checks to make sure name based userClass works for modify operations.
458     //     *
459     //     * @throws javax.naming.NamingException if the test encounters an error
460     //     */
461     //    public void testGrantModifyByName() throws NamingException
462     //    {
463     //        // create the non-admin user
464     //        createUser( "billyd", "billyd" );
465     //
466     //        // try an modify operation which should fail without any ACI
467     //        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
468     //
469     //        // now add a subentry that enables user billyd to modify an entry below ou=system
470     //        createAccessControlSubentry( "billydAdd", "{ " +
471     //                "identificationTag \"addAci\", " +
472     //                "precedence 14, " +
473     //                "authenticationLevel none, " +
474     //                "itemOrUserFirst userFirst: { " +
475     //                "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " +
476     //                "userPermissions { { " +
477     //                "protectedItems {entry, allUserAttributeTypesAndValues}, " +
478     //                "grantsAndDenials { grantModify, grantRead, grantBrowse } } } } }" );
479     //
480     //        // should work now that billyd is authorized by name
481     //        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
482     //    }
483     //
484     //
485     //    /**
486     //     * Checks to make sure subtree based userClass works for modify operations.
487     //     *
488     //     * @throws javax.naming.NamingException if the test encounters an error
489     //     */
490     //    public void testGrantModifyBySubtree() throws NamingException
491     //    {
492     //        // create the non-admin user
493     //        createUser( "billyd", "billyd" );
494     //
495     //        // try a modify operation which should fail without any ACI
496     //        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
497     //
498     //        // now add a subentry that enables user billyd to modify an entry below ou=system
499     //        createAccessControlSubentry( "billyAddBySubtree", "{ " +
500     //                "identificationTag \"addAci\", " +
501     //                "precedence 14, " +
502     //                "authenticationLevel none, " +
503     //                "itemOrUserFirst userFirst: { " +
504     //                "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " +
505     //                "userPermissions { { " +
506     //                "protectedItems {entry, allUserAttributeTypesAndValues}, " +
507     //                "grantsAndDenials { grantModify, grantRead, grantBrowse } } } } }" );
508     //
509     //        // should work now that billyd is authorized by the subtree userClass
510     //        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
511     //    }
512     //
513     //
514     //    /**
515     //     * Checks to make sure <b>allUsers</b> userClass works for modify operations.
516     //     *
517     //     * @throws javax.naming.NamingException if the test encounters an error
518     //     */
519     //    public void testGrantModifyAllUsers() throws NamingException
520     //    {
521     //        // create the non-admin user
522     //        createUser( "billyd", "billyd" );
523     //
524     //        // try an add operation which should fail without any ACI
525     //        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
526     //
527     //        // now add a subentry that enables anyone to add an entry below ou=system
528     //        createAccessControlSubentry( "anybodyAdd", "{ " +
529     //                "identificationTag \"addAci\", " +
530     //                "precedence 14, " +
531     //                "authenticationLevel none, " +
532     //                "itemOrUserFirst userFirst: { " +
533     //                "userClasses { allUsers }, " +
534     //                "userPermissions { { " +
535     //                "protectedItems {entry, allUserAttributeTypesAndValues}, " +
536     //                "grantsAndDenials { grantModify, grantRead, grantBrowse } } } } }" );
537     //
538     //        // see if we can now modify that test entry's number which we could not before
539     //        // should work with billyd now that all users are authorized
540     //        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
541     //    }
542     
543     
544     @Test
545     public void testPresciptiveACIModification() throws Exception
546     {
547         
548         ModificationItem[] mods = toItems( DirContext.ADD_ATTRIBUTE,
549             new BasicAttributes( "registeredAddress", "100 Park Ave.", true ) );
550 
551         createUser( "billyd", "billyd" );
552 
553         createAccessControlSubentry( "modifyACI", "{ " + "identificationTag \"modifyAci\", "
554             + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
555             + "userClasses { allUsers }, " + "userPermissions { "
556             + "{ protectedItems {entry, allUserAttributeTypesAndValues}, grantsAndDenials { grantModify, grantBrowse, grantAdd, grantRemove } } } } }" );
557 
558         assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
559         
560         mods = toItems( DirContext.REPLACE_ATTRIBUTE,
561             new BasicAttributes( "registeredAddress", "200 Park Ave.", true ) );
562         
563         changePresciptiveACI( "modifyACI", "{ " + "identificationTag \"modifyAci\", "
564             + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
565             + "userClasses { allUsers }, " + "userPermissions { "
566             + "{ protectedItems {entry, allUserAttributeTypesAndValues}, grantsAndDenials { denyModify } } } } }" );
567 
568         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
569         
570         deleteAccessControlSubentry( "modifyACI" );
571         
572     }
573     
574 
575     @Test
576     public void testMaxValueCountProtectedItem() throws Exception
577     {
578         createUser( "billyd", "billyd" );
579         createAccessControlSubentry( "mvcACI",
580             " {" +
581                 " identificationTag \"mvcACI\"," +
582                 " precedence 10," +
583                 " authenticationLevel simple," +
584                 " itemOrUserFirst userFirst:" + 
585                 " {" +
586                     " userClasses { allUsers }," +
587                     " userPermissions" + 
588                     " {" +
589                         " {" +
590                             " protectedItems { entry }," +
591                             " grantsAndDenials { grantModify, grantBrowse }" +
592                         " }" +
593                         " ," +
594                         " {" +
595                             " protectedItems" + 
596                             " {" +
597                                 " attributeType { description }," +
598                                 " allAttributeValues { description }," +
599                                 " maxValueCount" + 
600                                 " {" +
601                                     " { type description, maxCount 1 }" + 
602                                 " }" +
603                             " }" +
604                             " ," +
605                             " grantsAndDenials" + 
606                             " {" +
607                                 " grantRemove," +
608                                 " grantAdd" +
609                             " }" +
610                         " }" +
611                      " }" +
612                 " }" +
613             " }" );
614         
615         ModificationItem[] mods = toItems( DirContext.ADD_ATTRIBUTE,
616             new BasicAttributes( "description", "description 1", true ) );
617         
618         assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
619         
620         Attributes attrs = new BasicAttributes( true );
621         Attribute attr = new BasicAttribute( "description" );
622         attr.add( "description 1" );
623         attr.add( "description 2" );
624         attrs.put( attr );
625         mods = toItems( DirContext.ADD_ATTRIBUTE, attrs );
626         
627         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
628         
629         mods = toItems( DirContext.REPLACE_ATTRIBUTE, attrs );
630         
631         assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
632     }
633 }