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 an...@apache.org on 2014/10/30 11:54:37 UTC

svn commit: r1635464 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/ oak-jcr/src/test/java/or...

Author: angela
Date: Thu Oct 30 10:54:37 2014
New Revision: 1635464

URL: http://svn.apache.org/r1635464
Log:
OAK-2245 : UserImporter should always set the rep:authorizableID

Added:
    jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportFromJackrabbit.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ProtectedPropertyImporter.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/ImporterImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportPwExpiryTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java?rev=1635464&r1=1635463&r2=1635464&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java Thu Oct 30 10:54:37 2014
@@ -152,12 +152,12 @@ class UserImporter implements ProtectedP
      * Temporary store for the pw an imported new user to be able to call
      * the creation actions irrespective of the order of protected properties
      */
-    private Map<String, String> currentPw = new HashMap<String, String>(1);
+    private String currentPw;
 
     /**
      * Remember all new principals for impersonation handling.
      */
-    private Map<String, Principal> principals;
+    private Map<String, Principal> principals = new HashMap<String, Principal>();;
 
     UserImporter(ConfigurationParameters config) {
         String importBehaviorStr = config.getConfigValue(PARAM_IMPORT_BEHAVIOR, ImportBehavior.NAME_IGNORE);
@@ -221,146 +221,133 @@ class UserImporter implements ProtectedP
         checkInitialized();
 
         String propName = propInfo.getName();
-        boolean isPwdNode = isPwdNode(parent);
-        Tree authorizableTree = (isPwdNode) ? parent.getParent() : parent;
-        Authorizable a = userManager.getAuthorizable(authorizableTree);
-
-        if (a == null) {
-            log.warn("Cannot handle protected PropInfo " + propInfo + ". Node " + parent + " doesn't represent a valid Authorizable.");
-            return false;
-        }
-
-        if (REP_AUTHORIZABLE_ID.equals(propName)) {
-            if (!isValid(def, NT_REP_AUTHORIZABLE, false)) {
-                return false;
-            }
-            String id = propInfo.getTextValue().getString();
-            Authorizable existing = userManager.getAuthorizable(id);
-            if (a.getPath().equals(existing.getPath())) {
-                parent.setProperty(REP_AUTHORIZABLE_ID, id);
-            } else {
-                throw new AuthorizableExistsException(id);
-            }
-        } else if (REP_PRINCIPAL_NAME.equals(propName)) {
-            if (!isValid(def, NT_REP_AUTHORIZABLE, false)) {
+        if (isPwdNode(parent)) {
+            // overwrite any properties generated underneath the rep:pwd node
+            // by "UserManagerImpl#setPassword" by the properties defined by
+            // the XML to be imported. see OAK-1943 for the corresponding discussion.
+            return importPwdNodeProperty(parent, propInfo, def);
+        } else {
+            Authorizable a = userManager.getAuthorizable(parent);
+            if (a == null) {
+                log.warn("Cannot handle protected PropInfo " + propInfo + ". Node " + parent + " doesn't represent a valid Authorizable.");
                 return false;
             }
 
-            String principalName = propInfo.getTextValue().getString();
-            Principal principal = new PrincipalImpl(principalName);
-            userManager.checkValidPrincipal(principal, a.isGroup());
-            userManager.setPrincipal(parent, principal);
+            if (REP_AUTHORIZABLE_ID.equals(propName)) {
+                if (!isValid(def, NT_REP_AUTHORIZABLE, false)) {
+                    return false;
+                }
+                String id = propInfo.getTextValue().getString();
+                Authorizable existing = userManager.getAuthorizable(id);
+                if (a.getPath().equals(existing.getPath())) {
+                    parent.setProperty(REP_AUTHORIZABLE_ID, id);
+                } else {
+                    throw new AuthorizableExistsException(id);
+                }
+            } else if (REP_PRINCIPAL_NAME.equals(propName)) {
+                if (!isValid(def, NT_REP_AUTHORIZABLE, false)) {
+                    return false;
+                }
+
+                String principalName = propInfo.getTextValue().getString();
+                Principal principal = new PrincipalImpl(principalName);
+                userManager.checkValidPrincipal(principal, a.isGroup());
+                userManager.setPrincipal(parent, principal);
 
             /*
              Remember principal of new user/group for further processing
              of impersonators
              */
-            if (principals == null) {
-                principals = new HashMap<String, Principal>();
-            }
-            principals.put(principalName, a.getPrincipal());
+                if (principals == null) {
+                    principals = new HashMap<String, Principal>();
+                }
+                principals.put(principalName, a.getPrincipal());
 
-            /*
-            Execute authorizable actions for a NEW group as this is the
-            same place in the userManager#createGroup that the actions
-            are called.
-            In case of a NEW user the actions are executed if the password
-            has been imported before.
-            */
-            a = userManager.getAuthorizable(parent);
-            if (a == null) {
-                log.warn("Cannot handle protected PropInfo " + propInfo + ". Node " + parent + " doesn't represent a valid Authorizable.");
-                return false;
-            }
-            if (parent.getStatus() == Tree.Status.NEW) {
-                if (a.isGroup()) {
-                    userManager.onCreate((Group) a);
-                } else if (currentPw.containsKey(a.getID())) {
-                    userManager.onCreate((User) a, currentPw.remove(a.getID()));
+                return true;
+            } else if (REP_PASSWORD.equals(propName)) {
+                if (a.isGroup() || !isValid(def, NT_REP_USER, false)) {
+                    log.warn("Unexpected authorizable or definition for property rep:password");
+                    return false;
+                }
+                if (((User) a).isSystemUser()) {
+                    log.warn("System users may not have a password set.");
+                    return false;
                 }
-            }
-            return true;
-        } else if (REP_PASSWORD.equals(propName)) {
-            if (a.isGroup() || !isValid(def, NT_REP_USER, false)) {
-                log.warn("Unexpected authorizable or definition for property rep:password");
-                return false;
-            }
-            if (((User) a).isSystemUser()) {
-                log.warn("System users may not have a password set.");
-                return false;
-            }
 
-            String pw = propInfo.getTextValue().getString();
-            userManager.setPassword(parent, a.getID(), pw, false);
+                String pw = propInfo.getTextValue().getString();
+                userManager.setPassword(parent, a.getID(), pw, false);
+                currentPw = pw;
 
-            /*
-            Execute authorizable actions for a NEW user at this point after
-            having set the password if the principal name has already been
-            processed, otherwise postpone it.
-            */
-            if (parent.getStatus() == Tree.Status.NEW) {
-                if (parent.hasProperty(REP_PRINCIPAL_NAME)) {
-                    userManager.onCreate((User) a, pw);
-                } else {
-                    // principal name not yet available -> remember the pw
-                    currentPw.clear();
-                    currentPw.put(a.getID(), pw);
+                return true;
+
+            } else if (REP_IMPERSONATORS.equals(propName)) {
+                if (a.isGroup() || !isValid(def, MIX_REP_IMPERSONATABLE, true)) {
+                    log.warn("Unexpected authorizable or definition for property rep:impersonators");
+                    return false;
                 }
-            }
-            return true;
 
-        } else if (REP_IMPERSONATORS.equals(propName)) {
-            if (a.isGroup() || !isValid(def, MIX_REP_IMPERSONATABLE, true)) {
-                log.warn("Unexpected authorizable or definition for property rep:impersonators");
-                return false;
-            }
+                // since impersonators may be imported later on, postpone processing
+                // to the end.
+                // see -> process References
+                referenceTracker.processedReference(new Impersonators(a.getID(), propInfo.getTextValues()));
+                return true;
 
-            // since impersonators may be imported later on, postpone processing
-            // to the end.
-            // see -> process References
-            referenceTracker.processedReference(new Impersonators(a.getID(), propInfo.getTextValues()));
-            return true;
-
-        } else if (REP_DISABLED.equals(propName)) {
-            if (a.isGroup() || !isValid(def, NT_REP_USER, false)) {
-                log.warn("Unexpected authorizable or definition for property rep:disabled");
-                return false;
-            }
+            } else if (REP_DISABLED.equals(propName)) {
+                if (a.isGroup() || !isValid(def, NT_REP_USER, false)) {
+                    log.warn("Unexpected authorizable or definition for property rep:disabled");
+                    return false;
+                }
 
-            ((User) a).disable(propInfo.getTextValue().getString());
-            return true;
+                ((User) a).disable(propInfo.getTextValue().getString());
+                return true;
 
-        } else if (REP_MEMBERS.equals(propName)) {
-            if (!a.isGroup() || !isValid(def, NT_REP_MEMBER_REFERENCES, true)) {
-                return false;
-            }
-            // since group-members are references to user/groups that potentially
-            // are to be imported later on -> postpone processing to the end.
-            // see -> process References
-            getMembership(a.getID()).addMembers(propInfo.getTextValues());
-            return true;
+            } else if (REP_MEMBERS.equals(propName)) {
+                if (!a.isGroup() || !isValid(def, NT_REP_MEMBER_REFERENCES, true)) {
+                    return false;
+                }
+                // since group-members are references to user/groups that potentially
+                // are to be imported later on -> postpone processing to the end.
+                // see -> process References
+                getMembership(a.getID()).addMembers(propInfo.getTextValues());
+                return true;
 
-        } else if (isPwdNode) {
-            // overwrite any properties generated underneath the rep:pwd node
-            // by "UserManagerImpl#setPassword" by the properties defined by
-            // the XML to be imported. see OAK-1943 for the corresponding discussion.
-            int targetType = def.getRequiredType();
-            if (targetType == PropertyType.UNDEFINED) {
-                targetType = (REP_PASSWORD_LAST_MODIFIED.equals(propName)) ? PropertyType.LONG : PropertyType.STRING;
-            }
-            PropertyState property;
-            if (def.isMultiple()) {
-                property = PropertyStates.createProperty(propName, propInfo.getValues(targetType));
-            } else {
-                property = PropertyStates.createProperty(propName, propInfo.getValue(targetType));
-            };
-            parent.setProperty(property);
-        }// else: cannot handle -> return false
+            } // another protected property -> return false
+        }
 
+        // neither rep:pwd nor authorizable node -> not covered by this importer.
         return false;
     }
 
     @Override
+    public void propertiesCompleted(@Nonnull Tree protectedParent) throws IllegalStateException, ConstraintViolationException, RepositoryException {
+        Authorizable a = userManager.getAuthorizable(protectedParent);
+        if (a == null) {
+            // not an authorizable
+            return;
+        }
+
+        // make sure the authorizable ID property is always set even if the
+        // authorizable defined by the imported XML didn't provide rep:authorizableID
+        if (!protectedParent.hasProperty(REP_AUTHORIZABLE_ID)) {
+            protectedParent.setProperty(REP_AUTHORIZABLE_ID, a.getID(), Type.STRING);
+        }
+
+        /*
+        Execute authorizable actions for a NEW user at this point after
+        having set the password and the principal name (all protected properties
+        have been processed now).
+        */
+        if (protectedParent.getStatus() == Tree.Status.NEW) {
+            if (a.isGroup()) {
+                userManager.onCreate((Group) a);
+            } else {
+                userManager.onCreate((User) a, currentPw);
+            }
+        }
+        currentPw = null;
+    }
+
+    @Override
     public void processReferences() throws RepositoryException {
         checkInitialized();
 
@@ -498,6 +485,25 @@ class UserImporter implements ProtectedP
         return REP_PWD.equals(tree.getName()) && NT_REP_PASSWORD.equals(TreeUtil.getPrimaryTypeName(tree));
     }
 
+    private static boolean importPwdNodeProperty(@Nonnull Tree parent, @Nonnull PropInfo propInfo, @Nonnull PropertyDefinition def) throws RepositoryException {
+        String propName = propInfo.getName();
+        // overwrite any properties generated underneath the rep:pwd node
+        // by "UserManagerImpl#setPassword" by the properties defined by
+        // the XML to be imported. see OAK-1943 for the corresponding discussion.
+        int targetType = def.getRequiredType();
+        if (targetType == PropertyType.UNDEFINED) {
+            targetType = (REP_PASSWORD_LAST_MODIFIED.equals(propName)) ? PropertyType.LONG : PropertyType.STRING;
+        }
+        PropertyState property;
+        if (def.isMultiple()) {
+            property = PropertyStates.createProperty(propName, propInfo.getValues(targetType));
+        } else {
+            property = PropertyStates.createProperty(propName, propInfo.getValue(targetType));
+        }
+        parent.setProperty(property);
+        return true;
+    }
+
     /**
      * Handling the import behavior
      *

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ProtectedPropertyImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ProtectedPropertyImporter.java?rev=1635464&r1=1635463&r2=1635464&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ProtectedPropertyImporter.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ProtectedPropertyImporter.java Thu Oct 30 10:54:37 2014
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.spi.xm
 
 import javax.annotation.Nonnull;
 import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.PropertyDefinition;
 
 import org.apache.jackrabbit.oak.api.Tree;
@@ -45,4 +46,20 @@ public interface ProtectedPropertyImport
     boolean handlePropInfo(@Nonnull Tree parent, @Nonnull PropInfo protectedPropInfo,
                            @Nonnull PropertyDefinition def) throws RepositoryException;
 
+    /**
+     * Informs this importer that all properties to be imported below
+     * {@code protectedParent} have been processed by the importer. If this importer
+     * did not import any protected properties this method doesn't do anything.
+     * Otherwise it may perform some validation and cleanup required based
+     * on the set of protected properties handled by this importer.
+     *
+     * @param protectedParent The protected parent tree.
+     * @throws IllegalStateException If this method is called in an illegal state.
+     * @throws javax.jcr.nodetype.ConstraintViolationException If the set of
+     * properties was incomplete and the importer was not able to fix the problem.
+     * @throws RepositoryException If another error occurs.
+     */
+    void propertiesCompleted(@Nonnull Tree protectedParent) throws IllegalStateException,
+            ConstraintViolationException, RepositoryException;
+
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/ImporterImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/ImporterImpl.java?rev=1635464&r1=1635463&r2=1635464&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/ImporterImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/ImporterImpl.java Thu Oct 30 10:54:37 2014
@@ -28,6 +28,7 @@ import java.util.UUID;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.jcr.ImportUUIDBehavior;
 import javax.jcr.ItemExistsException;
 import javax.jcr.PathNotFoundException;
@@ -41,6 +42,9 @@ import javax.jcr.nodetype.PropertyDefini
 import javax.jcr.version.VersionException;
 import javax.jcr.version.VersionManager;
 
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.PropertyState;
@@ -295,20 +299,48 @@ public class ImporterImpl implements Imp
                 log.debug("Protected property " + pi.getName());
 
                 // notify the ProtectedPropertyImporter.
-                for (ProtectedItemImporter ppi : pItemImporters) {
-                    if (ppi instanceof ProtectedPropertyImporter
-                            && ((ProtectedPropertyImporter) ppi).handlePropInfo(tree, pi, def)) {
+                for (ProtectedPropertyImporter ppi : getPropertyImporters()) {
+                    if (ppi.handlePropInfo(tree, pi, def)) {
                         log.debug("Protected property -> delegated to ProtectedPropertyImporter");
                         break;
-                    } /* else: p-i-Importer isn't able to deal with this property.
-                                     try next pp-importer */
-
+                    } /* else: p-i-Importer isn't able to deal with this property. try next pp-importer */
                 }
             } else if (!ignoreRegular) {
                 // regular property -> create the property
                 createProperty(tree, pi, def);
             }
         }
+        for (ProtectedPropertyImporter ppi : getPropertyImporters()) {
+            ppi.propertiesCompleted(tree);
+        }
+    }
+
+    private Iterable<ProtectedPropertyImporter> getPropertyImporters() {
+        return Iterables.filter(Iterables.transform(pItemImporters, new Function<ProtectedItemImporter, ProtectedPropertyImporter>() {
+            @Nullable
+            @Override
+            public ProtectedPropertyImporter apply(@Nullable ProtectedItemImporter importer) {
+                if (importer instanceof ProtectedPropertyImporter) {
+                    return (ProtectedPropertyImporter) importer;
+                } else {
+                    return null;
+                }
+            }
+        }), Predicates.notNull());
+    }
+
+    private Iterable<ProtectedNodeImporter> getNodeImporters() {
+        return Iterables.filter(Iterables.transform(pItemImporters, new Function<ProtectedItemImporter, ProtectedNodeImporter>() {
+            @Nullable
+            @Override
+            public ProtectedNodeImporter apply(@Nullable ProtectedItemImporter importer) {
+                if (importer instanceof ProtectedNodeImporter) {
+                    return (ProtectedNodeImporter) importer;
+                } else {
+                    return null;
+                }
+            }
+        }), Predicates.notNull());
     }
 
     //-----------------------------------------------------------< Importer >---
@@ -355,10 +387,10 @@ public class ImporterImpl implements Imp
                 // if there is one, notify the ProtectedNodeImporter about the
                 // start of a item tree that is protected by this parent. If it
                 // potentially is able to deal with it, notify it about the child node.
-                for (ProtectedItemImporter pni : pItemImporters) {
-                    if (pni instanceof ProtectedNodeImporter && ((ProtectedNodeImporter) pni).start(parent)) {
+                for (ProtectedNodeImporter pni : getNodeImporters()) {
+                    if (pni.start(parent)) {
                         log.debug("Protected node -> delegated to ProtectedNodeImporter");
-                        pnImporter = (ProtectedNodeImporter) pni;
+                        pnImporter = pni;
                         pnImporter.startChildInfo(nodeInfo, propInfos);
                         break;
                     } /* else: p-i-Importer isn't able to deal with the protected tree.

Added: jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportFromJackrabbit.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportFromJackrabbit.java?rev=1635464&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportFromJackrabbit.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportFromJackrabbit.java Thu Oct 30 10:54:37 2014
@@ -0,0 +1,413 @@
+/*
+ * 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.jcr.security.user;
+
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.Node;
+import javax.jcr.nodetype.ConstraintViolationException;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.test.api.util.Text;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests to verify the behavior of importing user content packages exported
+ * from Jackrabbit 2.x and their behavior upon import into an Oak repository.
+ *
+ * @since Oak 1.2
+ * @see <a href="https://issues.apache.org/jira/browse/OAK-2245">OAK-2245</a>
+ */
+public class UserImportFromJackrabbit extends AbstractImportTest {
+
+    private String uid = "t";
+    private String randomNodeName = "f5aj6fp7q9834jof";
+    private String intermediatePath = "foo/bar/test";
+
+    @Override
+    protected String getTargetPath() {
+        return USERPATH;
+    }
+
+    @Override
+    protected String getImportBehavior() {
+        return null;
+    }
+
+    /**
+     * @since Oak 1.2
+     */
+    @Test
+    public void testImportCreatesAuthorizableId() throws Exception {
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\""+uid+"\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:disabled\" sv:type=\"String\"><sv:value>disabledUser</sv:value></sv:property>" +
+                "</sv:node>";
+
+        doImport(getTargetPath(), xml);
+
+        Authorizable newUser = userMgr.getAuthorizable(uid);
+        assertEquals(uid, newUser.getID());
+
+        Node n = adminSession.getNode(newUser.getPath());
+        assertTrue(n.hasProperty(UserConstants.REP_AUTHORIZABLE_ID));
+        assertEquals(uid, n.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString());
+
+        // saving changes of the import -> must succeed
+        adminSession.save();
+    }
+
+    /**
+     * @since Oak 1.2
+     */
+    @Test
+    public void testUUIDBehaviorReplace() throws Exception {
+        // create authorizable
+        User u = userMgr.createUser(uid, null, new PrincipalImpl("t"), getTargetPath() + "/foo/bar/test");
+        String initialPath = u.getPath();
+        adminSession.save();
+
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\""+uid+"\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:disabled\" sv:type=\"String\"><sv:value>disabledUser</sv:value></sv:property>" +
+                "</sv:node>";
+
+        doImport(getTargetPath(), xml, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
+
+        Authorizable newUser = userMgr.getAuthorizable(uid);
+
+        // replace should retain path
+        assertEquals(initialPath, newUser.getPath());
+        assertFalse(getTargetPath().equals(Text.getRelativeParent(newUser.getPath(), 1)));
+
+        Node n = adminSession.getNode(newUser.getPath());
+        assertTrue(n.hasProperty(UserConstants.REP_AUTHORIZABLE_ID));
+        assertEquals(uid, n.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString());
+
+        // saving changes of the import -> must succeed
+        adminSession.save();
+    }
+
+    /**
+     * @since Oak 1.2
+     */
+    @Test
+    public void testUUIDBehaviorRemove() throws Exception {
+        // create authorizable
+        User u = userMgr.createUser(uid, null, new PrincipalImpl(uid), intermediatePath);
+        String initialPath = u.getPath();
+        adminSession.save();
+
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\"t\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:disabled\" sv:type=\"String\"><sv:value>disabledUser</sv:value></sv:property>" +
+                "</sv:node>";
+
+        doImport(getTargetPath(), xml, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING);
+
+        Authorizable newUser = userMgr.getAuthorizable(uid);
+
+        // IMPORT_UUID_COLLISION_REMOVE_EXISTING should result in the user to
+        // be imported a the new path
+        assertEquals(getTargetPath(), Text.getRelativeParent(newUser.getPath(), 1));
+        assertFalse(initialPath.equals(newUser.getPath()));
+
+        Node n = adminSession.getNode(newUser.getPath());
+        assertTrue(n.hasProperty(UserConstants.REP_AUTHORIZABLE_ID));
+        assertEquals(uid, n.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString());
+
+        // saving changes of the import -> must succeed
+        adminSession.save();
+    }
+
+    /**
+     * @since Oak 1.2
+     */
+    @Test
+    public void testUUIDBehaviorReplaceFromRenamed() throws Exception {
+        // create authorizable
+        User u = userMgr.createUser(uid, null, new PrincipalImpl(uid), intermediatePath);
+        String initialPath = u.getPath();
+        String movedPath = Text.getRelativeParent(initialPath, 1) + '/' + randomNodeName;
+        adminSession.move(initialPath, movedPath);
+        adminSession.save();
+
+        // import 'correct' jr2 package which contains the encoded ID in the node name
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\""+uid+"\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:disabled\" sv:type=\"String\"><sv:value>disabledUser</sv:value></sv:property>" +
+                "</sv:node>";
+
+        doImport(getTargetPath(), xml, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
+
+        Authorizable newUser = userMgr.getAuthorizable(uid);
+
+        // replace should update the path
+        assertEquals("user path", Text.getRelativeParent(initialPath, 1) + '/' + uid, newUser.getPath());
+
+        Node n = adminSession.getNode(newUser.getPath());
+        assertTrue(n.hasProperty(UserConstants.REP_AUTHORIZABLE_ID));
+        assertEquals(UserConstants.REP_AUTHORIZABLE_ID, uid, n.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString());
+        assertEquals(UserConstants.REP_AUTHORIZABLE_ID, uid, newUser.getID());
+
+        // saving changes of the import must succeed.
+        adminSession.save();
+    }
+
+    /**
+     * @since Oak 1.2
+     */
+    @Test
+    public void testUUIDBehaviorReplaceFromRenamed2() throws Exception {
+        // create authorizable
+        User u = userMgr.createUser(uid, null, new PrincipalImpl(uid), intermediatePath);
+        String initialPath = u.getPath();
+        String movedPath = Text.getRelativeParent(initialPath, 1) + '/' + randomNodeName;
+        adminSession.move(initialPath, movedPath);
+        adminSession.save();
+
+        // we need to include the new node name in the sysview import, so that the importer uses the correct name.
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\"" + randomNodeName + "\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:disabled\" sv:type=\"String\"><sv:value>disabledUser</sv:value></sv:property>" +
+                "</sv:node>";
+
+        doImport(getTargetPath(), xml, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
+
+        Authorizable newUser = userMgr.getAuthorizable(uid);
+
+        // replace should retain path
+        assertEquals("user path", movedPath, newUser.getPath());
+
+        Node n = adminSession.getNode(newUser.getPath());
+        assertTrue(n.hasProperty(UserConstants.REP_AUTHORIZABLE_ID));
+        assertEquals(UserConstants.REP_AUTHORIZABLE_ID, randomNodeName, n.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString());
+
+        // saving changes must fail -> the original authorizable has been replaced
+        // and got the ID set as specified by the Jackrabbit XML. Since the latter
+        // specifies the modified authorizable ID, which doesn't represent the
+        // correct ID as hashed in the jcr:uuid, the CommitHook will detect
+        // the mismatch, which for the diff looks like a modified ID.
+        try {
+            adminSession.save();
+            fail("Importing an authorizable with mismatch between authorizableId and uuid must fail.");
+        } catch (ConstraintViolationException e) {
+            // success
+            assertTrue(e.getMessage().contains("OakConstraint0022"));
+        }
+    }
+
+    /**
+     * @since Oak 1.2
+     */
+    @Test
+    public void testUUIDBehaviorReplaceFromRenamed3() throws Exception {
+        // create authorizable
+        User u = userMgr.createUser(uid, null, new PrincipalImpl(uid), intermediatePath);
+        String originalPath = u.getPath();
+        adminSession.save();
+
+        // we need to include the new node name in the sysview import, so that the importer uses the correct name.
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\"" + randomNodeName + "\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:disabled\" sv:type=\"String\"><sv:value>disabledUser</sv:value></sv:property>" +
+                "</sv:node>";
+
+        doImport(getTargetPath(), xml, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
+
+        Authorizable newUser = userMgr.getAuthorizable(uid);
+
+        // replace should change the original path
+        String expectedPath = Text.getRelativeParent(originalPath, 1) + '/' + randomNodeName;
+        assertEquals("user path", expectedPath, newUser.getPath());
+
+        Node n = adminSession.getNode(newUser.getPath());
+        assertTrue(n.hasProperty(UserConstants.REP_AUTHORIZABLE_ID));
+        assertEquals(UserConstants.REP_AUTHORIZABLE_ID, randomNodeName, n.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString());
+
+        // saving changes of the import -> must fail as the authorizable ID
+        // has been modified (it no longer represents the correct ID due to the
+        // modified node name in combination with the fact that in JR 2.x
+        // the node name MUST contain the id as there is no rep:authorizableId.
+        try {
+            adminSession.save();
+            fail("Importing an authorizable with mismatch between authorizableId and uuid must fail.");
+        } catch (ConstraintViolationException e) {
+            // success
+            assertTrue(e.getMessage().contains("OakConstraint0021"));
+        }
+    }
+
+    /**
+     * @since Oak 1.2
+     */
+    @Test
+    public void testUUIDBehaviorRemoveFromRenamed() throws Exception {
+        // create authorizable
+        User u = userMgr.createUser(uid, null, new PrincipalImpl(uid), intermediatePath);
+        String initialPath = u.getPath();
+        String movedPath = Text.getRelativeParent(initialPath, 1) + '/' + randomNodeName;
+        adminSession.move(initialPath, movedPath);
+        adminSession.save();
+
+        // import 'correct' jr2 package which contains the encoded ID in the node name
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\""+uid+"\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:disabled\" sv:type=\"String\"><sv:value>disabledUser</sv:value></sv:property>" +
+                "</sv:node>";
+
+        doImport(getTargetPath(), xml, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING);
+
+        Authorizable newUser = userMgr.getAuthorizable(uid);
+
+        // IMPORT_UUID_COLLISION_REMOVE_EXISTING should import the user at the new path
+        assertEquals("user path", getTargetPath() + '/' + uid, newUser.getPath());
+
+        Node n = adminSession.getNode(newUser.getPath());
+        assertTrue(n.hasProperty(UserConstants.REP_AUTHORIZABLE_ID));
+        assertEquals(UserConstants.REP_AUTHORIZABLE_ID, uid, n.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString());
+        assertEquals(UserConstants.REP_AUTHORIZABLE_ID, uid, newUser.getID());
+
+        // saving changes of the import must succeed.
+        adminSession.save();
+    }
+
+    /**
+     * @since Oak 1.2
+     */
+    @Test
+    public void testUUIDBehaviorRemoveFromRenamed2() throws Exception {
+        // create authorizable
+        User u = userMgr.createUser(uid, null, new PrincipalImpl(uid), intermediatePath);
+        String initialPath = u.getPath();
+        String movedPath = Text.getRelativeParent(initialPath, 1) + '/' + randomNodeName;
+        adminSession.move(initialPath, movedPath);
+        adminSession.save();
+
+        // we need to include the new node name in the sysview import, so that the importer uses the correct name.
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\"" + randomNodeName + "\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:disabled\" sv:type=\"String\"><sv:value>disabledUser</sv:value></sv:property>" +
+                "</sv:node>";
+
+        doImport(getTargetPath(), xml, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING);
+
+        Authorizable newUser = userMgr.getAuthorizable(uid);
+
+        // IMPORT_UUID_COLLISION_REMOVE_EXISTING should import the user at the new path
+        assertEquals("user path", getTargetPath() + '/' + randomNodeName, newUser.getPath());
+
+        Node n = adminSession.getNode(newUser.getPath());
+        assertTrue(n.hasProperty(UserConstants.REP_AUTHORIZABLE_ID));
+        assertEquals(UserConstants.REP_AUTHORIZABLE_ID, randomNodeName, n.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString());
+
+        // saving changes of the import -> must fail as original user has been
+        // removed and the JR 2.x the node name doesn't contain the correct id,
+        // which is detected during save as it looks like the id had been modified.
+        try {
+            adminSession.save();
+            fail("Importing an authorizable with mismatch between authorizableId and uuid must fail.");
+        } catch (ConstraintViolationException e) {
+            // success
+            assertTrue(e.getMessage().contains("OakConstraint0021"));
+        }
+    }
+
+    /**
+     * @since Oak 1.2
+     */
+    @Test
+    public void testUUIDBehaviorRemoveFromRenamed3() throws Exception {
+        // create authorizable
+        User u = userMgr.createUser(uid, null, new PrincipalImpl(uid), intermediatePath);
+        String originalPath = u.getPath();
+        adminSession.save();
+
+        // we need to include the new node name in the sysview import, so that the importer uses the correct name.
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\"" + randomNodeName + "\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:User</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>e358efa4-89f5-3062-b10d-d7316b65649e</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>t</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:disabled\" sv:type=\"String\"><sv:value>disabledUser</sv:value></sv:property>" +
+                "</sv:node>";
+
+        doImport(getTargetPath(), xml, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING);
+
+        Authorizable newUser = userMgr.getAuthorizable(uid);
+
+        // replace should change the original path
+        String expectedPath = getTargetPath() + '/' + randomNodeName;
+        assertEquals("user path", expectedPath, newUser.getPath());
+        assertFalse((Text.getRelativeParent(originalPath,1) + '/' + randomNodeName).equals(newUser.getPath()));
+
+        Node n = adminSession.getNode(newUser.getPath());
+        assertTrue(n.hasProperty(UserConstants.REP_AUTHORIZABLE_ID));
+        assertEquals(UserConstants.REP_AUTHORIZABLE_ID, randomNodeName, n.getProperty(UserConstants.REP_AUTHORIZABLE_ID).getString());
+
+        // saving changes of the import -> must fail as the authorizable ID
+        // has been modified (it no longer represents the correct ID due to the
+        // fact that in JR 2.x the node name MUST contain the id.
+        try {
+            adminSession.save();
+            fail("Importing an authorizable with mismatch between authorizableId and uuid must fail.");
+        } catch (ConstraintViolationException e) {
+            // success
+            assertTrue(e.getMessage().contains("OakConstraint0021"));
+        }
+    }
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportPwExpiryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportPwExpiryTest.java?rev=1635464&r1=1635463&r2=1635464&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportPwExpiryTest.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportPwExpiryTest.java Thu Oct 30 10:54:37 2014
@@ -37,7 +37,8 @@ import static org.junit.Assert.assertTru
  * Testing user import with default {@link org.apache.jackrabbit.oak.spi.xml.ImportBehavior}
  * and pw-expiry content
  *
- * @see OAK-1922
+ * @see <a href="https://issues.apache.org/jira/browse/OAK-1922">OAK-1922</a>
+ * @see <a href="https://issues.apache.org/jira/browse/OAK-1943">OAK-1943</a>
  */
 public class UserImportPwExpiryTest extends AbstractImportTest {
 
@@ -101,6 +102,40 @@ public class UserImportPwExpiryTest exte
      * @since Oak 1.1
      */
     @Test
+    public void testImportUserCreatesPasswordLastModified2() throws Exception {
+        // import user without rep:pwd child node defined
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\"x\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\">" +
+                "      <sv:value>rep:User</sv:value>" +
+                "   </sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\">" +
+                "      <sv:value>9dd4e461-268c-3034-b5c8-564e155c67a6</sv:value>" +
+                "   </sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\">" +
+                "      <sv:value>pw</sv:value>" +
+                "   </sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\">" +
+                "      <sv:value>xPrincipal</sv:value>" +
+                "   </sv:property>" +
+                "</sv:node>";
+
+        doImport(USERPATH, xml);
+
+        // verify that the pwd node has still been created
+        Authorizable authorizable = userMgr.getAuthorizable("x");
+        Node userNode = adminSession.getNode(authorizable.getPath());
+        assertTrue(userNode.hasNode(UserConstants.REP_PWD));
+        Node pwdNode = userNode.getNode(UserConstants.REP_PWD);
+        assertTrue(pwdNode.getDefinition().isProtected());
+        assertTrue(pwdNode.hasProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED));
+        assertTrue(pwdNode.getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED).getDefinition().isProtected());
+    }
+
+    /**
+     * @since Oak 1.1
+     */
+    @Test
     public void testImportUserWithPwdProperties() throws Exception {
         // import user
         String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +



Re: svn commit: r1635464 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/ oak-jcr/src/test/java/or...

Posted by Julian Reschke <ju...@gmx.de>.
On 2014-10-30 11:54, angela@apache.org wrote:
> Author: angela
> Date: Thu Oct 30 10:54:37 2014
> New Revision: 1635464
>
> URL: http://svn.apache.org/r1635464
> Log:
> OAK-2245 : UserImporter should always set the rep:authorizableID
>
> Added:
>      jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportFromJackrabbit.java
> Modified:
>      jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java
>      jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ProtectedPropertyImporter.java
 > ...

...it seems this breaks the build:

> [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.
> 5.1:compile (default-compile) on project oak-authorization-cug: Compilation fail
> ure
> [ERROR] C:\projects\apache\oak\trunk\oak-authorization-cug\src\main\java\org\apa
> che\jackrabbit\oak\spi\security\authorization\cug\impl\CugImporter.java:[50,0] o
> rg.apache.jackrabbit.oak.spi.security.authorization.cug.impl.CugImporter is not
> abstract and does not override abstract method propertiesCompleted(org.apache.ja
> ckrabbit.oak.api.Tree) in org.apache.jackrabbit.oak.spi.xml.ProtectedPropertyImp
> orter

Best regards, Julian

Re: svn commit: r1635464 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/ oak-jcr/src/test/java/or...

Posted by Julian Reschke <ju...@gmx.de>.
On 2014-10-30 11:54, angela@apache.org wrote:
> Author: angela
> Date: Thu Oct 30 10:54:37 2014
> New Revision: 1635464
>
> URL: http://svn.apache.org/r1635464
> Log:
> OAK-2245 : UserImporter should always set the rep:authorizableID
>
> Added:
>      jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportFromJackrabbit.java
> Modified:
>      jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserImporter.java
>      jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/xml/ProtectedPropertyImporter.java
 > ...

...it seems this breaks the build:

> [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.
> 5.1:compile (default-compile) on project oak-authorization-cug: Compilation fail
> ure
> [ERROR] C:\projects\apache\oak\trunk\oak-authorization-cug\src\main\java\org\apa
> che\jackrabbit\oak\spi\security\authorization\cug\impl\CugImporter.java:[50,0] o
> rg.apache.jackrabbit.oak.spi.security.authorization.cug.impl.CugImporter is not
> abstract and does not override abstract method propertiesCompleted(org.apache.ja
> ckrabbit.oak.api.Tree) in org.apache.jackrabbit.oak.spi.xml.ProtectedPropertyImp
> orter

Best regards, Julian