You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2012/01/24 11:10:27 UTC

svn commit: r1235192 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/security/authentication/ main/java/org/apache/jackrabbit/core/security/user/ main/java/org/apache/jackrabbit/core/security/user/action/ test/java/org/...

Author: angela
Date: Tue Jan 24 10:10:27 2012
New Revision: 1235192

URL: http://svn.apache.org/viewvc?rev=1235192&view=rev
Log:
JCR-3218 : UserImporter should trigger execution AuthorizableActions in case of user/group creation

Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/CryptedSimpleCredentials.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/action/PasswordValidationAction.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableActionTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/CryptedSimpleCredentials.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/CryptedSimpleCredentials.java?rev=1235192&r1=1235191&r2=1235192&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/CryptedSimpleCredentials.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/CryptedSimpleCredentials.java Tue Jan 24 10:10:27 2012
@@ -22,6 +22,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
 import javax.jcr.SimpleCredentials;
 import java.io.UnsupportedEncodingException;
 import java.security.NoSuchAlgorithmException;
@@ -168,6 +169,23 @@ public class CryptedSimpleCredentials im
     }
 
     /**
+     * Creates a hash of the specified password if it is found to be plain text.
+     *
+     * @param password
+     * @return
+     * @throws javax.jcr.RepositoryException
+     */
+    public static String buildPasswordHash(String password) throws RepositoryException {
+        try {
+            return new CryptedSimpleCredentials("_", password).getPassword();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RepositoryException(e);
+        } catch (UnsupportedEncodingException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+    /**
      * @param pwd Plain text password
      * @param algorithm The algorithm to be used for the digest.
      * @param salt The salt to be used for the digest.

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java?rev=1235192&r1=1235191&r2=1235192&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java Tue Jan 24 10:10:27 2012
@@ -48,18 +48,13 @@ public class UserImpl extends Authorizab
     /**
      * Creates a hash of the specified password if it is found to be plain text.
      * 
-     * @param password
-     * @return
-     * @throws RepositoryException
+     * @param password The password string.
+     * @return Hash for the given password string.
+     * @throws RepositoryException If an error occurs.
+     * @see CryptedSimpleCredentials#buildPasswordHash(String)
      */
     static String buildPasswordValue(String password) throws RepositoryException {
-        try {
-            return new CryptedSimpleCredentials("_", password).getPassword();
-        } catch (NoSuchAlgorithmException e) {
-            throw new RepositoryException(e);
-        } catch (UnsupportedEncodingException e) {
-            throw new RepositoryException(e);
-        }
+        return CryptedSimpleCredentials.buildPasswordHash(password);
     }
 
     //-------------------------------------------------------< Authorizable >---

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java?rev=1235192&r1=1235191&r2=1235192&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java Tue Jan 24 10:10:27 2012
@@ -140,6 +140,17 @@ public class UserImporter implements Pro
 
     private int importBehavior = ImportBehavior.IGNORE;
 
+    /**
+     * Container used to collect group members stored in protected nodes.
+     */
+    private Membership currentMembership;
+
+    /**
+     * 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);
+
     public boolean init(JackrabbitSession session, NamePathResolver resolver,
                         boolean isWorkspaceImport,
                         int uuidBehavior, ReferenceChangeTracker referenceTracker) {
@@ -182,8 +193,10 @@ public class UserImporter implements Pro
         return initialized;
     }
 
-    // -----------------------------------------------------< ProtectedPropertyImporter >---
-
+    // -----------------------------------------< ProtectedPropertyImporter >---
+    /**
+     * @see ProtectedPropertyImporter#handlePropInfo(org.apache.jackrabbit.core.NodeImpl, org.apache.jackrabbit.core.xml.PropInfo, org.apache.jackrabbit.spi.QPropertyDefinition)
+     */
     public boolean handlePropInfo(NodeImpl parent, PropInfo protectedPropInfo, QPropertyDefinition def) throws RepositoryException {
         if (!initialized) {
             throw new IllegalStateException("Not initialized");
@@ -222,6 +235,22 @@ public class UserImporter implements Pro
                 Value v = protectedPropInfo.getValues(PropertyType.STRING, resolver)[0];
                 String princName = v.getString();
                 userManager.setPrincipal(parent, new PrincipalImpl(princName));
+
+                /*
+                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.
+                */
+                if (parent.isNew()) {
+                    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 (UserConstants.P_PASSWORD.equals(propName)) {
                 if (a.isGroup()) {
@@ -236,8 +265,23 @@ public class UserImporter implements Pro
                 }
 
                 Value v = protectedPropInfo.getValues(PropertyType.STRING, resolver)[0];
-                ((User) a).changePassword(v.getString());
+                String pw = v.getString();
+                ((User) a).changePassword(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.isNew()) {
+                    if (parent.hasProperty(UserConstants.P_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 (UserConstants.P_IMPERSONATORS.equals(propName)) {
@@ -314,10 +358,16 @@ public class UserImporter implements Pro
         }
     }
 
+    /**
+     * @see ProtectedPropertyImporter#handlePropInfo(org.apache.jackrabbit.core.NodeImpl, org.apache.jackrabbit.core.xml.PropInfo, org.apache.jackrabbit.spi.QPropertyDefinition)
+     */
     public boolean handlePropInfo(NodeState parent, PropInfo protectedPropInfo, QPropertyDefinition def) throws RepositoryException {
         return false;
     }
 
+    /**
+     * @see org.apache.jackrabbit.core.xml.ProtectedPropertyImporter#processReferences()
+     */
     public void processReferences() throws RepositoryException {
         if (!initialized) {
             throw new IllegalStateException("Not initialized");
@@ -494,25 +544,10 @@ public class UserImporter implements Pro
         }
     }
 
-    //------------------------------------------------------------< private >---
-    private void handleFailure(String msg) throws RepositoryException {
-        switch (importBehavior) {
-            case ImportBehavior.IGNORE:
-            case ImportBehavior.BESTEFFORT:
-                log.warn(msg);
-                break;
-            case ImportBehavior.ABORT:
-                throw new ConstraintViolationException(msg);
-            default:
-                // no other behavior. nothing to do.
-
-        }
-    }
-
-    // -----------------------------------------------------< ProtectedNodeImporter >---
-
-    private Membership currentMembership;
-
+    // ---------------------------------------------< ProtectedNodeImporter >---
+    /**
+     * @see ProtectedNodeImporter#start(org.apache.jackrabbit.core.NodeImpl)
+     */
     public boolean start(NodeImpl protectedParent) throws RepositoryException {
         String repMembers = resolver.getJCRName(UserConstants.NT_REP_MEMBERS);
         if (repMembers.equals(protectedParent.getPrimaryNodeType().getName())) {
@@ -537,10 +572,16 @@ public class UserImporter implements Pro
         }
     }
 
+    /**
+     * @see ProtectedNodeImporter#start(org.apache.jackrabbit.core.state.NodeState)
+     */
     public boolean start(NodeState protectedParent) {
         return false;
     }
 
+    /**
+     * @see ProtectedNodeImporter#start(org.apache.jackrabbit.core.NodeImpl)
+     */
     public void startChildInfo(NodeInfo childInfo, List<PropInfo> propInfos) throws RepositoryException {
         assert (currentMembership != null);
 
@@ -558,14 +599,23 @@ public class UserImporter implements Pro
         }
     }
 
+    /**
+     * @see org.apache.jackrabbit.core.xml.ProtectedNodeImporter#endChildInfo()
+     */
     public void endChildInfo() throws RepositoryException {
     }
 
+    /**
+     * @see ProtectedNodeImporter#end(org.apache.jackrabbit.core.NodeImpl)
+     */
     public void end(NodeImpl protectedParent) throws RepositoryException {
         referenceTracker.processedReference(currentMembership);
         currentMembership = null;
     }
 
+    /**
+     * @see ProtectedNodeImporter#end(org.apache.jackrabbit.core.state.NodeState)
+     */
     public void end(NodeState protectedParent) {
     }
 
@@ -585,7 +635,28 @@ public class UserImporter implements Pro
         this.importBehavior = ImportBehavior.valueFromName(importBehaviorStr);
     }
 
-    //--------------------------------------------------------------------------
+    //------------------------------------------------------------< private >---
+    /**
+     * Handling the import behavior
+     *
+     * @param msg
+     * @throws RepositoryException
+     */
+    private void handleFailure(String msg) throws RepositoryException {
+        switch (importBehavior) {
+            case ImportBehavior.IGNORE:
+            case ImportBehavior.BESTEFFORT:
+                log.warn(msg);
+                break;
+            case ImportBehavior.ABORT:
+                throw new ConstraintViolationException(msg);
+            default:
+                // no other behavior. nothing to do.
+
+        }
+    }
+
+    //------------------------------------------------------< inner classes >---
     /**
      * Inner class used to postpone import of group membership to the very end
      * of the import. This allows to import membership of user/groups that

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java?rev=1235192&r1=1235191&r2=1235192&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java Tue Jan 24 10:10:27 2012
@@ -1049,7 +1049,7 @@ public class UserManagerImpl extends Pro
      * @param pw The password.
      * @throws RepositoryException If an exception occurs.
      */
-    private void onCreate(User user, String pw) throws RepositoryException {
+    void onCreate(User user, String pw) throws RepositoryException {
         for (AuthorizableAction action : authorizableActions) {
             action.onCreate(user, pw, session);
         }
@@ -1063,7 +1063,7 @@ public class UserManagerImpl extends Pro
      * @param group The new group.
      * @throws RepositoryException If an exception occurs.
      */
-    private void onCreate(Group group) throws RepositoryException {
+    void onCreate(Group group) throws RepositoryException {
         for (AuthorizableAction action : authorizableActions) {
             action.onCreate(group, session);
         }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/action/PasswordValidationAction.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/action/PasswordValidationAction.java?rev=1235192&r1=1235191&r2=1235192&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/action/PasswordValidationAction.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/action/PasswordValidationAction.java Tue Jan 24 10:10:27 2012
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.core.security.user.action;
 
 import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.core.security.authentication.CryptedSimpleCredentials;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -100,11 +101,19 @@ public class PasswordValidationAction ex
      * doesn't match the specified password pattern.
      */
     private void validatePassword(String password) throws RepositoryException {
-        if (password != null) {
+        if (password != null && isPlainText(password)) {
             if (pattern != null && !pattern.matcher(password).matches()) {
                 throw new ConstraintViolationException("Password violates password constraint (" + pattern.pattern() + ").");
             }
         }
     }
 
+    private static boolean isPlainText(String password) {
+        try {
+            return !CryptedSimpleCredentials.buildPasswordHash(password).equals(password);
+        } catch (RepositoryException e) {
+            // failed to build hash from pw -> proceed with the validation.
+            return true;
+        }
+    }
 }
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableActionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableActionTest.java?rev=1235192&r1=1235191&r2=1235192&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableActionTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableActionTest.java Tue Jan 24 10:10:27 2012
@@ -230,7 +230,27 @@ public class AuthorizableActionTest exte
         }
     }
 
+    public void testPasswordValidationActionIgnoresHashedPwString() throws Exception {
+        User u = null;
 
+        try {
+            String uid = getTestPrincipal().getName();
+            u = impl.createUser(uid, buildPassword(uid));
+
+            PasswordValidationAction pwAction = new PasswordValidationAction();
+            pwAction.setConstraint("^.*(?=.{8,})(?=.*[a-z])(?=.*[A-Z]).*");
+            setActions(pwAction);
+
+            String hashed = ((UserImpl) u).buildPasswordValue("DWkej32H");
+            u.changePassword(hashed);
+
+        } finally {
+            if (u != null) {
+                u.remove();
+            }
+            save(superuser);
+        }
+    }
 
     //--------------------------------------------------------------------------
     private class TestAction extends AbstractAuthorizableAction {

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java?rev=1235192&r1=1235191&r2=1235192&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java Tue Jan 24 10:10:27 2012
@@ -33,6 +33,8 @@ import org.apache.jackrabbit.core.config
 import org.apache.jackrabbit.core.security.SecurityConstants;
 import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
 import org.apache.jackrabbit.core.security.user.UserImporter.ImportBehavior;
+import org.apache.jackrabbit.core.security.user.action.AccessControlAction;
+import org.apache.jackrabbit.core.security.user.action.AuthorizableAction;
 import org.apache.jackrabbit.core.util.ReferenceChangeTracker;
 import org.apache.jackrabbit.core.xml.ImportHandler;
 import org.apache.jackrabbit.core.xml.ProtectedNodeImporter;
@@ -69,7 +71,11 @@ import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.retention.RetentionManager;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlList;
 import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.Privilege;
 import javax.jcr.version.VersionException;
 import javax.security.auth.Subject;
 import java.io.ByteArrayInputStream;
@@ -128,6 +134,7 @@ public class UserImporterTest extends Ab
         }
         sImpl.save();
 
+        // make sure the target node for group-import exists
         Authorizable administrators = umgr.getAuthorizable(SecurityConstants.ADMINISTRATORS_NAME);
         if (administrators == null) {
             groupIdToRemove = umgr.createGroup(new PrincipalImpl(SecurityConstants.ADMINISTRATORS_NAME)).getID();
@@ -655,8 +662,23 @@ public class UserImporterTest extends Ab
     }
 
     public void testImportNewMembers() throws IOException, RepositoryException, SAXException, NotExecutableException {
-        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><sv:node sv:name=\"gFolder\" 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:AuthorizableFolder</sv:value></sv:property><sv:node sv:name=\"g\"><sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:Group</sv:value></sv:property><sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>b2f5ff47-4366-31b6-a533-d8dc3614845d</sv:value></sv:property><sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>g</sv:value></sv:property></sv:node><sv:node sv:name=\"g1\"><sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:Group</sv:value></sv:property><sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>0120a4f9-196a-3f9e-b9f5-23f31f914da7</sv:value></sv:property><sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>g1</sv:value></sv:property><sv:property sv:name=\"rep:members\" sv:type=\"WeakReference\"><sv:value>b2f5ff47-4366-31b6-a533-d8dc3614845d</sv:value></sv:property></sv:node></sv:no
 de>";
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+                "<sv:node sv:name=\"gFolder\" 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:AuthorizableFolder</sv:value>" +
+                "</sv:property>" +
+                "<sv:node sv:name=\"g\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:Group</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>b2f5ff47-4366-31b6-a533-d8dc3614845d</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>g</sv:value></sv:property>" +
+                "</sv:node>" +
+                "<sv:node sv:name=\"g1\">" +
+                "   <sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:Group</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>0120a4f9-196a-3f9e-b9f5-23f31f914da7</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>g1</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:members\" sv:type=\"WeakReference\"><sv:value>b2f5ff47-4366-31b6-a533-d8dc3614845d</sv:value></sv:property>" +
+                "</sv:node>" +
+                "</sv:node>";
 
         NodeImpl target = (NodeImpl) sImpl.getNode(umgr.getGroupsPath());
         try {
@@ -1326,6 +1348,165 @@ public class UserImporterTest extends Ab
         }
     }
 
+    public void testActionExecutionForUser() throws Exception {
+        TestAction testAction = new TestAction();
+
+        umgr.setAuthorizableActions(new AuthorizableAction[] {testAction});
+
+        // import user
+        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>pw</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>tPrincipal</sv:value></sv:property>" +
+                "</sv:node>";
+
+        NodeImpl target = (NodeImpl) sImpl.getNode(umgr.getUsersPath());
+        try {
+            doImport(target, xml);
+            assertEquals(testAction.id, "t");
+            assertEquals(testAction.pw, "pw");
+        } finally {
+            sImpl.refresh(false);
+        }
+    }
+
+    public void testActionExecutionForGroup() throws Exception {
+        TestAction testAction = new TestAction();
+
+        umgr.setAuthorizableActions(new AuthorizableAction[] {testAction});
+
+        // import group
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\"g\" 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:Group</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>b2f5ff47-4366-31b6-a533-d8dc3614845d</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>gPrincipal</sv:value></sv:property>" +
+                "</sv:node>";
+
+        NodeImpl target = (NodeImpl) sImpl.getNode(umgr.getGroupsPath());
+        try {
+            doImport(target, xml);
+            assertEquals(testAction.id, "g");
+            assertNull(testAction.pw);
+        } finally {
+            sImpl.refresh(false);
+        }
+    }
+
+    public void testAccessControlActionExecutionForUser() throws Exception {
+        AccessControlAction a1 = new AccessControlAction();
+        a1.setUserPrivilegeNames(Privilege.JCR_ALL);
+
+        umgr.setAuthorizableActions(new AuthorizableAction[] {a1});
+
+        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>tPrincipal</sv:value></sv:property>" +
+                "</sv:node>";
+
+        NodeImpl target = (NodeImpl) sImpl.getNode(umgr.getUsersPath());
+        try {
+            doImport(target, xml);
+
+            Authorizable a = umgr.getAuthorizable("t");
+            assertNotNull(a);
+            assertFalse(a.isGroup());
+
+            AccessControlManager acMgr = sImpl.getAccessControlManager();
+            AccessControlPolicy[] policies = acMgr.getPolicies(a.getPath());
+            assertNotNull(policies);
+            assertEquals(1, policies.length);
+            assertTrue(policies[0] instanceof AccessControlList);
+
+            AccessControlEntry[] aces = ((AccessControlList) policies[0]).getAccessControlEntries();
+            assertEquals(1, aces.length);
+            assertEquals("tPrincipal", aces[0].getPrincipal().getName());
+
+        } finally {
+            sImpl.refresh(false);
+        }
+    }
+
+    public void testAccessControlActionExecutionForUser2() throws Exception {
+        AccessControlAction a1 = new AccessControlAction();
+        a1.setUserPrivilegeNames(Privilege.JCR_ALL);
+
+        umgr.setAuthorizableActions(new AuthorizableAction[] {a1});
+
+        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:principalName\" sv:type=\"String\"><sv:value>tPrincipal</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:password\" sv:type=\"String\"><sv:value>{sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375</sv:value></sv:property>" +
+                "</sv:node>";
+
+        NodeImpl target = (NodeImpl) sImpl.getNode(umgr.getUsersPath());
+        try {
+            doImport(target, xml);
+
+            Authorizable a = umgr.getAuthorizable("t");
+            assertNotNull(a);
+            assertFalse(a.isGroup());
+
+            AccessControlManager acMgr = sImpl.getAccessControlManager();
+            AccessControlPolicy[] policies = acMgr.getPolicies(a.getPath());
+            assertNotNull(policies);
+            assertEquals(1, policies.length);
+            assertTrue(policies[0] instanceof AccessControlList);
+
+            AccessControlEntry[] aces = ((AccessControlList) policies[0]).getAccessControlEntries();
+            assertEquals(1, aces.length);
+            assertEquals("tPrincipal", aces[0].getPrincipal().getName());
+
+        } finally {
+            sImpl.refresh(false);
+        }
+    }
+
+    public void testAccessControlActionExecutionForGroup() throws Exception {
+        AccessControlAction a1 = new AccessControlAction();
+        a1.setGroupPrivilegeNames(Privilege.JCR_READ);
+
+        umgr.setAuthorizableActions(new AuthorizableAction[] {a1});
+
+        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<sv:node sv:name=\"g\" 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:Group</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"jcr:uuid\" sv:type=\"String\"><sv:value>b2f5ff47-4366-31b6-a533-d8dc3614845d</sv:value></sv:property>" +
+                "   <sv:property sv:name=\"rep:principalName\" sv:type=\"String\"><sv:value>gPrincipal</sv:value></sv:property>" +
+                "</sv:node>";
+
+        NodeImpl target = (NodeImpl) sImpl.getNode(umgr.getGroupsPath());
+        try {
+            doImport(target, xml);
+
+            Authorizable a = umgr.getAuthorizable("g");
+            assertNotNull(a);
+            assertTrue(a.isGroup());
+
+            AccessControlManager acMgr = sImpl.getAccessControlManager();
+            AccessControlPolicy[] policies = acMgr.getPolicies(a.getPath());
+            assertNotNull(policies);
+            assertEquals(1, policies.length);
+            assertTrue(policies[0] instanceof AccessControlList);
+
+            AccessControlEntry[] aces = ((AccessControlList) policies[0]).getAccessControlEntries();
+            assertEquals(1, aces.length);
+            assertEquals("gPrincipal", aces[0].getPrincipal().getName());
+
+        } finally {
+            sImpl.refresh(false);
+        }
+    }
+
+    //--------------------------------------------------------------------------
+
     private void doImport(NodeImpl target, String xml) throws IOException, SAXException, RepositoryException {
         InputStream in = new ByteArrayInputStream(xml.getBytes("UTF-8"));
         SessionImporter importer = new SessionImporter(target, sImpl,
@@ -1377,6 +1558,25 @@ public class UserImporterTest extends Ab
 
     }
 
+    private final class TestAction implements AuthorizableAction {
+        private String id;
+        private String pw;
+
+        public void onCreate(Group group, Session session) throws RepositoryException {
+            id = group.getID();
+        }
+        public void onCreate(User user, String password, Session session) throws RepositoryException {
+            id = user.getID();
+            pw = password;
+        }
+        public void onRemove(Authorizable authorizable, Session session) throws RepositoryException {
+            // ignore
+        }
+        public void onPasswordChange(User user, String newPassword, Session session) throws RepositoryException {
+            pw = newPassword;
+        }
+    }
+
     private final class DummySession implements JackrabbitSession {
 
         private DummySession() {