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