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 2010/08/17 16:47:31 UTC

svn commit: r986335 [1/2] - in /jackrabbit/branches/2.0/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/observation/ main/java/org/apache/jackrabbit/core/query/lucene/ main/java/org/apache/jackrabbit/core...

Author: angela
Date: Tue Aug 17 14:47:30 2010
New Revision: 986335

URL: http://svn.apache.org/viewvc?rev=986335&view=rev
Log:
2.0: Merged revision 921394 (JCR-2420), 945528 (JCR-2630), 950440 (1st iteration of JCR-2573)

Added:
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlListener.java
      - copied unchanged from r950440, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlListener.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlModifications.java
      - copied unchanged from r950440, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlModifications.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlObserver.java
      - copied unchanged from r950440, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlObserver.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/CachingEntryCollector.java
      - copied unchanged from r950440, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/CachingEntryCollector.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollector.java
      - copied unchanged from r950440, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollector.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilter.java
      - copied unchanged from r950440, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilter.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilterImpl.java
      - copied unchanged from r950440, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilterImpl.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/EntriesCache.java
      - copied unchanged from r950440, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/EntriesCache.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollectorTest.java
      - copied unchanged from r950440, jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollectorTest.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserAccessControlProviderTest.java
      - copied unchanged from r945528, jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserAccessControlProviderTest.java
Modified:
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/GlobPattern.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleAccessManager.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleLoginModule.java
    jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/AccessManagerTest.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractEntryTest.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/AbstractWriteTest.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/EntryTest.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/TestAll.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/WriteTest.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/combined/WriteTest.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/principalbased/EntryTest.java
    jackrabbit/branches/2.0/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TestAll.java

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java Tue Aug 17 14:47:30 2010
@@ -1741,11 +1741,11 @@ public class BatchedItemOperations exten
             }
             // copy properties
             for (Name propName : srcState.getPropertyNames()) {
-                Path propPath = PathFactoryImpl.getInstance().create(srcPath, propName, true);
-                if (!srcAccessMgr.canRead(propPath)) {
+                Path propPath = PathFactoryImpl.getInstance().create(srcPath, propName, true);                
+                PropertyId propId = new PropertyId(srcState.getNodeId(), propName);
+                if (!srcAccessMgr.canRead(propPath, propId)) {
                     continue;
                 }
-                PropertyId propId = new PropertyId(srcState.getNodeId(), propName);
                 PropertyState srcChildState =
                         (PropertyState) srcStateMgr.getItemState(propId);
 

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java Tue Aug 17 14:47:30 2010
@@ -38,7 +38,7 @@ import org.apache.jackrabbit.core.id.Pro
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.nodetype.EffectiveNodeType;
 import org.apache.jackrabbit.core.nodetype.NodeTypeConflictException;
-import org.apache.jackrabbit.core.security.AccessManager;
+import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.core.state.ChildNodeEntry;
 import org.apache.jackrabbit.core.state.ItemState;
 import org.apache.jackrabbit.core.state.ItemStateException;
@@ -50,6 +50,7 @@ import org.apache.jackrabbit.core.state.
 import org.apache.jackrabbit.core.util.Dumpable;
 import org.apache.jackrabbit.core.version.VersionHistoryImpl;
 import org.apache.jackrabbit.core.version.VersionImpl;
+import org.apache.jackrabbit.core.security.AccessManager;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
@@ -425,26 +426,48 @@ public class ItemManager implements Dump
         if (state == null) {
             throw new InvalidItemStateException(data.getId() + ": the item does not exist anymore");
         }
-        if (state.getStatus() == ItemState.STATUS_NEW &&
-                !data.getDefinition().isProtected()) {
-            // NEW items can always be read as long they have been added
-            // through the API and NOT by the system (i.e. protected props).
-            return true;
+        if (state.getStatus() == ItemState.STATUS_NEW) {
+            if (!data.getDefinition().isProtected()) {
+                /*
+                NEW items can always be read as long they have been added through
+                the API and NOT by the system (i.e. protected items).
+                */
+                return true;
+            } else {
+                /*
+                NEW protected (system) item:
+                need use the path to evaluate the effective permissions.
+                */
+                return (path == null) ?
+                        session.getAccessManager().isGranted(data.getId(), AccessManager.READ) :
+                        session.getAccessManager().isGranted(path, Permission.READ);
+            }
         } else {
-            return (path == null) ?
-                    canRead(data.getId()) :
-                    session.getAccessManager().canRead(path);
+            /* item is not NEW -> save to call acMgr.canRead(Path,ItemId) */
+            return session.getAccessManager().canRead(path, data.getId());
         }
     }
 
     /**
-     * @param id
-     * @return true if the item with the given <code>id</code> can be read;
+     * @param parent The item data of the parent node.
+     * @param childId
+     * @return true if the item with the given <code>childId</code> can be read;
      * <code>false</code> otherwise.
      * @throws RepositoryException
      */
-    private boolean canRead(ItemId id) throws RepositoryException {
-        return session.getAccessManager().isGranted(id, AccessManager.READ);
+    private boolean canRead(ItemData parent, ItemId childId) throws RepositoryException {
+        if (parent.getStatus() == ItemState.STATUS_EXISTING) {
+            /*
+             child item is for sure not NEW (because then the parent was modified).
+             safe to use AccessManager#canRead(Path, ItemId).
+             */
+            return session.getAccessManager().canRead(null, childId);
+        } else {
+            /*
+             child could be NEW -> don't use AccessManager#canRead(Path, ItemId)
+             */
+            return session.getAccessManager().isGranted(childId, AccessManager.READ);
+        }
     }
 
     //--------------------------------------------------< item access methods >
@@ -685,7 +708,7 @@ public class ItemManager implements Dump
         NodeState state = (NodeState) data.getState();
         for (ChildNodeEntry entry : state.getChildNodeEntries()) {
             // make sure any of the properties can be read.
-            if (canRead(entry.getId())) {
+            if (canRead(data, entry.getId())) {
                 return true;
             }
         }
@@ -746,7 +769,7 @@ public class ItemManager implements Dump
         while (iter.hasNext()) {
             Name propName = iter.next();
             // make sure any of the properties can be read.
-            if (canRead(new PropertyId(parentId, propName))) {
+            if (canRead(data, new PropertyId(parentId, propName))) {
                 return true;
             }
         }

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Tue Aug 17 14:47:30 2010
@@ -3000,7 +3000,7 @@ public class NodeImpl extends ItemImpl i
      *
      * @return parent id
      */
-    NodeId getParentId() {
+    public NodeId getParentId() {
         return data.getParentId();
     }
 

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java Tue Aug 17 14:47:30 2010
@@ -48,10 +48,10 @@ class SystemSession extends SessionImpl 
     /**
      * Package private factory method
      *
-     * @param rep
-     * @param wspConfig
-     * @return
-     * @throws RepositoryException
+     * @param repositoryContext The repository context
+     * @param wspConfig The workspace configuration
+     * @return A new instance of <code>SystemSession</code>
+     * @throws RepositoryException If an error occurs
      */
     static SystemSession create(RepositoryImpl rep, WorkspaceConfig wspConfig)
             throws RepositoryException {
@@ -68,7 +68,9 @@ class SystemSession extends SessionImpl 
      * private constructor
      *
      * @param rep
-     * @param wspConfig
+     * @param subject The subject
+     * @param wspConfig The workspace configuration
+     * @throws javax.jcr.RepositoryException If an error occurs.
      */
     private SystemSession(RepositoryImpl rep, Subject subject,
                           WorkspaceConfig wspConfig)
@@ -192,10 +194,9 @@ class SystemSession extends SessionImpl 
         /**
          * Always returns true.
          *
-         * @see AccessManager#canRead(Path)
-         * @param itemPath
+         * @see AccessManager#canRead(org.apache.jackrabbit.spi.Path,org.apache.jackrabbit.core.id.ItemId)
          */
-        public boolean canRead(Path itemPath) throws RepositoryException {
+        public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
             return true;
         }
 

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java Tue Aug 17 14:47:30 2010
@@ -63,6 +63,7 @@ import java.util.Set;
 public class UserPerWorkspaceSecurityManager extends DefaultSecurityManager {
 
     private final Map<String, PrincipalProviderRegistry> ppRegistries = new HashMap<String, PrincipalProviderRegistry>();
+    private final Object monitor = new Object();
 
     /**
      * List of workspace names for which {@link #createSystemUsers} has already
@@ -72,25 +73,27 @@ public class UserPerWorkspaceSecurityMan
 
     private PrincipalProviderRegistry getPrincipalProviderRegistry(SessionImpl s) throws RepositoryException {
         String wspName = s.getWorkspace().getName();
-        PrincipalProviderRegistry p = ppRegistries.get(wspName);
-        if (p == null) {
-            SystemSession systemSession;
-            if (s instanceof SystemSession) {
-                systemSession = (SystemSession) s;
-            } else {
-                RepositoryImpl repo = (RepositoryImpl) getRepository();
-                systemSession = repo.getSystemSession(wspName);
-                // TODO: review again... this workaround is used in several places.
-                repo.onSessionCreated(systemSession);
-            }
+        synchronized (monitor) {
+            PrincipalProviderRegistry p = ppRegistries.get(wspName);
+            if (p == null) {
+                SystemSession systemSession;
+                if (s instanceof SystemSession) {
+                    systemSession = (SystemSession) s;
+                } else {
+                    RepositoryImpl repo = (RepositoryImpl) getRepository();
+                    systemSession = repo.getSystemSession(wspName);
+                    // TODO: review again... this workaround is used in several places.
+                    repo.onSessionCreated(systemSession);
+                }
 
-            PrincipalProvider defaultPP = new DefaultPrincipalProvider(systemSession, (UserManagerImpl) getUserManager(systemSession));
-            defaultPP.init(new Properties());
-            
-            p = new WorkspaceBasedPrincipalProviderRegistry(defaultPP);
-            ppRegistries.put(wspName, p);
+                PrincipalProvider defaultPP = new DefaultPrincipalProvider(systemSession, (UserManagerImpl) getUserManager(systemSession));
+                defaultPP.init(new Properties());
+
+                p = new WorkspaceBasedPrincipalProviderRegistry(defaultPP);
+                ppRegistries.put(wspName, p);
+            }
+            return p;
         }
-        return p;
     }
 
     //------------------------------------------< JackrabbitSecurityManager >---
@@ -110,7 +113,7 @@ public class UserPerWorkspaceSecurityMan
     @Override
     public void dispose(String workspaceName) {
         super.dispose(workspaceName);
-        synchronized (ppRegistries) {
+        synchronized (monitor) {
             PrincipalProviderRegistry reg = ppRegistries.remove(workspaceName);
             if (reg != null) {
                 reg.getDefault().close();
@@ -124,7 +127,7 @@ public class UserPerWorkspaceSecurityMan
     @Override
     public void close() {
         super.close();
-        synchronized (ppRegistries) {
+        synchronized (monitor) {
             for (PrincipalProviderRegistry registry : ppRegistries.values()) {
                 registry.getDefault().close();
             }

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java Tue Aug 17 14:47:30 2010
@@ -301,6 +301,6 @@ class EventConsumer {
      */
     private boolean canRead(EventState eventState) throws RepositoryException {
         Path targetPath = pathFactory.create(eventState.getParentPath(), eventState.getChildRelPath().getName(), eventState.getChildRelPath().getNormalizedIndex(), true);
-        return session.getAccessManager().canRead(targetPath);
+        return session.getAccessManager().canRead(targetPath, null);
     }
 }

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java Tue Aug 17 14:47:30 2010
@@ -371,8 +371,7 @@ public abstract class QueryResultImpl im
             throws RepositoryException {
         for (ScoreNode node : nodes) {
             try {
-                // TODO: rather use AccessManager.canRead(Path)
-                if (node != null && !accessMgr.isGranted(node.getNodeId(), AccessManager.READ)) {
+                if (node != null && !accessMgr.canRead(null, node.getNodeId())) {
                     return false;
                 }
             } catch (ItemNotFoundException e) {

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java Tue Aug 17 14:47:30 2010
@@ -171,13 +171,24 @@ public interface AccessManager {
     boolean isGranted(Path parentPath, Name childName, int permissions) throws RepositoryException;
 
     /**
-     * Determines whether the item at the specified absolute path can be read.
+     * Determines whether the item with the specified <code>itemPath</code>
+     * or <code>itemId</code> can be read. Either of the two parameters
+     * may be <code>null</code>.<br>
+     * Note, that this method should only be called for persisted items as NEW
+     * items may not be visible to the permission evaluation.
+     * For new items {@link #isGranted(Path, int)} should be used instead.<p/>
+     * If this method is called with both Path and ItemId it is left to the
+     * evaluation, which parameter is used.
      *
-     * @param itemPath Path to the item to be tested.s
+     * @param itemPath The path to the item or <code>null</code> if itemId
+     * should be used to determine the READ permission.
+     * @param itemId Id of the item to be tested or <code>null</code> if the
+     * itemPath should be used to determine the permission.
      * @return <code>true</code> if the item can be read; otherwise <code>false</code>.
-     * @throws RepositoryException if an error occurs.
+     * @throws RepositoryException if the item is NEW and only an itemId is
+     * specified or if another error occurs.
      */
-    boolean canRead(Path itemPath) throws RepositoryException;
+    boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException;
 
     /**
      * Determines whether the subject of the current context is granted access

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java Tue Aug 17 14:47:30 2010
@@ -76,21 +76,6 @@ import java.util.Set;
 public class DefaultAccessManager extends AbstractAccessControlManager implements AccessManager {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultAccessManager.class);
-    private static final CompiledPermissions NO_PERMISSION = new CompiledPermissions() {
-        public void close() {
-            //nop
-        }
-        public boolean grants(Path absPath, int permissions) {
-            // deny everything
-            return false;
-        }
-        public int getPrivileges(Path absPath) {
-            return PrivilegeRegistry.NO_PRIVILEGE;
-        }
-        public boolean canReadAll() {
-            return false;
-        }
-    };
 
     private boolean initialized;
 
@@ -161,7 +146,7 @@ public class DefaultAccessManager extend
         } else {
             log.warn("No AccessControlProvider defined -> no access is granted.");
             editor = null;
-            compiledPermissions = NO_PERMISSION;
+            compiledPermissions = CompiledPermissions.NO_PERMISSION;
         }
 
         initialized = true;
@@ -230,6 +215,7 @@ public class DefaultAccessManager extend
             if ((actions & REMOVE) == REMOVE) {
                 perm |= (id.denotesNode()) ? Permission.REMOVE_NODE : Permission.REMOVE_PROPERTY;
             }
+            
             Path path = hierMgr.getPath(id);
             return isGranted(path, perm);
         }
@@ -255,13 +241,14 @@ public class DefaultAccessManager extend
     }
 
     /**
-     * @see AccessManager#canRead(Path)
+     * @see AccessManager#canRead(org.apache.jackrabbit.spi.Path,org.apache.jackrabbit.core.id.ItemId)
      */
-    public boolean canRead(Path itemPath) throws RepositoryException {
+    public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
+        checkInitialized();
         if (compiledPermissions.canReadAll()) {
             return true;
         } else {
-            return isGranted(itemPath, Permission.READ);
+            return compiledPermissions.canRead(itemPath, itemId);
         }
     }
 

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java Tue Aug 17 14:47:30 2010
@@ -121,7 +121,7 @@ public class SimpleJBossAccessManager im
         return internalIsGranted(permissions);
     }
 
-    public boolean canRead(Path itemPath) throws RepositoryException {
+    public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
         return true;
     }
 

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java Tue Aug 17 14:47:30 2010
@@ -76,12 +76,9 @@ public abstract class AbstractACLTemplat
      * 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;
+    protected abstract List<? extends AccessControlEntry> getEntries();
 
     //--------------------------------------< JackrabbitAccessControlPolicy >---
     /**
@@ -101,6 +98,20 @@ public abstract class AbstractACLTemplat
     }
 
     /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#size()
+     */
+    public int size() {
+        return getEntries().size();
+    }
+
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#isEmpty()
+     */
+    public boolean isEmpty() {
+        return getEntries().isEmpty();
+    }
+
+    /**
      *
      * @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.
@@ -131,6 +142,14 @@ public abstract class AbstractACLTemplat
 
     //--------------------------------------------------< AccessControlList >---
     /**
+     * @see javax.jcr.security.AccessControlList#getAccessControlEntries()
+     */
+    public AccessControlEntry[] getAccessControlEntries() throws RepositoryException {       
+        List<? extends AccessControlEntry> l = getEntries();
+        return l.toArray(new AccessControlEntry[l.size()]);
+    }
+
+    /**
      * @see javax.jcr.security.AccessControlList#addAccessControlEntry(java.security.Principal , javax.jcr.security.Privilege[])
      */
     public boolean addAccessControlEntry(Principal principal, Privilege[] privileges)

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java Tue Aug 17 14:47:30 2010
@@ -25,9 +25,14 @@ import javax.jcr.Session;
 import javax.jcr.observation.ObservationManager;
 import javax.jcr.security.Privilege;
 
+import org.apache.jackrabbit.core.ItemImpl;
+import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.id.ItemId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.security.SystemPrincipal;
 import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 
@@ -35,7 +40,7 @@ import org.apache.jackrabbit.spi.commons
  * <code>AbstractAccessControlProvider</code>...
  */
 public abstract class AbstractAccessControlProvider implements AccessControlProvider,
-        AccessControlUtils {
+        AccessControlUtils, AccessControlConstants {
 
     /**
      * Constant for the name of the configuration option "omit-default-permission".
@@ -98,6 +103,9 @@ public abstract class AbstractAccessCont
             public boolean canReadAll() {
                 return true;
             }
+            public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
+                return true;
+            }
         };
     }
 
@@ -131,11 +139,48 @@ public abstract class AbstractAccessCont
             public boolean canReadAll() {
                 return false;
             }
+            public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
+                if (itemPath != null) {
+                    return !isAcItem(itemPath);
+                } else {
+                    return !isAcItem(session.getItemManager().getItem(itemId));
+                }
+            }
         };
     }
 
     //-------------------------------------------------< AccessControlUtils >---
     /**
+     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(Path)
+     */
+    public boolean isAcItem(Path absPath) throws RepositoryException {
+        Path.Element[] elems = absPath.getElements();
+        // start looking for a rep:policy name starting from the last element.
+        // NOTE: with the current content structure max. 3 levels must be looked
+        // at as the rep:policy node may only have ACE nodes with a properties.
+        if (elems.length > 1) {
+            for (int index = elems.length-1, j = 1; index >= 0 && j <= 3; index--, j++) {
+                if (N_POLICY.equals(elems[index].getName())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Test if the given node is itself a rep:ACL or a rep:ACE node.
+     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(org.apache.jackrabbit.core.ItemImpl)
+     */
+    public boolean isAcItem(ItemImpl item) throws RepositoryException {
+        NodeImpl n = ((item.isNode()) ? (NodeImpl) item : (NodeImpl) item.getParent());
+        Name ntName = ((NodeTypeImpl) n.getPrimaryNodeType()).getQName();
+        return ntName.equals(NT_REP_ACL) ||
+                ntName.equals(NT_REP_GRANT_ACE) ||
+                ntName.equals(NT_REP_DENY_ACE);
+    }
+
+    /**
      * @see AccessControlUtils#isAdminOrSystem(Set)
      */
     public boolean isAdminOrSystem(Set<Principal> principals) {

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java Tue Aug 17 14:47:30 2010
@@ -16,12 +16,11 @@
  */
 package org.apache.jackrabbit.core.security.authorization;
 
-import java.util.Map;
-
 import org.apache.commons.collections.map.LRUMap;
 import org.apache.jackrabbit.spi.Path;
 
 import javax.jcr.RepositoryException;
+import java.util.Map;
 
 /**
  * <code>AbstractCompiledPermissions</code>...
@@ -30,6 +29,7 @@ public abstract class AbstractCompiledPe
 
     // cache mapping a Path to a 'Result' containing permissions and privileges.
     private final Map<Path, Result> cache;
+    private final Object monitor = new Object();
 
     @SuppressWarnings("unchecked")
     protected AbstractCompiledPermissions() {
@@ -44,7 +44,7 @@ public abstract class AbstractCompiledPe
      */
     public Result getResult(Path absPath) throws RepositoryException {
         Result result;
-        synchronized (cache) {
+        synchronized (monitor) {
             result = cache.get(absPath);
             if (result == null) {
                 result = buildResult(absPath);
@@ -66,7 +66,7 @@ public abstract class AbstractCompiledPe
      * Removes all entries from the cache.
      */
     protected void clearCache() {
-        synchronized (cache) {
+        synchronized (monitor) {
             cache.clear();
         }
     }
@@ -113,20 +113,13 @@ public abstract class AbstractCompiledPe
         private final int allowPrivileges;
         private final int denyPrivileges;
 
-        private final int hashCode;
+        private int hashCode = -1;
 
         public Result(int allows, int denies, int allowPrivileges, int denyPrivileges) {
             this.allows = allows;
             this.denies = denies;
             this.allowPrivileges = allowPrivileges;
             this.denyPrivileges = denyPrivileges;
-
-            int h = 17;
-            h = 37 * h + allows;
-            h = 37 * h + denies;
-            h = 37 * h + allowPrivileges;
-            h = 37 * h + denyPrivileges;
-            hashCode = h;
         }
 
         public boolean grants(int permissions) {
@@ -148,13 +141,23 @@ public abstract class AbstractCompiledPe
         /**
          * @see Object#hashCode()
          */
+        @Override
         public int hashCode() {
+            if (hashCode == -1) {
+                int h = 17;
+                h = 37 * h + allows;
+                h = 37 * h + denies;
+                h = 37 * h + allowPrivileges;
+                h = 37 * h + denyPrivileges;
+                hashCode = h;
+            }
             return hashCode;
         }
 
         /**
          * @see Object#equals(Object)
          */
+        @Override
         public boolean equals(Object object) {
             if (object == this) {
                 return true;

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java Tue Aug 17 14:47:30 2010
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.core.security.authorization;
 
 import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.core.id.ItemId;
 
 import javax.jcr.RepositoryException;
 
@@ -72,4 +73,48 @@ public interface CompiledPermissions {
      * @throws RepositoryException if an error occurs
      */
     boolean canReadAll() throws RepositoryException;
+
+    /**
+     * Returns <code>true</code> if READ permission is granted for the
+     * <i>existing</i> item with the given <code>Path</code> and/or
+     * <code>ItemId</code>.
+     * This method acts as shortcut for {@link #grants(Path, int)} where
+     * permissions is {@link Permission#READ} and allows to shorten the
+     * evaluation time given the fact that a check for READ permissions is
+     * considered to be the most frequent test.<br>
+     * If both Path and ItemId are not <code>null</code> it is left to the
+     * implementation which parameter to use.n
+     *
+     * @param itemPath The path to the item or <code>null</code> if the ID
+     * should be used to determine the READ permission.
+     * @param itemId The itemId or <code>null</code> if the path should be
+     * used to determine the READ permission.
+     * @return <code>true</code> if the READ permission is granted.
+     * @throws RepositoryException If no item exists with the specified path or
+     * itemId or if some other error occurs.
+     */
+    boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException;
+
+    /**
+     * Static implementation of a <code>CompiledPermissions</code> that doesn't
+     * grant any permissions at all.
+     */
+    public static final CompiledPermissions NO_PERMISSION = new CompiledPermissions() {
+        public void close() {
+            //nop
+        }
+        public boolean grants(Path absPath, int permissions) {
+            // deny everything
+            return false;
+        }
+        public int getPrivileges(Path absPath) {
+            return PrivilegeRegistry.NO_PRIVILEGE;
+        }
+        public boolean canReadAll() {
+            return false;
+        }
+        public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
+            return false;
+        }
+    };
 }

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java Tue Aug 17 14:47:30 2010
@@ -26,6 +26,7 @@ import javax.jcr.Value;
 import javax.jcr.PropertyType;
 
 import java.security.Principal;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Collections;
@@ -48,6 +49,8 @@ public class UnmodifiableAccessControlLi
 
     private final String path;
 
+    private int hashCode = 0;
+
     /**
      * Construct a new <code>UnmodifiableAccessControlList</code>
      *
@@ -78,10 +81,21 @@ public class UnmodifiableAccessControlLi
      *
      * @param accessControlEntries A list of {@link AccessControlEntry access control entries}.
      */
-    public UnmodifiableAccessControlList(List<AccessControlEntry> accessControlEntries) {
+    public UnmodifiableAccessControlList(List<? extends AccessControlEntry> accessControlEntries) {
+        this(accessControlEntries, null, Collections.<String, Integer>emptyMap());
+    }
+
+    /**
+     * Construct a new <code>UnmodifiableAccessControlList</code>
+     *
+     * @param accessControlEntries
+     * @param path
+     * @param restrictions
+     */
+    public UnmodifiableAccessControlList(List<? extends AccessControlEntry> accessControlEntries, String path, Map<String, Integer>restrictions) {
         this.accessControlEntries = accessControlEntries.toArray(new AccessControlEntry[accessControlEntries.size()]);
-        path = null;
-        restrictions = Collections.emptyMap();
+        this.path = path;
+        this.restrictions = restrictions;
     }
 
     //--------------------------------------------------< AccessControlList >---
@@ -110,10 +124,17 @@ public class UnmodifiableAccessControlLi
         throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getApplicablePolicies in order to obtain an modifiable ACL.");
     }
 
+    //----------------------------------------< JackrabbitAccessControlList >---
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#getRestrictionNames()
+     */
     public String[] getRestrictionNames() {
         return restrictions.keySet().toArray(new String[restrictions.size()]);
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#getRestrictionType(String)
+     */
     public int getRestrictionType(String restrictionName) {
         if (restrictions.containsKey(restrictionName)) {
             return restrictions.get(restrictionName);
@@ -122,27 +143,83 @@ public class UnmodifiableAccessControlLi
         }
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#isEmpty()
+     */
     public boolean isEmpty() {
         return accessControlEntries.length == 0;
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#size()
+     */
     public int size() {
         return accessControlEntries.length;
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#addEntry(Principal, Privilege[], boolean)
+     */
     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.");
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#addEntry(Principal, Privilege[], boolean, Map)
+     */
     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.");
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#orderBefore(AccessControlEntry, AccessControlEntry)
+     */
     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.");
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#getPath()
+     */
     public String getPath() {
         return path;
     }
+
+    //-------------------------------------------------------------< Object >---
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        if (hashCode == 0) {
+            int result = 17;
+            result = 37 * result + (path != null ? path.hashCode() : 0);
+            for (AccessControlEntry entry : accessControlEntries) {
+                result = 37 * result + entry.hashCode();
+            }
+            for (String restrictionName : restrictions.keySet()) {
+                result = 37 * (restrictionName + "." + restrictions.get(restrictionName)).hashCode();
+            }
+            hashCode = result;
+        }
+        return hashCode;
+    }
+
+    /**
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj instanceof UnmodifiableAccessControlList) {
+            UnmodifiableAccessControlList acl = (UnmodifiableAccessControlList) obj;
+            return ((path == null) ? acl.path == null : path.equals(acl.path)) &&
+                    Arrays.equals(accessControlEntries, acl.accessControlEntries) &&
+                    restrictions.equals(acl.restrictions);
+        }
+        return false;
+    }
 }
\ No newline at end of file

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java Tue Aug 17 14:47:30 2010
@@ -44,7 +44,6 @@ import javax.jcr.ValueFormatException;
 import javax.jcr.NodeIterator;
 import javax.jcr.security.AccessControlEntry;
 import javax.jcr.security.AccessControlException;
-import javax.jcr.security.AccessControlList;
 import javax.jcr.security.AccessControlPolicy;
 import javax.jcr.security.Privilege;
 import java.security.Principal;
@@ -87,7 +86,7 @@ public class ACLEditor extends Protected
      * @return the control list
      * @throws RepositoryException if an error occurs
      */
-    AccessControlList getACL(NodeImpl aclNode) throws RepositoryException {
+    ACLTemplate getACL(NodeImpl aclNode) throws RepositoryException {
         return new ACLTemplate(aclNode, privilegeRegistry);
     }
 

Modified: jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java?rev=986335&r1=986334&r2=986335&view=diff
==============================================================================
--- jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java (original)
+++ jackrabbit/branches/2.0/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java Tue Aug 17 14:47:30 2010
@@ -16,53 +16,48 @@
  */
 package org.apache.jackrabbit.core.security.authorization.acl;
 
-import java.security.Principal;
-import java.security.acl.Group;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Iterator;
-
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.Value;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventIterator;
-import javax.jcr.security.AccessControlEntry;
-import javax.jcr.security.AccessControlList;
-import javax.jcr.security.AccessControlManager;
-import javax.jcr.security.AccessControlPolicy;
-import javax.jcr.security.Privilege;
-
+import org.apache.commons.collections.map.LRUMap;
 import org.apache.jackrabbit.api.security.principal.PrincipalManager;
 import org.apache.jackrabbit.core.ItemImpl;
+import org.apache.jackrabbit.core.ItemManager;
 import org.apache.jackrabbit.core.NodeImpl;
-import org.apache.jackrabbit.core.PropertyImpl;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.id.ItemId;
 import org.apache.jackrabbit.core.id.NodeId;
-import org.apache.jackrabbit.core.observation.SynchronousEventListener;
+import org.apache.jackrabbit.core.id.PropertyId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.authorization.AccessControlListener;
 import org.apache.jackrabbit.core.security.authorization.AbstractAccessControlProvider;
 import org.apache.jackrabbit.core.security.authorization.AbstractCompiledPermissions;
 import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
 import org.apache.jackrabbit.core.security.authorization.AccessControlEditor;
+import org.apache.jackrabbit.core.security.authorization.AccessControlModifications;
 import org.apache.jackrabbit.core.security.authorization.CompiledPermissions;
 import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
 import org.apache.jackrabbit.core.security.authorization.UnmodifiableAccessControlList;
+import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
-import org.apache.jackrabbit.util.Text;
-import org.apache.commons.collections.iterators.IteratorChain;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.jcr.ItemNotFoundException;
+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.AccessControlPolicy;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * The ACLProvider generates access control policies out of the items stored
  * in the workspace applying the following rules:
@@ -101,28 +96,12 @@ public class ACLProvider extends Abstrac
      */
     private NodeId rootNodeId;
 
-    //-------------------------------------------------< AccessControlUtils >---
     /**
-     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(Path)
+     * Cache to ease the retrieval of ACEs defined for a given node. This cache
+     * is used by the ACLPermissions created individually for each Session
+     * instance.
      */
-    public boolean isAcItem(Path absPath) throws RepositoryException {
-        Path.Element[] elems = absPath.getElements();
-        for (Path.Element elem : elems) {
-            if (N_POLICY.equals(elem.getName())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Test if the given node is itself a rep:ACL or a rep:ACE node.
-     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(ItemImpl)
-     */
-    public boolean isAcItem(ItemImpl item) throws RepositoryException {
-        NodeImpl n = ((item.isNode()) ? (NodeImpl) item : (NodeImpl) item.getParent());
-        return n.isNodeType(NT_REP_ACL) || n.isNodeType(NT_REP_ACE);
-    }
+    private EntryCollector entryCollector;
 
     //----------------------------------------------< AccessControlProvider >---
     /**
@@ -137,11 +116,20 @@ public class ACLProvider extends Abstrac
         NodeImpl root = (NodeImpl) session.getRootNode();
         rootNodeId = root.getNodeId();
         systemEditor = new ACLEditor(systemSession, this);
+
         // TODO: replace by configurable default policy (see JCR-2331)
         boolean initializedWithDefaults = !configuration.containsKey(PARAM_OMIT_DEFAULT_PERMISSIONS);
         if (initializedWithDefaults && !isAccessControlled(root)) {
             initRootACL(session, systemEditor);
         }
+
+        entryCollector = createEntryCollector((SessionImpl) systemSession);
+    }
+
+    @Override
+    public void close() {
+        super.close();        
+        entryCollector.close();
     }
 
     /**
@@ -152,7 +140,7 @@ public class ACLProvider extends Abstrac
         checkInitialized();
 
         NodeImpl targetNode = (NodeImpl) session.getNode(session.getJCRPath(absPath));
-        NodeImpl node = getNode(targetNode);
+        NodeImpl node = getNode(targetNode, isAcItem(targetNode));
         List<AccessControlList> acls = new ArrayList<AccessControlList>();
 
         // collect all ACLs effective at node
@@ -198,22 +186,37 @@ public class ACLProvider extends Abstrac
             return true;
         } else {
             CompiledPermissions cp = new AclPermissions(principals, false);
-            return cp.grants(PathFactoryImpl.getInstance().getRootPath(), Permission.READ);
+            return cp.canRead(null, rootNodeId);
         }
     }
 
     //----------------------------------------------------------< protected >---
     /**
+     * Create the <code>EntryCollector</code> instance that is used by this
+     * provider to gather the effective ACEs for a given list of principals at a
+     * given node during AC evaluation.
+     *
+     * @param systemSession The system session to create the entry collector for.
+     * @return A new instance of <code>CachingEntryCollector</code>.
+     * @throws RepositoryException If an error occurs.
+     * @see #retrieveResultEntries(NodeImpl, EntryFilter)
+     */
+    protected EntryCollector createEntryCollector(SessionImpl systemSession) throws RepositoryException {
+        return new CachingEntryCollector(systemSession, systemEditor, rootNodeId);
+    }
+
+    /**
      * Retrieve an iterator of <code>AccessControlEntry</code> to be evaluated
      * upon {@link AbstractCompiledPermissions#buildResult}.
      *
      * @param node Target node.
-     * @param principalNames List of principal names.
+     * @param filter The entry filter used to collect the access control entries.
      * @return an iterator of <code>AccessControlEntry</code>.
      * @throws RepositoryException If an error occurs.
      */
-    protected Iterator<AccessControlEntry> retrieveResultEntries(NodeImpl node, List<String> principalNames) throws RepositoryException {
-        return new Entries(node, principalNames).iterator();
+    protected Iterator<AccessControlEntry> retrieveResultEntries(NodeImpl node, EntryFilter filter) throws RepositoryException {
+        Iterator<AccessControlEntry> itr = entryCollector.collectEntries(node, filter).iterator();
+        return itr;
     }
 
     //------------------------------------------------------------< private >---
@@ -223,13 +226,17 @@ public class ACLProvider extends Abstrac
      * searched and returned.
      *
      * @param targetNode The node for which AC information needs to be retrieved.
-     * @return the node
+     * @param isAcItem true if the specified target node defines access control
+     * content; false otherwise.
+     * @return the given <code>targetNode</code> or the nearest non-ac-parent
+     * in case the <code>targetNode</code> itself defines access control content.
      * @throws RepositoryException if an error occurs
      */
-    private NodeImpl getNode(NodeImpl targetNode) throws RepositoryException {
+    private NodeImpl getNode(NodeImpl targetNode, boolean isAcItem) throws RepositoryException {
         NodeImpl node;
-        if (isAcItem(targetNode)) {
-            if (targetNode.isNodeType(NT_REP_ACL)) {
+        if (isAcItem) {
+            Name ntName = ((NodeTypeImpl) targetNode.getPrimaryNodeType()).getQName();
+            if (ntName.equals(NT_REP_ACL)) {
                 node = (NodeImpl) targetNode.getParent();
             } else {
                 node = (NodeImpl) targetNode.getParent().getParent();
@@ -252,10 +259,8 @@ public class ACLProvider extends Abstrac
         // if the given node is access-controlled, construct a new ACL and add
         // it to the list
         if (isAccessControlled(node)) {
-            // build acl for the access controlled node
-            NodeImpl aclNode = node.getNode(N_POLICY);
-            AccessControlList acl = systemEditor.getACL(aclNode);
-            acls.add(new UnmodifiableAccessControlList(acl));
+            // retrieve the entries for the access controlled node
+            acls.add(new UnmodifiableAccessControlList(entryCollector.getEntries(node), node.getPath(), Collections.<String, Integer>emptyMap()));
         }
         // then, recursively look for access controlled parents up the hierarchy.
         if (!rootNodeId.equals(node.getId())) {
@@ -320,22 +325,24 @@ public class ACLProvider extends Abstrac
      * and if it has a child node named
      * {@link AccessControlConstants#N_POLICY "rep:ACL"}.
      *
-     * @param node hte node
-     * @return <code>true</code> if the node is access controlled;
-     *         <code>false</code> otherwise.
+     * @param node the node to be tested
+     * @return <code>true</code> if the node is access controlled and has a
+     * rep:policy child; <code>false</code> otherwise.
      * @throws RepositoryException if an error occurs
      */
     static boolean isAccessControlled(NodeImpl node) throws RepositoryException {
-        return node.isNodeType(NT_REP_ACCESS_CONTROLLABLE) && node.hasNode(N_POLICY);
+        return node.hasNode(N_POLICY) && node.isNodeType(NT_REP_ACCESS_CONTROLLABLE);
     }
 
     //------------------------------------------------< CompiledPermissions >---
     /**
      *
      */
-    private class AclPermissions extends AbstractCompiledPermissions implements SynchronousEventListener {
+    private class AclPermissions extends AbstractCompiledPermissions implements AccessControlListener {
 
         private final List<String> principalNames;
+        private final Map<NodeId, Boolean> readCache = new LRUMap(1000);
+        private final Object monitor = new Object();
 
         private AclPermissions(Set<Principal> principals) throws RepositoryException {
             this(principals, true);
@@ -350,66 +357,17 @@ public class ACLProvider extends Abstrac
             if (listenToEvents) {
                 /*
                  Make sure this AclPermission recalculates the permissions if
-                 any ACL concerning it is modified. interesting events are:
-                 - new ACE-entry for any of the principals (NODE_ADDED)
-                 - changing ACE-entry for any of the principals (PROPERTY_CHANGED)
-                   > new permissions granted/denied
-                   >
-                 - removed ACE-entry for any of the principals (NODE_REMOVED)
-                */
-                int events = Event.PROPERTY_CHANGED | Event.NODE_ADDED | Event.NODE_REMOVED;
-                String[] ntNames = new String[] {
-                        resolver.getJCRName(NT_REP_ACE),
-                        resolver.getJCRName(NT_REP_ACL)
-                };
-                observationMgr.addEventListener(this, events, session.getRootNode().getPath(), true, null, ntNames, true);
+                 any ACL concerning it is modified.
+                 */
+                 entryCollector.addListener(this);
             }
         }
 
-        //------------------------------------< AbstractCompiledPermissions >---
-        /**
-         * @see AbstractCompiledPermissions#buildResult(Path)
-         */
-        @Override
-        protected Result buildResult(Path absPath) throws RepositoryException {
-            boolean existingNode = false;
-            NodeImpl node = null;
-            String jcrPath = resolver.getJCRPath(absPath);
-
-            if (session.nodeExists(jcrPath)) {
-                node = (NodeImpl) session.getNode(jcrPath);
-                existingNode = true;
-            } else {
-                // path points to existing prop or non-existing item (node or prop).
-                // -> find the nearest persisted node
-                String parentPath = Text.getRelativeParent(jcrPath, 1);
-                while (parentPath.length() > 0) {
-                    if (session.nodeExists(parentPath)) {
-                        node = (NodeImpl) session.getNode(parentPath);
-                        break;
-                    }
-                    parentPath = Text.getRelativeParent(parentPath, 1);
-                }
-            }
-
-            if (node == null) {
-                // should never get here
-                throw new ItemNotFoundException("Item out of hierarchy.");
-            }
-
-            boolean isAcItem = isAcItem(absPath);
-
+        private Result buildResult(NodeImpl node, boolean existingNode, boolean isAcItem, EntryFilter filter) throws RepositoryException {
             // retrieve all ACEs at path or at the direct ancestor of path that
             // apply for the principal names.
-            Iterator<AccessControlEntry> entries = retrieveResultEntries(getNode(node), principalNames);
-            // build a list of ACEs that are defined locally at the node
-            List<AccessControlEntry> localACEs;
-            if (existingNode && isAccessControlled(node)) {
-                NodeImpl aclNode = node.getNode(N_POLICY);
-                localACEs = Arrays.asList(systemEditor.getACL(aclNode).getAccessControlEntries());
-            } else {
-                localACEs = Collections.emptyList();
-            }
+            Iterator<AccessControlEntry> entries = retrieveResultEntries(getNode(node, isAcItem), filter);
+
             /*
              Calculate privileges and permissions:
              Since the ACEs only define privileges on a node and do not allow
@@ -426,12 +384,14 @@ public class ACLProvider extends Abstrac
 
             while (entries.hasNext()) {
                 ACLTemplate.Entry ace = (ACLTemplate.Entry) entries.next();
-                // Determine if the ACE is defined on the node at absPath (locally):
-                // Except for READ-privileges the permissions must be determined
-                // from privileges defined for the parent. Consequently aces
-                // defined locally must be treated different than inherited entries.
+                /*
+                 Determine if the ACE is defined on the node at absPath (locally):
+                 Except for READ-privileges the permissions must be determined
+                 from privileges defined for the parent. Consequently aces
+                 defined locally must be treated different than inherited entries.
+                 */
                 int entryBits = ace.getPrivilegeBits();
-                boolean isLocal = localACEs.contains(ace);
+                boolean isLocal = existingNode && ace.isLocal(node.getNodeId());
                 if (!isLocal) {
                     if (ace.isAllow()) {
                         parentAllows |= Permission.diff(entryBits, parentDenies);
@@ -452,184 +412,115 @@ public class ACLProvider extends Abstrac
             return new Result(allows, denies, allowPrivileges, denyPrivileges);
         }
 
-        //--------------------------------------------< CompiledPermissions >---
+        //------------------------------------< AbstractCompiledPermissions >---
         /**
-         * @see CompiledPermissions#close()
+         * @see AbstractCompiledPermissions#buildResult(Path)
          */
-        public void close() {
+        @Override
+        protected Result buildResult(Path absPath) throws RepositoryException {
+            boolean existingNode = false;
+            NodeImpl node;
+
+            ItemManager itemMgr = session.getItemManager();
             try {
-                observationMgr.removeEventListener(this);
+                ItemImpl item = itemMgr.getItem(absPath);
+                if (item.isNode()) {
+                    node = (NodeImpl) item;
+                    existingNode = true;
+                } else {
+                    node = (NodeImpl) item.getParent();
+                }
             } catch (RepositoryException e) {
-                log.debug("Unable to unregister listener: ", e.getMessage());
-            }
-            super.close();
-        }
-
-        //--------------------------------------------------< EventListener >---
-        /**
-         * @see javax.jcr.observation.EventListener#onEvent(EventIterator)
-         */
-        public synchronized void onEvent(EventIterator events) {
-            // only invalidate cache if any of the events affects the
-            // nodes defining permissions for principals compiled here.
-            boolean clearCache = false;
-            while (events.hasNext() && !clearCache) {
-                try {
-                    Event ev = events.nextEvent();
-                    String path = ev.getPath();
-                    switch (ev.getType()) {
-                        case Event.NODE_ADDED:
-                            // test if the new node is an ACE node that affects
-                            // the permission of any of the principals listed in
-                            // principalNames.
-                            NodeImpl n = (NodeImpl) session.getNode(path);
-                            if (n.isNodeType(NT_REP_ACE) &&
-                                    principalNames.contains(n.getProperty(P_PRINCIPAL_NAME).getString())) {
-                                clearCache = true;
-                            }
-                            break;
-                        case Event.PROPERTY_REMOVED:
-                        case Event.NODE_REMOVED:
-                            // can't find out if the removed ACL/ACE node was
-                            // relevant for the principals
-                            clearCache = true;
-                            break;
-                        case Event.PROPERTY_ADDED:
-                        case Event.PROPERTY_CHANGED:
-                            // test if the added/changed prop belongs to an ACe
-                            // node and affects the permission of any of the
-                            // principals listed in principalNames.
-                            PropertyImpl p = (PropertyImpl) session.getProperty(path);
-                            NodeImpl parent = (NodeImpl) p.getParent();
-                            if (parent.isNodeType(NT_REP_ACE)) {
-                                String principalName = null;
-                                if (P_PRIVILEGES.equals(p.getQName())) {
-                                    // test if principal-name sibling-prop matches
-                                    principalName = parent.getProperty(P_PRINCIPAL_NAME).getString();
-                                } else if (P_PRINCIPAL_NAME.equals(p.getQName())) {
-                                    // a new ace or an ace change its principal-name.
-                                    principalName = p.getString();
-                                }
-                                if (principalName != null &&
-                                        principalNames.contains(principalName)) {
-                                    clearCache = true;
-                                }
-                            }
-                            break;
-                        case Event.NODE_MOVED:
-                            // protected ac nodes cannot be moved around
-                            // -> nothing to do TODO check again
-                            break;
-                        default:
-                            // illegal event-type: should never occur. ignore
+                // path points to a non-persisted item.
+                // -> find the nearest persisted node starting from the root.
+                Path.Element[] elems = absPath.getElements();
+                NodeImpl parent = (NodeImpl) session.getRootNode();
+                for (int i = 1; i < elems.length - 1; i++) {
+                    Name name = elems[i].getName();
+                    int index = elems[i].getIndex();
+                    if (!parent.hasNode(name, index)) {
+                        // last persisted node reached
+                        break;
                     }
-                } catch (RepositoryException e) {
-                    // should not get here
-                    log.warn("Internal error: ", e.getMessage());
+                    parent = parent.getNode(name, index);
+
                 }
+                node = parent;
             }
-            if (clearCache) {
-                clearCache();
+
+            if (node == null) {
+                // should never get here
+                throw new ItemNotFoundException("Item out of hierarchy.");
             }
-        }
-    }
 
-    //--------------------------------------------------------------------------
-    /**
-     * Inner class used to collect ACEs for a given set of principals throughout
-     * the node hierarchy.
-     */
-    private class Entries {
+            boolean isAcItem = isAcItem(absPath);
+            return buildResult(node, existingNode, isAcItem, new EntryFilterImpl(principalNames));
+        }
 
-        private final Collection<String> principalNames;
-        private final List<AccessControlEntry> userAces = new ArrayList();
-        private final List<AccessControlEntry> groupAces = new ArrayList();
-
-        private Entries(NodeImpl node, Collection<String> principalNames) throws RepositoryException {
-            this.principalNames = principalNames;
-            collectEntries(node);
-        }
-
-        private void collectEntries(NodeImpl node) throws RepositoryException {
-            // if the given node is access-controlled, construct a new ACL and add
-            // it to the list
-            if (isAccessControlled(node)) {
-                // build acl for the access controlled node
-                NodeImpl aclNode = node.getNode(N_POLICY);
-                //collectEntries(aclNode, principalNamesToEntries);
-                collectEntriesFromAcl(aclNode);
-            }
-            // recursively look for access controlled parents up the hierarchy.
-            if (!rootNodeId.equals(node.getId())) {
-                NodeImpl parentNode = (NodeImpl) node.getParent();
-                collectEntries(parentNode);
+        /**
+         * @see AbstractCompiledPermissions#clearCache()
+         */
+        @Override
+        protected void clearCache() {
+            synchronized (monitor) {
+                readCache.clear();
             }
+            super.clearCache();
         }
 
+        //--------------------------------------------< CompiledPermissions >---
         /**
-         * Separately collect the entries defined for the user and group
-         * principals.
-         *
-         * @param aclNode acl node
-         * @throws RepositoryException if an error occurs
+         * @see CompiledPermissions#close()
          */
-        private void collectEntriesFromAcl(NodeImpl aclNode) throws RepositoryException {
-            SessionImpl sImpl = (SessionImpl) aclNode.getSession();
-            PrincipalManager principalMgr = sImpl.getPrincipalManager();
-            AccessControlManager acMgr = sImpl.getAccessControlManager();
-
-            // first collect aces present on the given aclNode.
-            List<AccessControlEntry> gaces = new ArrayList<AccessControlEntry>();
-            List<AccessControlEntry> uaces = new ArrayList<AccessControlEntry>();
-
-            NodeIterator itr = aclNode.getNodes();
-            while (itr.hasNext()) {
-                NodeImpl aceNode = (NodeImpl) itr.nextNode();
-                String principalName = aceNode.getProperty(AccessControlConstants.P_PRINCIPAL_NAME).getString();
-                // only process aceNode if 'principalName' is contained in the given set
-                if (principalNames.contains(principalName)) {
-                    Principal princ = principalMgr.getPrincipal(principalName);
-                    if (princ == null) {
-                        log.warn("Principal with name " + principalName + " unknown to PrincipalManager -> Ignored from AC evaluation.");
-                        continue;
-                    }
+        @Override
+        public void close() {
+            entryCollector.removeListener(this);
+            super.close();
+        }
 
-                    Value[] privValues = aceNode.getProperty(AccessControlConstants.P_PRIVILEGES).getValues();
-                    Privilege[] privs = new Privilege[privValues.length];
-                    for (int i = 0; i < privValues.length; i++) {
-                        privs[i] = acMgr.privilegeFromName(privValues[i].getString());
-                    }
-                    // create a new ACEImpl (omitting validation check)
-                    AccessControlEntry ace = new ACLTemplate.Entry(
-                            princ,
-                            privs,
-                            aceNode.isNodeType(AccessControlConstants.NT_REP_GRANT_ACE),
-                            sImpl.getValueFactory());
-                    // add it to the proper list (e.g. separated by principals)
-                    /**
-                     * NOTE: access control entries must be collected in reverse
-                     * order in order to assert proper evaluation.
-                     */
-                    if (princ instanceof Group) {
-                        gaces.add(0, ace);
-                    } else {
-                        uaces.add(0, ace);
-                    }
-                }
+        /**
+         * @see CompiledPermissions#canRead(Path, ItemId)
+         */
+        public boolean canRead(Path path, ItemId itemId) throws RepositoryException {
+            ItemId id = (itemId == null) ? session.getHierarchyManager().resolvePath(path) : itemId;
+            /* currently READ access cannot be denied to individual properties.
+               if the parent node is readable the properties are as well.
+               this simplifies the canRead test as well as the caching.
+             */
+            boolean existingNode = false;
+            NodeId nodeId;
+            if (id.denotesNode()) {
+                nodeId = (NodeId) id;
+                // since method may only be called for existing nodes the
+                // flag be set to true if the id identifies a node.
+                existingNode = true;
+            } else {
+                nodeId = ((PropertyId) id).getParentId();
             }
 
-            // add the lists of aces to the overall lists that contain the entries
-            // throughout the hierarchy.
-            if (!gaces.isEmpty()) {
-                groupAces.addAll(gaces);
-            }
-            if (!uaces.isEmpty()) {
-                userAces.addAll(uaces);
+            boolean canRead;
+            synchronized (monitor) {
+                if (readCache.containsKey(nodeId)) {
+                    canRead = readCache.get(nodeId);
+                } else {
+                    ItemManager itemMgr = session.getItemManager();
+                    NodeImpl node = (NodeImpl) itemMgr.getItem(nodeId);
+                    Result result = buildResult(node, existingNode, isAcItem(node), new EntryFilterImpl(principalNames));
+
+                    canRead = result.grants(Permission.READ);
+                    readCache.put(nodeId, canRead);
+                }
             }
+            return canRead;
         }
 
-        private Iterator<AccessControlEntry> iterator() {
-            return new IteratorChain(userAces.iterator(), groupAces.iterator());
+        //----------------------------------------< ACLModificationListener >---
+        /**
+         * @see org.apache.jackrabbit.core.security.authorization.AccessControlListener#acModified(AccessControlModifications)
+         */
+        public void acModified(AccessControlModifications modifications) {
+            // ignore the details of the modifications and clear all caches.
+            clearCache();
         }
     }
 }