You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2022/01/10 10:42:37 UTC

[jackrabbit-filevault] 01/01: JCRVLT-587 check for duplicate jcr:uuid property values

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch feature/JCRVLT-587-duplicate-uuid-validator
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git

commit e8e7f86f84b88cd6a3ca322e972240e136679f24
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Mon Jan 10 11:41:22 2022 +0100

    JCRVLT-587 check for duplicate jcr:uuid property values
---
 src/site/markdown/validation.md                    |  2 +
 .../spi/impl/DuplicateUuidValidator.java           | 67 +++++++++++++++++++
 .../spi/impl/DuplicateUuidValidatorFactory.java    | 52 +++++++++++++++
 .../spi/impl/DuplicateUuidValidatorTest.java       | 77 ++++++++++++++++++++++
 4 files changed, 198 insertions(+)

diff --git a/src/site/markdown/validation.md b/src/site/markdown/validation.md
index 6aab21d..176e987 100644
--- a/src/site/markdown/validation.md
+++ b/src/site/markdown/validation.md
@@ -61,6 +61,8 @@ ID  |  Description | Options | Incremental Execution Limitations
 `jackrabbit-packagetype` | Checks if the package type is correctly set for this package, i.e. is compliant with all rules outlined at [Package Types](packagetypes.html). | *jcrInstallerNodePathRegex*:  the regular expression which all JCR paths of OSGi bundles and configurations within packages must match (default=`/([^/]*/){0,4}?(install|config)[\./].*`). This should match the paths being picked up by [JCR Installer](https://sling.apache.org/documentation/bundles/jcr-installer-provider. [...]
 `jackrabbit-nodetypes` | Checks if all non empty elements within [DocView files](docview.html) have the mandatory property `jcr:primaryType` set and follow the [node type definition of their given type](https://jackrabbit.apache.org/jcr/node-types.html). | *cnds*: A URI pointing to one or multiple [CNDs](https://jackrabbit.apache.org/jcr/node-type-notation.html) (separated by `,`) which define the additional namespaces and nodetypes used apart from the [default ones defined in JCR 2.0](h [...]
 `jackrabbit-accesscontrol` | Checks that [access control list nodes (primary type `rep:ACL`, `rep:CugPolicy` and `rep:PrincipalPolicy`)](https://jackrabbit.apache.org/oak/docs/security/accesscontrol/default.html#Representation_in_the_Repository) are only used when the [package property's](./properties.html) `acHandling` is set to something but `ignore` or `clear` and also that there is at least one access control list node otherwise | none | Validation message in case no access control l [...]
+`jackrabbit-duplicateuuid` | Checks that every value of property `jcr:uuid` is unique. Compare with [Referenceable Nodes
+](./referenceablenodes.html) | none | might emit false negatives (i.e. not detect duplicates)
 
 ### Custom Validators
 
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DuplicateUuidValidator.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DuplicateUuidValidator.java
new file mode 100644
index 0000000..dabc7ac
--- /dev/null
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DuplicateUuidValidator.java
@@ -0,0 +1,67 @@
+/*
+ * 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.vault.validation.spi.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.util.DocViewNode;
+import org.apache.jackrabbit.vault.validation.spi.DocumentViewXmlValidator;
+import org.apache.jackrabbit.vault.validation.spi.NodeContext;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *  Check for duplicate jcr:uuid values
+ */
+public class DuplicateUuidValidator implements DocumentViewXmlValidator {
+
+    protected static final String MESSAGE_DUPLICATE_UUID = "Found the same jcr:uuid value '%s' in '%s' and '%s'";
+    private final ValidationMessageSeverity severity;
+    private final WorkspaceFilter filter;
+    
+    
+    private Map<String, String> uuidsAndPaths;
+    
+    public DuplicateUuidValidator(ValidationMessageSeverity severity, WorkspaceFilter filter) {
+        this.severity = severity;
+        this.filter = filter;
+        uuidsAndPaths = new HashMap<>();
+    }
+
+    @Override
+    public Collection<ValidationMessage> validate(@NotNull DocViewNode node, @NotNull NodeContext nodeContext, boolean isRoot) {
+        if (node.uuid != null && filter.contains(nodeContext.getNodePath())) {
+            String duplicateUuidPath = uuidsAndPaths.put(node.uuid, nodeContext.getNodePath());
+            if (duplicateUuidPath != null) {
+                return Collections.singleton(new ValidationMessage(severity, String.format(MESSAGE_DUPLICATE_UUID, node.uuid, duplicateUuidPath, nodeContext.getNodePath())));
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public @Nullable Collection<ValidationMessage> done() {
+        return null;
+    }
+
+}
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DuplicateUuidValidatorFactory.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DuplicateUuidValidatorFactory.java
new file mode 100644
index 0000000..71d3803
--- /dev/null
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/DuplicateUuidValidatorFactory.java
@@ -0,0 +1,52 @@
+/*
+ * 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.vault.validation.spi.impl;
+
+import org.apache.jackrabbit.vault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.vault.validation.spi.Validator;
+import org.apache.jackrabbit.vault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.vault.validation.spi.ValidatorSettings;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.kohsuke.MetaInfServices;
+
+@MetaInfServices
+public final class DuplicateUuidValidatorFactory implements ValidatorFactory {
+
+    @Override
+    public @Nullable Validator createValidator(@NotNull ValidationContext context, @NotNull ValidatorSettings settings) {
+        // TODO: check cross-package duplicates
+        return new DuplicateUuidValidator(settings.getDefaultSeverity(), context.getFilter());
+    }
+
+    @Override
+    public boolean shouldValidateSubpackages() {
+        return true;
+    }
+
+    @Override
+    public @NotNull String getId() {
+        return ValidatorFactory.ID_PREFIX_JACKRABBIT + "duplicateuuid";
+    }
+
+    @Override
+    public int getServiceRanking() {
+        return 0;
+    }
+
+    
+}
diff --git a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/DuplicateUuidValidatorTest.java b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/DuplicateUuidValidatorTest.java
new file mode 100644
index 0000000..4e2fbcc
--- /dev/null
+++ b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/DuplicateUuidValidatorTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.vault.validation.spi.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.vault.fs.config.ConfigurationException;
+import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
+import org.apache.jackrabbit.vault.util.DocViewNode;
+import org.apache.jackrabbit.vault.util.DocViewProperty;
+import org.apache.jackrabbit.vault.validation.AnyValidationMessageMatcher;
+import org.apache.jackrabbit.vault.validation.ValidationExecutorTest;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
+import org.apache.jackrabbit.vault.validation.spi.util.NodeContextImpl;
+import org.hamcrest.MatcherAssert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DuplicateUuidValidatorTest {
+
+    private DuplicateUuidValidator validator;
+
+    @Before
+    public void setUp() throws IOException, ConfigurationException {
+        try (InputStream input = this.getClass().getResourceAsStream("/filter.xml"))  {
+            DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
+            filter.load(input);
+            validator = new DuplicateUuidValidator(ValidationMessageSeverity.ERROR, filter);
+        }
+    }
+
+    @Test
+    public void testWithDuplicates() {
+        Map<String, DocViewProperty> props = new HashMap<>();
+        props.put("prop1", new DocViewProperty("prop1", new String[] { "value1" } , false, PropertyType.STRING));
+
+        // order node only (no other property)
+        DocViewNode node = new DocViewNode("jcr:root", "jcr:root", "1", Collections.emptyMap(), null, null);
+        MatcherAssert.assertThat(validator.validate(node, new NodeContextImpl("/apps/test/node1", Paths.get("node1"), Paths.get("")), false), AnyValidationMessageMatcher.noValidationInCollection());
+        
+        // another order node (to be covered by another file)
+        node = new DocViewNode("jcr:root", "jcr:root", "2", Collections.emptyMap(), null, null);
+        MatcherAssert.assertThat(validator.validate(node, new NodeContextImpl("/apps/test/node2", Paths.get("node2"), Paths.get("")), false), AnyValidationMessageMatcher.noValidationInCollection());
+        
+        // another order node only
+        node = new DocViewNode("jcr:root", "jcr:root", "1", Collections.emptyMap(), null, null);
+        ValidationExecutorTest.assertViolation(validator.validate(node, new NodeContextImpl("/apps/test/node3", Paths.get("node3"), Paths.get("")), false),
+                new ValidationMessage(ValidationMessageSeverity.ERROR, String.format(DuplicateUuidValidator.MESSAGE_DUPLICATE_UUID, "1", "/apps/test/node1", "/apps/test/node3")));
+
+        MatcherAssert.assertThat(validator.done(), AnyValidationMessageMatcher.noValidationInCollection());
+    }
+
+    // TODO: check duplicates in different packages
+    
+}