You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2013/02/18 12:19:01 UTC

svn commit: r1447202 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization: PermissionHook.java PermissionProviderImpl.java permission/CompiledPermissionImpl.java

Author: angela
Date: Mon Feb 18 11:19:00 2013
New Revision: 1447202

URL: http://svn.apache.org/r1447202
Log:
OAK-527: permissions (wip)

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionHook.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionProviderImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionHook.java?rev=1447202&r1=1447201&r2=1447202&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionHook.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionHook.java Mon Feb 18 11:19:00 2013
@@ -16,27 +16,39 @@
  */
 package org.apache.jackrabbit.oak.security.authorization;
 
+import java.util.Collections;
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import javax.jcr.RepositoryException;
 
+import com.google.common.collect.Lists;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.core.ReadOnlyRoot;
 import org.apache.jackrabbit.oak.core.ReadOnlyTree;
 import org.apache.jackrabbit.oak.core.TreeImpl;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryPropertyBuilder;
 import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.security.privilege.PrivilegeBits;
+import org.apache.jackrabbit.oak.security.privilege.PrivilegeDefinitionStore;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+import org.apache.jackrabbit.oak.spi.state.PropertyBuilder;
+import org.apache.jackrabbit.oak.util.TreeUtil;
 import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * {@code CommitHook} implementation that processes any modification made to
  * access control content and updates persisted permission caches associated
@@ -55,15 +67,17 @@ public class PermissionHook implements C
     @Nonnull
     @Override
     public NodeState processCommit(final NodeState before, NodeState after) throws CommitFailedException {
-        NodeBuilder rootBuilder = after.builder();
+        NodeBuilder rootAfter = after.builder();
 
-        NodeBuilder permissionRoot = getPermissionRoot(rootBuilder, workspaceName);
+        NodeBuilder permissionRoot = getPermissionRoot(rootAfter, workspaceName);
         ReadOnlyNodeTypeManager ntMgr = ReadOnlyNodeTypeManager.getInstance(before);
+        PrivilegeDefinitionStore privilegeStore = new PrivilegeDefinitionStore(new ReadOnlyRoot(before));
 
-        after.compareAgainstBaseState(before, new Diff(new Node(rootBuilder), permissionRoot, ntMgr));
-        return rootBuilder.getNodeState();
+        after.compareAgainstBaseState(before, new Diff(new BeforeNode(before), new Node(rootAfter), permissionRoot, privilegeStore, ntMgr));
+        return rootAfter.getNodeState();
     }
 
+    @Nonnull
     private NodeBuilder getPermissionRoot(NodeBuilder rootBuilder, String workspaceName) {
         NodeBuilder store = rootBuilder.child(NodeTypeConstants.JCR_SYSTEM).child(REP_PERMISSION_STORE);
         NodeBuilder permissionRoot;
@@ -76,16 +90,39 @@ public class PermissionHook implements C
         return permissionRoot;
     }
 
+    private static Tree getTree(String name, NodeState nodeState) {
+        // FIXME: this readonlytree is not properly connect to it's parent
+        return new ReadOnlyTree(null, name, nodeState);
+    }
+
+    private static int getAceIndex(BaseNode aclNode, String aceName) {
+        PropertyState ordering = checkNotNull(aclNode.getNodeState().getProperty(TreeImpl.OAK_CHILD_ORDER));
+        return Lists.newArrayList(ordering.getValue(Type.STRINGS)).indexOf(aceName);
+    }
+
+    private static String generateName(NodeBuilder principalRoot, Entry entry) {
+        StringBuilder name = new StringBuilder();
+        name.append((entry.isAllow) ? 'a' : 'd').append('-').append(principalRoot.getChildNodeCount());
+        return name.toString();
+    }
+
     private static class Diff implements NodeStateDiff {
 
-        private final ReadOnlyNodeTypeManager ntMgr;
-        private final NodeBuilder permissionRoot;
+        private final BeforeNode parentBefore;
         private final Node parentAfter;
+        private final NodeBuilder permissionRoot;
+        private final PrivilegeDefinitionStore privilegeStore;
+        private final ReadOnlyNodeTypeManager ntMgr;
 
-        private Diff(@Nonnull Node node, NodeBuilder permissionRoot, ReadOnlyNodeTypeManager ntMgr) {
-            this.ntMgr = ntMgr;
+        private Diff(@Nonnull BeforeNode parentBefore, @Nonnull Node parentAfter,
+                     @Nonnull NodeBuilder permissionRoot,
+                     @Nonnull PrivilegeDefinitionStore privilegeStore,
+                     @Nonnull ReadOnlyNodeTypeManager ntMgr) {
+            this.parentBefore = parentBefore;
+            this.parentAfter = parentAfter;
             this.permissionRoot = permissionRoot;
-            this.parentAfter = node;
+            this.privilegeStore = privilegeStore;
+            this.ntMgr = ntMgr;
         }
 
         @Override
@@ -96,7 +133,7 @@ public class PermissionHook implements C
         @Override
         public void propertyChanged(PropertyState before, PropertyState after) {
             if (isACL(parentAfter) && TreeImpl.OAK_CHILD_ORDER.equals(before.getName())) {
-                updateEntries();
+                // TODO: update if order has changed without child-node modifications
             }
         }
 
@@ -110,19 +147,22 @@ public class PermissionHook implements C
             if (isACE(name, after)) {
                 addEntry(name, after);
             } else {
-                NodeState before = MemoryNodeState.EMPTY_NODE;
+                BeforeNode before = new BeforeNode(parentBefore.getPath(), name, MemoryNodeState.EMPTY_NODE);
                 Node node = new Node(parentAfter, name);
-                after.compareAgainstBaseState(before, new Diff(node, permissionRoot, ntMgr));
+                after.compareAgainstBaseState(before.getNodeState(), new Diff(before, node, permissionRoot, privilegeStore, ntMgr));
             }
         }
 
         @Override
-        public void childNodeChanged(String name, NodeState before, NodeState after) {
+        public void childNodeChanged(String name, final NodeState before, NodeState after) {
             if (isACE(name, before) || isACE(name, after)) {
                 updateEntry(name, before, after);
+            } else if (REP_RESTRICTIONS.equals(name)) {
+                updateEntry(parentAfter.getName(), parentBefore.getNodeState(), parentAfter.getNodeState());
             } else {
-                Node node = new Node(parentAfter, name);
-                after.compareAgainstBaseState(before, new Diff(node, permissionRoot, ntMgr));
+                BeforeNode nodeBefore = new BeforeNode(parentBefore.getPath(), name, before);
+                Node nodeAfter = new Node(parentAfter, name);
+                after.compareAgainstBaseState(before, new Diff(nodeBefore, nodeAfter, permissionRoot, privilegeStore, ntMgr));
             }
         }
 
@@ -131,8 +171,9 @@ public class PermissionHook implements C
             if (isACE(name, before)) {
                 removeEntry(name, before);
             } else {
-                Node after = new Node(parentAfter.path, name);
-                after.builder.getNodeState().compareAgainstBaseState(before, new Diff(after, permissionRoot, ntMgr));
+                BeforeNode nodeBefore = new BeforeNode(parentBefore.getPath(), name, before);
+                Node after = new Node(parentAfter.getPath(), name, MemoryNodeState.EMPTY_NODE);
+                after.getNodeState().compareAgainstBaseState(before, new Diff(nodeBefore, after, permissionRoot, privilegeStore, ntMgr));
             }
         }
 
@@ -153,67 +194,206 @@ public class PermissionHook implements C
             }
         }
 
-        private static String getAccessControlledPath(Node aclNode) {
-            return Text.getRelativeParent(aclNode.path, 1);
+        private static String getAccessControlledPath(BaseNode aclNode) {
+            if (REP_REPO_POLICY.equals(aclNode.getName())) {
+                return "";
+            } else {
+                return Text.getRelativeParent(aclNode.getPath(), 1);
+            }
         }
 
-        private static Tree getTree(String name, NodeState nodeState) {
-            // FIXME: this readonlytree is not properly connect to it's parent
-            return new ReadOnlyTree(null, name, nodeState);
+        private void addEntry(String name, NodeState ace) {
+            Entry entry = createEntry(name, ace, parentAfter);
+            entry.writeTo(permissionRoot.child(entry.principalName));
         }
 
-        private void addEntry(String name, NodeState after) {
-            String accessControlledPath = getAccessControlledPath(parentAfter);
-            // TODO
-            //log.info("add entry:" + name);
+        private void removeEntry(String name, NodeState ace) {
+            Entry entry = createEntry(name, ace, parentBefore);
+            String permissionName = getPermissionNodeName(entry);
+            if (permissionName != null) {
+                permissionRoot.child(entry.principalName).removeNode(permissionName);
+            }
         }
 
-        private void removeEntry(String name, NodeState after) {
-            String accessControlledPath = getAccessControlledPath(parentAfter);
-            // TODO
-            //log.info("remove entry" + name);
+        private void updateEntry(String name, NodeState before, NodeState after) {
+            removeEntry(name, before);
+            addEntry(name, after);
+        }
+
+        @CheckForNull
+        private String getPermissionNodeName(Entry aceEntry) {
+            if (permissionRoot.hasChildNode(aceEntry.principalName)) {
+                NodeBuilder principalRoot = permissionRoot.child(aceEntry.principalName);
+                for (String childName : principalRoot.getChildNodeNames()) {
+                    NodeState state = principalRoot.child(childName).getNodeState();
+                    if (aceEntry.isSame(childName, state)) {
+                        return childName;
+                    }
+                }
+                log.warn("No entry node for " + aceEntry);
+            } else {
+                // inconsistency: removing an ACE that doesn't have a corresponding
+                // entry in the permission store.
+                log.warn("Missing permission node for principal " + aceEntry.principalName);
+            }
+            return null;
         }
 
-        private void updateEntry(String name, NodeState after, NodeState before) {
-            String accessControlledPath = getAccessControlledPath(parentAfter);
-            // TODO
-            //log.info("update"+ name);
-        }
+        @Nonnull
+        private Entry createEntry(String name, NodeState ace, BaseNode acl) {
+            Tree aceTree = getTree(name, ace);
+            String principalName = checkNotNull(TreeUtil.getString(aceTree, REP_PRINCIPAL_NAME));
+            PrivilegeBits privilegeBits = privilegeStore.getBits(TreeUtil.getString(aceTree, REP_PRIVILEGES));
+            boolean isAllow = NT_REP_GRANT_ACE.equals(TreeUtil.getPrimaryTypeName(aceTree));
+            // TODO: respect restrictions
 
-        private void updateEntries() {
-            String accessControlledPath = getAccessControlledPath(parentAfter);
-            NodeState aclState = parentAfter.getNodeState();
+            String accessControlledPath = getAccessControlledPath(acl);
+            int index = getAceIndex(acl, name);
 
-            // TODO
+            return new Entry(accessControlledPath, index, principalName, privilegeBits, isAllow);
         }
     }
 
-    private static final class Node {
+    private static abstract class BaseNode {
 
         private final String path;
+
+        private BaseNode(String path) {
+            this.path = path;
+        }
+
+        private BaseNode(String parentPath, String name) {
+            this.path = PathUtils.concat(parentPath, new String[]{name});
+        }
+
+        String getName() {
+            return Text.getName(path);
+        }
+
+        String getPath() {
+            return path;
+        }
+
+        abstract NodeState getNodeState();
+    }
+
+    private static class BeforeNode extends BaseNode {
+
+        private final NodeState nodeState;
+
+        BeforeNode(NodeState root) {
+            super("/");
+            this.nodeState = root;
+        }
+
+
+        BeforeNode(String parentPath, String name, NodeState nodeState) {
+            super(parentPath, name);
+            this.nodeState = nodeState;
+        }
+
+        @Override
+        NodeState getNodeState() {
+            return nodeState;
+        }
+    }
+
+    private static class Node extends BaseNode {
+
         private final NodeBuilder builder;
 
         private Node(NodeBuilder rootBuilder) {
-            this.path = "/";
+            super("/");
             this.builder = rootBuilder;
         }
 
-        private Node(String parentPath, String name) {
-            this.path = PathUtils.concat(parentPath, name);
-            this.builder = MemoryNodeState.EMPTY_NODE.builder();
+        private Node(String parentPath, String name, NodeState state) {
+            super(parentPath, name);
+            this.builder = state.builder();
         }
 
         private Node(Node parent, String name) {
+            super(parent.getPath(), name);
             this.builder = parent.builder.child(name);
-            this.path = PathUtils.concat(parent.path, name);
         }
 
-        private String getName() {
-            return Text.getName(path);
+        NodeState getNodeState() {
+            return builder.getNodeState();
         }
+    }
 
-        private NodeState getNodeState() {
-            return builder.getNodeState();
+    private static final class Entry {
+
+        private final String accessControlledPath;
+        private final int index;
+
+        private final String principalName;
+        private final PrivilegeBits privilegeBits;
+        private final boolean isAllow;
+
+        private Entry(@Nonnull String accessControlledPath,
+                      int index,
+                      @Nonnull String principalName,
+                      @Nonnull PrivilegeBits privilegeBits,
+                      boolean isAllow) {
+            this.accessControlledPath = accessControlledPath;
+            this.index = index;
+
+            this.principalName = principalName;
+            this.privilegeBits = privilegeBits;
+            this.isAllow = isAllow;
+        }
+
+        private void writeTo(NodeBuilder principalRoot) {
+            String entryName = generateName(principalRoot, this);
+            principalRoot.child(entryName)
+                    .setProperty("rep:accessControlledPath", accessControlledPath)
+                    .setProperty("rep:index", index)
+                    .setProperty(privilegeBits.asPropertyState("rep:privileges"));
+            // TODO: append restrictions
+
+            PropertyState ordering = principalRoot.getProperty(TreeImpl.OAK_CHILD_ORDER);
+            if (ordering == null) {
+                principalRoot.setProperty(TreeImpl.OAK_CHILD_ORDER, Collections.singleton(entryName), Type.NAMES);
+            } else {
+                PropertyBuilder pb = MemoryPropertyBuilder.copy(Type.NAME, ordering);
+                // TODO: determine ordering index
+                int index = 0;
+                pb.setValue(entryName, index);
+                principalRoot.setProperty(pb.getPropertyState());
+            }
+        }
+
+        private boolean isSame(String name, NodeState node) {
+            Tree entry = getTree(name, node);
+
+            if (isAllow == (name.charAt(0) == 'a')) {
+                return false;
+            }
+            if (!privilegeBits.equals(PrivilegeBits.getInstance(node.getProperty(REP_PRIVILEGES)))) {
+                return false;
+            }
+            if (!principalName.equals(TreeUtil.getString(entry, REP_PRINCIPAL_NAME))) {
+                return false;
+            }
+            if (index != entry.getProperty("rep:index").getValue(Type.LONG)) {
+                return false;
+            }
+            if (!accessControlledPath.equals(TreeUtil.getString(entry, "rep:accessControlledPath"))) {
+                return false;
+            }
+            // TODO: respect restrictions
+
+            return true;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("entry: ").append(accessControlledPath);
+            sb.append(';').append(principalName);
+            sb.append(';').append(isAllow ? "allow" : "deny");
+            sb.append(';').append(privilegeBits);
+            return sb.toString();
         }
     }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionProviderImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionProviderImpl.java?rev=1447202&r1=1447201&r2=1447202&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionProviderImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/PermissionProviderImpl.java Mon Feb 18 11:19:00 2013
@@ -100,8 +100,8 @@ public class PermissionProviderImpl impl
 
     @Override
     public boolean canRead(@Nonnull Tree tree) {
-        if (acContext.definesTree(tree)) {
-            return compiledPermissions.isGranted(tree, Permissions.READ_ACCESS_CONTROL);
+        if (isAccessControlContent(tree)) {
+            return canReadAccessControlContent(tree, null);
         } else if (isVersionContent(tree)) {
             return canReadVersionContent(tree, null);
         } else {
@@ -111,8 +111,8 @@ public class PermissionProviderImpl impl
 
     @Override
     public boolean canRead(@Nonnull Tree tree, @Nonnull PropertyState property) {
-        if (acContext.definesTree(tree)) {
-            return compiledPermissions.isGranted(tree, property, Permissions.READ_ACCESS_CONTROL);
+        if (isAccessControlContent(tree)) {
+            return canReadAccessControlContent(tree, property);
         } else if (isVersionContent(tree)) {
             return canReadVersionContent(tree, property);
         } else {
@@ -174,6 +174,35 @@ public class PermissionProviderImpl impl
         return (tree == null) ? null : (ReadOnlyTree) tree;
     }
 
+    private boolean isAccessControlContent(@Nonnull Tree tree) {
+        return acContext.definesTree(tree);
+    }
+
+    private boolean canReadAccessControlContent(@Nonnull Tree acTree, @Nullable PropertyState acProperty) {
+        if (acProperty != null) {
+            return compiledPermissions.isGranted(acTree, acProperty, Permissions.READ_ACCESS_CONTROL);
+        } else {
+            return compiledPermissions.isGranted(acTree, Permissions.READ_ACCESS_CONTROL);
+        }
+    }
+
+    private static boolean isVersionContent(@Nonnull Tree tree) {
+        if (tree.isRoot()) {
+            return false;
+        }
+        if (VersionConstants.VERSION_NODE_NAMES.contains(tree.getName())) {
+            return true;
+        } else if (VersionConstants.VERSION_NODE_TYPE_NAMES.contains(TreeUtil.getPrimaryTypeName(tree))) {
+            return true;
+        } else {
+            return isVersionContent(tree.getPath());
+        }
+    }
+
+    private static boolean isVersionContent(@Nonnull String path) {
+        return VersionConstants.SYSTEM_PATHS.contains(Text.getAbsoluteParent(path, 1));
+    }
+
     private boolean canReadVersionContent(@Nonnull Tree versionStoreTree, @Nullable PropertyState property) {
         String versionablePath = getVersionablePath(versionStoreTree, property);
         if (versionablePath != null) {
@@ -211,21 +240,4 @@ public class PermissionProviderImpl impl
         }
         return Strings.emptyToNull(versionablePath);
     }
-
-    private static boolean isVersionContent(@Nonnull Tree tree) {
-        if (tree.isRoot()) {
-            return false;
-        }
-        if (VersionConstants.VERSION_NODE_NAMES.contains(tree.getName())) {
-            return true;
-        } else if (VersionConstants.VERSION_NODE_TYPE_NAMES.contains(TreeUtil.getPrimaryTypeName(tree))) {
-            return true;
-        } else {
-            return isVersionContent(tree.getPath());
-        }
-    }
-
-    private static boolean isVersionContent(@Nonnull String path) {
-        return VersionConstants.SYSTEM_PATHS.contains(Text.getAbsoluteParent(path, 1));
-    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java?rev=1447202&r1=1447201&r2=1447202&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authorization/permission/CompiledPermissionImpl.java Mon Feb 18 11:19:00 2013
@@ -77,20 +77,18 @@ public class CompiledPermissionImpl impl
 
     @Override
     public boolean isGranted(long permissions) {
-        // TODO
+        // TODO: only evaluate entries that are defined for the "" path.
         return false;
     }
 
     @Override
     public boolean isGranted(Tree tree, long permissions) {
-        // TODO
-        return false;
+        return hasPermissions(tree, null, permissions);
     }
 
     @Override
     public boolean isGranted(Tree parent, PropertyState property, long permissions) {
-        // TODO
-        return false;
+        return hasPermissions(parent, property, permissions);
     }
 
     @Override
@@ -110,9 +108,16 @@ public class CompiledPermissionImpl impl
     }
 
     //------------------------------------------------------------< private >---
+    private boolean hasPermissions(@Nonnull Tree tree, @Nullable PropertyState property,
+                                   long permissions) {
+        // TODO
+        return false;
+    }
+
 
     private PrivilegeBits getPrivilegeBits(@Nullable Tree tree) {
-        return PrivilegeBits.EMPTY; // TODO
+        // TODO
+        return PrivilegeBits.EMPTY;
     }
 
     private static final class Key implements Comparable<Key> {