Author: jsedding
Date: Tue Nov  1 11:35:24 2016
New Revision: 1767478

URL: http://svn.apache.org/viewvc?rev=1767478&view=rev
Log:
OAK-5043: Very old JR2 repositories may have invalid nodetypes for groupsPath 
and usersPath

- add test and Editor implementation to fix the jcr:primaryType if needed
- only applies to RepositoryUpgrade, not RepositorySidegrade

Added:
    
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
   (with props)
    
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
   (with props)
Modified:
    
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
    
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java

Modified: 
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java?rev=1767478&r1=1767477&r2=1767478&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
 Tue Nov  1 11:35:24 2016
@@ -125,6 +125,7 @@ import org.apache.jackrabbit.oak.upgrade
 import org.apache.jackrabbit.oak.upgrade.nodestate.report.LoggingReporter;
 import org.apache.jackrabbit.oak.upgrade.nodestate.report.ReportingNodeState;
 import org.apache.jackrabbit.oak.upgrade.nodestate.NodeStateCopier;
+import org.apache.jackrabbit.oak.upgrade.security.AuthorizableFolderEditor;
 import org.apache.jackrabbit.oak.upgrade.security.GroupEditorProvider;
 import org.apache.jackrabbit.oak.upgrade.security.RestrictionEditorProvider;
 import org.apache.jackrabbit.oak.upgrade.version.VersionCopyConfiguration;
@@ -513,6 +514,9 @@ public class RepositoryUpgrade {
             String groupsPath = userConf.getParameters().getConfigValue(
                     UserConstants.PARAM_GROUP_PATH,
                     UserConstants.DEFAULT_GROUP_PATH);
+            String usersPath = userConf.getParameters().getConfigValue(
+                    UserConstants.PARAM_USER_PATH,
+                    UserConstants.DEFAULT_USER_PATH);
 
             // hooks specific to the upgrade, need to run first
             hooks.add(new EditorHook(new CompositeEditorProvider(
@@ -520,7 +524,8 @@ public class RepositoryUpgrade {
                     new GroupEditorProvider(groupsPath),
                     // copy referenced version histories
                     new VersionableEditor.Provider(sourceRoot, workspaceName, 
versionCopyConfiguration),
-                    new SameNameSiblingsEditor.Provider()
+                    new SameNameSiblingsEditor.Provider(),
+                    AuthorizableFolderEditor.provider(groupsPath, usersPath)
             )));
 
             // this editor works on the VersionableEditor output, so it can't 
be

Added: 
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java?rev=1767478&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
 Tue Nov  1 11:35:24 2016
@@ -0,0 +1,97 @@
+package org.apache.jackrabbit.oak.upgrade.security;
+
+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.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static 
org.apache.jackrabbit.oak.spi.security.user.UserConstants.NT_REP_AUTHORIZABLE_FOLDER;
+
+/**
+ * There are occasions where in old JR2 repositories not all ancestors on
+ * the users path are of type {@code rep:AuthorizableFolder}, thus leading
+ * to exceptions after repository upgrade.
+ * <br/>
+ * In order to avoid such situations, this hook verifies that all nodes on
+ * the users and groups paths are of type {@code rep:AuthorizableFolder} and
+ * fixes the node-type if it is incorrect.
+ */
+public class AuthorizableFolderEditor extends DefaultEditor {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(AuthorizableFolderEditor.class);
+
+    private final NodeBuilder currentBuilder;
+
+    private final String groupsPath;
+
+    private final String usersPath;
+
+    private final String path;
+
+    public AuthorizableFolderEditor(final NodeBuilder builder, final String 
path, final String groupsPath, final String usersPath) {
+        this.currentBuilder = builder;
+        this.groupsPath = groupsPath;
+        this.usersPath = usersPath;
+        this.path = path;
+    }
+
+    public static EditorProvider provider(final String groupsPath, final 
String usersPath) {
+        return new EditorProvider() {
+            @Override
+            public Editor getRootEditor(final NodeState before,
+                                        final NodeState after,
+                                        final NodeBuilder builder,
+                                        final CommitInfo info) throws 
CommitFailedException {
+                return new AuthorizableFolderEditor(builder, "/", groupsPath, 
usersPath);
+            }
+        };
+    }
+
+    @Override
+    public void propertyAdded(final PropertyState after) throws 
CommitFailedException {
+         propertyChanged(null, after);
+    }
+
+    @Override
+    public void propertyChanged(final PropertyState before, final 
PropertyState after) throws CommitFailedException {
+        if (JCR_PRIMARYTYPE.equals(after.getName())) {
+            String nodeType = after.getValue(Type.STRING);
+            LOG.debug("Found {}/jcr:primaryType = {}", path, nodeType);
+            if (!nodeType.equals(NT_REP_AUTHORIZABLE_FOLDER) && 
expectAuthorizableFolder(path)) {
+                LOG.info("Changed {}/jcr:primaryType from {} to {}", path, 
nodeType, NT_REP_AUTHORIZABLE_FOLDER);
+                currentBuilder.setProperty(JCR_PRIMARYTYPE, 
NT_REP_AUTHORIZABLE_FOLDER, Type.NAME);
+            }
+        }
+    }
+
+    private boolean expectAuthorizableFolder(final String path) {
+        return !PathUtils.denotesRoot(path)
+                && (PathUtils.isAncestor(path, groupsPath) || 
path.equals(groupsPath)
+                || PathUtils.isAncestor(path, usersPath) || 
path.equals(usersPath));
+    }
+
+    @Override
+    public Editor childNodeAdded(final String name, final NodeState after) 
throws CommitFailedException {
+        return childNodeChanged(name, null, after);
+    }
+
+    @Override
+    public Editor childNodeChanged(final String name, final NodeState before, 
final NodeState after) throws CommitFailedException {
+        final String childPath = PathUtils.concat(path, name);
+        if (expectAuthorizableFolder(childPath)) {
+            LOG.debug("Found {} - descending", childPath);
+            return new AuthorizableFolderEditor(currentBuilder.child(name), 
childPath, groupsPath, usersPath);
+        } else {
+            return null;
+        }
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java?rev=1767478&r1=1767477&r2=1767478&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java
 Tue Nov  1 11:35:24 2016
@@ -160,24 +160,30 @@ public abstract class AbstractRepository
     protected void assertExisting(final String... paths) throws 
RepositoryException {
         final Session session = createAdminSession();
         try {
-            for (final String path : paths) {
-                final String relPath = path.substring(1);
-                assertTrue("node " + path + " should exist", 
session.getRootNode().hasNode(relPath));
-            }
+            assertExisting(session, paths);
         } finally {
             session.logout();
         }
     }
 
+    protected void assertExisting(final Session session, final String... 
paths) throws RepositoryException {
+        for (final String path : paths) {
+            assertTrue("node " + path + " should exist", 
session.nodeExists(path));
+        }
+    }
+
     protected void assertMissing(final String... paths) throws 
RepositoryException {
         final Session session = createAdminSession();
         try {
-            for (final String path : paths) {
-                final String relPath = path.substring(1);
-                assertFalse("node " + path + " should not exist", 
session.getRootNode().hasNode(relPath));
-            }
+            assertMissing(session, paths);
         } finally {
             session.logout();
         }
     }
+
+    protected void assertMissing(final Session session, final String... paths) 
throws RepositoryException {
+        for (final String path : paths) {
+            assertFalse("node " + path + " should not exist", 
session.nodeExists(path));
+        }
+    }
 }

Added: 
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java?rev=1767478&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
 Tue Nov  1 11:35:24 2016
@@ -0,0 +1,111 @@
+package org.apache.jackrabbit.oak.upgrade;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Test;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import java.io.InputStream;
+
+import static org.junit.Assert.assertThat;
+
+public class AuthorizableFolderEditorTest extends 
AbstractRepositoryUpgradeTest {
+
+    // this repository config sets the groupsPath and usersPath to match
+    // this tests expectations
+    private static final String REPOSITORY_XML_FILE = 
"repository-groupmember.xml";
+
+    private static final String TEST_GROUP = 
"AuthorizableFolderEditorTest-Group";
+
+    private static final String TEST_USER = 
"AuthorizableFolderEditorTest-User";
+
+    private static final String HOME_PATH = "/home";
+
+    private static final String GROUPS_PATH = HOME_PATH + "/groups";
+
+    private static final String USERS_PATH = HOME_PATH + "/users";
+
+    private static final String CONTROL_PATH = HOME_PATH + "/control";
+
+    @Override
+    protected void createSourceContent(final Session session) throws Exception 
{
+        UserManager userMgr = ((JackrabbitSession) session).getUserManager();
+        userMgr.autoSave(false);
+        Group group = userMgr.createGroup(TEST_GROUP);
+        User user = userMgr.createUser(TEST_USER, "secret");
+        group.addMember(user);
+        session.save();
+
+        // simulate the error, set node types to incorrect values
+        Node home = session.getNode("/home");
+        home.setPrimaryType(JcrConstants.NT_UNSTRUCTURED);
+        home.getNode("users").setPrimaryType(JcrConstants.NT_UNSTRUCTURED);
+        home.getNode("groups").setPrimaryType(JcrConstants.NT_UNSTRUCTURED);
+        home.addNode("control", JcrConstants.NT_UNSTRUCTURED);
+        session.save();
+    }
+
+    @Override
+    public InputStream getRepositoryConfig() {
+        return 
getClass().getClassLoader().getResourceAsStream(REPOSITORY_XML_FILE);
+    }
+
+    @Test
+    public void verifyCorrectedNodeTypes() throws RepositoryException {
+        final Session session = createAdminSession();
+        assertExisting(session, HOME_PATH, USERS_PATH, GROUPS_PATH, 
CONTROL_PATH);
+
+        assertThat(session.getNode(HOME_PATH), 
hasNodeType(UserConstants.NT_REP_AUTHORIZABLE_FOLDER));
+        assertThat(session.getNode(USERS_PATH), 
hasNodeType(UserConstants.NT_REP_AUTHORIZABLE_FOLDER));
+        assertThat(session.getNode(GROUPS_PATH), 
hasNodeType(UserConstants.NT_REP_AUTHORIZABLE_FOLDER));
+        assertThat(session.getNode(CONTROL_PATH), 
hasNodeType(JcrConstants.NT_UNSTRUCTURED));
+    }
+
+    private static Matcher<? super Node> hasNodeType(final String 
expectedNodeType) {
+        return new TypeSafeMatcher<Node>() {
+
+            private String path;
+
+            @Override
+            protected boolean matchesSafely(final Node node) {
+                path = getPath(node);
+                return getNodeTypeName(node).equals(expectedNodeType);
+            }
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText("the node " + path + " to be of type 
").appendValue(expectedNodeType);
+            }
+
+            @Override
+            protected void describeMismatchSafely(final Node node, final 
Description mismatchDescription) {
+                mismatchDescription.appendText(" was 
").appendValue(getNodeTypeName(node));
+            }
+
+            private String getNodeTypeName(final Node node) {
+                try {
+                    return node.getPrimaryNodeType().getName();
+                } catch (RepositoryException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
+            private String getPath(final Node node) {
+                try {
+                    return node.getPath();
+                } catch (RepositoryException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to