Return-Path: X-Original-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Delivered-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 99DDBDD03 for ; Mon, 18 Feb 2013 11:19:28 +0000 (UTC) Received: (qmail 70140 invoked by uid 500); 18 Feb 2013 11:19:28 -0000 Delivered-To: apmail-jackrabbit-oak-commits-archive@jackrabbit.apache.org Received: (qmail 70079 invoked by uid 500); 18 Feb 2013 11:19:27 -0000 Mailing-List: contact oak-commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: oak-dev@jackrabbit.apache.org Delivered-To: mailing list oak-commits@jackrabbit.apache.org Received: (qmail 70055 invoked by uid 99); 18 Feb 2013 11:19:26 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 18 Feb 2013 11:19:26 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 18 Feb 2013 11:19:21 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 53BFD238890B; Mon, 18 Feb 2013 11:19:01 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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 Date: Mon, 18 Feb 2013 11:19:01 -0000 To: oak-commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20130218111901.53BFD238890B@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 {