You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ak...@apache.org on 2005/10/15 19:54:49 UTC

svn commit: r321394 - in /directory/apacheds/trunk/core/src: main/java/org/apache/ldap/server/authz/ main/java/org/apache/ldap/server/authz/support/ test/org/apache/ldap/server/authz/ test/org/apache/ldap/server/authz/support/

Author: akarasulu
Date: Sat Oct 15 10:54:41 2005
New Revision: 321394

URL: http://svn.apache.org/viewcvs?rev=321394&view=rev
Log:
changes ...

 o added rename, move and combined test cases 
 o added modify test cases 
 o added more utility functions to the AbstractAuthorizationTest 
 o commented out code for requiring grantRemove/grantAdd perms on old RDN
   deletes in modifyRdn operations that change the RDN - this does not 
   comply with X.501 although it makes a lot of sense
 o OldAuthenticationService now disables itself for its static rules when
   the basic authorization service is enabled.
 o fixed bug in move operation handling to make sure import/export/rename
   permissions are correctly mandated for respective overloads 
 o fixed bug in AbstractAuthorizationTest: was not looking up operational 
   attribute administrativeRole properly to create subentries under ou=system
 o fixed bug in MicroOperationFilter: filter accepted ACI without testing
   the presence of all required micro operation permissions.
 o fixed bug in GroupCache: group cache was not properly updating the cache on
   modify operations on groups that are a groupOfNames and groupOfUniqueNames
 o fixed NPE in GroupCache
 o fixed bug in AuthorizationService in lookup(Name, String[]) where the 
   attribute ids where not being used for nexus lookups.

TODO: 

 o more testing with different kinds of ACI constructs should add to the total
   number of tests per operation 
 o doco badly needed
 o need to make the code not use the nexus for search list and lookups - the 
   next interceptor should be used


Added:
    directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/ModifyAuthorizationTest.java   (with props)
    directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/MoveRenameAuthorizationTest.java   (with props)
Modified:
    directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/AuthorizationService.java
    directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/GroupCache.java
    directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/OldAuthorizationService.java
    directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/support/MicroOperationFilter.java
    directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/AbstractAuthorizationTest.java
    directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/support/MicroOperationFilterTest.java

Modified: directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/AuthorizationService.java
URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/AuthorizationService.java?rev=321394&r1=321393&r2=321394&view=diff
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/AuthorizationService.java (original)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/AuthorizationService.java Sat Oct 15 10:54:41 2005
@@ -35,7 +35,6 @@
 import org.apache.ldap.common.aci.ACIItem;
 import org.apache.ldap.common.exception.LdapNamingException;
 import org.apache.ldap.common.message.ResultCodeEnum;
-import org.apache.ldap.common.util.NamespaceTools;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -524,7 +523,7 @@
 
     public Attributes lookup( NextInterceptor next, Name dn, String[] attrIds ) throws NamingException
     {
-        Attributes entry = nexus.lookup( dn, attrIds );
+        Attributes entry = nexus.lookup( dn );
         LdapPrincipal user = ( ( ServerContext ) InvocationStack.getInstance().peek().getCaller() ).getPrincipal();
 
         if ( user.getName().equalsIgnoreCase( DirectoryPartitionNexus.ADMIN_PRINCIPAL ) || ! enabled )
@@ -579,32 +578,32 @@
         engine.checkPermission( next, userGroups, user.getJndiName(), user.getAuthenticationLevel(), name, null,
                 null, Collections.singleton( MicroOperation.RENAME ), tuples, entry );
 
-        if ( deleteOldRn )
-        {
-            String oldRn = name.get( name.size() - 1 );
-            if ( NamespaceTools.hasCompositeComponents( oldRn ) )
-            {
-                String[] comps = NamespaceTools.getCompositeComponents( oldRn );
-                for ( int ii = 0; ii < comps.length; ii++ )
-                {
-                    String id = NamespaceTools.getRdnAttribute( comps[ii] );
-                    String value = NamespaceTools.getRdnValue( comps[ii] );
-                    engine.checkPermission( next, userGroups, user.getJndiName(),
-                            user.getAuthenticationLevel(), name, id,
-                            value, Collections.singleton( MicroOperation.REMOVE ),
-                            tuples, entry );
-                }
-            }
-            else
-            {
-                String id = NamespaceTools.getRdnAttribute( oldRn );
-                String value = NamespaceTools.getRdnValue( oldRn );
-                engine.checkPermission( next, userGroups, user.getJndiName(),
-                        user.getAuthenticationLevel(), name, id,
-                        value, Collections.singleton( MicroOperation.REMOVE ),
-                        tuples, entry );
-            }
-        }
+//        if ( deleteOldRn )
+//        {
+//            String oldRn = name.get( name.size() - 1 );
+//            if ( NamespaceTools.hasCompositeComponents( oldRn ) )
+//            {
+//                String[] comps = NamespaceTools.getCompositeComponents( oldRn );
+//                for ( int ii = 0; ii < comps.length; ii++ )
+//                {
+//                    String id = NamespaceTools.getRdnAttribute( comps[ii] );
+//                    String value = NamespaceTools.getRdnValue( comps[ii] );
+//                    engine.checkPermission( next, userGroups, user.getJndiName(),
+//                            user.getAuthenticationLevel(), name, id,
+//                            value, Collections.singleton( MicroOperation.REMOVE ),
+//                            tuples, entry );
+//                }
+//            }
+//            else
+//            {
+//                String id = NamespaceTools.getRdnAttribute( oldRn );
+//                String value = NamespaceTools.getRdnValue( oldRn );
+//                engine.checkPermission( next, userGroups, user.getJndiName(),
+//                        user.getAuthenticationLevel(), name, id,
+//                        value, Collections.singleton( MicroOperation.REMOVE ),
+//                        tuples, entry );
+//            }
+//        }
 
         next.modifyRn( name, newRn, deleteOldRn );
         tupleCache.subentryRenamed( name, newName );
@@ -635,8 +634,9 @@
         addSubentryAciTuples( tuples, oriChildName, entry );
 
         Collection perms = new HashSet();
-        perms.add( MicroOperation.RENAME );
+        perms.add( MicroOperation.IMPORT );
         perms.add( MicroOperation.EXPORT );
+        perms.add( MicroOperation.RENAME );
         engine.checkPermission( next, userGroups, user.getJndiName(), user.getAuthenticationLevel(),
                 oriChildName, null, null, perms, tuples, entry );
 
@@ -647,32 +647,32 @@
         engine.checkPermission( next, userGroups, user.getJndiName(), user.getAuthenticationLevel(),
                 oriChildName, null, null, Collections.singleton( MicroOperation.IMPORT ), tuples, entry );
 
-        if ( deleteOldRn )
-        {
-            String oldRn = oriChildName.get( oriChildName.size() - 1 );
-            if ( NamespaceTools.hasCompositeComponents( oldRn ) )
-            {
-                String[] comps = NamespaceTools.getCompositeComponents( oldRn );
-                for ( int ii = 0; ii < comps.length; ii++ )
-                {
-                    String id = NamespaceTools.getRdnAttribute( comps[ii] );
-                    String value = NamespaceTools.getRdnValue( comps[ii] );
-                    engine.checkPermission( next, userGroups, user.getJndiName(),
-                            user.getAuthenticationLevel(), oriChildName, id,
-                            value, Collections.singleton( MicroOperation.REMOVE ),
-                            tuples, entry );
-                }
-            }
-            else
-            {
-                String id = NamespaceTools.getRdnAttribute( oldRn );
-                String value = NamespaceTools.getRdnValue( oldRn );
-                engine.checkPermission( next, userGroups, user.getJndiName(),
-                        user.getAuthenticationLevel(), oriChildName, id,
-                        value, Collections.singleton( MicroOperation.REMOVE ),
-                        tuples, entry );
-            }
-        }
+//        if ( deleteOldRn )
+//        {
+//            String oldRn = oriChildName.get( oriChildName.size() - 1 );
+//            if ( NamespaceTools.hasCompositeComponents( oldRn ) )
+//            {
+//                String[] comps = NamespaceTools.getCompositeComponents( oldRn );
+//                for ( int ii = 0; ii < comps.length; ii++ )
+//                {
+//                    String id = NamespaceTools.getRdnAttribute( comps[ii] );
+//                    String value = NamespaceTools.getRdnValue( comps[ii] );
+//                    engine.checkPermission( next, userGroups, user.getJndiName(),
+//                            user.getAuthenticationLevel(), oriChildName, id,
+//                            value, Collections.singleton( MicroOperation.REMOVE ),
+//                            tuples, entry );
+//                }
+//            }
+//            else
+//            {
+//                String id = NamespaceTools.getRdnAttribute( oldRn );
+//                String value = NamespaceTools.getRdnValue( oldRn );
+//                engine.checkPermission( next, userGroups, user.getJndiName(),
+//                        user.getAuthenticationLevel(), oriChildName, id,
+//                        value, Collections.singleton( MicroOperation.REMOVE ),
+//                        tuples, entry );
+//            }
+//        }
 
         next.move( oriChildName, newParentName, newRn, deleteOldRn );
         tupleCache.subentryRenamed( oriChildName, newName );

Modified: directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/GroupCache.java
URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/GroupCache.java?rev=321394&r1=321393&r2=321394&view=diff
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/GroupCache.java (original)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/GroupCache.java Sat Oct 15 10:54:41 2005
@@ -132,6 +132,21 @@
     {
         Attribute oc = entry.get( OC_ATTR );
 
+        if ( oc == null )
+        {
+            if ( entry.get( MEMBER_ATTR ) != null )
+            {
+                return entry.get( MEMBER_ATTR );
+            }
+
+            if ( entry.get( UNIQUEMEMBER_ATTR ) != null )
+            {
+                return entry.get( UNIQUEMEMBER_ATTR );
+            }
+
+            return null;
+        }
+
         if ( oc.contains( GROUPOFNAMES_OC ) )
         {
             return entry.get( MEMBER_ATTR );
@@ -320,7 +335,7 @@
      */
     public void groupModified( Name name, int modOp, Attributes mods, Attributes entry ) throws NamingException
     {
-        Attribute members = getMemberAttribute( entry );
+        Attribute members = getMemberAttribute( mods );
 
         if ( members == null )
         {
@@ -353,6 +368,12 @@
         {
             String group = ( String ) list.next();
             Set members = ( Set ) groups.get( group );
+
+            if ( members == null )
+            {
+                continue;
+            }
+
             if ( members.contains( member ) )
             {
                 if ( memberGroups == null )

Modified: directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/OldAuthorizationService.java
URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/OldAuthorizationService.java?rev=321394&r1=321393&r2=321394&view=diff
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/OldAuthorizationService.java (original)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/OldAuthorizationService.java Sat Oct 15 10:54:41 2005
@@ -76,6 +76,8 @@
      * the name parser used by this service
      */
     private DnParser dnParser;
+    private boolean enabled = true;
+
 
 
     /**
@@ -90,6 +92,9 @@
     {
         AttributeTypeRegistry atr = factoryCfg.getGlobalRegistries().getAttributeTypeRegistry();
         dnParser = new DnParser( new ConcreteNameComponentNormalizer( atr ) );
+
+        // disable this static module if basic access control mechanisms are enabled
+        enabled = ! factoryCfg.getStartupConfiguration().isAccessControlEnabled();
     }
 
 
@@ -99,6 +104,12 @@
 
     public void delete( NextInterceptor nextInterceptor, Name name ) throws NamingException
     {
+        if ( !enabled )
+        {
+            nextInterceptor.delete( name );
+            return;
+        }
+
         Name principalDn = getPrincipal().getJndiName();
 
         if ( name.toString().equals( "" ) )
@@ -162,7 +173,11 @@
      */
     public void modify( NextInterceptor nextInterceptor, Name name, int modOp, Attributes attrs ) throws NamingException
     {
-        protectModifyAlterations( name );
+        if ( enabled )
+        {
+            protectModifyAlterations( name );
+        }
+
         nextInterceptor.modify( name, modOp, attrs );
     }
 
@@ -175,7 +190,10 @@
      */
     public void modify( NextInterceptor nextInterceptor, Name name, ModificationItem[] items ) throws NamingException
     {
-        protectModifyAlterations( name );
+        if ( enabled )
+        {
+            protectModifyAlterations( name );
+        }
         nextInterceptor.modify( name, items );
     }
 
@@ -192,13 +210,6 @@
 
         if ( !principalDn.equals( ADMIN_DN ) )
         {
-            if ( dn == ADMIN_DN || dn.equals( ADMIN_DN ) )
-            {
-                String msg = "User " + principalDn;
-                msg += " does not have permission to modify the admin account.";
-                throw new LdapNoPermissionException( msg );
-            }
-
             if ( dn.size() > 2 && dn.startsWith( USER_BASE_DN ) )
             {
                 String msg = "User " + principalDn;
@@ -232,14 +243,20 @@
 
     public void modifyRn( NextInterceptor nextInterceptor, Name name, String newRn, boolean deleteOldRn ) throws NamingException
     {
-        protectDnAlterations( name );
+        if ( enabled )
+        {
+            protectDnAlterations( name );
+        }
         nextInterceptor.modifyRn( name, newRn, deleteOldRn );
     }
 
 
     public void move( NextInterceptor nextInterceptor, Name oriChildName, Name newParentName ) throws NamingException
     {
-        protectDnAlterations( oriChildName );
+        if ( enabled )
+        {
+            protectDnAlterations( oriChildName );
+        }
         nextInterceptor.move( oriChildName, newParentName );
     }
 
@@ -248,7 +265,10 @@
             Name oriChildName, Name newParentName, String newRn,
             boolean deleteOldRn ) throws NamingException
     {
-        protectDnAlterations( oriChildName );
+        if ( enabled )
+        {
+            protectDnAlterations( oriChildName );
+        }
         nextInterceptor.move( oriChildName, newParentName, newRn, deleteOldRn );
     }
 
@@ -294,9 +314,9 @@
     public Attributes lookup( NextInterceptor nextInterceptor, Name name ) throws NamingException
     {
         Attributes attributes = nextInterceptor.lookup( name );
-        if ( attributes == null )
+        if ( ! enabled || attributes == null )
         {
-            return null;
+            return attributes;
         }
 
         protectLookUp( name );
@@ -307,9 +327,9 @@
     public Attributes lookup( NextInterceptor nextInterceptor, Name name, String[] attrIds ) throws NamingException
     {
         Attributes attributes = nextInterceptor.lookup( name, attrIds );
-        if ( attributes == null )
+        if ( ! enabled || attributes == null )
         {
-            return null;
+            return attributes;
         }
 
         protectLookUp( name );
@@ -375,31 +395,36 @@
             SearchControls searchCtls ) throws NamingException
     {
         NamingEnumeration e = nextInterceptor.search( base, env, filter, searchCtls );
+        if ( !enabled )
+        {
+            return e;
+        }
         //if ( searchCtls.getReturningAttributes() != null )
         //{
         //    return null;
         //}
         
-        LdapContext ctx =
-            ( LdapContext ) InvocationStack.getInstance().peek().getCaller();
+        LdapContext ctx = ( LdapContext ) InvocationStack.getInstance().peek().getCaller();
         return new SearchResultFilteringEnumeration( e, searchCtls, ctx,
-                new SearchResultFilter()
+            new SearchResultFilter()
+            {
+                public boolean accept( LdapContext ctx, SearchResult result, SearchControls controls )
+                        throws NamingException
                 {
-                    public boolean accept( LdapContext ctx, SearchResult result,
-                                           SearchControls controls )
-                            throws NamingException
-                    {
-                        return OldAuthorizationService.this.isSearchable( ctx, result );
-                    }
-                } );
+                    return OldAuthorizationService.this.isSearchable( ctx, result );
+                }
+            });
     }
 
 
     public NamingEnumeration list( NextInterceptor nextInterceptor, Name base ) throws NamingException
     {
         NamingEnumeration e = nextInterceptor.list( base );
-        LdapContext ctx =
-            ( LdapContext ) InvocationStack.getInstance().peek().getCaller();
+        if ( !enabled )
+        {
+            return e;
+        }
+        LdapContext ctx = ( LdapContext ) InvocationStack.getInstance().peek().getCaller();
         
         return new SearchResultFilteringEnumeration( e, null, ctx,
             new SearchResultFilter()

Modified: directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/support/MicroOperationFilter.java
URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/support/MicroOperationFilter.java?rev=321394&r1=321393&r2=321394&view=diff
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/support/MicroOperationFilter.java (original)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/authz/support/MicroOperationFilter.java Sat Oct 15 10:54:41 2005
@@ -40,7 +40,11 @@
  */
 public class MicroOperationFilter implements ACITupleFilter
 {
-    public Collection filter( Collection tuples, OperationScope scope, NextInterceptor next, Collection userGroupNames, Name userName, Attributes userEntry, AuthenticationLevel authenticationLevel, Name entryName, String attrId, Object attrValue, Attributes entry, Collection microOperations ) throws NamingException
+    public Collection filter( Collection tuples, OperationScope scope, NextInterceptor next,
+                              Collection userGroupNames, Name userName, Attributes userEntry,
+                              AuthenticationLevel authenticationLevel, Name entryName, String attrId,
+                              Object attrValue, Attributes entry, Collection microOperations )
+            throws NamingException
     {
         if( tuples.size() == 0 )
         {
@@ -50,13 +54,20 @@
         for( Iterator i = tuples.iterator(); i.hasNext(); )
         {
             ACITuple tuple = ( ACITuple ) i.next();
-            boolean retain = false;
+
+            /*
+             * The ACITuple must contain all the MicroOperations specified within the
+             * microOperations argument.  Just matching a single microOperation is not
+             * enough.  All must be matched to retain the ACITuple.
+             */
+
+            boolean retain = true;
             for( Iterator j = microOperations.iterator(); j.hasNext(); )
             {
                 MicroOperation microOp = ( MicroOperation ) j.next();
-                if( tuple.getMicroOperations().contains( microOp ) )
+                if( ! tuple.getMicroOperations().contains( microOp ) )
                 {
-                    retain = true;
+                    retain = false;
                     break;
                 }
             }

Modified: directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/AbstractAuthorizationTest.java
URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/AbstractAuthorizationTest.java?rev=321394&r1=321393&r2=321394&view=diff
==============================================================================
--- directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/AbstractAuthorizationTest.java (original)
+++ directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/AbstractAuthorizationTest.java Sat Oct 15 10:54:41 2005
@@ -114,6 +114,20 @@
 
 
     /**
+     * Deletes a user with a specific UID under ou=users,ou=system.
+     *
+     * @param uid the RDN value for the user to delete
+     * @throws NamingException if there are problems removing the user
+     * i.e. user does not exist
+     */
+    public void deleteUser( String uid ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        adminCtx.destroySubcontext( "uid="+uid+",ou=users" );
+    }
+
+
+    /**
      * Creates a simple user as an inetOrgPerson under the ou=users,ou=system
      * container.  The user's RDN attribute is the uid argument.  This argument
      * is also used as the value of the two MUST attributes: sn and cn.
@@ -160,6 +174,23 @@
 
 
     /**
+     * Removes a user from a group.
+     *
+     * @param userUid the RDN attribute value of the user to remove from the group
+     * @param groupCn the RDN attribute value of the group to have user removed from
+     * @throws NamingException if there are problems accessing the group
+     */
+    public void removeUserFromGroup( String userUid, String groupCn ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        Attributes changes = new BasicAttributes( "uniqueMember",
+                "uid="+userUid+",ou=users,ou=system", true );
+        adminCtx.modifyAttributes( "cn="+groupCn+",ou=groups",
+                DirContext.REMOVE_ATTRIBUTE, changes );
+    }
+
+
+    /**
      * Gets the context at ou=system as a specific user.
      *
      * @param user the DN of the user to get the context as
@@ -193,6 +224,13 @@
     }
 
 
+    public void deleteAccessControlSubentry( String cn ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        adminCtx.destroySubcontext( "cn=" + cn );
+    }
+
+
     /**
      * Creates an access control subentry under ou=system whose subtree covers
      * the entire naming context.
@@ -206,7 +244,7 @@
         DirContext adminCtx = getContextAsAdmin();
 
         // modify ou=system to be an AP for an A/C AA if it is not already
-        Attributes ap = adminCtx.getAttributes( "" );
+        Attributes ap = adminCtx.getAttributes( "", new String[] { "administrativeRole" } );
         Attribute administrativeRole = ap.get( "administrativeRole" );
         if ( administrativeRole == null || ! administrativeRole.contains( SubentryService.AC_AREA ) )
         {

Added: directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/ModifyAuthorizationTest.java
URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/ModifyAuthorizationTest.java?rev=321394&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/ModifyAuthorizationTest.java (added)
+++ directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/ModifyAuthorizationTest.java Sat Oct 15 10:54:41 2005
@@ -0,0 +1,544 @@
+/*
+ *   Copyright 2004 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.ldap.server.authz;
+
+
+import org.apache.ldap.common.exception.LdapNoPermissionException;
+import org.apache.ldap.common.name.LdapName;
+
+import javax.naming.NamingException;
+import javax.naming.NamingEnumeration;
+import javax.naming.Name;
+import javax.naming.directory.*;
+import java.util.List;
+import java.util.ArrayList;
+
+
+/**
+ * Tests whether or not authorization around entry modify operations work properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ModifyAuthorizationTest extends AbstractAuthorizationTest
+{
+    /**
+     * Checks if an attribute of a simple entry (an organizationalUnit) with an RDN
+     * relative to ou=system can be modified by a specific non-admin user.  If a
+     * permission exception is encountered it is caught and false is returned,
+     * otherwise true is returned.  The entry is deleted after being created just in case
+     * subsequent calls to this method are made in the same test case: the admin account
+     * is used to add and delete this test entry so permissions to add and delete are not
+     * required to test the modify operation by the user.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param entryRdn the relative DN, relative to ou=system where entry is created
+     * for modification test
+     * @param mods the modifications to make to the entry
+     * @return true if the modifications can be made by the user at the specified location,
+     * false otherwise.
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanModifyAs( String uid, String password, String entryRdn, ModificationItem[] mods )
+            throws NamingException
+    {
+        // create the entry with the telephoneNumber attribute to modify
+        Attributes testEntry = new BasicAttributes( "ou", "testou", true );
+        Attribute objectClass = new BasicAttribute( "objectClass" );
+        testEntry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+        testEntry.put( "telephoneNumber", "867-5309" );  // jenny don't change your number
+
+        DirContext adminContext = getContextAsAdmin();
+
+        try
+        {
+            // create the entry as admin
+            LdapName userName = new LdapName( "uid="+uid+",ou=users,ou=system" );
+            adminContext.createSubcontext( entryRdn, testEntry );
+
+            // modify the entry as the user
+            DirContext userContext = getContextAs( userName, password );
+            userContext.modifyAttributes( entryRdn, mods );
+
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+        finally
+        {
+            // let's clean up
+            adminContext.destroySubcontext( entryRdn );
+        }
+    }
+
+
+    /**
+     * Checks if an attribute of a simple entry (an organizationalUnit) with an RDN
+     * relative to ou=system can be modified by a specific non-admin user.  If a
+     * permission exception is encountered it is caught and false is returned,
+     * otherwise true is returned.  The entry is deleted after being created just in case
+     * subsequent calls to this method are made in the same test case: the admin account
+     * is used to add and delete this test entry so permissions to add and delete are not
+     * required to test the modify operation by the user.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param entryRdn the relative DN, relative to ou=system where entry is created
+     * for modification test
+     * @param mods the attributes to modify in the entry
+     * @param modOp the modification operation to use for all attributes
+     * @return true if the modifications can be made by the user at the specified location,
+     * false otherwise.
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanModifyAs( String uid, String password, String entryRdn, int modOp, Attributes mods )
+            throws NamingException
+    {
+        // create the entry with the telephoneNumber attribute to modify
+        Attributes testEntry = new BasicAttributes( "ou", "testou", true );
+        Attribute objectClass = new BasicAttribute( "objectClass" );
+        testEntry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+        testEntry.put( "telephoneNumber", "867-5309" );  // jenny don't change your number
+
+        DirContext adminContext = getContextAsAdmin();
+
+        try
+        {
+            // create the entry as admin
+            LdapName userName = new LdapName( "uid="+uid+",ou=users,ou=system" );
+            adminContext.createSubcontext( entryRdn, testEntry );
+
+            // modify the entry as the user
+            DirContext userContext = getContextAs( userName, password );
+            userContext.modifyAttributes( entryRdn, modOp, mods );
+
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+        finally
+        {
+            // let's clean up
+            adminContext.destroySubcontext( entryRdn );
+        }
+    }
+
+
+    /**
+     * Checks if a user can modify an attribute of their own entry.  Users are
+     * presumed to reside under ou=users,ou=system.  If a permission exception is
+     * encountered it is caught and false is returned, otherwise true is returned.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param mods the attributes to modify in the entry
+     * @param modOp the modification operation to use for all attributes
+     * @return true if the modifications can be made by the user his/her own entry,
+     * false otherwise.
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanSelfModify( String uid, String password, int modOp, Attributes mods )
+            throws NamingException
+    {
+        try
+        {
+            // modify the entry as the user
+            Name userEntry = new LdapName( "uid="+uid+",ou=users,ou=system" );
+            DirContext userContext = getContextAs( userEntry, password, userEntry.toString() );
+            userContext.modifyAttributes( "", modOp, mods );
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * Checks if a user can modify an attribute of their own entry.  Users are
+     * presumed to reside under ou=users,ou=system.  If a permission exception is
+     * encountered it is caught and false is returned, otherwise true is returned.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param mods the attributes to modify in the entry
+     * @return true if the modifications can be made by the user his/her own entry,
+     * false otherwise.
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanSelfModify( String uid, String password, ModificationItem[] mods )
+            throws NamingException
+    {
+        try
+        {
+            // modify the entry as the user
+            Name userEntry = new LdapName( "uid="+uid+",ou=users,ou=system" );
+            DirContext userContext = getContextAs( userEntry, password, userEntry.toString() );
+            userContext.modifyAttributes( "", mods );
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * Converts a set of attributes and a modification operation type into a MoficationItem array.
+     *
+     * @param modOp the modification operation to perform
+     * @param changes the modifications to the attribute
+     * @return the array of modification items represting the changes
+     * @throws NamingException if there are problems accessing attributes
+     */
+    private ModificationItem[] toItems( int modOp, Attributes changes ) throws NamingException
+    {
+        List mods = new ArrayList();
+        NamingEnumeration list = changes.getAll();
+        while ( list.hasMore() )
+        {
+            Attribute attr = ( Attribute ) list.next();
+            mods.add( new ModificationItem( modOp, attr ) );
+        }
+        ModificationItem[] modArray = new ModificationItem[mods.size()];
+        return ( ModificationItem[] ) mods.toArray( modArray );
+    }
+
+
+    public void testSelfModification() throws NamingException
+    {
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Addition
+        // ----------------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // create the password modification
+        ModificationItem[] mods = toItems( DirContext.REPLACE_ATTRIBUTE,
+                new BasicAttributes( "userPassword", "williams", true ) );
+
+        // try a modify operation which should fail without any ACI
+        assertFalse( checkCanSelfModify( "billyd", "billyd", mods ) );
+
+        // Gives grantModify, and grantRead perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "selfModifyUserPassword",
+                "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { thisEntry }, " +
+                "userPermissions { " +
+                        "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse, grantRead } }, " +
+                        "{ protectedItems {allAttributeValues {userPassword}}, grantsAndDenials { grantAdd, grantRemove } } " +
+                        "} } }" );
+
+        // try a modify operation which should succeed with ACI
+        assertTrue( checkCanSelfModify( "billyd", "billyd", mods ) );
+        deleteAccessControlSubentry( "selfModifyUserPassword" );
+    }
+
+
+    /**
+     * Checks to make sure group membership based userClass works for modify operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    public void testGrantModifyByAdministrators() throws NamingException
+    {
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Addition
+        // ----------------------------------------------------------------------------------
+
+        // create the add modifications
+        ModificationItem[] mods = toItems( DirContext.ADD_ATTRIBUTE,
+                new BasicAttributes( "registeredAddress", "100 Park Ave.", true ) );
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try a modify operation which should fail without any ACI
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+
+        // Gives grantModify, and grantRead perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyAdd",
+                "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
+                "userPermissions { " +
+                        "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, " +
+                        "{ protectedItems {allAttributeValues {registeredAddress}}, grantsAndDenials { grantAdd } } " +
+                        "} } }" );
+
+        // see if we can now add that test entry which we could not before
+        // add op should still fail since billd is not in the admin group
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        deleteAccessControlSubentry( "administratorModifyAdd" );
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Removal
+        // ----------------------------------------------------------------------------------
+
+        // now let's test to see if we can perform a modify with a delete op
+        mods = toItems( DirContext.REMOVE_ATTRIBUTE,
+                new BasicAttributes( "telephoneNumber", "867-5309", true ) );
+
+        // make sure we cannot remove the telephone number from the test entry
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+
+        // Gives grantModify, and grantRead perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyRemove", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
+                "userPermissions { " +
+                        "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, " +
+                        "{ protectedItems {allAttributeValues {telephoneNumber}}, grantsAndDenials { grantRemove } } " +
+                        "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        deleteAccessControlSubentry( "administratorModifyRemove" );
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Replace (requires both grantRemove and grantAdd on attrs)
+        // ----------------------------------------------------------------------------------
+
+        // now let's test to see if we can perform a modify with a delete op
+        mods = toItems( DirContext.REPLACE_ATTRIBUTE,
+                new BasicAttributes( "telephoneNumber", "867-5309", true ) );
+
+        // make sure we cannot remove the telephone number from the test entry
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+
+        // Gives grantModify, and grantRead perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyReplace", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
+                "userPermissions { " +
+                        "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, " +
+                        "{ protectedItems {allAttributeValues {telephoneNumber}}, grantsAndDenials { grantAdd, grantRemove } } " +
+                        "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        deleteAccessControlSubentry( "administratorModifyReplace" );
+
+        /* =================================================================================
+         *              DO IT ALL OVER AGAIN BUT USE THE OTHER MODIFY METHOD
+         * ================================================================================= */
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Addition
+        // ----------------------------------------------------------------------------------
+
+        // create the add modifications
+        Attributes changes = new BasicAttributes( "registeredAddress", "100 Park Ave.", true );
+
+        // try a modify operation which should fail without any ACI
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.ADD_ATTRIBUTE, changes ) );
+
+        // Gives grantModify, and grantRead perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyAdd", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
+                "userPermissions { " +
+                        "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, " +
+                        "{ protectedItems {allAttributeValues {registeredAddress}}, grantsAndDenials { grantAdd } } " +
+                        "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.ADD_ATTRIBUTE, changes ) );
+        deleteAccessControlSubentry( "administratorModifyAdd" );
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Removal
+        // ----------------------------------------------------------------------------------
+
+        // now let's test to see if we can perform a modify with a delete op
+        changes = new BasicAttributes( "telephoneNumber", "867-5309", true );
+
+        // make sure we cannot remove the telephone number from the test entry
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REMOVE_ATTRIBUTE, changes ) );
+
+        // Gives grantModify, and grantRead perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyRemove", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
+                "userPermissions { " +
+                        "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, " +
+                        "{ protectedItems {allAttributeValues {telephoneNumber}}, grantsAndDenials { grantRemove } } " +
+                        "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REMOVE_ATTRIBUTE, changes ) );
+        deleteAccessControlSubentry( "administratorModifyRemove" );
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Replace (requires both grantRemove and grantAdd on attrs)
+        // ----------------------------------------------------------------------------------
+
+        // now let's test to see if we can perform a modify with a delete op
+        changes = new BasicAttributes( "telephoneNumber", "867-5309", true );
+
+        // make sure we cannot remove the telephone number from the test entry
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REPLACE_ATTRIBUTE, changes ) );
+
+        // Gives grantModify, and grantRead perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyReplace", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
+                "userPermissions { " +
+                        "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, " +
+                        "{ protectedItems {allAttributeValues {telephoneNumber}}, grantsAndDenials { grantAdd, grantRemove } } " +
+                        "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REPLACE_ATTRIBUTE, changes ) );
+        deleteAccessControlSubentry( "administratorModifyReplace" );
+    }
+
+
+//    /**
+//     * Checks to make sure name based userClass works for modify operations.
+//     *
+//     * @throws javax.naming.NamingException if the test encounters an error
+//     */
+//    public void testGrantModifyByName() throws NamingException
+//    {
+//        // create the non-admin user
+//        createUser( "billyd", "billyd" );
+//
+//        // try an modify operation which should fail without any ACI
+//        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+//
+//        // now add a subentry that enables user billyd to modify an entry below ou=system
+//        createAccessControlSubentry( "billydAdd", "{ " +
+//                "identificationTag \"addAci\", " +
+//                "precedence 14, " +
+//                "authenticationLevel none, " +
+//                "itemOrUserFirst userFirst: { " +
+//                "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " +
+//                "userPermissions { { " +
+//                "protectedItems {entry, allUserAttributeTypesAndValues}, " +
+//                "grantsAndDenials { grantModify, grantRead, grantBrowse } } } } }" );
+//
+//        // should work now that billyd is authorized by name
+//        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+//    }
+//
+//
+//    /**
+//     * Checks to make sure subtree based userClass works for modify operations.
+//     *
+//     * @throws javax.naming.NamingException if the test encounters an error
+//     */
+//    public void testGrantModifyBySubtree() throws NamingException
+//    {
+//        // create the non-admin user
+//        createUser( "billyd", "billyd" );
+//
+//        // try a modify operation which should fail without any ACI
+//        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+//
+//        // now add a subentry that enables user billyd to modify an entry below ou=system
+//        createAccessControlSubentry( "billyAddBySubtree", "{ " +
+//                "identificationTag \"addAci\", " +
+//                "precedence 14, " +
+//                "authenticationLevel none, " +
+//                "itemOrUserFirst userFirst: { " +
+//                "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " +
+//                "userPermissions { { " +
+//                "protectedItems {entry, allUserAttributeTypesAndValues}, " +
+//                "grantsAndDenials { grantModify, grantRead, grantBrowse } } } } }" );
+//
+//        // should work now that billyd is authorized by the subtree userClass
+//        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+//    }
+//
+//
+//    /**
+//     * Checks to make sure <b>allUsers</b> userClass works for modify operations.
+//     *
+//     * @throws javax.naming.NamingException if the test encounters an error
+//     */
+//    public void testGrantModifyAllUsers() throws NamingException
+//    {
+//        // create the non-admin user
+//        createUser( "billyd", "billyd" );
+//
+//        // try an add operation which should fail without any ACI
+//        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+//
+//        // now add a subentry that enables anyone to add an entry below ou=system
+//        createAccessControlSubentry( "anybodyAdd", "{ " +
+//                "identificationTag \"addAci\", " +
+//                "precedence 14, " +
+//                "authenticationLevel none, " +
+//                "itemOrUserFirst userFirst: { " +
+//                "userClasses { allUsers }, " +
+//                "userPermissions { { " +
+//                "protectedItems {entry, allUserAttributeTypesAndValues}, " +
+//                "grantsAndDenials { grantModify, grantRead, grantBrowse } } } } }" );
+//
+//        // see if we can now modify that test entry's number which we could not before
+//        // should work with billyd now that all users are authorized
+//        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+//    }
+}

Propchange: directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/ModifyAuthorizationTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/MoveRenameAuthorizationTest.java
URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/MoveRenameAuthorizationTest.java?rev=321394&view=auto
==============================================================================
--- directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/MoveRenameAuthorizationTest.java (added)
+++ directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/MoveRenameAuthorizationTest.java Sat Oct 15 10:54:41 2005
@@ -0,0 +1,482 @@
+/*
+ *   Copyright 2004 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.ldap.server.authz;
+
+
+import org.apache.ldap.common.exception.LdapNoPermissionException;
+import org.apache.ldap.common.name.LdapName;
+
+import javax.naming.NamingException;
+import javax.naming.directory.*;
+
+
+/**
+ * Tests whether or not authorization around entry renames and moves work properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class MoveRenameAuthorizationTest extends AbstractAuthorizationTest
+{
+    /**
+     * Checks if a simple entry (organizationalUnit) can be renamed at an RDN relative
+     * to ou=system by a specific non-admin user.  If a permission exception
+     * is encountered it is caught and false is returned, otherwise true is returned
+     * when the entry is created.  The entry is deleted after being created just in case
+     * subsequent calls to this method do not fail: the admin account is used to delete
+     * this test entry so permissions to delete are not required to delete it by the user.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param entryRdn the relative DN, relative to ou=system where entry renames are tested
+     * @param newRdn the new RDN for the entry under ou=system
+     * @return true if the entry can be renamed by the user at the specified location, false otherwise
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanRenameAs( String uid, String password, String entryRdn, String newRdn )
+            throws NamingException
+    {
+        Attributes testEntry = new BasicAttributes( "ou", "testou", true );
+        Attribute objectClass = new BasicAttribute( "objectClass" );
+        testEntry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+
+        DirContext adminContext = getContextAsAdmin();
+        try
+        {
+            // create the new entry as the admin user
+            adminContext.createSubcontext( entryRdn, testEntry );
+
+            LdapName userName = new LdapName( "uid="+uid+",ou=users,ou=system" );
+            DirContext userContext = getContextAs( userName, password );
+            userContext.rename( entryRdn, newRdn );
+
+            // delete the renamed context as the admin user
+            adminContext.destroySubcontext( newRdn );
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            // delete the original context as the admin user since rename
+            // of newly created test entry did not succeed
+            adminContext.destroySubcontext( entryRdn );
+            return false;
+        }
+    }
+
+
+    /**
+     * Checks to make sure group membership based userClass works for renames,
+     * moves and moves with renames.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    public void testGrantByAdministrators() throws NamingException
+    {
+        // ----------------------------------------------------------------------------
+        // Test simple RDN change: NO SUBTREE MOVEMENT!
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try the rename operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // Gives grantRename perm to all users in the Administrators group for entries
+        createAccessControlSubentry( "grantRenameByAdmin", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantRename, grantBrowse } } } } }" );
+
+        // see if we can now rename that test entry which we could not before
+        // rename op should still fail since billyd is not in the admin group
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try a rename operation which should succeed with ACI and group membership change
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now let's cleanup
+        removeUserFromGroup( "billyd", "Administrators" );
+        deleteAccessControlSubentry( "grantRenameByAdmin" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move and RDN change at the same time.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an move w/ rdn change which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // Gives grantRename, grantImport, grantExport perm to all users in the Administrators
+        // group for entries - browse is needed just to read navigate the tree at root
+        createAccessControlSubentry( "grantRenameMoveByAdmin", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantExport, grantImport, grantRename, grantBrowse } } } } }" );
+
+        // see if we can move and rename the test entry which we could not before
+        // op should still fail since billyd is not in the admin group
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try move w/ rdn change which should succeed with ACI and group membership change
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now let's cleanup
+        removeUserFromGroup( "billyd", "Administrators" );
+        deleteAccessControlSubentry( "grantRenameMoveByAdmin" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move ONLY without any RDN changes.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try move operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // Gives grantImport, and grantExport perm to all users in the Administrators group for entries
+        createAccessControlSubentry( "grantMoveByAdmin", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantExport, grantImport, grantBrowse } } } } }" );
+
+        // see if we can now move that test entry which we could not before
+        // op should still fail since billyd is not in the admin group
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try move operation which should succeed with ACI and group membership change
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now let's cleanup
+        removeUserFromGroup( "billyd", "Administrators" );
+        deleteAccessControlSubentry( "grantMoveByAdmin" );
+        deleteUser( "billyd" );
+    }
+
+
+    /**
+     * Checks to make sure name based userClass works for rename, move, and
+     * rename with move operation access controls.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    public void testGrantByName() throws NamingException
+    {
+        // ----------------------------------------------------------------------------
+        // Test simple RDN change: NO SUBTREE MOVEMENT!
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try the rename operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // Gives grantRename perm specifically to the billyd user
+        createAccessControlSubentry( "grantRenameByName", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantRename, grantBrowse } } } } }" );
+
+        // try a rename operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameByName" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move and RDN change at the same time.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an move w/ rdn change which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // Gives grantRename, grantImport, grantExport perm to billyd user on entries
+        createAccessControlSubentry( "grantRenameMoveByName", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantExport, grantImport, grantRename, grantBrowse } } } } }" );
+
+        // try move w/ rdn change which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameMoveByName" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move ONLY without any RDN changes.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try move operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // Gives grantImport, and grantExport perm to billyd user for entries
+        createAccessControlSubentry( "grantMoveByName", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantExport, grantImport, grantBrowse } } } } }" );
+
+        // try move operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantMoveByName" );
+        deleteUser( "billyd" );
+    }
+
+
+    /**
+     * Checks to make sure subtree based userClass works for rename, move, and
+     * rename with move operation access controls.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    public void testGrantBySubtree() throws NamingException
+    {
+        // ----------------------------------------------------------------------------
+        // Test simple RDN change: NO SUBTREE MOVEMENT!
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try the rename operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // Gives grantRename perm for entries to those users selected by the subtree
+        createAccessControlSubentry( "grantRenameByTree", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantRename, grantBrowse } } } } }" );
+
+        // try a rename operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameByTree" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move and RDN change at the same time.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an move w/ rdn change which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // Gives grantRename, grantImport, grantExport for entries to users selected by subtree
+        createAccessControlSubentry( "grantRenameMoveByTree", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantExport, grantImport, grantRename, grantBrowse } } } } }" );
+
+        // try move w/ rdn change which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameMoveByTree" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move ONLY without any RDN changes.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try move operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // Gives grantImport, and grantExport perm for entries to subtree selected users
+        createAccessControlSubentry( "grantMoveByTree", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantExport, grantImport, grantBrowse } } } } }" );
+
+        // try move operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantMoveByTree" );
+        deleteUser( "billyd" );
+    }
+
+
+    /**
+     * Checks to make sure the <b>anyUser</b> userClass works for rename, move, and
+     * rename with move operation access controls.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    public void testGrantByAnyuser() throws NamingException
+    {
+        // ----------------------------------------------------------------------------
+        // Test simple RDN change: NO SUBTREE MOVEMENT!
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try the rename operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // Gives grantRename perm for entries to any user
+        createAccessControlSubentry( "grantRenameByAny", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { allUsers }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantRename, grantBrowse } } } } }" );
+
+        // try a rename operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameByAny" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move and RDN change at the same time.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an move w/ rdn change which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // Gives grantRename, grantImport, grantExport for entries to any user
+        createAccessControlSubentry( "grantRenameMoveByAny", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { allUsers }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantExport, grantImport, grantRename, grantBrowse } } } } }" );
+
+        // try move w/ rdn change which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameMoveByAny" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move ONLY without any RDN changes.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try move operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // Gives grantImport, and grantExport perm for entries to any user
+        createAccessControlSubentry( "grantMoveByAny", "{ " +
+                "identificationTag \"addAci\", " +
+                "precedence 14, " +
+                "authenticationLevel none, " +
+                "itemOrUserFirst userFirst: { " +
+                "userClasses { allUsers }, " +
+                "userPermissions { { " +
+                "protectedItems {entry}, " +
+                "grantsAndDenials { grantExport, grantImport, grantBrowse } } } } }" );
+
+        // try move operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantMoveByAny" );
+        deleteUser( "billyd" );
+    }
+}

Propchange: directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/MoveRenameAuthorizationTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/support/MicroOperationFilterTest.java
URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/support/MicroOperationFilterTest.java?rev=321394&r1=321393&r2=321394&view=diff
==============================================================================
--- directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/support/MicroOperationFilterTest.java (original)
+++ directory/apacheds/trunk/core/src/test/org/apache/ldap/server/authz/support/MicroOperationFilterTest.java Sat Oct 15 10:54:41 2005
@@ -54,6 +54,7 @@
         USER_OPERATIONS_A.add( MicroOperation.BROWSE );
         USER_OPERATIONS_B.add( MicroOperation.COMPARE );
         USER_OPERATIONS_B.add( MicroOperation.DISCLOSE_ON_ERROR );
+        TUPLE_OPERATIONS.add( MicroOperation.ADD );
         TUPLE_OPERATIONS.add( MicroOperation.BROWSE );
         TUPLE_OPERATIONS.add( MicroOperation.EXPORT );
     }