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.server.AbstractTestCase;
21  import org.apache.ldap.server.partition.DirectoryPartitionNexus;
22  import org.apache.ldap.server.subtree.SubentryService;
23  import org.apache.ldap.common.name.LdapName;
24  
25  import javax.naming.directory.*;
26  import javax.naming.NamingException;
27  import javax.naming.Name;
28  import java.util.Hashtable;
29  
30  
31  /***
32   * A base class used for authorization tests.  It has some extra utility methods
33   * added to it which are required by all authorization tests.  Note that we use
34   * the admin test case otherwise failures will result without browse permission
35   * when setting up the test case for non-admin users.  Anyway we do not use the
36   * context created for the non-admin user since it is anonymous, we get our own
37   * contexts.
38   *
39   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
40   * @version $Rev$
41   */
42  public abstract class AbstractAuthorizationTest extends AbstractTestCase
43  {
44      /***
45       * Creates an abstract authorization test case which enables the
46       * authorization subsystem of the server.
47       */
48      public AbstractAuthorizationTest()
49      {
50          super( DirectoryPartitionNexus.ADMIN_PRINCIPAL, "secret" );
51          super.configuration.setAccessControlEnabled( true );
52      }
53  
54  
55      // -----------------------------------------------------------------------
56      // Utility methods used by subclasses
57      // -----------------------------------------------------------------------
58  
59  
60      /***
61       * Gets a context at ou=system as the admin user.
62       *
63       * @return the admin context at ou=system
64       * @throws NamingException if there are problems creating the context
65       */
66      public DirContext getContextAsAdmin() throws NamingException
67      {
68          return getContextAsAdmin( DirectoryPartitionNexus.SYSTEM_PARTITION_SUFFIX );
69      }
70  
71  
72      /***
73       * Gets a context at some dn within the directory as the admin user.
74       * Should be a dn of an entry under ou=system since no other partitions
75       * are enabled.
76       *
77       * @param dn the DN of the context to get
78       * @return the context for the DN as the admin user
79       * @throws NamingException if is a problem initializing or getting the context
80       */
81      public DirContext getContextAsAdmin( String dn ) throws NamingException
82      {
83          Hashtable env = ( Hashtable ) sysRoot.getEnvironment().clone();
84          env.put( DirContext.PROVIDER_URL, dn );
85          env.put( DirContext.SECURITY_AUTHENTICATION, "simple" );
86          env.put( DirContext.SECURITY_PRINCIPAL, DirectoryPartitionNexus.ADMIN_PRINCIPAL );
87          env.put( DirContext.SECURITY_CREDENTIALS, "secret" );
88          return new InitialDirContext( env );
89      }
90  
91  
92      /***
93       * Creates a group using the groupOfUniqueNames objectClass under the
94       * ou=groups,ou=sytem container with an initial member.
95       *
96       * @param cn the common name of the group used as the RDN attribute
97       * @param firstMemberDn the DN of the first member of this group
98       * @return the distinguished name of the group entry
99       * @throws NamingException if there are problems creating the new group like
100      * it exists already
101      */
102     public Name createGroup( String cn, String firstMemberDn ) throws NamingException
103     {
104         DirContext adminCtx = getContextAsAdmin();
105         Attributes group = new BasicAttributes( "cn", cn, true );
106         Attribute objectClass = new BasicAttribute( "objectClass" );
107         group.put( objectClass );
108         objectClass.add( "top" );
109         objectClass.add( "groupOfUniqueNames" );
110         group.put( "uniqueMember", firstMemberDn );
111         adminCtx.createSubcontext( "cn="+cn+",ou=groups", group );
112         return new LdapName( "cn="+cn+",ou=groups,ou=system" );
113     }
114 
115 
116     /***
117      * Deletes a user with a specific UID under ou=users,ou=system.
118      *
119      * @param uid the RDN value for the user to delete
120      * @throws NamingException if there are problems removing the user
121      * i.e. user does not exist
122      */
123     public void deleteUser( String uid ) throws NamingException
124     {
125         DirContext adminCtx = getContextAsAdmin();
126         adminCtx.destroySubcontext( "uid="+uid+",ou=users" );
127     }
128 
129 
130     /***
131      * Creates a simple user as an inetOrgPerson under the ou=users,ou=system
132      * container.  The user's RDN attribute is the uid argument.  This argument
133      * is also used as the value of the two MUST attributes: sn and cn.
134      *
135      * @param uid the value of the RDN attriubte (uid), the sn and cn attributes
136      * @param password the password to use to create the user
137      * @return the dn of the newly created user entry
138      * @throws NamingException if there are problems creating the user entry
139      */
140     public Name createUser( String uid, String password ) throws NamingException
141     {
142         DirContext adminCtx = getContextAsAdmin();
143         Attributes user = new BasicAttributes( "uid", uid, true );
144         user.put( "userPassword", password );
145         Attribute objectClass = new BasicAttribute( "objectClass" );
146         user.put( objectClass );
147         objectClass.add( "top" );
148         objectClass.add( "person" );
149         objectClass.add( "organizationalPerson" );
150         objectClass.add( "inetOrgPerson" );
151         user.put( "sn", uid );
152         user.put( "cn", uid );
153         adminCtx.createSubcontext( "uid="+uid+",ou=users", user );
154         return new LdapName( "uid="+uid+",ou=users,ou=system" );
155     }
156 
157 
158     /***
159      * Adds an existing user under ou=users,ou=system to an existing group under the
160      * ou=groups,ou=system container.
161      *
162      * @param userUid the uid of the user to add to the group
163      * @param groupCn the cn of the group to add the user to
164      * @throws NamingException if the group does not exist
165      */
166     public void addUserToGroup( String userUid, String groupCn ) throws NamingException
167     {
168         DirContext adminCtx = getContextAsAdmin();
169         Attributes changes = new BasicAttributes( "uniqueMember",
170                 "uid="+userUid+",ou=users,ou=system", true );
171         adminCtx.modifyAttributes( "cn="+groupCn+",ou=groups",
172                 DirContext.ADD_ATTRIBUTE, changes );
173     }
174 
175 
176     /***
177      * Removes a user from a group.
178      *
179      * @param userUid the RDN attribute value of the user to remove from the group
180      * @param groupCn the RDN attribute value of the group to have user removed from
181      * @throws NamingException if there are problems accessing the group
182      */
183     public void removeUserFromGroup( String userUid, String groupCn ) throws NamingException
184     {
185         DirContext adminCtx = getContextAsAdmin();
186         Attributes changes = new BasicAttributes( "uniqueMember",
187                 "uid="+userUid+",ou=users,ou=system", true );
188         adminCtx.modifyAttributes( "cn="+groupCn+",ou=groups",
189                 DirContext.REMOVE_ATTRIBUTE, changes );
190     }
191 
192 
193     /***
194      * Gets the context at ou=system as a specific user.
195      *
196      * @param user the DN of the user to get the context as
197      * @param password the password of the user
198      * @return the context as the user
199      * @throws NamingException if the user does not exist or authx fails
200      */
201     public DirContext getContextAs( Name user, String password ) throws NamingException
202     {
203         return getContextAs( user, password, DirectoryPartitionNexus.SYSTEM_PARTITION_SUFFIX );
204     }
205 
206 
207     /***
208      * Gets the context at any DN under ou=system as a specific user.
209      *
210      * @param user the DN of the user to get the context as
211      * @param password the password of the user
212      * @param dn the distinguished name of the entry to get the context for
213      * @return the context representing the entry at the dn as a specific user
214      * @throws NamingException if the does not exist or authx fails
215      */
216     public DirContext getContextAs( Name user, String password, String dn ) throws NamingException
217     {
218         Hashtable env = ( Hashtable ) sysRoot.getEnvironment().clone();
219         env.put( DirContext.PROVIDER_URL, dn );
220         env.put( DirContext.SECURITY_AUTHENTICATION, "simple" );
221         env.put( DirContext.SECURITY_PRINCIPAL, user.toString() );
222         env.put( DirContext.SECURITY_CREDENTIALS, password );
223         return new InitialDirContext( env );
224     }
225 
226 
227     public void deleteAccessControlSubentry( String cn ) throws NamingException
228     {
229         DirContext adminCtx = getContextAsAdmin();
230         adminCtx.destroySubcontext( "cn=" + cn );
231     }
232 
233 
234     /***
235      * Creates an access control subentry under ou=system whose subtree covers
236      * the entire naming context.
237      *
238      * @param cn the common name and rdn for the subentry
239      * @param aciItem the prescriptive ACI attribute value
240      * @throws NamingException if there is a problem creating the subentry
241      */
242     public void createAccessControlSubentry( String cn, String aciItem ) throws NamingException
243     {
244         createAccessControlSubentry( cn, "{}", aciItem );
245     }
246 
247 
248     /***
249      * Creates an access control subentry under ou=system whose subtree covers
250      * the entire naming context.
251      *
252      * @param cn the common name and rdn for the subentry
253      * @param subtree the subtreeSpecification for the subentry
254      * @param aciItem the prescriptive ACI attribute value
255      * @throws NamingException if there is a problem creating the subentry
256      */
257     public void createAccessControlSubentry( String cn, String subtree, String aciItem ) throws NamingException
258     {
259         DirContext adminCtx = getContextAsAdmin();
260 
261         // modify ou=system to be an AP for an A/C AA if it is not already
262         Attributes ap = adminCtx.getAttributes( "", new String[] { "administrativeRole" } );
263         Attribute administrativeRole = ap.get( "administrativeRole" );
264         if ( administrativeRole == null || ! administrativeRole.contains( SubentryService.AC_AREA ) )
265         {
266             Attributes changes = new BasicAttributes( "administrativeRole", SubentryService.AC_AREA, true );
267             adminCtx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, changes );
268         }
269 
270         // now add the A/C subentry below ou=system
271         Attributes subentry = new BasicAttributes( "cn", cn, true );
272         Attribute objectClass = new BasicAttribute( "objectClass" );
273         subentry.put( objectClass );
274         objectClass.add( "top" );
275         objectClass.add( "subentry" );
276         objectClass.add( "accessControlSubentry" );
277         subentry.put( "subtreeSpecification", subtree );
278         subentry.put( "prescriptiveACI", aciItem );
279         adminCtx.createSubcontext( "cn=" + cn, subentry );
280     }
281 
282 
283     /***
284      * Adds and entryACI attribute to an entry specified by a relative name
285      * with respect to ou=system
286      *
287      * @param rdn a name relative to ou=system
288      * @param aciItem the entryACI attribute value
289      * @throws NamingException if there is a problem adding the attribute
290      */
291     public void addEntryACI( Name rdn, String aciItem ) throws NamingException
292     {
293         DirContext adminCtx = getContextAsAdmin();
294 
295         // modify the entry relative to ou=system to include the aciItem
296         Attributes changes = new BasicAttributes( "entryACI", aciItem, true );
297         adminCtx.modifyAttributes( rdn, DirContext.ADD_ATTRIBUTE, changes );
298     }
299 
300 
301     /***
302      * Adds and subentryACI attribute to ou=system
303      *
304      * @param aciItem the subentryACI attribute value
305      * @throws NamingException if there is a problem adding the attribute
306      */
307     public void addSubentryACI( String aciItem ) throws NamingException
308     {
309         DirContext adminCtx = getContextAsAdmin();
310 
311         // modify the entry relative to ou=system to include the aciItem
312         Attributes changes = new BasicAttributes( "subentryACI", aciItem, true );
313         adminCtx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, changes );
314     }
315 }