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