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 to...@apache.org on 2017/05/25 07:51:42 UTC

svn commit: r1796134 - in /jackrabbit/oak/trunk: oak-core/ oak-store-composite/ oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/ oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/

Author: tomekr
Date: Thu May 25 07:51:42 2017
New Revision: 1796134

URL: http://svn.apache.org/viewvc?rev=1796134&view=rev
Log:
OAK-6256: Prevent creating the across-mounts references

Added:
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidator.java
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorProvider.java
    jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/pom.xml
    jackrabbit/oak/trunk/oak-store-composite/pom.xml

Modified: jackrabbit/oak/trunk/oak-core/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/pom.xml?rev=1796134&r1=1796133&r2=1796134&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-core/pom.xml Thu May 25 07:51:42 2017
@@ -58,6 +58,7 @@
               org.apache.jackrabbit.oak.plugins.index.nodetype,
               org.apache.jackrabbit.oak.plugins.index.property,
               org.apache.jackrabbit.oak.plugins.index.property.jmx,
+              org.apache.jackrabbit.oak.plugins.index.property.strategy,
               org.apache.jackrabbit.oak.plugins.index.reference,
               org.apache.jackrabbit.oak.plugins.itemsave,
               org.apache.jackrabbit.oak.plugins.lock,
@@ -86,8 +87,10 @@
               org.apache.jackrabbit.oak.spi.security.user.util,
               org.apache.jackrabbit.oak.spi.xml,
               org.apache.jackrabbit.oak.query,
+              org.apache.jackrabbit.oak.query.ast,
               org.apache.jackrabbit.oak.query.fulltext,
               org.apache.jackrabbit.oak.query.facet,
+              org.apache.jackrabbit.oak.query.index,
               org.apache.jackrabbit.oak.security,
               org.apache.jackrabbit.oak.util,
             </Export-Package>

Modified: jackrabbit/oak/trunk/oak-store-composite/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/pom.xml?rev=1796134&r1=1796133&r2=1796134&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-store-composite/pom.xml Thu May 25 07:51:42 2017
@@ -85,6 +85,11 @@
     </dependency>
     <dependency>
       <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>oak-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
       <artifactId>oak-store-spi</artifactId>
       <version>${project.version}</version>
     </dependency>

Added: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidator.java?rev=1796134&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidator.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidator.java Thu May 25 07:51:42 2017
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.composite;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.property.Multiplexers;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
+import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.collect.ImmutableSet.of;
+import static com.google.common.collect.Maps.newHashMap;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.INTEGRITY;
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+
+public class CrossMountReferenceValidator extends DefaultValidator {
+
+    private static final Filter EMPTY_FILTER = new FilterImpl(null, null, new QueryEngineSettings());
+
+    /** Parent editor, or {@code null} if this is the root editor. */
+    private final CrossMountReferenceValidator parent;
+
+    /** Name of this node, or {@code null} for the root node. */
+    private final String name;
+
+    /** Path of this editor, built lazily in {@link #getPath()}. */
+    private String path;
+
+    /** UUID -> referencable node path */
+    private final Map<String, String> newReferencableNodes;
+
+    /** UUID -> referencing node */
+    private final Multimap<String, String> newReferences;
+
+    private final MountInfoProvider mip;
+
+    private final NodeState uuidDefinition;
+
+    private final Set<IndexStoreStrategy> uuidStores;
+
+    private CrossMountReferenceValidator(CrossMountReferenceValidator parent, String name) {
+        this.name = name;
+        this.parent = parent;
+        this.path = null;
+        this.mip = parent.mip;
+        this.newReferencableNodes = parent.newReferencableNodes;
+        this.newReferences = parent.newReferences;
+        this.uuidDefinition = parent.uuidDefinition;
+        this.uuidStores = parent.uuidStores;
+    }
+
+    public CrossMountReferenceValidator(NodeState root, MountInfoProvider mip) {
+        this.name = null;
+        this.parent = null;
+        this.path = "/";
+        this.mip = mip;
+        this.newReferencableNodes = newHashMap();
+        this.newReferences = ArrayListMultimap.create();
+        this.uuidDefinition = root.getChildNode(INDEX_DEFINITIONS_NAME).getChildNode("uuid");
+        this.uuidStores = Multiplexers.getStrategies(true, mip, uuidDefinition, INDEX_CONTENT_NODE_NAME);
+    }
+
+    @Override
+    public void enter(NodeState before, NodeState after)
+            throws CommitFailedException {
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after)
+            throws CommitFailedException {
+        if (parent != null) {
+            return;
+        }
+        for (Map.Entry<String, Collection<String>> e : newReferences.asMap().entrySet()) {
+            String uuid = e.getKey();
+            String passivePath = getPathByUuid(uuid);
+            if (passivePath == null) {
+                throw new CommitFailedException(INTEGRITY, 1,
+                        "Can't find path for the uuid: " + uuid);
+            }
+            Mount m1 = mip.getMountByPath(passivePath);
+
+            for (String activePath : e.getValue()) {
+                Mount m2 = mip.getMountByPath(activePath);
+                if (!m1.equals(m2)) {
+                    throw new CommitFailedException(INTEGRITY, 1,
+                            "Unable to reference the node [" + passivePath + "] from node [" + activePath + "]. Referencing across the mounts is not allowed.");
+                }
+            }
+        }
+    }
+
+    private String getPathByUuid(String uuid) {
+        if (newReferencableNodes.containsKey(uuid)) {
+            return newReferencableNodes.get(uuid);
+        }
+        for (IndexStoreStrategy store : uuidStores) {
+            for (String path : store.query(EMPTY_FILTER, null, uuidDefinition, of(uuid))) {
+                return path;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void propertyAdded(PropertyState after)
+            throws CommitFailedException {
+        checkProperty(after);
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after)
+            throws CommitFailedException {
+        checkProperty(after);
+    }
+
+    private void checkProperty(PropertyState property) {
+        Type<?> type = property.getType();
+        if (type == Type.REFERENCE) {
+            newReferences.put(property.getValue(Type.REFERENCE), getPath());
+        } else if (type == Type.REFERENCES) {
+            for (String r : property.getValue(Type.REFERENCES)) {
+                newReferences.put(r, getPath());
+            }
+        } else if (type == Type.STRING && JCR_UUID.equals(property.getName())) {
+            newReferencableNodes.put(property.getValue(Type.STRING), getPath());
+        }
+    }
+
+    @Override
+    public Validator childNodeAdded(String name, NodeState after)
+            throws CommitFailedException {
+        return new CrossMountReferenceValidator(this, name);
+    }
+
+    @Override
+    public Validator childNodeChanged(
+            String name, NodeState before, NodeState after)
+            throws CommitFailedException {
+        return new CrossMountReferenceValidator(this, name);
+    }
+
+    /**
+     * Returns the path of this node, building it lazily when first requested.
+     */
+    private String getPath() {
+        if (path == null) {
+            path = concat(parent.getPath(), name);
+        }
+        return path;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorProvider.java?rev=1796134&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorProvider.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorProvider.java Thu May 25 07:51:42 2017
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.composite;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * {@link Validator} which detects references crossing the mount boundaries
+ */
+@Component(label = "Apache Jackrabbit Oak CrossMountReferenceValidatorProvider")
+@Property(name = "type", value = "crossMountRefValidator", propertyPrivate = true)
+@Service({ValidatorProvider.class, EditorProvider.class})
+public class CrossMountReferenceValidatorProvider extends ValidatorProvider {
+
+    @Reference
+    private MountInfoProvider mountInfoProvider = Mounts.defaultMountInfoProvider();
+
+    @Override
+    protected Validator getRootValidator(NodeState before, NodeState after, CommitInfo info) {
+        return new CrossMountReferenceValidator(after, mountInfoProvider);
+    }
+
+    CrossMountReferenceValidatorProvider with(MountInfoProvider mountInfoProvider) {
+        this.mountInfoProvider = mountInfoProvider;
+        return this;
+    }
+}

Added: jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorTest.java?rev=1796134&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorTest.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CrossMountReferenceValidatorTest.java Thu May 25 07:51:42 2017
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.composite;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.CompositeIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceEditorProvider;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.CompositeEditorProvider;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.oak.InitialContent.INITIAL_CONTENT;
+import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
+
+public class CrossMountReferenceValidatorTest {
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    private EditorHook hook;
+
+    @Before
+    public void initializeHook() {
+        MountInfoProvider mip = Mounts.newBuilder()
+                .mount("foo", "/a")
+                .build();
+        hook = new EditorHook(new CompositeEditorProvider(
+                new IndexUpdateProvider(new CompositeIndexEditorProvider(
+                        new PropertyIndexEditorProvider().with(mip),
+                        new ReferenceEditorProvider().with(mip))),
+                        new CrossMountReferenceValidatorProvider().with(mip)));
+    }
+
+    @Test
+    public void globalToPrivateReference() throws Exception{
+        NodeState root = INITIAL_CONTENT;
+
+        NodeBuilder builder = root.builder();
+        NodeState before = builder.getNodeState();
+
+        builder.child("a").setProperty(createProperty(JCR_UUID, "u1", Type.STRING));
+        builder.child("b").setProperty(createProperty("foo", "u1", Type.REFERENCE));
+
+        NodeState after = builder.getNodeState();
+
+        thrown.expect(CommitFailedException.class);
+        thrown.expectMessage("OakIntegrity0001: Unable to reference the node [/a] from node [/b]. Referencing across the mounts is not allowed.");
+        hook.processCommit(before, after, CommitInfo.EMPTY);
+    }
+
+    @Test
+    public void privateToGlobalReference() throws Exception{
+        NodeState root = INITIAL_CONTENT;
+
+        NodeBuilder builder = root.builder();
+        NodeState before = builder.getNodeState();
+
+        builder.child("a").setProperty(createProperty("foo", "u1", Type.REFERENCE));
+        builder.child("b").setProperty(createProperty(JCR_UUID, "u1", Type.STRING));
+
+        NodeState after = builder.getNodeState();
+
+        thrown.expect(CommitFailedException.class);
+        thrown.expectMessage("OakIntegrity0001: Unable to reference the node [/b] from node [/a]. Referencing across the mounts is not allowed.");
+        hook.processCommit(before, after, CommitInfo.EMPTY);
+    }
+}