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

pauls pushed a commit to branch issues/SLING-9962
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-cpconverter.git

commit b1dd6e4aad279887fff324247df8693bec93ebe9
Author: Karl Pauls <[email protected]>
AuthorDate: Wed Jan 13 16:20:04 2021 +0100

    SLING-9962: DefaultAclManager#addPaths prone to causing 
ConstraintViolationException
---
 .../accesscontrol/DefaultAclManager.java           | 73 +++++++++++++++++-----
 .../cpconverter/accesscontrol/MixinParser.java     | 52 +++++++++++++++
 .../features/DefaultFeaturesManager.java           |  2 +-
 .../cpconverter/accesscontrol/AclManagerTest.java  | 23 +++----
 .../handlers/RepPolicyEntryHandlerTest.java        |  4 --
 .../cpconverter/accesscontrol/asd/not/.content.xml | 20 ++++++
 6 files changed, 139 insertions(+), 35 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java
 
b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java
index 4f581a3..7bbbe35 100644
--- 
a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java
+++ 
b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java
@@ -31,18 +31,9 @@ import org.jetbrains.annotations.Nullable;
 import javax.jcr.NamespaceException;
 import java.io.File;
 import java.io.FileInputStream;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 public final class DefaultAclManager implements AclManager {
@@ -104,6 +95,22 @@ public final class DefaultAclManager implements AclManager {
                 formatter.format("create service user %s with path %s%n", 
systemUser.getId(), systemUser.getIntermediatePath());
             }
 
+            Set<RepoPath> paths = acls.entrySet().stream()
+                    .filter(entry -> getSystemUser(entry.getKey()).isPresent())
+                    .map(Entry::getValue)
+                    .flatMap(Collection::stream)
+                    
.map(AccessControlEntry::getRepositoryPath).collect(Collectors.toSet());
+
+            paths.stream()
+                    .filter(path -> !paths.stream().anyMatch(other -> 
!other.equals(path) && other.startsWith(path))).
+                    
filter(((Predicate<RepoPath>)RepoPath::isRepositoryPath).negate()).
+                    map(path -> computePathWithTypes(path, packageAssemblers))
+                    .filter(Objects::nonNull)
+                    .
+                    forEach(
+                            path -> formatter.format("create path %s%n", path)
+                    );
+
             // add the acls
             acls.forEach((systemUserID, authorizations) ->
                 getSystemUser(systemUserID).ifPresent(systemUser ->
@@ -131,11 +138,6 @@ public final class DefaultAclManager implements AclManager 
{
                 authorizationsIterator.remove();
             }
         }
-
-        // make sure all paths are created first
-
-        addPaths(authorizations, packageAssemblers, formatter);
-
         // finally add ACLs
 
         addAclStatement(formatter, systemUser, authorizations);
@@ -185,6 +187,45 @@ public final class DefaultAclManager implements AclManager 
{
         }
     }
 
+    private static @Nullable String computePathWithTypes(@NotNull RepoPath 
path, @NotNull List<VaultPackageAssembler> packageAssemblers) {
+        path = new 
RepoPath(PlatformNameFormat.getPlatformPath(path.toString()));
+
+        boolean type = false;
+        String current = "";
+        for (String part : path.toString().substring(1).split("/")) {
+            current += current.isEmpty() ? part : "/" + part;
+            for (VaultPackageAssembler packageAssembler : packageAssemblers) {
+                File currentContent = packageAssembler.getEntry(current + "/" 
+ CONTENT_XML_FILE_NAME);
+                if (currentContent.isFile()) {
+                    String primary;
+                    String mixin;
+                    try (FileInputStream input = new 
FileInputStream(currentContent);
+                        FileInputStream input2 = new 
FileInputStream(currentContent)) {
+                        primary = new 
PrimaryTypeParser(DEFAULT_TYPE).parse(input);
+                        mixin = new MixinParser(DEFAULT_TYPE).parse(input2);
+                        current += "(" + primary;
+                        if (mixin != null) {
+                            mixin = mixin.trim();
+                            if (mixin.startsWith("[")) {
+                                mixin = mixin.substring(1, mixin.length() - 1);
+                            }
+                            current += " mixin " + mixin;
+                        }
+                        current += ")";
+                        type = true;
+                    } catch (Exception e) {
+                        throw new RuntimeException("A fatal error occurred 
while parsing the '"
+                                + currentContent
+                                + "' file, see nested exceptions: "
+                                + e);
+                    }
+                }
+            }
+        }
+
+        return type ? new RepoPath(current).toString() : null;
+    }
+
        private static @NotNull String computePathType(@NotNull RepoPath path, 
@NotNull List<VaultPackageAssembler> packageAssemblers) {
         path = new 
RepoPath(PlatformNameFormat.getPlatformPath(path.toString()));
 
diff --git 
a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/MixinParser.java
 
b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/MixinParser.java
new file mode 100644
index 0000000..8da9b2f
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/MixinParser.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.sling.feature.cpconverter.accesscontrol;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.sling.feature.cpconverter.shared.AbstractJcrNodeParser;
+import org.jetbrains.annotations.NotNull;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+final class MixinParser extends AbstractJcrNodeParser<String> {
+
+    private String detectedPrimaryType;
+    private String mixins;
+
+    public MixinParser(@NotNull String primaryType) {
+        super(primaryType);
+    }
+
+    @Override
+    protected void onJcrRootNode(String uri, String localName, String qName, 
Attributes attributes, String primaryType)
+            throws SAXException {
+        detectedPrimaryType = primaryType;
+        mixins = attributes.getValue(JcrConstants.JCR_MIXINTYPES);
+    }
+
+    @Override
+    protected void onJcrRootElement(String uri, String localName, String 
qName, Attributes attributes)
+            throws SAXException {
+        // not needed
+    }
+
+    @Override
+    protected String getParsingResult() {
+        return mixins;
+    }
+
+}
diff --git 
a/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
 
b/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
index 202189e..86fa637 100644
--- 
a/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
+++ 
b/src/main/java/org/apache/sling/feature/cpconverter/features/DefaultFeaturesManager.java
@@ -92,7 +92,7 @@ public class DefaultFeaturesManager implements 
FeaturesManager {
     }
 
     public DefaultFeaturesManager(@NotNull File tempDir) {
-        this(true, 20, tempDir, null, null, null);
+        this(true, 20, tempDir, null, null, new HashMap<>());
     }
 
     public DefaultFeaturesManager(boolean mergeConfigurations,
diff --git 
a/src/test/java/org/apache/sling/feature/cpconverter/accesscontrol/AclManagerTest.java
 
b/src/test/java/org/apache/sling/feature/cpconverter/accesscontrol/AclManagerTest.java
index 77a25b2..e7072eb 100644
--- 
a/src/test/java/org/apache/sling/feature/cpconverter/accesscontrol/AclManagerTest.java
+++ 
b/src/test/java/org/apache/sling/feature/cpconverter/accesscontrol/AclManagerTest.java
@@ -75,7 +75,10 @@ public class AclManagerTest {
         
aclManager.addAcl("acs-commons-package-replication-status-event-service", 
newAcl(true, "jcr:read,crx:replicate,jcr:removeNode", "/home/users/system"));
 
         VaultPackageAssembler assembler = mock(VaultPackageAssembler.class);
-        when(assembler.getEntry(anyString())).thenReturn(new 
File(System.getProperty("java.io.tmpdir")));
+        when(assembler.getEntry(anyString())).thenReturn(tempDir.toFile());
+        when(assembler.getEntry("asd/not/.content.xml")).thenReturn(new 
File(getClass().getResource("asd/not/.content.xml").getFile()));
+
+
         Feature feature = new Feature(new ArtifactId("org.apache.sling", 
"org.apache.sling.cp2fm", "0.0.1", null, null));
 
         FeaturesManager fm = Mockito.spy(new 
DefaultFeaturesManager(tempDir.toFile()));
@@ -90,11 +93,7 @@ public class AclManagerTest {
         // acs-commons-on-deploy-scripts-service will be missed
         String expected =
                 "create service user 
acs-commons-package-replication-status-event-service with path 
/home/users/system" + System.lineSeparator() +
-                "create path (sling:Folder) /asd" + System.lineSeparator() +
-                "create path (sling:Folder) /asd/not" + System.lineSeparator() 
+
-                "create path (sling:Folder) /asd/not/system" + 
System.lineSeparator() +
-                "create path (sling:Folder) /asd/not/system/user" + 
System.lineSeparator() +
-                "create path (sling:Folder) /asd/not/system/user/path" + 
System.lineSeparator() +
+                "create path /asd/not(any:Type mixin 
rep:AccessControllable)/system/user/path" + System.lineSeparator() +
                 // see SLING-8561
                 // "set ACL for 
acs-commons-package-replication-status-event-service\n" +
                 // "allow jcr:read,crx:replicate,jcr:removeNode on 
/asd/public\n" +
@@ -122,7 +121,9 @@ public class AclManagerTest {
         
aclManager.addAcl("acs-commons-package-replication-status-event-service", 
newAcl(true, "jcr:read,rep:write,rep:indexDefinitionManagement", 
"/asd/not/system/user/path"));
 
         VaultPackageAssembler assembler = mock(VaultPackageAssembler.class);
-        when(assembler.getEntry(anyString())).thenReturn(new 
File(System.getProperty("java.io.tmpdir")));
+        when(assembler.getEntry(anyString())).thenReturn(tempDir.toFile());
+        when(assembler.getEntry("asd/not/.content.xml")).thenReturn(new 
File(getClass().getResource("asd/not/.content.xml").getFile()));
+
         Feature feature = new Feature(new ArtifactId("org.apache.sling", 
"org.apache.sling.cp2fm", "0.0.1", null, null));
 
         FeaturesManager fm = Mockito.spy(new 
DefaultFeaturesManager(tempDir.toFile()));
@@ -137,11 +138,7 @@ public class AclManagerTest {
         // aacs-commons-ensure-oak-index-service will be missed
         String expected =
                 "create service user 
acs-commons-package-replication-status-event-service with path 
/home/users/system" + System.lineSeparator() +
-                "create path (sling:Folder) /asd" + System.lineSeparator() +
-                "create path (sling:Folder) /asd/not" + System.lineSeparator() 
+
-                "create path (sling:Folder) /asd/not/system" + 
System.lineSeparator() +
-                "create path (sling:Folder) /asd/not/system/user" + 
System.lineSeparator() +
-                "create path (sling:Folder) /asd/not/system/user/path" + 
System.lineSeparator() +
+                "create path /asd/not(any:Type mixin 
rep:AccessControllable)/system/user/path" + System.lineSeparator() +
                 "set ACL for 
acs-commons-package-replication-status-event-service" + System.lineSeparator() +
                 "allow jcr:read,rep:write,rep:indexDefinitionManagement on 
/asd/not/system/user/path" + System.lineSeparator() +
                 "end" + System.lineSeparator();
@@ -191,8 +188,6 @@ public class AclManagerTest {
 
         String expected =
                 "create service user sys-usr with path /home/users/system" + 
System.lineSeparator() +
-                "create path (sling:Folder) /content" + System.lineSeparator() 
+
-                "create path (sling:Folder) /content/cq:tags" + 
System.lineSeparator() +
                 "set ACL for sys-usr" + System.lineSeparator() +
                 "allow jcr:read on /content/cq:tags" + System.lineSeparator() +
                 "allow jcr:write on /content/cq:tags" + System.lineSeparator() 
+
diff --git 
a/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java
 
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java
index cbd1b0c..a9bf8eb 100644
--- 
a/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java
+++ 
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java
@@ -190,10 +190,6 @@ public final class RepPolicyEntryHandlerTest {
 
         String expected =
                 "create service user 
acs-commons-package-replication-status-event-service with path 
/this/is/a/completely/different/path" + System.lineSeparator() +
-                "create path (sling:Folder) /home" + System.lineSeparator() +
-                "create path (sling:Folder) /home/users" + 
System.lineSeparator() +
-                "create path (sling:Folder) /home/users/system" + 
System.lineSeparator() +
-                "create path (sling:Folder) /home/users/system/asd" + 
System.lineSeparator() +
                 "set ACL for 
acs-commons-package-replication-status-event-service" + System.lineSeparator() +
                 "allow 
jcr:read,rep:write,jcr:readAccessControl,jcr:modifyAccessControl on 
/home/users/system/asd" + System.lineSeparator() +
                 "deny jcr:write on /home/users/system/asd" + 
System.lineSeparator() +
diff --git 
a/src/test/resources/org/apache/sling/feature/cpconverter/accesscontrol/asd/not/.content.xml
 
b/src/test/resources/org/apache/sling/feature/cpconverter/accesscontrol/asd/not/.content.xml
new file mode 100644
index 0000000..c1a8a69
--- /dev/null
+++ 
b/src/test/resources/org/apache/sling/feature/cpconverter/accesscontrol/asd/not/.content.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"; 
xmlns:jcr="http://www.jcp.org/jcr/1.0"; xmlns:rep="internal"
+          jcr:mixinTypes="[rep:AccessControllable]"
+          jcr:primaryType="any:Type"/>
\ No newline at end of file

Reply via email to