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.constants.ServerDNConstants;
24  import org.apache.directory.server.core.DefaultDirectoryService;
25  import org.apache.directory.server.core.DirectoryService;
26  import org.apache.directory.server.core.integ.DirectoryServiceFactory;
27  import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
28  import org.apache.directory.server.core.subtree.SubentryInterceptor;
29  import org.apache.directory.shared.ldap.constants.SchemaConstants;
30  import org.apache.directory.shared.ldap.name.LdapDN;
31  
32  import javax.naming.Name;
33  import javax.naming.NamingException;
34  import javax.naming.directory.Attribute;
35  import javax.naming.directory.Attributes;
36  import javax.naming.directory.BasicAttribute;
37  import javax.naming.directory.BasicAttributes;
38  import javax.naming.directory.DirContext;
39  import javax.naming.directory.InitialDirContext;
40  import javax.naming.ldap.LdapContext;
41  import java.util.Hashtable;
42  
43  
44  /**
45   * Some extra utility methods added to it which are required by all
46   * authorization tests.
47   *
48   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
49   * @version $Rev: 691024 $
50   */
51  public class AutzIntegUtils
52  {
53      public static DirectoryService service;
54  
55  
56      public static class ServiceFactory implements DirectoryServiceFactory
57      {
58          public DirectoryService newInstance() 
59          {
60              DefaultDirectoryService service = new DefaultDirectoryService();
61              service.setAccessControlEnabled( true );
62              service.getChangeLog().setEnabled( true );
63              AutzIntegUtils.service = service;
64              return service;
65          }
66      }
67  
68  
69      public static class DefaultServiceFactory implements DirectoryServiceFactory
70      {
71          public DirectoryService newInstance() 
72          {
73              DefaultDirectoryService service = new DefaultDirectoryService();
74              service.setAccessControlEnabled( false );
75              service.getChangeLog().setEnabled( true );
76              AutzIntegUtils.service = service;
77              return service;
78          }
79      }
80  
81  
82      // -----------------------------------------------------------------------
83      // Utility methods used by subclasses
84      // -----------------------------------------------------------------------
85  
86      /**
87       * Gets a context at ou=system as the admin user.
88       *
89       * @return the admin context at ou=system
90       * @throws NamingException if there are problems creating the context
91       */
92      public static DirContext getContextAsAdmin() throws Exception
93      {
94          return getSystemContext( service );
95      }
96  
97  
98      /**
99       * Gets a context at some dn within the directory as the admin user.
100      * Should be a dn of an entry under ou=system since no other partitions
101      * are enabled.
102      *
103      * @param dn the DN of the context to get
104      * @return the context for the DN as the admin user
105      * @throws NamingException if is a problem initializing or getting the context
106      */
107     @SuppressWarnings("unchecked")
108     public static DirContext getContextAsAdmin( String dn ) throws Exception
109     {
110         LdapContext sysRoot = getSystemContext( service );
111         Hashtable<String,Object> env = ( Hashtable<String,Object> ) sysRoot.getEnvironment().clone();
112         env.put( DirContext.PROVIDER_URL, dn );
113         env.put( DirContext.SECURITY_AUTHENTICATION, "simple" );
114         env.put( DirContext.SECURITY_PRINCIPAL, "uid=admin, ou=system" );
115         env.put( DirContext.SECURITY_CREDENTIALS, "secret" );
116         env.put( DirContext.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
117         env.put( DirectoryService.JNDI_KEY, service );
118         return new InitialDirContext( env );
119     }
120 
121 
122     /**
123      * Creates a group using the groupOfUniqueNames objectClass under the
124      * ou=groups,ou=sytem container with an initial member.
125      *
126      * @param cn the common name of the group used as the RDN attribute
127      * @param firstMemberDn the DN of the first member of this group
128      * @return the distinguished name of the group entry
129      * @throws NamingException if there are problems creating the new group like
130      * it exists already
131      */
132     public static Name createGroup( String cn, String firstMemberDn ) throws Exception
133     {
134         DirContext adminCtx = getContextAsAdmin();
135         Attributes group = new BasicAttributes( "cn", cn, true );
136         Attribute objectClass = new BasicAttribute( "objectClass" );
137         group.put( objectClass );
138         objectClass.add( "top" );
139         objectClass.add( "groupOfUniqueNames" );
140         group.put( "uniqueMember", firstMemberDn );
141         adminCtx.createSubcontext( "cn=" + cn + ",ou=groups", group );
142         return new LdapDN( "cn=" + cn + ",ou=groups,ou=system" );
143     }
144 
145 
146     /**
147      * Deletes a user with a specific UID under ou=users,ou=system.
148      *
149      * @param uid the RDN value for the user to delete
150      * @throws NamingException if there are problems removing the user
151      * i.e. user does not exist
152      */
153     public static void deleteUser( String uid ) throws Exception
154     {
155         DirContext adminCtx = getContextAsAdmin();
156         adminCtx.destroySubcontext( "uid=" + uid + ",ou=users" );
157     }
158 
159 
160     /**
161      * Creates a simple user as an inetOrgPerson under the ou=users,ou=system
162      * container.  The user's RDN attribute is the uid argument.  This argument
163      * is also used as the value of the two MUST attributes: sn and cn.
164      *
165      * @param uid the value of the RDN attriubte (uid), the sn and cn attributes
166      * @param password the password to use to create the user
167      * @return the dn of the newly created user entry
168      * @throws NamingException if there are problems creating the user entry
169      */
170     public static Name createUser( String uid, String password ) throws Exception
171     {
172         DirContext adminCtx = getContextAsAdmin();
173         Attributes user = new BasicAttributes( "uid", uid, true );
174         user.put( "userPassword", password );
175         Attribute objectClass = new BasicAttribute( "objectClass" );
176         user.put( objectClass );
177         objectClass.add( "top" );
178         objectClass.add( "person" );
179         objectClass.add( "organizationalPerson" );
180         objectClass.add( "inetOrgPerson" );
181         user.put( "sn", uid );
182         user.put( "cn", uid );
183         adminCtx.createSubcontext( "uid=" + uid + ",ou=users", user );
184         return new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
185     }
186 
187 
188     /**
189      * Creates a simple groupOfUniqueNames under the ou=groups,ou=system
190      * container.  The admin user is always a member of this newly created 
191      * group.
192      *
193      * @param groupName the name of the cgroup to create
194      * @return the DN of the group as a Name object
195      * @throws NamingException if the group cannot be created
196      */
197     public static Name createGroup( String groupName ) throws Exception
198     {
199         DirContext adminCtx = getContextAsAdmin();
200         Attributes group = new BasicAttributes( true );
201         Attribute objectClass = new BasicAttribute( "objectClass" );
202         group.put( objectClass );
203         objectClass.add( "top" );
204         objectClass.add( "groupOfUniqueNames" );
205         
206         // TODO might be ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED
207         group.put( "uniqueMember", "uid=admin, ou=system" );
208         adminCtx.createSubcontext( "cn=" + groupName + ",ou=groups", group );
209         return new LdapDN( "cn=" + groupName + ",ou=groups,ou=system" );
210     }
211 
212 
213     /**
214      * Adds an existing user under ou=users,ou=system to an existing group under the
215      * ou=groups,ou=system container.
216      *
217      * @param userUid the uid of the user to add to the group
218      * @param groupCn the cn of the group to add the user to
219      * @throws NamingException if the group does not exist
220      */
221     public static void addUserToGroup( String userUid, String groupCn ) throws Exception
222     {
223         DirContext adminCtx = getContextAsAdmin();
224         Attributes changes = new BasicAttributes( "uniqueMember", "uid=" + userUid + ",ou=users,ou=system", true );
225         adminCtx.modifyAttributes( "cn=" + groupCn + ",ou=groups", DirContext.ADD_ATTRIBUTE, changes );
226     }
227 
228 
229     /**
230      * Removes a user from a group.
231      *
232      * @param userUid the RDN attribute value of the user to remove from the group
233      * @param groupCn the RDN attribute value of the group to have user removed from
234      * @throws NamingException if there are problems accessing the group
235      */
236     public static void removeUserFromGroup( String userUid, String groupCn ) throws Exception
237     {
238         DirContext adminCtx = getContextAsAdmin();
239         Attributes changes = new BasicAttributes( "uniqueMember", "uid=" + userUid + ",ou=users,ou=system", true );
240         adminCtx.modifyAttributes( "cn=" + groupCn + ",ou=groups", DirContext.REMOVE_ATTRIBUTE, changes );
241     }
242 
243 
244     /**
245      * Gets the context at ou=system as a specific user.
246      *
247      * @param user the DN of the user to get the context as
248      * @param password the password of the user
249      * @return the context as the user
250      * @throws NamingException if the user does not exist or authx fails
251      */
252     public static DirContext getContextAs( Name user, String password ) throws Exception
253     {
254         return getContextAs( user, password, ServerDNConstants.SYSTEM_DN );
255     }
256 
257 
258     /**
259      * Gets the context at any DN under ou=system as a specific user.
260      *
261      * @param user the DN of the user to get the context as
262      * @param password the password of the user
263      * @param dn the distinguished name of the entry to get the context for
264      * @return the context representing the entry at the dn as a specific user
265      * @throws NamingException if the does not exist or authx fails
266      */
267     @SuppressWarnings("unchecked")
268     public static DirContext getContextAs( Name user, String password, String dn ) throws Exception
269     {
270         LdapContext sysRoot = getSystemContext( service );
271         Hashtable<String,Object> env = ( Hashtable<String,Object> ) sysRoot.getEnvironment().clone();
272         env.put( DirContext.PROVIDER_URL, dn );
273         env.put( DirContext.SECURITY_AUTHENTICATION, "simple" );
274         env.put( DirContext.SECURITY_PRINCIPAL, user.toString() );
275         env.put( DirContext.SECURITY_CREDENTIALS, password );
276         env.put( DirContext.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
277         env.put( DirectoryService.JNDI_KEY, service );
278         return new InitialDirContext( env );
279     }
280 
281 
282     public static void deleteAccessControlSubentry( String cn ) throws Exception
283     {
284         DirContext adminCtx = getContextAsAdmin();
285         adminCtx.destroySubcontext( "cn=" + cn );
286     }
287 
288 
289     /**
290      * Creates an access control subentry under ou=system whose subtree covers
291      * the entire naming context.
292      *
293      * @param cn the common name and rdn for the subentry
294      * @param aciItem the prescriptive ACI attribute value
295      * @throws NamingException if there is a problem creating the subentry
296      */
297     public static void createAccessControlSubentry( String cn, String aciItem ) throws Exception
298     {
299         createAccessControlSubentry( cn, "{}", aciItem );
300     }
301 
302 
303     /**
304      * Creates an access control subentry under ou=system whose subtree covers
305      * the entire naming context.
306      *
307      * @param cn the common name and rdn for the subentry
308      * @param subtree the subtreeSpecification for the subentry
309      * @param aciItem the prescriptive ACI attribute value
310      * @throws NamingException if there is a problem creating the subentry
311      */
312     public static void createAccessControlSubentry( String cn, String subtree, String aciItem ) throws Exception
313     {
314         DirContext adminCtx = getContextAsAdmin();
315 
316         // modify ou=system to be an AP for an A/C AA if it is not already
317         Attributes ap = adminCtx.getAttributes( "", new String[]
318             { "administrativeRole" } );
319         Attribute administrativeRole = ap.get( "administrativeRole" );
320         if ( administrativeRole == null || !administrativeRole.contains( SubentryInterceptor.AC_AREA ) )
321         {
322             Attributes changes = new BasicAttributes( "administrativeRole", SubentryInterceptor.AC_AREA, true );
323             adminCtx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, changes );
324         }
325 
326         // now add the A/C subentry below ou=system
327         Attributes subentry = new BasicAttributes( "cn", cn, true );
328         Attribute objectClass = new BasicAttribute( "objectClass" );
329         subentry.put( objectClass );
330         objectClass.add( "top" );
331         objectClass.add( SchemaConstants.SUBENTRY_OC );
332         objectClass.add( "accessControlSubentry" );
333         subentry.put( "subtreeSpecification", subtree );
334         subentry.put( "prescriptiveACI", aciItem );
335         adminCtx.createSubcontext( "cn=" + cn, subentry );
336     }
337 
338 
339     /**
340      * Adds and entryACI attribute to an entry specified by a relative name
341      * with respect to ou=system
342      *
343      * @param rdn a name relative to ou=system
344      * @param aciItem the entryACI attribute value
345      * @throws NamingException if there is a problem adding the attribute
346      */
347     public static void addEntryACI( Name rdn, String aciItem ) throws Exception
348     {
349         DirContext adminCtx = getContextAsAdmin();
350 
351         // modify the entry relative to ou=system to include the aciItem
352         Attributes changes = new BasicAttributes( "entryACI", aciItem, true );
353         adminCtx.modifyAttributes( rdn, DirContext.ADD_ATTRIBUTE, changes );
354     }
355 
356 
357     /**
358      * Adds and subentryACI attribute to ou=system
359      *
360      * @param aciItem the subentryACI attribute value
361      * @throws NamingException if there is a problem adding the attribute
362      */
363     public static void addSubentryACI( String aciItem ) throws Exception
364     {
365         DirContext adminCtx = getContextAsAdmin();
366 
367         // modify the entry relative to ou=system to include the aciItem
368         Attributes changes = new BasicAttributes( "subentryACI", aciItem, true );
369         adminCtx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, changes );
370     }
371     
372     
373     /**
374      * Replaces values of an prescriptiveACI attribute of a subentry subordinate
375      * to ou=system.
376      *
377      * @param cn the common name of the aci subentry
378      * @param aciItem the new value for the ACI item
379      * @throws NamingException if the modify fails
380      */
381     public static void changePresciptiveACI( String cn, String aciItem ) throws Exception
382     {
383         DirContext adminCtx = getContextAsAdmin();
384         Attributes changes = new BasicAttributes( "prescriptiveACI", aciItem, true );
385         adminCtx.modifyAttributes( "cn=" + cn, DirContext.REPLACE_ATTRIBUTE, changes );
386     }
387     
388     public static void addPrescriptiveACI( String cn, String aciItem ) throws Exception
389     {
390         DirContext adminCtx = getContextAsAdmin();
391         Attributes changes = new BasicAttributes( "prescriptiveACI", aciItem, true );
392         adminCtx.modifyAttributes( "cn=" + cn, DirContext.ADD_ATTRIBUTE, changes );
393     }
394 }