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