You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2009/11/18 13:38:26 UTC

svn commit: r881752 - in /jackrabbit/trunk: jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/secur...

Author: angela
Date: Wed Nov 18 12:38:25 2009
New Revision: 881752

URL: http://svn.apache.org/viewvc?rev=881752&view=rev
Log:
JCR-2397 - AccessControlEntries should be orderable

Modified:
    jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlList.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java
    jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplateTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplateTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplateTest.java

Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlList.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlList.java?rev=881752&r1=881751&r2=881752&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlList.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/JackrabbitAccessControlList.java Wed Nov 18 12:38:25 2009
@@ -18,10 +18,12 @@
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.security.AccessControlException;
 import javax.jcr.security.AccessControlList;
 import javax.jcr.security.AccessControlPolicy;
 import javax.jcr.security.Privilege;
+import javax.jcr.security.AccessControlEntry;
 import java.security.Principal;
 import java.util.Map;
 
@@ -112,4 +114,22 @@
     boolean addEntry(Principal principal, Privilege[] privileges,
                      boolean isAllow, Map<String, Value> restrictions)
             throws AccessControlException, RepositoryException;
+
+    /**
+     * If the <code>AccessControlList</code> implementation supports
+     * reordering of entries the specified <code>srcEntry</code> is inserted
+     * at the position of the specified <code>destEntry</code>.<p/>
+     * If <code>destEntry</code> is <code>null</code> the entry is moved to the
+     * end of the list.<p/>
+     * If srcChildRelPath and destChildRelPath are the same no changes are made.
+     * 
+     * @param srcEntry The access control entry to be moved within the list.
+     * @param destEntry The entry before which the <code>srcEntry</code> will be moved.
+     * @throws AccessControlException If any of the given entries is invalid or
+     * cannot be handled by the implementation.
+     * @throws UnsupportedRepositoryOperationException If ordering is not supported.
+     * @throws RepositoryException If another error occurs.
+     */
+    void orderBefore(AccessControlEntry srcEntry, AccessControlEntry destEntry)
+            throws AccessControlException, UnsupportedRepositoryOperationException, RepositoryException;
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java?rev=881752&r1=881751&r2=881752&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java Wed Nov 18 12:38:25 2009
@@ -19,14 +19,19 @@
 import java.security.Principal;
 import java.util.Collections;
 import java.util.Map;
+import java.util.List;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.security.AccessControlException;
 import javax.jcr.security.Privilege;
+import javax.jcr.security.AccessControlEntry;
 
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * <code>AbstractACLTemplate</code>...
@@ -34,6 +39,8 @@
 public abstract class AbstractACLTemplate implements JackrabbitAccessControlList,
         AccessControlConstants {
 
+    private static Logger log = LoggerFactory.getLogger(AbstractACLTemplate.class);
+
     /**
      * Path of the node this ACL template has been created for.
      */
@@ -65,6 +72,17 @@
                                             boolean isAllow,
                                             Map<String, Value> restrictions) throws AccessControlException;
 
+    /**
+     * Return the list of entries, if they are held in a orderable list.
+     *
+     * @return the list of entries.
+     * @throws UnsupportedRepositoryOperationException If the implementation
+     * does not held the entries in a list.
+     * @throws RepositoryException
+     * @see #orderBefore(AccessControlEntry, AccessControlEntry)
+     */
+    protected abstract List<? extends AccessControlEntry> getEntries() throws UnsupportedRepositoryOperationException, RepositoryException;
+
     //--------------------------------------< JackrabbitAccessControlPolicy >---
     /**
      * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy#getPath()
@@ -82,6 +100,34 @@
         return addEntry(principal, privileges, isAllow, Collections.<String, Value>emptyMap());
     }
 
+    /**
+     *
+     * @param srcEntry The access control entry to be moved within the list.
+     * @param destEntry The entry before which the <code>srcEntry</code> will be moved.
+     * @throws AccessControlException
+     * @throws UnsupportedRepositoryOperationException
+     * @throws RepositoryException
+     */
+    public void orderBefore(AccessControlEntry srcEntry, AccessControlEntry destEntry) throws AccessControlException, UnsupportedRepositoryOperationException, RepositoryException {
+        if (srcEntry.equals(destEntry)) {
+            log.debug("srcEntry equals destEntry -> no reordering required.");
+            return;
+        }
+
+        List entries = getEntries();
+        int index = (destEntry == null) ? entries.size()-1 : entries.indexOf(destEntry);
+        if (index < 0) {
+            throw new AccessControlException("destEntry not contained in this AccessControlList");
+        } else {
+            if (entries.remove(srcEntry)) {
+                // re-insert the srcEntry at the new position.
+                entries.add(index, srcEntry);
+            } else {
+                // src entry not contained in this list.
+                throw new AccessControlException("srcEntry not contained in this AccessControlList");
+            }
+        }
+    }
 
     //--------------------------------------------------< AccessControlList >---
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java?rev=881752&r1=881751&r2=881752&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java Wed Nov 18 12:38:25 2009
@@ -130,12 +130,16 @@
         return accessControlEntries.length;
     }
 
-    public boolean addEntry(Principal principal, Privilege[] privileges, boolean isAllow) throws AccessControlException, RepositoryException {
-        throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getApplicablePolicies in order to obtain an modifiable ACL.");
+    public boolean addEntry(Principal principal, Privilege[] privileges, boolean isAllow) throws AccessControlException {
+        throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getPolicy or #getApplicablePolicies in order to obtain an modifiable ACL.");
     }
 
-    public boolean addEntry(Principal principal, Privilege[] privileges, boolean isAllow, Map<String, Value> restrictions) throws AccessControlException, RepositoryException {
-        throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getApplicablePolicies in order to obtain an modifiable ACL.");
+    public boolean addEntry(Principal principal, Privilege[] privileges, boolean isAllow, Map<String, Value> restrictions) throws AccessControlException {
+        throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getPolicy or #getApplicablePolicies in order to obtain an modifiable ACL.");
+    }
+
+    public void orderBefore(AccessControlEntry srcEntry, AccessControlEntry destEntry) throws AccessControlException {
+        throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getPolicy or #getApplicablePolicy in order to obtain a modifiable ACL.");
     }
 
     public String getPath() {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java?rev=881752&r1=881751&r2=881752&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java Wed Nov 18 12:38:25 2009
@@ -19,7 +19,6 @@
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -48,9 +47,11 @@
 import org.slf4j.LoggerFactory;
 
 /**
- * Implementation of the {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlList} interface that
- * is detached from the effective access control content. Consequently, any
- * modifications applied to this ACL only take effect, if the policy gets
+ * Implementation of the
+ * {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlList}
+ * interface that is detached from the effective access control content.
+ * Consequently, any modifications applied to this ACL only take effect, if
+ * the policy gets
  * {@link javax.jcr.security.AccessControlManager#setPolicy(String, javax.jcr.security.AccessControlPolicy) reapplied}
  * to the <code>AccessControlManager</code> and the changes are saved.
  */
@@ -59,11 +60,10 @@
     private static final Logger log = LoggerFactory.getLogger(ACLTemplate.class);
 
     /**
-     * Map containing the entries of this ACL Template using the principal
-     * name as key. The value represents a List containing maximal one grant
-     * and one deny ACE per principal.
+     * List containing the entries of this ACL Template with maximal one
+     * grant and one deny ACE per principal.
      */
-    private final Map<String, List<Entry>> entries = new LinkedHashMap<String, List<Entry>>();
+    private final List<Entry> entries = new ArrayList<Entry>();
 
     /**
      * The principal manager used for validation checks
@@ -140,57 +140,54 @@
         }
     }
 
-    private List<? extends AccessControlEntry> internalGetEntries() {
-        List<Entry> l = new ArrayList<Entry>();
-        for (List<Entry> o : entries.values()) {
-            l.addAll(o);
-        }
-        return l;
-    }
-
     private List<Entry> internalGetEntries(Principal principal) {
         String principalName = principal.getName();
-        if (entries.containsKey(principalName)) {
-            return entries.get(principalName);
-        } else {
-            return new ArrayList<Entry>(2);
+        List entriesPerPrincipal = new ArrayList(2);
+        for (Entry entry : entries) {
+            if (principalName.equals(entry.getPrincipal().getName())) {
+                entriesPerPrincipal.add(entry);
+            }
         }
+        return entriesPerPrincipal;
     }
 
     private synchronized boolean internalAdd(Entry entry) throws AccessControlException {
         Principal principal = entry.getPrincipal();
-        List<Entry> l = internalGetEntries(principal);
-        if (l.isEmpty()) {
-            // simple case: just add the new entry
-            l.add(entry);
-            entries.put(principal.getName(), l);
+        List<Entry> entriesPerPrincipal = internalGetEntries(principal);
+        if (entriesPerPrincipal.isEmpty()) {
+            // simple case: just add the new entry at the end of the list.
+            entries.add(entry);
             return true;
         } else {
-            if (l.contains(entry)) {
+            if (entriesPerPrincipal.contains(entry)) {
                 // the same entry is already contained -> no modification
                 return false;
             }
             // check if need to adjust existing entries
+            int updateIndex = -1;
             Entry complementEntry = null;
-            Entry[] entries = l.toArray(new Entry[l.size()]);
-            for (int i = 0; i < entries.length; i++) {
-                if (entry.isAllow() == entries[i].isAllow()) {
-                    int existingPrivs = entries[i].getPrivilegeBits();
+
+            for (Entry e : entriesPerPrincipal) {
+                if (entry.isAllow() == e.isAllow()) {
+                    int existingPrivs = e.getPrivilegeBits();
                     if ((existingPrivs | ~entry.getPrivilegeBits()) == -1) {
                         // all privileges to be granted/denied are already present
                         // in the existing entry -> not modified
                         return false;
                     }
 
+                    // remember the index of the existing entry to be updated later on.
+                    updateIndex = entries.indexOf(e);
+
                     // remove the existing entry and create a new that includes
                     // both the new privileges and the existing ones.
-                    l.remove(i);
-                    int mergedBits = entries[i].getPrivilegeBits() | entry.getPrivilegeBits();
+                    entries.remove(e);
+                    int mergedBits = e.getPrivilegeBits() | entry.getPrivilegeBits();
                     Privilege[] mergedPrivs = privilegeRegistry.getPrivileges(mergedBits);
                     // omit validation check.
                     entry = new Entry(entry.getPrincipal(), mergedPrivs, entry.isAllow(), valueFactory);
                 } else {
-                    complementEntry = entries[i];
+                    complementEntry = e;
                 }
             }
 
@@ -198,22 +195,34 @@
             // grant/deny the same privileges -> remove privileges that are now
             // denied/granted.
             if (complementEntry != null) {
+
                 int complPrivs = complementEntry.getPrivilegeBits();
                 int resultPrivs = Permission.diff(complPrivs, entry.getPrivilegeBits());
+
                 if (resultPrivs == PrivilegeRegistry.NO_PRIVILEGE) {
-                    l.remove(complementEntry);
+                    // remove the complement entry as the new entry covers
+                    // all privileges granted by the existing entry.
+                    entries.remove(complementEntry);
+                    updateIndex--;
+                    
                 } else if (resultPrivs != complPrivs) {
-                    l.remove(complementEntry);
-                    // omit validation check
+                    // replace the existing entry having the privileges adjusted
+                    int index = entries.indexOf(complementEntry);
+                    entries.remove(complementEntry);
                     Entry tmpl = new Entry(entry.getPrincipal(),
                             privilegeRegistry.getPrivileges(resultPrivs),
                             !entry.isAllow(), valueFactory);
-                    l.add(tmpl);
+                    entries.add(index, tmpl);
                 } /* else: does not need to be modified.*/
             }
 
-            // finally add the new entry at the end.
-            l.add(entry);
+            // finally update the existing entry or add the new entry passed
+            // to this method at the end.
+            if (updateIndex < 0) {
+                entries.add(entry);
+            } else {
+                entries.add(updateIndex, entry);
+            }
             return true;
         }
     }
@@ -236,12 +245,19 @@
         }
     }
 
+    /**
+     * @see org.apache.jackrabbit.core.security.authorization.AbstractACLTemplate#getEntries()
+     */
+    protected List<? extends AccessControlEntry> getEntries() {
+        return entries;
+    }
+
     //--------------------------------------------------< AccessControlList >---
     /**
      * @see javax.jcr.security.AccessControlList#getAccessControlEntries()
      */
     public AccessControlEntry[] getAccessControlEntries() throws RepositoryException {
-        List<? extends AccessControlEntry> l = internalGetEntries();
+        List<? extends AccessControlEntry> l = getEntries();
         return l.toArray(new AccessControlEntry[l.size()]);
     }
 
@@ -253,11 +269,8 @@
         if (!(ace instanceof Entry)) {
             throw new AccessControlException("Invalid AccessControlEntry implementation " + ace.getClass().getName() + ".");
         }
-        List<Entry> l = internalGetEntries(ace.getPrincipal());
-        if (l.remove(ace)) {
-            if (l.isEmpty()) {
-                entries.remove(ace.getPrincipal().getName());
-            }
+        if (entries.contains(ace)) {
+            entries.remove(ace);
         } else {
             throw new AccessControlException("AccessControlEntry " + ace + " cannot be removed from ACL defined at " + getPath());
         }
@@ -294,7 +307,7 @@
      * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#size()
      */
     public int size() {
-        return internalGetEntries().size();
+        return getEntries().size();
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java?rev=881752&r1=881751&r2=881752&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java Wed Nov 18 12:38:25 2009
@@ -167,7 +167,14 @@
             throw new AccessControlException("Missing mandatory restriction: " + jcrNodePathName);
         }
     }
-    
+
+    /**
+     * @see org.apache.jackrabbit.core.security.authorization.AbstractACLTemplate#getEntries() 
+     */
+    protected List<? extends AccessControlEntry> getEntries() {
+        return entries;
+    }
+
     //----------------------------------------< JackrabbitAccessControlList >---
     /**
      * @see JackrabbitAccessControlList#getRestrictionNames()

Modified: jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd?rev=881752&r1=881751&r2=881752&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd Wed Nov 18 12:38:25 2009
@@ -558,6 +558,7 @@
   abstract
 
 [rep:ACL] > rep:Policy
+  orderable
   + * (rep:ACE) = rep:GrantACE protected IGNORE
 
 [rep:ACE]

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplateTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplateTest.java?rev=881752&r1=881751&r2=881752&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplateTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplateTest.java Wed Nov 18 12:38:25 2009
@@ -66,6 +66,8 @@
 
     protected abstract JackrabbitAccessControlList createEmptyTemplate(String path) throws RepositoryException;
 
+    protected abstract Principal getSecondPrincipal() throws Exception;
+
     public void testEmptyTemplate() throws RepositoryException {
         JackrabbitAccessControlList pt = createEmptyTemplate(getTestPath());
 
@@ -260,4 +262,63 @@
             // success
         }
     }
+
+    public void testReorder() throws Exception {
+        Privilege[] read = privilegesFromName(Privilege.JCR_READ);
+        Privilege[] write = privilegesFromName(Privilege.JCR_WRITE);
+
+        Principal p2 = getSecondPrincipal();
+
+        AbstractACLTemplate acl = (AbstractACLTemplate) createEmptyTemplate(getTestPath());
+        acl.addAccessControlEntry(testPrincipal, read);
+        acl.addEntry(testPrincipal, write, false);
+        acl.addAccessControlEntry(p2, write);
+
+        AccessControlEntry[] entries = acl.getAccessControlEntries();
+
+        assertEquals(3, entries.length);
+        AccessControlEntry aReadTP = entries[0];
+        AccessControlEntry dWriteTP = entries[1];
+        AccessControlEntry aWriteP2 = entries[2];
+
+        // reorder aWriteP2 to the first position
+        acl.orderBefore(aWriteP2, aReadTP);
+        assertEquals(0, acl.getEntries().indexOf(aWriteP2));
+        assertEquals(1, acl.getEntries().indexOf(aReadTP));
+        assertEquals(2, acl.getEntries().indexOf(dWriteTP));
+
+        // reorder aReadTP to the end of the list
+        acl.orderBefore(aReadTP, null);
+        assertEquals(0, acl.getEntries().indexOf(aWriteP2));
+        assertEquals(1, acl.getEntries().indexOf(dWriteTP));
+        assertEquals(2, acl.getEntries().indexOf(aReadTP));
+    }
+
+    public void testReorderInvalidElements() throws Exception {
+        Privilege[] read = privilegesFromName(Privilege.JCR_READ);
+        Privilege[] write = privilegesFromName(Privilege.JCR_WRITE);
+
+        Principal p2 = getSecondPrincipal();
+
+        AbstractACLTemplate acl = (AbstractACLTemplate) createEmptyTemplate(getTestPath());
+        acl.addAccessControlEntry(testPrincipal, read);
+        acl.addAccessControlEntry(p2, write);
+
+        AbstractACLTemplate acl2 = (AbstractACLTemplate) createEmptyTemplate(getTestPath());
+        acl2.addEntry(testPrincipal, write, false);
+
+        AccessControlEntry invalid = acl2.getEntries().get(0);
+        try {
+            acl.orderBefore(invalid, acl.getEntries().get(0));
+            fail("src entry not contained in list -> reorder should fail.");
+        } catch (AccessControlException e) {
+            // success
+        }
+        try {
+            acl.orderBefore(acl.getEntries().get(0), invalid);
+            fail("dest entry not contained in list -> reorder should fail.");
+        } catch (AccessControlException e) {
+            // success
+        }
+    }
 }
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplateTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplateTest.java?rev=881752&r1=881751&r2=881752&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplateTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplateTest.java Wed Nov 18 12:38:25 2009
@@ -18,11 +18,13 @@
 
 import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
 import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
 import org.apache.jackrabbit.api.security.principal.PrincipalManager;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.security.authorization.AbstractACLTemplateTest;
 import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
+import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
 import org.apache.jackrabbit.test.NotExecutableException;
 
 import javax.jcr.RepositoryException;
@@ -32,12 +34,15 @@
 import java.security.Principal;
 import java.security.acl.Group;
 import java.util.Collections;
+import java.util.Map;
 
 /**
  * <code>ACLTemplateTest</code>...
  */
 public class ACLTemplateTest extends AbstractACLTemplateTest {
 
+    private Map<String, Value> restrictions = Collections.<String, Value>emptyMap();
+
     protected String getTestPath() {
         return "/ab/c/d";
     }
@@ -49,10 +54,14 @@
         return new ACLTemplate(path, princicipalMgr, privilegeRegistry, sImpl.getValueFactory());
     }
 
+    protected Principal getSecondPrincipal() throws Exception {
+        return pMgr.getEveryone();
+    }
+
     public void testMultipleEntryEffect() throws RepositoryException, NotExecutableException {
         JackrabbitAccessControlList pt = createEmptyTemplate(getTestPath());
         Privilege[] privileges = privilegesFromName(Privilege.JCR_READ);
-        pt.addEntry(testPrincipal, privileges, true, Collections.<String, Value>emptyMap());
+        pt.addEntry(testPrincipal, privileges, true, restrictions);
 
         // new entry extends privileges.
         privileges = privilegesFromNames(new String[] {
@@ -60,7 +69,7 @@
                 Privilege.JCR_ADD_CHILD_NODES});
         assertTrue(pt.addEntry(testPrincipal,
                 privileges,
-                true, Collections.<String, Value>emptyMap()));
+                true, restrictions));
 
         // net-effect: only a single allow-entry with both privileges
         assertTrue(pt.size() == 1);
@@ -68,14 +77,14 @@
 
         // adding just ADD_CHILD_NODES -> must not remove READ privilege
         Privilege[] achPrivs = privilegesFromName(Privilege.JCR_ADD_CHILD_NODES);
-        assertFalse(pt.addEntry(testPrincipal, achPrivs, true, Collections.<String, Value>emptyMap()));
+        assertFalse(pt.addEntry(testPrincipal, achPrivs, true, restrictions));
         // net-effect: only a single allow-entry with add_child_nodes + read privilege
         assertTrue(pt.size() == 1);
         assertSamePrivileges(privileges, pt.getAccessControlEntries()[0].getPrivileges());
 
         // revoke the 'READ' privilege
         privileges = privilegesFromName(Privilege.JCR_READ);
-        assertTrue(pt.addEntry(testPrincipal, privileges, false, Collections.<String, Value>emptyMap()));
+        assertTrue(pt.addEntry(testPrincipal, privileges, false, restrictions));
         // net-effect: 2 entries one allowing ADD_CHILD_NODES, the other denying READ
         assertTrue(pt.size() == 2);
         assertSamePrivileges(privilegesFromName(Privilege.JCR_ADD_CHILD_NODES),
@@ -160,13 +169,113 @@
         JackrabbitAccessControlList pt = createEmptyTemplate(getTestPath());
         Privilege[] privileges = privilegesFromName(Privilege.JCR_READ);
 
-        pt.addEntry(testPrincipal, privileges, true, Collections.<String, Value>emptyMap());
+        pt.addEntry(testPrincipal, privileges, true, restrictions);
 
         // same entry but with revers 'isAllow' flag
-        assertTrue(pt.addEntry(testPrincipal, privileges, false, Collections.<String, Value>emptyMap()));
+        assertTrue(pt.addEntry(testPrincipal, privileges, false, restrictions));
 
         // net-effect: only a single deny-read entry
-        assertTrue(pt.size() == 1);
+        assertEquals(1, pt.size());
         assertSamePrivileges(privileges, pt.getAccessControlEntries()[0].getPrivileges());
     }
+
+    public void testUpdateEntry() throws RepositoryException, NotExecutableException {
+        JackrabbitAccessControlList pt = createEmptyTemplate(getTestPath());
+
+        Privilege[] readPriv = privilegesFromName(Privilege.JCR_READ);
+        Privilege[] writePriv = privilegesFromName(Privilege.JCR_WRITE);
+
+        Principal principal2 = pMgr.getEveryone();
+
+        pt.addEntry(testPrincipal, readPriv, true, restrictions);
+        pt.addEntry(principal2, readPriv, true, restrictions);
+        pt.addEntry(testPrincipal, writePriv, false, restrictions);
+
+        // adding an entry that should update the existing allow-entry for everyone.
+        pt.addEntry(principal2, writePriv, true, restrictions);
+
+        AccessControlEntry[] entries = pt.getAccessControlEntries();
+        assertEquals(3, entries.length);
+        JackrabbitAccessControlEntry princ2AllowEntry = (JackrabbitAccessControlEntry) entries[1];
+        assertEquals(principal2, princ2AllowEntry.getPrincipal());
+        assertTrue(princ2AllowEntry.isAllow());
+        assertSamePrivileges(new Privilege[] {readPriv[0], writePriv[0]}, princ2AllowEntry.getPrivileges());
+    }
+
+    public void testUpdateComplementaryEntry() throws RepositoryException, NotExecutableException {
+        JackrabbitAccessControlList pt = createEmptyTemplate(getTestPath());
+
+        Privilege[] readPriv = privilegesFromName(Privilege.JCR_READ);
+        Privilege[] writePriv = privilegesFromName(Privilege.JCR_WRITE);
+        Principal principal2 = pMgr.getEveryone();
+
+        pt.addEntry(testPrincipal, readPriv, true, restrictions);
+        pt.addEntry(principal2, readPriv, true, restrictions);
+        pt.addEntry(testPrincipal, writePriv, false, restrictions);
+        pt.addEntry(principal2, writePriv, true, restrictions);
+        // entry complementary to the first entry
+        // -> must remove the allow-READ entry and update the deny-WRITE entry.
+        pt.addEntry(testPrincipal, readPriv, false, restrictions);
+
+        AccessControlEntry[] entries = pt.getAccessControlEntries();
+
+        assertEquals(2, entries.length);
+
+        JackrabbitAccessControlEntry first = (JackrabbitAccessControlEntry) entries[0];
+        assertEquals(principal2, first.getPrincipal());
+
+        JackrabbitAccessControlEntry second = (JackrabbitAccessControlEntry) entries[1];
+        assertEquals(testPrincipal, second.getPrincipal());
+        assertFalse(second.isAllow());
+        assertSamePrivileges(new Privilege[] {readPriv[0], writePriv[0]}, second.getPrivileges());
+    }
+
+    public void testTwoEntriesPerPrincipal() throws RepositoryException, NotExecutableException {
+        JackrabbitAccessControlList pt = createEmptyTemplate(getTestPath());
+
+        Privilege[] readPriv = privilegesFromName(Privilege.JCR_READ);
+        Privilege[] writePriv = privilegesFromName(Privilege.JCR_WRITE);
+        Privilege[] acReadPriv = privilegesFromName(Privilege.JCR_READ_ACCESS_CONTROL);
+
+        pt.addEntry(testPrincipal, readPriv, true, restrictions);
+        pt.addEntry(testPrincipal, writePriv, true, restrictions);
+        pt.addEntry(testPrincipal, acReadPriv, true, restrictions);
+
+        pt.addEntry(testPrincipal, readPriv, false, restrictions);
+        pt.addEntry(new PrincipalImpl(testPrincipal.getName()), readPriv, false, restrictions);
+        pt.addEntry(new Principal() {
+            public String getName() {
+                return testPrincipal.getName();
+            }
+        }, readPriv, false, restrictions);
+
+        AccessControlEntry[] entries = pt.getAccessControlEntries();
+        assertEquals(2, entries.length);
+    }
+
+    /**
+     * Test if new entries get appended at the end of the list.
+     *
+     * @throws RepositoryException
+     * @throws NotExecutableException
+     */
+    public void testNewEntriesAppendedAtEnd() throws RepositoryException, NotExecutableException {
+        JackrabbitAccessControlList pt = createEmptyTemplate(getTestPath());
+
+        Privilege[] readPriv = privilegesFromName(Privilege.JCR_READ);
+        Privilege[] writePriv = privilegesFromName(Privilege.JCR_WRITE);
+
+        pt.addEntry(testPrincipal, readPriv, true, restrictions);
+        pt.addEntry(pMgr.getEveryone(), readPriv, true, restrictions);
+        pt.addEntry(testPrincipal, writePriv, false, restrictions);
+
+        AccessControlEntry[] entries = pt.getAccessControlEntries();
+
+        assertEquals(3, entries.length);
+
+        JackrabbitAccessControlEntry last = (JackrabbitAccessControlEntry) entries[2];
+        assertEquals(testPrincipal, last.getPrincipal());
+        assertEquals(false, last.isAllow());
+        assertEquals(writePriv[0], last.getPrivileges()[0]);
+    }
 }
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplateTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplateTest.java?rev=881752&r1=881751&r2=881752&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplateTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplateTest.java Wed Nov 18 12:38:25 2009
@@ -25,6 +25,7 @@
 import javax.jcr.RepositoryException;
 import java.util.Arrays;
 import java.util.List;
+import java.security.Principal;
 
 /**
  * <code>ACLTemplateTest</code>...
@@ -42,6 +43,10 @@
         return new ACLTemplate(testPrincipal, testPath, (SessionImpl) superuser, superuser.getValueFactory());
     }
 
+    protected Principal getSecondPrincipal() throws Exception {
+        return testPrincipal;
+    }
+
     public void testGetRestrictionNames() throws RepositoryException {
         List names = Arrays.asList(createEmptyTemplate(getTestPath()).getRestrictionNames());