You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by en...@apache.org on 2010/02/27 03:02:09 UTC

svn commit: r916893 - in /sling/trunk: bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/util/ bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/ bundles/jcr/jackrabbit-accessmanager/src/main/java/org/apache/sli...

Author: enorman
Date: Sat Feb 27 02:02:09 2010
New Revision: 916893

URL: http://svn.apache.org/viewvc?rev=916893&view=rev
Log:
SLING-1411 Add replaceAccessControlEntry method to AccessControlUtil
Thanks to Ray Davis for the contribution.

Modified:
    sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/util/AccessControlUtil.java
    sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java
    sling/trunk/bundles/jcr/jackrabbit-accessmanager/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/ModifyAceServlet.java
    sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/accessManager/ModifyAceTest.java

Modified: sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/util/AccessControlUtil.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/util/AccessControlUtil.java?rev=916893&r1=916892&r2=916893&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/util/AccessControlUtil.java (original)
+++ sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/util/AccessControlUtil.java Sat Feb 27 02:02:09 2010
@@ -18,10 +18,22 @@
  */
 package org.apache.sling.jcr.base.util;
 
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import javax.jcr.AccessDeniedException;
 import javax.jcr.RepositoryException;
@@ -31,12 +43,10 @@
 import javax.jcr.security.AccessControlException;
 import javax.jcr.security.AccessControlList;
 import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.AccessControlPolicyIterator;
 import javax.jcr.security.Privilege;
 
-import org.apache.jackrabbit.api.JackrabbitSession;
-import org.apache.jackrabbit.api.security.principal.PrincipalManager;
-import org.apache.jackrabbit.api.security.user.UserManager;
-
 /**
  * A simple utility class providing utilities with respect to
  * access control over repositories.
@@ -60,7 +70,7 @@
     // the name of the JackrabbitAccessControlEntry method
     private static final String METHOD_JACKRABBIT_ACE_IS_ALLOW = "isAllow";
 
-
+    private static final Logger log = LoggerFactory.getLogger(AccessControlUtil.class);
 
     // ---------- SessionImpl methods -----------------------------------------------------
 
@@ -201,6 +211,141 @@
     	Class[] types = new Class[] {Principal.class, Privilege[].class, boolean.class, Map.class};
 		return safeInvokeRepoMethod(acl, METHOD_JACKRABBIT_ACL_ADD_ENTRY, Boolean.class, args, types);
     }
+    
+    /**
+     * Replaces existing access control entries in the ACL for the specified
+     * <code>principal</code> and <code>resourcePath</code>. Any existing granted
+     * or denied privileges which do not conflict with the specified privileges
+     * are maintained. Where conflicts exist, existing privileges are dropped.
+     * The end result will be at most two ACEs for the principal: one for grants
+     * and one for denies. Aggregate privileges are disaggregated before checking
+     * for conflicts.
+     * @param session
+     * @param resourcePath
+     * @param principal
+     * @param grantedPrivilegeNames
+     * @param deniedPrivilegeNames
+     * @param removedPrivilegeNames privileges which, if they exist, should be
+     * removed for this principal and resource
+     * @throws RepositoryException
+     */
+    public static void replaceAccessControlEntry(Session session, String resourcePath, Principal principal, 
+    			String[] grantedPrivilegeNames, String[] deniedPrivilegeNames, String[] removedPrivilegeNames)
+        		throws RepositoryException {
+    	AccessControlManager accessControlManager = getAccessControlManager(session);
+    	Set<String> specifiedPrivilegeNames = new HashSet<String>();
+    	Set<String> newGrantedPrivilegeNames = disaggregateToPrivilegeNames(accessControlManager, grantedPrivilegeNames, specifiedPrivilegeNames);
+    	Set<String> newDeniedPrivilegeNames = disaggregateToPrivilegeNames(accessControlManager, deniedPrivilegeNames, specifiedPrivilegeNames);
+    	disaggregateToPrivilegeNames(accessControlManager, removedPrivilegeNames, specifiedPrivilegeNames);
+
+    	// Get or create the ACL for the node.
+    	AccessControlList acl = null;
+    	AccessControlPolicy[] policies = accessControlManager.getPolicies(resourcePath);
+    	for (AccessControlPolicy policy : policies) {
+    		if (policy instanceof AccessControlList) {
+    			acl = (AccessControlList) policy;
+    			break;
+    		}
+    	}
+    	if (acl == null) {
+    		AccessControlPolicyIterator applicablePolicies = accessControlManager.getApplicablePolicies(resourcePath);
+    		while (applicablePolicies.hasNext()) {
+    			AccessControlPolicy policy = applicablePolicies.nextAccessControlPolicy();
+    			if (policy instanceof AccessControlList) {
+    				acl = (AccessControlList) policy;
+    				break;
+    			}
+    		}
+    	}
+    	if (acl == null) {
+    		throw new RepositoryException("Could not obtain ACL for resource " + resourcePath);
+    	}
+    	// Used only for logging.
+    	Set<Privilege> oldGrants = null;
+    	Set<Privilege> oldDenies = null;
+    	if (log.isDebugEnabled()) {
+    		oldGrants = new HashSet<Privilege>();
+    		oldDenies = new HashSet<Privilege>();
+    	}
+      
+    	// Combine all existing ACEs for the target principal.
+    	AccessControlEntry[] accessControlEntries = acl.getAccessControlEntries();
+    	for (AccessControlEntry ace : accessControlEntries) {
+    		if (principal.equals(ace.getPrincipal())) {
+    			if (log.isDebugEnabled()) {
+    				log.debug("Found Existing ACE for principal {} on resource {}", new Object[] {principal.getName(), resourcePath});
+    			}
+    			boolean isAllow = isAllow(ace);
+    			Privilege[] privileges = ace.getPrivileges();
+    			if (log.isDebugEnabled()) {
+    				if (isAllow) {
+    					oldGrants.addAll(Arrays.asList(privileges));
+    				} else {
+    					oldDenies.addAll(Arrays.asList(privileges));
+    				}
+    			}
+    			for (Privilege privilege : privileges) {
+    				Set<String> maintainedPrivileges = disaggregateToPrivilegeNames(privilege);
+    				// If there is any overlap with the newly specified privileges, then
+    				// break the existing privilege down; otherwise, maintain as is.
+    				if (!maintainedPrivileges.removeAll(specifiedPrivilegeNames)) {
+    					// No conflicts, so preserve the original.
+    					maintainedPrivileges.clear();
+    					maintainedPrivileges.add(privilege.getName());
+    				}
+    				if (!maintainedPrivileges.isEmpty()) {
+    					if (isAllow) {
+    						newGrantedPrivilegeNames.addAll(maintainedPrivileges);
+    					} else {
+    						newDeniedPrivilegeNames.addAll(maintainedPrivileges);
+    					}
+    				}
+    			}
+    			// Remove the old ACE.
+    			acl.removeAccessControlEntry(ace);
+    		}
+    	}
+
+    	//add a fresh ACE with the granted privileges
+    	List<Privilege> grantedPrivilegeList = new ArrayList<Privilege>();
+    	for (String name : newGrantedPrivilegeNames) {
+    		Privilege privilege = accessControlManager.privilegeFromName(name);
+    		grantedPrivilegeList.add(privilege);
+    	}
+    	if (grantedPrivilegeList.size() > 0) {
+    		acl.addAccessControlEntry(principal, grantedPrivilegeList.toArray(new Privilege[grantedPrivilegeList.size()]));
+    	}
+
+    	//if the authorizable is a user (not a group) process any denied privileges
+    	UserManager userManager = getUserManager(session);
+    	Authorizable authorizable = userManager.getAuthorizable(principal);
+    	if (!authorizable.isGroup()) {
+    		//add a fresh ACE with the denied privileges
+    		List<Privilege> deniedPrivilegeList = new ArrayList<Privilege>();
+    		for (String name : newDeniedPrivilegeNames) {
+    			Privilege privilege = accessControlManager.privilegeFromName(name);
+    			deniedPrivilegeList.add(privilege);
+    		}        
+    		if (deniedPrivilegeList.size() > 0) {
+    			addEntry(acl, principal, deniedPrivilegeList.toArray(new Privilege[deniedPrivilegeList.size()]), false);
+    		}
+    	}
+
+    	accessControlManager.setPolicy(resourcePath, acl);
+    	if (log.isDebugEnabled()) {
+    		List<String> oldGrantedNames = new ArrayList<String>(oldGrants.size());
+    		for (Privilege privilege : oldGrants) {
+    			oldGrantedNames.add(privilege.getName());
+    		}
+    		List<String> oldDeniedNames = new ArrayList<String>(oldDenies.size());
+    		for (Privilege privilege : oldDenies) {
+    			oldDeniedNames.add(privilege.getName());
+    		}
+    		log.debug("Updated ACE for principalId {} for resource {} from grants {}, denies {} to grants {}, denies {}", new Object [] {
+    				authorizable.getID(), resourcePath, oldGrantedNames, oldDeniedNames, newGrantedPrivilegeNames, newDeniedPrivilegeNames
+    			});
+    	}
+	}
 
     // ---------- AccessControlEntry methods -----------------------------------------------
 
@@ -264,4 +409,40 @@
 		else
 			return null;
 	}
+  
+	/**
+	 * Helper routine to transform an input array of privilege names into a set in
+	 * a null-safe way while also adding its disaggregated privileges to an input set.
+	 */
+	private static Set<String> disaggregateToPrivilegeNames(AccessControlManager accessControlManager, 
+			String[] privilegeNames, Set<String> disaggregatedPrivilegeNames)
+      throws RepositoryException {
+		Set<String> originalPrivilegeNames = new HashSet<String>();
+		if (privilegeNames != null) {
+			for (String privilegeName : privilegeNames) {
+				originalPrivilegeNames.add(privilegeName);
+				Privilege privilege = accessControlManager.privilegeFromName(privilegeName);
+				disaggregatedPrivilegeNames.addAll(disaggregateToPrivilegeNames(privilege));
+			}
+		}
+		return originalPrivilegeNames;
+	}
+
+	/**
+	 * Transform an aggregated privilege into a set of disaggregated privilege
+	 * names. If the privilege is not an aggregate, the set will contain the
+	 * original name.
+	 */
+	private static Set<String> disaggregateToPrivilegeNames(Privilege privilege) {
+		Set<String> disaggregatedPrivilegeNames = new HashSet<String>();
+		if (privilege.isAggregate()) {
+			Privilege[] privileges = privilege.getAggregatePrivileges();
+			for (Privilege disaggregate : privileges) {
+				disaggregatedPrivilegeNames.add(disaggregate.getName());
+			}
+		} else {
+			disaggregatedPrivilegeNames.add(privilege.getName());
+		}
+		return disaggregatedPrivilegeNames;
+	}
 }

Modified: sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java?rev=916893&r1=916892&r2=916893&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java (original)
+++ sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java Sat Feb 27 02:02:09 2010
@@ -18,6 +18,13 @@
  */
 package org.apache.sling.jcr.contentloader.internal;
 
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.sling.jcr.base.util.AccessControlUtil;
+
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.security.MessageDigest;
@@ -26,11 +33,9 @@
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -47,18 +52,6 @@
 import javax.jcr.Session;
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
-import javax.jcr.security.AccessControlEntry;
-import javax.jcr.security.AccessControlList;
-import javax.jcr.security.AccessControlManager;
-import javax.jcr.security.AccessControlPolicy;
-import javax.jcr.security.AccessControlPolicyIterator;
-import javax.jcr.security.Privilege;
-
-import org.apache.jackrabbit.api.security.user.Authorizable;
-import org.apache.jackrabbit.api.security.user.Group;
-import org.apache.jackrabbit.api.security.user.User;
-import org.apache.jackrabbit.api.security.user.UserManager;
-import org.apache.sling.jcr.base.util.AccessControlUtil;
 
 /**
  * The <code>ContentLoader</code> creates the nodes and properties.
@@ -810,120 +803,17 @@
 			throws RepositoryException {
 		final Node parentNode = this.parentNodeStack.peek();
 		Session session = parentNode.getSession();
-
-		UserManager userManager = AccessControlUtil.getUserManager(session);
-		Authorizable authorizable = userManager.getAuthorizable(principalId);
-		if (authorizable == null) {
+		PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(session);
+		Principal principal = principalManager.getPrincipal(principalId);
+		if (principal == null) {
 			throw new RepositoryException("No principal found for id: " + principalId);
 		}
-
 		String resourcePath = parentNode.getPath();
 
-		AccessControlManager accessControlManager = AccessControlUtil.getAccessControlManager(session);
-		AccessControlList updatedAcl = null;
-		AccessControlPolicy[] policies = accessControlManager.getPolicies(resourcePath);
-		for (AccessControlPolicy policy : policies) {
-		  if (policy instanceof AccessControlList) {
-		    updatedAcl = (AccessControlList)policy;
-		    break;
-		  }
-		}
-		if (updatedAcl == null) {
-		  AccessControlPolicyIterator applicablePolicies = accessControlManager.getApplicablePolicies(resourcePath);
-		  while (applicablePolicies.hasNext()) {
-		    AccessControlPolicy policy = applicablePolicies.nextAccessControlPolicy();
-		    if (policy instanceof AccessControlList) {
-		      updatedAcl = (AccessControlList)policy;
-		    }
-		  }
-		}
-		if (updatedAcl == null) {
-			throw new RepositoryException("Unable to find or create an access control policy to update for " + resourcePath);
-		}
-
-		Set<String> postedPrivilegeNames = new HashSet<String>();
-		if (grantedPrivilegeNames != null) {
-			postedPrivilegeNames.addAll(Arrays.asList(grantedPrivilegeNames));
+		if ((grantedPrivilegeNames != null) || (deniedPrivilegeNames != null)) {
+			AccessControlUtil.replaceAccessControlEntry(session, resourcePath, principal,
+					grantedPrivilegeNames, deniedPrivilegeNames, null);
 		}
-		if (deniedPrivilegeNames != null) {
-			postedPrivilegeNames.addAll(Arrays.asList(deniedPrivilegeNames));
-		}
-
-		List<Privilege> preserveGrantedPrivileges = new ArrayList<Privilege>();
-		List<Privilege> preserveDeniedPrivileges = new ArrayList<Privilege>();
-
-		//keep track of the existing Aces for the target principal
-		AccessControlEntry[] accessControlEntries = updatedAcl.getAccessControlEntries();
-		List<AccessControlEntry> oldAces = new ArrayList<AccessControlEntry>();
-		for (AccessControlEntry ace : accessControlEntries) {
-			if (principalId.equals(ace.getPrincipal().getName())) {
-				oldAces.add(ace);
-
-				boolean isAllow = AccessControlUtil.isAllow(ace);
-				Privilege[] privileges = ace.getPrivileges();
-				for (Privilege privilege : privileges) {
-					String privilegeName = privilege.getName();
-					if (!postedPrivilegeNames.contains(privilegeName)) {
-						//this privilege was not posted, so record the existing state to be
-						// preserved when the ACE is re-created below
-						if (isAllow) {
-							preserveGrantedPrivileges.add(privilege);
-						} else {
-							preserveDeniedPrivileges.add(privilege);
-						}
-					}
-				}
-			}
-		}
-
-		//remove the old aces
-		if (!oldAces.isEmpty()) {
-			for (AccessControlEntry ace : oldAces) {
-				updatedAcl.removeAccessControlEntry(ace);
-			}
-		}
-
-		//add a fresh ACE with the granted privileges
-		List<Privilege> grantedPrivilegeList = new ArrayList<Privilege>();
-		if (grantedPrivilegeNames != null) {
-		  for (String name : grantedPrivilegeNames) {
-			  if (name.length() == 0) {
-				  continue; //empty, skip it.
-			  }
-			  Privilege privilege = accessControlManager.privilegeFromName(name);
-			  grantedPrivilegeList.add(privilege);
-	    }
-		}
-		//add the privileges that should be preserved
-		grantedPrivilegeList.addAll(preserveGrantedPrivileges);
-
-		if (grantedPrivilegeList.size() > 0) {
-			Principal principal = authorizable.getPrincipal();
-			updatedAcl.addAccessControlEntry(principal, grantedPrivilegeList.toArray(new Privilege[grantedPrivilegeList.size()]));
-		}
-
-		//if the authorizable is a user (not a group) process any denied privileges
-		if (!authorizable.isGroup()) {
-			//add a fresh ACE with the denied privileges
-			List<Privilege> deniedPrivilegeList = new ArrayList<Privilege>();
-			if (deniedPrivilegeNames != null) {
-			  for (String name : deniedPrivilegeNames) {
-				  if (name.length() == 0) {
-					  continue; //empty, skip it.
-				  }
-				  Privilege privilege = accessControlManager.privilegeFromName(name);
-				  deniedPrivilegeList.add(privilege);
-			  }
-			}
-			//add the privileges that should be preserved
-			deniedPrivilegeList.addAll(preserveDeniedPrivileges);
-			if (deniedPrivilegeList.size() > 0) {
-				Principal principal = authorizable.getPrincipal();
-				AccessControlUtil.addEntry(updatedAcl, principal, deniedPrivilegeList.toArray(new Privilege[deniedPrivilegeList.size()]), false);
-			}
-		}
-
-		accessControlManager.setPolicy(resourcePath, updatedAcl);
 	}
 
 	/**

Modified: sling/trunk/bundles/jcr/jackrabbit-accessmanager/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/ModifyAceServlet.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/jackrabbit-accessmanager/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/ModifyAceServlet.java?rev=916893&r1=916892&r2=916893&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/jackrabbit-accessmanager/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/ModifyAceServlet.java (original)
+++ sling/trunk/bundles/jcr/jackrabbit-accessmanager/src/main/java/org/apache/sling/jcr/jackrabbit/accessmanager/post/ModifyAceServlet.java Sat Feb 27 02:02:09 2010
@@ -16,8 +16,15 @@
  */
 package org.apache.sling.jcr.jackrabbit.accessmanager.post;
 
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceNotFoundException;
+import org.apache.sling.api.servlets.HtmlResponse;
+import org.apache.sling.jcr.base.util.AccessControlUtil;
+import org.apache.sling.servlets.post.Modification;
+
 import java.security.Principal;
-import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
@@ -26,31 +33,16 @@
 import javax.jcr.Item;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.security.AccessControlEntry;
-import javax.jcr.security.AccessControlList;
-import javax.jcr.security.AccessControlManager;
-import javax.jcr.security.Privilege;
-
-import org.apache.jackrabbit.api.security.user.Authorizable;
-import org.apache.jackrabbit.api.security.user.UserManager;
-import org.apache.sling.api.SlingHttpServletRequest;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceNotFoundException;
-import org.apache.sling.api.servlets.HtmlResponse;
-import org.apache.sling.jcr.base.util.AccessControlUtil;
-import org.apache.sling.servlets.post.Modification;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * <p>
- * Sling Post Servlet implementation for modifying the ACE for a principal on a JCR
+ * Sling Post Servlet implementation for modifying the ACEs for a principal on a JCR
  * resource.
  * </p>
  * <h2>Rest Service Description</h2>
  * <p>
- * Delete a set of Ace's from a node, the node is identified as a resource by the request
- * url &gt;resource&lt;.modifyAce.html
+ * Modify a principal's ACEs for the node identified as a resource by the request
+ * URL &gt;resource&lt;.modifyAce.html
  * </p>
  * <h4>Methods</h4>
  * <ul>
@@ -59,11 +51,11 @@
  * <h4>Post Parameters</h4>
  * <dl>
  * <dt>principalId</dt>
- * <dd>The principal of the Ace to modify in the ACL specified by the path.</dd>
+ * <dd>The principal of the ACEs to modify in the ACL specified by the path.</dd>
  * <dt>privilege@*</dt>
- * <dd>One of more privileges, either granted or denied, where set the permission in the
- * stored ACE is modified to match the request. Any permissions that are present in the
- * stored ACE, but are not in the request are left untouched.</dd>
+ * <dd>One or more privileges, either granted or denied or none, which will be applied
+ * to (or removed from) the node ACL. Any permissions that are present in an
+ * existing ACE for the principal but not in the request are left untouched.</dd>
  * </dl>
  *
  * <h4>Response</h4>
@@ -92,11 +84,6 @@
 public class ModifyAceServlet extends AbstractAccessPostServlet {
 	private static final long serialVersionUID = -9182485466670280437L;
 
-	/**
-     * default log
-     */
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
 	/* (non-Javadoc)
 	 * @see org.apache.sling.jackrabbit.accessmanager.post.AbstractAccessPostServlet#handleOperation(org.apache.sling.api.SlingHttpServletRequest, org.apache.sling.api.servlets.HtmlResponse, java.util.List)
 	 */
@@ -113,88 +100,25 @@
 		if (principalId == null) {
 			throw new RepositoryException("principalId was not submitted.");
 		}
-		UserManager userManager = AccessControlUtil.getUserManager(session);
-		Authorizable authorizable = userManager.getAuthorizable(principalId);
-		if (authorizable == null) {
-			throw new RepositoryException("No principal found for id: " + principalId);
-		}
-
-    	String resourcePath = null;
-    	Resource resource = request.getResource();
-    	if (resource == null) {
+		PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(session);
+		Principal principal = principalManager.getPrincipal(principalId);
+		String resourcePath = null;
+		Resource resource = request.getResource();
+		if (resource == null) {
 			throw new ResourceNotFoundException("Resource not found.");
-    	} else {
-    		Item item = resource.adaptTo(Item.class);
-    		if (item != null) {
-    			resourcePath = item.getPath();
-    		} else {
-    			throw new ResourceNotFoundException("Resource is not a JCR Node");
-    		}
-    	}
-
-		try {
-			AccessControlManager accessControlManager = AccessControlUtil.getAccessControlManager(session);
-
-			//SLING-997: keep track of the privilege names that were posted, so the others can be preserved
-			PrivilegesState privilegesInfo = new PrivilegesState(log.isDebugEnabled());
-			
-			//calculate which privileges were POSTed.
-			collectPostedPrivilegeNames(request, 
-					accessControlManager,
-					privilegesInfo);	
-
-			//find the existing ACL
-			AccessControlList updatedAcl = getAccessControlList(accessControlManager, resourcePath, true);
-
-			//keep track of the existing ACEs for the target principal
-			List<AccessControlEntry> oldAces = processExistingAccessControlEntries(resourcePath, 
-					authorizable, 
-					accessControlManager, 
-					updatedAcl, 
-					privilegesInfo); 
-			
-			//remove the old ACEs.  Re-created below.
-			if (!oldAces.isEmpty()) {
-				for (AccessControlEntry ace : oldAces) {
-					updatedAcl.removeAccessControlEntry(ace);
-				}
+		} else {
+			Item item = resource.adaptTo(Item.class);
+			if (item != null) {
+				resourcePath = item.getPath();
+			} else {
+				throw new ResourceNotFoundException("Resource is not a JCR Node");
 			}
-			
-			//re-aggregate the granted/denied privileges into the best matched aggregate privilege
-			reaggregatePrivileges(resourcePath,
-					accessControlManager,
-					privilegesInfo);
-			
-			//add fresh ACEs with the granted privileges
-			buildFreshAccessControlEntries(authorizable, 
-					accessControlManager, 
-					updatedAcl, 
-					privilegesInfo);
-			
-			//store the updated ACL
-			accessControlManager.setPolicy(resourcePath, updatedAcl);
-			if (session.hasPendingChanges()) {
-				session.save();
-			}
-
-			if (log.isDebugEnabled()) {
-				log.debug("Updated ACE for principalId {0} for resource {1) from {2} to {3}", new Object [] {
-						authorizable.getID(), resourcePath, privilegesInfo.oldPrivileges.toString(), privilegesInfo.newPrivileges.toString()
-				});
-			}
-		} catch (RepositoryException re) {
-			throw new RepositoryException("Failed to create ace.", re);
 		}
-	}
-
-
-	/**
-	 * Collect the privileges to assign from the http request.
-	 */
-	private void collectPostedPrivilegeNames(SlingHttpServletRequest request,
-			AccessControlManager accessControlManager,
-			PrivilegesState privilegesInfo) throws RepositoryException {
-
+    
+		// Collect the modified privileges from the request.
+		Set<String> grantedPrivilegeNames = new HashSet<String>();
+		Set<String> deniedPrivilegeNames = new HashSet<String>();
+		Set<String> removedPrivilegeNames = new HashSet<String>();
 		Enumeration<?> parameterNames = request.getParameterNames();
 		while (parameterNames.hasMoreElements()) {
 			Object nextElement = parameterNames.nextElement();
@@ -202,237 +126,31 @@
 				String paramName = (String)nextElement;
 				if (paramName.startsWith("privilege@")) {
 					String privilegeName = paramName.substring(10);
-					//keep track of which privileges should be changed
-					privilegesInfo.postedPrivilegeNames.add(privilegeName); 
-					
 					String parameterValue = request.getParameter(paramName);
 					if (parameterValue != null && parameterValue.length() > 0) {
 						if ("granted".equals(parameterValue)) {
-							Privilege privilege = accessControlManager.privilegeFromName(privilegeName);
-							if (privilege.isAggregate()) {
-								Privilege[] aggregatePrivileges = privilege.getAggregatePrivileges();
-								for (Privilege privilege2 : aggregatePrivileges) {
-									privilegesInfo.grantedPrivilegeNames.add(privilege2.getName());
-								}
-							} else {
-								privilegesInfo.grantedPrivilegeNames.add(privilegeName);
-							}
+							grantedPrivilegeNames.add(privilegeName);
 						} else if ("denied".equals(parameterValue)) {
-							Privilege privilege = accessControlManager.privilegeFromName(privilegeName);
-							if (privilege.isAggregate()) {
-								Privilege[] aggregatePrivileges = privilege.getAggregatePrivileges();
-								for (Privilege privilege2 : aggregatePrivileges) {
-									privilegesInfo.deniedPrivilegeNames.add(privilege2.getName());
-								}
-							} else {
-								privilegesInfo.deniedPrivilegeNames.add(privilegeName);
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-	
-	/**
-	 * Process the existing ACL to determine which privileges to preserve.
-	 */
-	private List<AccessControlEntry> processExistingAccessControlEntries(
-			String resourcePath,
-			Authorizable authorizable,
-			AccessControlManager accessControlManager,
-			AccessControlList updatedAcl,
-			PrivilegesState privilegesInfo) throws RepositoryException {
-		
-		String principalId = authorizable.getPrincipal().getName();
-		AccessControlEntry[] accessControlEntries = updatedAcl.getAccessControlEntries();
-		List<AccessControlEntry> oldAces = new ArrayList<AccessControlEntry>();
-		for (AccessControlEntry ace : accessControlEntries) {
-			if (principalId.equals(ace.getPrincipal().getName())) {
-				if (log.isDebugEnabled()) {
-					log.debug("Found Existing ACE for principal {0} on resource: ", new Object[] {principalId, resourcePath});
-				}
-				oldAces.add(ace);
-
-				boolean isAllow = AccessControlUtil.isAllow(ace);
-				Privilege[] privileges = ace.getPrivileges();
-
-				//build a list of (merged and expanded) privileges 
-				Set<Privilege> mergedExistingPrivileges = new HashSet<Privilege>();
-				for (Privilege privilege : privileges) {
-					if (privilege.isAggregate()) {
-						Privilege[] aggregatePrivileges = privilege.getAggregatePrivileges();
-						for (Privilege privilege2 : aggregatePrivileges) {
-							mergedExistingPrivileges.add(privilege2);
-						}
-					} else {
-						mergedExistingPrivileges.add(privilege);
-					}
-				}
-				
-				//now process the merged privileges set
-				for (Privilege privilege : mergedExistingPrivileges) {
-					String privilegeName = privilege.getName();
-					if (!privilegesInfo.postedPrivilegeNames.contains(privilegeName)) {
-						//this privilege was not posted, so record the existing state to be 
-						// preserved when the ACE is re-created below
-						if (isAllow) {
-							privilegesInfo.grantedPrivilegeNames.add(privilegeName);
-						} else {
-							privilegesInfo.deniedPrivilegeNames.add(privilegeName);
-						}
-					}
-
-					if (log.isDebugEnabled()) {
-						//collect the information for debug logging
-						if (privilegesInfo.oldPrivileges.length() > 0) {
-							privilegesInfo.oldPrivileges.append(", "); //separate entries by commas
-						}
-						if (isAllow) {
-							privilegesInfo.oldPrivileges.append("granted=");
+							deniedPrivilegeNames.add(privilegeName);
 						} else {
-							privilegesInfo.oldPrivileges.append("denied=");
+							removedPrivilegeNames.add(privilegeName);
 						}
-						privilegesInfo.oldPrivileges.append(privilege.getName());
-					}
-				}
-			}
-		}
-		
-		return oldAces;
-	}
-	
-	/**
-	 * Given the set of granted/denied privileges, try to combine them
-	 * into the best aggregate Privilege that contains them all.
-	 */
-	private void reaggregatePrivileges(
-			String resourcePath,
-			AccessControlManager accessControlManager,
-			PrivilegesState privilegesInfo) throws RepositoryException {
-		Privilege[] supportedPrivileges = accessControlManager.getSupportedPrivileges(resourcePath);
-		for (Privilege privilege : supportedPrivileges) {
-			if (privilege.isAggregate()) {
-				boolean grantedAggregatePrivilege = true;
-				boolean deniedAggregatePrivilege = true;
-				Privilege[] aggregatePrivileges = privilege.getAggregatePrivileges();
-				for (Privilege privilege2 : aggregatePrivileges) {
-					String name = privilege2.getName();
-					if (!privilegesInfo.grantedPrivilegeNames.contains(name)) {
-						grantedAggregatePrivilege = false;
-					}
-					if (!privilegesInfo.deniedPrivilegeNames.contains(name)) {
-						deniedAggregatePrivilege = false;
-					}
-				}
-				
-				if (grantedAggregatePrivilege) {
-					//add the aggregate privilege and remove the containing parts
-					privilegesInfo.grantedPrivilegeNames.add(privilege.getName());
-					for (Privilege privilege2 : aggregatePrivileges) {
-						String name = privilege2.getName();
-						privilegesInfo.grantedPrivilegeNames.remove(name);
-					}						
-				}
-
-				if (deniedAggregatePrivilege) {
-					//add the aggregate privilege and remove the containing parts
-					privilegesInfo.deniedPrivilegeNames.add(privilege.getName());
-					for (Privilege privilege2 : aggregatePrivileges) {
-						String name = privilege2.getName();
-						privilegesInfo.deniedPrivilegeNames.remove(name);
-					}						
-				}
-			}
-		}
-	}
-	
-	/**
-	 * Create new ACE for the granted and denied privileges.
-	 */
-	private void buildFreshAccessControlEntries(
-			Authorizable authorizable,
-			AccessControlManager accessControlManager,			
-			AccessControlList updatedAcl,
-			PrivilegesState privilegesInfo) throws RepositoryException {
-		List<Privilege> grantedPrivilegeList = new ArrayList<Privilege>();
-		for (String name : privilegesInfo.grantedPrivilegeNames) {
-			if (name.length() == 0) {
-				continue; //empty, skip it.
-			}
-			Privilege privilege = accessControlManager.privilegeFromName(name);
-			grantedPrivilegeList.add(privilege);
-		}
-		
-		if (log.isDebugEnabled()) {
-			for (Privilege privilege : grantedPrivilegeList) {
-				if (privilegesInfo.newPrivileges.length() > 0) {
-					privilegesInfo.newPrivileges.append(", "); //separate entries by commas
-				}
-				privilegesInfo.newPrivileges.append("granted=");
-				privilegesInfo.newPrivileges.append(privilege.getName());
-			}				
-		}
-
-		if (grantedPrivilegeList.size() > 0) {
-			Principal principal = authorizable.getPrincipal();
-			updatedAcl.addAccessControlEntry(principal, grantedPrivilegeList.toArray(new Privilege[grantedPrivilegeList.size()]));
-		}
-
-		//if the authorizable is a user (not a group) process any denied privileges
-		if (!authorizable.isGroup()) {
-			//add a fresh ACE with the denied privileges
-			List<Privilege> deniedPrivilegeList = new ArrayList<Privilege>();
-			for (String name : privilegesInfo.deniedPrivilegeNames) {
-				if (name.length() == 0) {
-					continue; //empty, skip it.
-				}
-				Privilege privilege = accessControlManager.privilegeFromName(name);
-				deniedPrivilegeList.add(privilege);
-			}
-			
-			if (log.isDebugEnabled()) {
-				for (Privilege privilege : deniedPrivilegeList) {
-					if (privilegesInfo.newPrivileges.length() > 0) {
-						privilegesInfo.newPrivileges.append(", "); //separate entries by commas
 					}
-					privilegesInfo.newPrivileges.append("denied=");
-					privilegesInfo.newPrivileges.append(privilege.getName());
 				}
 			}
-			
-			if (deniedPrivilegeList.size() > 0) {
-				Principal principal = authorizable.getPrincipal();
-				AccessControlUtil.addEntry(updatedAcl, principal, deniedPrivilegeList.toArray(new Privilege[deniedPrivilegeList.size()]), false);
-			}
 		}
-	}
-	
-	/**
-	 * Contains the information about the privilege state as it 
-	 * progresses through the update process. 
-	 */
-	private static class PrivilegesState {
-		//stores the names of the privileges that were POSTed
-		Set<String> postedPrivilegeNames = new HashSet<String>();
-		
-		//stores the names of the privileges to be granted 
-		Set<String> grantedPrivilegeNames = new HashSet<String>();
-
-		//stores the names of the privileges to be denied 
-		Set<String> deniedPrivilegeNames = new HashSet<String>();
-
-		//collects debug information about the previous privileges
-		StringBuilder oldPrivileges = null;
-		
-		//collects debug information about the new privileges
-		StringBuilder newPrivileges = null;
 
-		public PrivilegesState(boolean isDebugEnabled) {
-			if (isDebugEnabled) {
-				oldPrivileges = new StringBuilder();
-				newPrivileges = new StringBuilder();
+		// Make the actual changes.
+		try {
+			AccessControlUtil.replaceAccessControlEntry(session, resourcePath, principal,
+					grantedPrivilegeNames.toArray(new String[grantedPrivilegeNames.size()]),
+					deniedPrivilegeNames.toArray(new String[deniedPrivilegeNames.size()]),
+					removedPrivilegeNames.toArray(new String[removedPrivilegeNames.size()]));
+			if (session.hasPendingChanges()) {
+				session.save();
 			}
+		} catch (RepositoryException re) {
+			throw new RepositoryException("Failed to create ace.", re);
 		}
 	}
 }

Modified: sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/accessManager/ModifyAceTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/accessManager/ModifyAceTest.java?rev=916893&r1=916892&r2=916893&view=diff
==============================================================================
--- sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/accessManager/ModifyAceTest.java (original)
+++ sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/accessManager/ModifyAceTest.java Sat Feb 27 02:02:09 2010
@@ -398,10 +398,9 @@
 		postParams2.add(new NameValuePair("principalId", testUserId));
 		//jcr:read is not posted, so it should remain in the granted ACE
 		
-		//post the remaining privileges that when combined, correspond to the jcr:write aggregate privilege
-		postParams2.add(new NameValuePair("privilege@jcr:addChildNodes", "denied")); //add a new privilege
-		postParams2.add(new NameValuePair("privilege@jcr:removeChildNodes", "denied")); //add a new privilege
-		postParams2.add(new NameValuePair("privilege@jcr:modifyProperties", "denied")); //add a new privilege
+		//deny the full jcr:write aggregate privilege, which should merge with the
+		//existing part.
+		postParams2.add(new NameValuePair("privilege@jcr:write", "denied")); //add a new privilege
 		
 		assertAuthenticatedPostStatus(creds, postUrl, HttpServletResponse.SC_OK, postParams2, null);