1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.authz;
18  
19  
20  import org.apache.ldap.common.exception.LdapNoPermissionException;
21  import org.apache.ldap.common.name.LdapName;
22  
23  import javax.naming.NamingException;
24  import javax.naming.directory.*;
25  
26  
27  /***
28   * Tests whether or not authorization rules for entry deletion works properly.
29   *
30   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
31   * @version $Rev$
32   */
33  public class DeleteAuthorizationTest extends AbstractAuthorizationTest
34  {
35      /***
36       * Checks if a simple entry (organizationalUnit) can be deleted from the DIT at an
37       * RDN relative to ou=system by a specific non-admin user.  The entry is first
38       * created using the admin account which can do anything without limitations.
39       * After creating the entry as the admin an attempt is made to delete it as the
40       * specified user.
41       *
42       * If a permission exception is encountered it is caught and false is returned,
43       * otherwise true is returned when the entry is created.  The entry is deleted by the
44       * admin user after a delete failure to make sure the entry is deleted if subsequent
45       * calls are made to this method: the admin account is used to delete this test entry
46       * so permissions to delete are not required to delete it by the specified user.
47       *
48       * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
49       * @param password the password of this user
50       * @param entryRdn the relative DN, relative to ou=system where entry creation then deletion is tested
51       * @return true if the entry can be created by the user at the specified location, false otherwise
52       * @throws javax.naming.NamingException if there are problems conducting the test
53       */
54      public boolean checkCanDeleteEntryAs( String uid, String password, String entryRdn ) throws NamingException
55      {
56          Attributes testEntry = new BasicAttributes( "ou", "testou", true );
57          Attribute objectClass = new BasicAttribute( "objectClass" );
58          testEntry.put( objectClass );
59          objectClass.add( "top" );
60          objectClass.add( "organizationalUnit" );
61  
62          DirContext adminContext = getContextAsAdmin();
63          try
64          {
65              // create the entry as the admin
66              LdapName userName = new LdapName( "uid="+uid+",ou=users,ou=system" );
67              adminContext.createSubcontext( entryRdn, testEntry );
68  
69              // delete the newly created context as the user
70              DirContext userContext = getContextAs( userName, password );
71              userContext.destroySubcontext( entryRdn );
72  
73              return true;
74          }
75          catch ( LdapNoPermissionException e )
76          {
77              adminContext.destroySubcontext( entryRdn );
78              return false;
79          }
80      }
81  
82  
83      /***
84       * Checks to make sure group membership based userClass works for delete operations.
85       *
86       * @throws javax.naming.NamingException if the test encounters an error
87       */
88      public void testGrantDeleteAdministrators() throws NamingException
89      {
90          // create the non-admin user
91          createUser( "billyd", "billyd" );
92  
93          // try a delete operation which should fail without any ACI
94          assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
95  
96          // Gives grantRemove perm to all users in the Administrators group for
97          // entries and all attribute types and values
98          createAccessControlSubentry( "administratorAdd", "{ " +
99                  "identificationTag \"addAci\", " +
100                 "precedence 14, " +
101                 "authenticationLevel none, " +
102                 "itemOrUserFirst userFirst: { " +
103                 "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
104                 "userPermissions { { " +
105                 "protectedItems {entry}, " +
106                 "grantsAndDenials { grantRemove, grantBrowse } } } } }" );
107 
108         // see if we can now delete that test entry which we could not before
109         // delete op should still fail since billd is not in the admin group
110         assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
111 
112         // now add billyd to the Administrator group and try again
113         addUserToGroup( "billyd", "Administrators" );
114 
115         // try a delete operation which should succeed with ACI and group membership change
116         assertTrue( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
117     }
118 
119 
120     /***
121      * Checks to make sure name based userClass works for delete operations.
122      *
123      * @throws javax.naming.NamingException if the test encounters an error
124      */
125     public void testGrantDeleteByName() throws NamingException
126     {
127         // create the non-admin user
128         createUser( "billyd", "billyd" );
129 
130         // try a delete operation which should fail without any ACI
131         assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
132 
133         // now add a subentry that enables user billyd to delete an entry below ou=system
134         createAccessControlSubentry( "billydAdd", "{ " +
135                 "identificationTag \"addAci\", " +
136                 "precedence 14, " +
137                 "authenticationLevel none, " +
138                 "itemOrUserFirst userFirst: { " +
139                 "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " +
140                 "userPermissions { { " +
141                 "protectedItems {entry}, " +
142                 "grantsAndDenials { grantRemove, grantBrowse } } } } }" );
143 
144         // should work now that billyd is authorized by name
145         assertTrue( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
146     }
147 
148 
149     /***
150      * Checks to make sure subtree based userClass works for delete operations.
151      *
152      * @throws javax.naming.NamingException if the test encounters an error
153      */
154     public void testGrantDeleteBySubtree() throws NamingException
155     {
156         // create the non-admin user
157         createUser( "billyd", "billyd" );
158 
159         // try a delete operation which should fail without any ACI
160         assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
161 
162         // now add a subentry that enables user billyd to delte an entry below ou=system
163         createAccessControlSubentry( "billyAddBySubtree", "{ " +
164                 "identificationTag \"addAci\", " +
165                 "precedence 14, " +
166                 "authenticationLevel none, " +
167                 "itemOrUserFirst userFirst: { " +
168                 "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " +
169                 "userPermissions { { " +
170                 "protectedItems {entry}, " +
171                 "grantsAndDenials { grantRemove, grantBrowse } } } } }" );
172 
173         // should work now that billyd is authorized by the subtree userClass
174         assertTrue( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
175     }
176 
177 
178     /***
179      * Checks to make sure <b>allUsers</b> userClass works for delete operations.
180      *
181      * @throws javax.naming.NamingException if the test encounters an error
182      */
183     public void testGrantDeleteAllUsers() throws NamingException
184     {
185         // create the non-admin user
186         createUser( "billyd", "billyd" );
187 
188         // try a delete operation which should fail without any ACI
189         assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
190 
191         // now add a subentry that enables anyone to add an entry below ou=system
192         createAccessControlSubentry( "anybodyAdd", "{ " +
193                 "identificationTag \"addAci\", " +
194                 "precedence 14, " +
195                 "authenticationLevel none, " +
196                 "itemOrUserFirst userFirst: { " +
197                 "userClasses { allUsers }, " +
198                 "userPermissions { { " +
199                 "protectedItems {entry}, " +
200                 "grantsAndDenials { grantRemove, grantBrowse } } } } }" );
201 
202         // see if we can now delete that test entry which we could not before
203         // should work now with billyd now that all users are authorized
204         assertTrue( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
205     }
206 }