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

enorman pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-jackrabbit-usermanager.git


The following commit(s) were added to refs/heads/master by this push:
     new 5d7689e  SLING-9814 Add canDisable and canChangePassword methods to 
AuthorizablePrivilegesInfo
5d7689e is described below

commit 5d7689edf124275852f302d56c9e6af1fa92ef2a
Author: Eric Norman <[email protected]>
AuthorDate: Mon Oct 12 13:09:17 2020 -0700

    SLING-9814 Add canDisable and canChangePassword methods to
    AuthorizablePrivilegesInfo
---
 pom.xml                                            |    4 +-
 .../usermanager/AuthorizablePrivilegesInfo.java    |   26 +
 .../impl/AuthorizablePrivilegesInfoImpl.java       |  105 +-
 .../it/AuthorizablePrivilegesInfoIT.java           | 1295 +++++++++++---------
 .../usermanager/it/post/ChangeUserPasswordIT.java  |  258 +++-
 5 files changed, 1066 insertions(+), 622 deletions(-)

diff --git a/pom.xml b/pom.xml
index 828c719..d12d347 100644
--- a/pom.xml
+++ b/pom.xml
@@ -128,8 +128,8 @@
         </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
-            <artifactId>jackrabbit-api</artifactId>
-            <version>2.3.0</version>
+            <artifactId>oak-jackrabbit-api</artifactId>
+            <version>1.18.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git 
a/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
 
b/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
index af86136..b3db3f9 100644
--- 
a/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
+++ 
b/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
@@ -96,4 +96,30 @@ public interface AuthorizablePrivilegesInfo {
     boolean canUpdateGroupMembers(Session jcrSession,
             String groupId);
 
+    /**
+     * Checks whether the current user has been granted privileges
+     * to disable the specified user.
+     *
+     * @param jcrSession the JCR session of the current user
+     * @param userId the user id to check
+     * @return true if the current user has the privileges, false otherwise
+     */
+    default boolean canDisable(Session jcrSession,
+            String userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Checks whether the current user has been granted privileges
+     * to change the password of the specified user.
+     *
+     * @param jcrSession the JCR session of the current user
+     * @param userId the user id to check
+     * @return true if the current user has the privileges, false otherwise
+     */
+    default boolean canChangePassword(Session jcrSession,
+            String userId) {
+        throw new UnsupportedOperationException();
+    }
+
 }
\ No newline at end of file
diff --git 
a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/AuthorizablePrivilegesInfoImpl.java
 
b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/AuthorizablePrivilegesInfoImpl.java
index e7da94c..e91c31e 100644
--- 
a/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/AuthorizablePrivilegesInfoImpl.java
+++ 
b/src/main/java/org/apache/sling/jackrabbit/usermanager/impl/AuthorizablePrivilegesInfoImpl.java
@@ -36,6 +36,7 @@ import 
org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
 import org.apache.sling.commons.osgi.OsgiUtil;
 import org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo;
+import org.apache.sling.jackrabbit.usermanager.ChangeUserPassword;
 import org.apache.sling.jackrabbit.usermanager.CreateUser;
 import org.apache.sling.jcr.base.util.AccessControlUtil;
 import org.osgi.framework.BundleContext;
@@ -98,6 +99,16 @@ public class AuthorizablePrivilegesInfoImpl implements 
AuthorizablePrivilegesInf
     private String usersPath;
     private String groupsPath;
     private boolean selfRegistrationEnabled;
+    private boolean alwaysAllowSelfChangePassword = false;
+
+    @Reference(cardinality=ReferenceCardinality.OPTIONAL, policy = 
ReferencePolicy.DYNAMIC)
+    private void bindChangeUserPassword(ChangeUserPassword changeUserPassword, 
Map<String, Object> properties) {
+        alwaysAllowSelfChangePassword = 
OsgiUtil.toBoolean(properties.get("alwaysAllowSelfChangePassword"), false);
+    }
+    @SuppressWarnings("unused")
+    private void unbindChangeUserPassword(ChangeUserPassword 
changeUserPassword, Map<String, Object> properties) {
+        alwaysAllowSelfChangePassword = false;
+    }
     
     @Reference(cardinality=ReferenceCardinality.OPTIONAL, policy = 
ReferencePolicy.DYNAMIC)
     private void bindUserConfiguration(UserConfiguration userConfig, 
Map<String, Object> properties) {
@@ -266,19 +277,12 @@ public class AuthorizablePrivilegesInfoImpl implements 
AuthorizablePrivilegesInf
             PropertyUpdateTypes... propertyUpdateTypes) {
         boolean hasRights = false;
         try {
-            if (jcrSession.getUserID().equals(principalId)) {
-                //user is allowed to update it's own properties
-                hasRights = true;
-            } else {
-                UserManager userManager = 
AccessControlUtil.getUserManager(jcrSession);
-                Authorizable currentUser = 
userManager.getAuthorizable(jcrSession.getUserID());
-
-                if (currentUser instanceof User) {
-                    if (((User)currentUser).isAdmin()) {
-                        return true; //admin user has full control
-                    }
-                }
+            UserManager userManager = 
AccessControlUtil.getUserManager(jcrSession);
+            Authorizable currentUser = 
userManager.getAuthorizable(jcrSession.getUserID());
 
+            if (currentUser instanceof User && ((User)currentUser).isAdmin()) {
+                hasRights = true; //admin user has full control
+            } else {
                 Authorizable authorizable = 
userManager.getAuthorizable(principalId);
                 if (authorizable == null) {
                     log.debug("Failed to find authorizable: {}", principalId);
@@ -322,9 +326,84 @@ public class AuthorizablePrivilegesInfoImpl implements 
AuthorizablePrivilegesInf
         return hasRights;
     }
 
+    /* (non-Javadoc)
+     * @see 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canDisable(javax.jcr.Session,
 java.lang.String)
+     */
+    @Override
+    public boolean canDisable(Session jcrSession, String userId) {
+        boolean hasRights = false;
+        try {
+            UserManager userManager = 
AccessControlUtil.getUserManager(jcrSession);
+            Authorizable currentUser = 
userManager.getAuthorizable(jcrSession.getUserID());
 
-    // ---------- SCR Integration 
----------------------------------------------
+            if (currentUser instanceof User && ((User)currentUser).isAdmin()) {
+                hasRights = true; //admin user has full control
+            } else {
+                Authorizable authorizable = 
userManager.getAuthorizable(userId);
+                if (!(authorizable instanceof User)) {
+                    log.debug("Failed to find user: {}", userId);
+                } else {
+                    String path = authorizable.getPath();
+                    if (path != null) {
+                        //check if the non-admin user has sufficient rights on 
the home folder
+                        AccessControlManager acm = 
jcrSession.getAccessControlManager();
+                        Set<Privilege> requiredPrivileges = new HashSet<>();
+                        
requiredPrivileges.add(acm.privilegeFromName(Privilege.JCR_READ));
+                        
requiredPrivileges.add(acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT));
+                        hasRights = acm.hasPrivileges(path, 
requiredPrivileges.toArray(new Privilege[requiredPrivileges.size()]));
+                    }
+                }
+            }
+        } catch (RepositoryException e) {
+            log.warn("Failed to determine if {} can disable user {}", 
jcrSession.getUserID(), userId);
+        }
+        return hasRights;
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canChangePassword(javax.jcr.Session,
 java.lang.String)
+     */
+    @Override
+    public boolean canChangePassword(Session jcrSession, String userId) {
+        boolean hasRights = false;
+        try {
+            UserManager userManager = 
AccessControlUtil.getUserManager(jcrSession);
+            Authorizable currentUser = 
userManager.getAuthorizable(jcrSession.getUserID());
 
+            Authorizable authorizable = userManager.getAuthorizable(userId);
+            if (!(authorizable instanceof User)) {
+                log.debug("Failed to find user: {}", userId);
+            } else {
+                if (((User)authorizable).isSystemUser() || 
"anonymous".equals(authorizable.getID())) {
+                    hasRights = false; //system users and anonymous have no 
passwords
+                } else if (currentUser instanceof User && 
((User)currentUser).isAdmin()) {
+                    hasRights = true; //admin user has full control
+                } else {
+                    // otherwise let's check the granted privileges
+                    String path = authorizable.getPath();
+                    if (path != null) {
+                        //check if the non-admin user has sufficient rights on 
the home folder
+                        AccessControlManager acm = 
jcrSession.getAccessControlManager();
+                        Set<Privilege> requiredPrivileges = new HashSet<>();
+                        
requiredPrivileges.add(acm.privilegeFromName(Privilege.JCR_READ));
+                        
requiredPrivileges.add(acm.privilegeFromName(PrivilegeConstants.REP_USER_MANAGEMENT));
+                        hasRights = acm.hasPrivileges(path, 
requiredPrivileges.toArray(new Privilege[requiredPrivileges.size()]));
+                    }
+
+                    if (!hasRights && jcrSession.getUserID().equals(userId)) {
+                        // check if the ChangeUserPassword service is 
configured to always allow
+                        // a user to change their own password.
+                        hasRights = alwaysAllowSelfChangePassword;
+                    }
+                }
+            }
+        } catch (RepositoryException e) {
+            log.warn("Failed to determine if {} can change the password of 
user {}", jcrSession.getUserID(), userId);
+        }
+        return hasRights;
+    }
+
+    // ---------- SCR Integration 
----------------------------------------------
 
     @Activate
     protected void activate(BundleContext bundleContext, Map<String, Object> 
properties)
diff --git 
a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/AuthorizablePrivilegesInfoIT.java
 
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/AuthorizablePrivilegesInfoIT.java
index 939a4d0..fbc2347 100644
--- 
a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/AuthorizablePrivilegesInfoIT.java
+++ 
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/AuthorizablePrivilegesInfoIT.java
@@ -45,6 +45,7 @@ import 
org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
 import org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo;
 import 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo.PropertyUpdateTypes;
+import org.apache.sling.jackrabbit.usermanager.ChangeUserPassword;
 import org.apache.sling.jackrabbit.usermanager.CreateGroup;
 import org.apache.sling.jackrabbit.usermanager.CreateUser;
 import org.apache.sling.jackrabbit.usermanager.DeleteGroup;
@@ -53,6 +54,7 @@ import org.apache.sling.jackrabbit.usermanager.UpdateGroup;
 import org.apache.sling.jackrabbit.usermanager.UpdateUser;
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.jcr.base.util.AccessControlUtil;
+import org.apache.sling.jcr.jackrabbit.accessmanager.DeleteAces;
 import org.apache.sling.jcr.jackrabbit.accessmanager.ModifyAce;
 import org.apache.sling.servlets.post.Modification;
 import org.junit.After;
@@ -76,95 +78,101 @@ import org.slf4j.LoggerFactory;
 @RunWith(PaxExam.class)
 @ExamReactorStrategy(PerClass.class)
 public class AuthorizablePrivilegesInfoIT extends UserManagerTestSupport {
-       private static AtomicLong counter = new AtomicLong(0);
+    private static AtomicLong counter = new AtomicLong(0);
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
     @Inject
     protected BundleContext bundleContext;
-    
+
     @Inject
     protected SlingRepository repository;
 
-       @Inject
-       private AuthorizablePrivilegesInfo privilegesInfo;
-       
-       @Inject
-       private UserConfiguration userConfig;
-       
-       @Inject
-       private CreateUser createUser;
+    @Inject
+    private AuthorizablePrivilegesInfo privilegesInfo;
+
+    @Inject
+    private UserConfiguration userConfig;
+
+    @Inject
+    private CreateUser createUser;
 
-       @Inject
-       private CreateGroup createGroup;
+    @Inject
+    private CreateGroup createGroup;
+
+    @Inject
+    private UpdateUser updateUser;
+
+    @Inject
+    private UpdateGroup updateGroup;
 
-       @Inject
-       private UpdateUser updateUser;
+    @Inject
+    private DeleteUser deleteUser;
 
-       @Inject
-       private UpdateGroup updateGroup;
+    @Inject
+    private DeleteGroup deleteGroup;
 
-       @Inject
-       private DeleteUser deleteUser;
+    @Inject
+    private ModifyAce modifyAce;
 
-       @Inject
-       private DeleteGroup deleteGroup;
+    @Inject
+    private DeleteAces deleteAces;
 
-       @Inject
-       private ModifyAce modifyAce;
+    @Inject
+    private ChangeUserPassword changeUserPassword;
 
-       @Rule
-       public TestName testName = new TestName();
+    @Rule
+    public TestName testName = new TestName();
 
     protected Session adminSession;
     protected User user1;
     protected Session user1Session;
 
-       @Configuration
-       public Option[] configuration() {
-           return options(
-               baseConfiguration()
-           );
-       }
+    @Configuration
+    public Option[] configuration() {
+        return options(
+            baseConfiguration()
+        );
+    }
 
     @Before
     public void setup() throws Exception {
         adminSession = repository.login(new SimpleCredentials("admin", 
"admin".toCharArray()));
-               assertNotNull("Expected adminSession to not be null", 
adminSession);
-
-               user1 = createUser.createUser(adminSession, 
createUniqueName("user"), "testPwd", "testPwd", 
-                               Collections.emptyMap(), new 
ArrayList<Modification>());
-               assertNotNull("Expected user1 to not be null", user1);
-               
-               if (adminSession.hasPendingChanges()) {
-                       adminSession.save();
-               }
-               
-               user1Session = repository.login(new 
SimpleCredentials(user1.getID(), "testPwd".toCharArray()));
-               assertNotNull("Expected user1Session to not be null", 
user1Session);
+        assertNotNull("Expected adminSession to not be null", adminSession);
+
+        user1 = createUser.createUser(adminSession, createUniqueName("user"), 
"testPwd", "testPwd", 
+                Collections.emptyMap(), new ArrayList<Modification>());
+        assertNotNull("Expected user1 to not be null", user1);
+        
+        if (adminSession.hasPendingChanges()) {
+            adminSession.save();
+        }
+        
+        user1Session = repository.login(new SimpleCredentials(user1.getID(), 
"testPwd".toCharArray()));
+        assertNotNull("Expected user1Session to not be null", user1Session);
     }
 
     @After
     public void teardown() {
-       try {
-                       adminSession.refresh(false);
-                       if (user1 != null) {
-                               deleteUser.deleteUser(adminSession, 
user1.getID(), new ArrayList<>());
-                       }
-
-                       if (adminSession.hasPendingChanges()) {
-                               adminSession.save();
-                       }
-               } catch (RepositoryException e) {
-                       logger.warn("Failed to delete user: " + e.getMessage(), 
e);
-               }
-
-       user1Session.logout();
+        try {
+            adminSession.refresh(false);
+            if (user1 != null) {
+                deleteUser.deleteUser(adminSession, user1.getID(), new 
ArrayList<>());
+            }
+
+            if (adminSession.hasPendingChanges()) {
+                adminSession.save();
+            }
+        } catch (RepositoryException e) {
+            logger.warn("Failed to delete user: " + e.getMessage(), e);
+        }
+
+        user1Session.logout();
         adminSession.logout();
     }
 
-       protected String createUniqueName(String prefix) {
-               return String.format("%s_%s%d", prefix, 
testName.getMethodName(), counter.incrementAndGet());
-       }
+    protected String createUniqueName(String prefix) {
+        return String.format("%s_%s%d", prefix, testName.getMethodName(), 
counter.incrementAndGet());
+    }
 
     /**
      * Checks whether the current user has been granted privileges
@@ -172,49 +180,49 @@ public class AuthorizablePrivilegesInfoIT extends 
UserManagerTestSupport {
      */
     @Test
     public void canAddUser() throws Exception {
-               assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
-               
-               User user2 = null;
-               try {
-                       // initially user can't do the operations
-                       assertFalse("Should not be allowed to add user",
-                                       
privilegesInfo.canAddUser(user1Session));
-                       
-                       String usersPath = 
userConfig.getParameters().getConfigValue("usersPath", (String)null);
-                       assertNotNull("Users Path should not be null", 
usersPath);
-                       assertTrue("Users Path should exist",
-                                       adminSession.itemExists(usersPath));
-
-                       // grant user1 rights
-                       Map<String, String> privileges = new HashMap<>();
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ), "granted");
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ_ACCESS_CONTROL), "granted");
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_MODIFY_ACCESS_CONTROL), "granted");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_WRITE), "granted");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
-                       modifyAce.modifyAce(adminSession, usersPath, 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       assertTrue("Should be allowed to add user",
-                                       
privilegesInfo.canAddUser(user1Session));
-
-                       // verify that the user can actually add the user
-                       try {
-                               Map<String, String> propMap = new HashMap<>();
-                               propMap.put("prop1", "value1");
-                               propMap.put("nested/prop2", "value2");
-                               user2 = createUser.createUser(user1Session, 
createUniqueName("user"), "testPwd", "testPwd", 
-                                               propMap, new 
ArrayList<Modification>());
-                               assertNotNull("Expected user2 to not be null", 
user2);
-                       } catch (RepositoryException e) {
-                               logger.error("Did not expect 
RepositoryException when adding user: " + e.getMessage(), e);
-                               fail("Did not expect RepositoryException when 
adding user: " + e.getMessage());
-                       }
-               } finally {
-                       if (user2 != null) {
-                               deleteUser.deleteUser(adminSession, 
user2.getID(), new ArrayList<>());
-                       }
-               }
+        assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
+
+        User user2 = null;
+        try {
+            // initially user can't do the operations
+            assertFalse("Should not be allowed to add user",
+                    privilegesInfo.canAddUser(user1Session));
+
+            String usersPath = 
userConfig.getParameters().getConfigValue("usersPath", (String)null);
+            assertNotNull("Users Path should not be null", usersPath);
+            assertTrue("Users Path should exist",
+                    adminSession.itemExists(usersPath));
+
+            // grant user1 rights
+            Map<String, String> privileges = new HashMap<>();
+            privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ_ACCESS_CONTROL), "granted");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_MODIFY_ACCESS_CONTROL), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_WRITE), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+            modifyAce.modifyAce(adminSession, usersPath, user1.getID(),
+                    privileges,
+                    "first");
+            assertTrue("Should be allowed to add user",
+                    privilegesInfo.canAddUser(user1Session));
+
+            // verify that the user can actually add the user
+            try {
+                Map<String, String> propMap = new HashMap<>();
+                propMap.put("prop1", "value1");
+                propMap.put("nested/prop2", "value2");
+                user2 = createUser.createUser(user1Session, 
createUniqueName("user"), "testPwd", "testPwd", 
+                        propMap, new ArrayList<Modification>());
+                assertNotNull("Expected user2 to not be null", user2);
+            } catch (RepositoryException e) {
+                logger.error("Did not expect RepositoryException when adding 
user: " + e.getMessage(), e);
+                fail("Did not expect RepositoryException when adding user: " + 
e.getMessage());
+            }
+        } finally {
+            if (user2 != null) {
+                deleteUser.deleteUser(adminSession, user2.getID(), new 
ArrayList<>());
+            }
+        }
     }
 
     /**
@@ -223,71 +231,71 @@ public class AuthorizablePrivilegesInfoIT extends 
UserManagerTestSupport {
      */
     @Test
     public void canAddGroup() throws Exception {
-               assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
-               
-               workaroundMissingGroupsPath();
-
-               Group group1 = null;
-               try {
-                       // initially user can't do the operations
-                       assertFalse("Should not be allowed to add group",
-                                       
privilegesInfo.canAddGroup(user1Session));
-                       
-                       String groupsPath = 
userConfig.getParameters().getConfigValue("groupsPath", (String)null);
-                       assertNotNull("Groups Path should not be null", 
groupsPath);
-                       assertTrue("Groups Path should exist",
-                                       adminSession.itemExists(groupsPath));
-                       
-                       // grant user1 rights
-                       Map<String, String> privileges = new HashMap<>();
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ), "granted");
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ_ACCESS_CONTROL), "granted");
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_MODIFY_ACCESS_CONTROL), "granted");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_WRITE), "granted");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
-                       modifyAce.modifyAce(adminSession, groupsPath, 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       assertTrue("Should be allowed to add group",
-                                       
privilegesInfo.canAddGroup(user1Session));
-
-                       // verify that the user can actually add the user
-                       try {
-                               Map<String, String> propMap = new HashMap<>();
-                               propMap.put("prop1", "value1");
-                               propMap.put("nested/prop2", "value2");
-                               group1 = createGroup.createGroup(user1Session, 
createUniqueName("group"), 
-                                               propMap, new 
ArrayList<Modification>());
-                               assertNotNull("Expected group1 to not be null", 
group1);
-                       } catch (RepositoryException e) {
-                               logger.error("Did not expect 
RepositoryException when adding group: " + e.getMessage(), e);
-                               fail("Did not expect RepositoryException when 
adding group: " + e.getMessage());
-                       }
-               } finally {
-                       if (group1 != null) {
-                               deleteGroup.deleteGroup(user1Session, 
group1.getID(), new ArrayList<>());
-                       }
-               }
+        assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
+
+        workaroundMissingGroupsPath();
+
+        Group group1 = null;
+        try {
+            // initially user can't do the operations
+            assertFalse("Should not be allowed to add group",
+                    privilegesInfo.canAddGroup(user1Session));
+
+            String groupsPath = 
userConfig.getParameters().getConfigValue("groupsPath", (String)null);
+            assertNotNull("Groups Path should not be null", groupsPath);
+            assertTrue("Groups Path should exist",
+                    adminSession.itemExists(groupsPath));
+
+            // grant user1 rights
+            Map<String, String> privileges = new HashMap<>();
+            privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ_ACCESS_CONTROL), "granted");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_MODIFY_ACCESS_CONTROL), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_WRITE), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+            modifyAce.modifyAce(adminSession, groupsPath, user1.getID(),
+                    privileges,
+                    "first");
+            assertTrue("Should be allowed to add group",
+                    privilegesInfo.canAddGroup(user1Session));
+
+            // verify that the user can actually add the user
+            try {
+                Map<String, String> propMap = new HashMap<>();
+                propMap.put("prop1", "value1");
+                propMap.put("nested/prop2", "value2");
+                group1 = createGroup.createGroup(user1Session, 
createUniqueName("group"),
+                        propMap, new ArrayList<Modification>());
+                assertNotNull("Expected group1 to not be null", group1);
+            } catch (RepositoryException e) {
+                logger.error("Did not expect RepositoryException when adding 
group: " + e.getMessage(), e);
+                fail("Did not expect RepositoryException when adding group: " 
+ e.getMessage());
+            }
+        } finally {
+            if (group1 != null) {
+                deleteGroup.deleteGroup(user1Session, group1.getID(), new 
ArrayList<>());
+            }
+        }
     }
 
-       /**
-        * The oak groupsPath appears to be missing until the first group is 
created.
-        * So create a group as the admin user to get it bootstrapped. This 
makes it
-        * possible for non-admin users to create groups without requiring 
extra access 
-        * rights to the intermediate parents of the groupsPath folder.
-        */
-       protected void workaroundMissingGroupsPath() throws RepositoryException 
{
-               String groupsPath = 
userConfig.getParameters().getConfigValue("groupsPath", (String)null);
-               assertNotNull("Groups Path should not be null", groupsPath);
-               if (!adminSession.itemExists(groupsPath)) {
-                       // create a group and the remove it
-                       Group tempGroup = createGroup.createGroup(adminSession, 
createUniqueName("group"), 
-                                       Collections.emptyMap(), new 
ArrayList<Modification>());
-                       deleteGroup.deleteGroup(adminSession, 
tempGroup.getID(), new ArrayList<>());
-               }
-               assertTrue("Groups Path should exist",
-                               adminSession.itemExists(groupsPath));
-       }
+    /**
+     * The oak groupsPath appears to be missing until the first group is 
created.
+     * So create a group as the admin user to get it bootstrapped. This makes 
it
+     * possible for non-admin users to create groups without requiring extra 
access
+     * rights to the intermediate parents of the groupsPath folder.
+     */
+    protected void workaroundMissingGroupsPath() throws RepositoryException {
+        String groupsPath = 
userConfig.getParameters().getConfigValue("groupsPath", (String)null);
+        assertNotNull("Groups Path should not be null", groupsPath);
+        if (!adminSession.itemExists(groupsPath)) {
+            // create a group and the remove it
+            Group tempGroup = createGroup.createGroup(adminSession, 
createUniqueName("group"),
+                    Collections.emptyMap(), new ArrayList<Modification>());
+            deleteGroup.deleteGroup(adminSession, tempGroup.getID(), new 
ArrayList<>());
+        }
+        assertTrue("Groups Path should exist",
+                adminSession.itemExists(groupsPath));
+    }
 
     /**
      * Checks whether the current user has been granted privileges
@@ -295,297 +303,297 @@ public class AuthorizablePrivilegesInfoIT extends 
UserManagerTestSupport {
      */
     @Test
     public void canUpdateProperties() throws Exception {
-               assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
-
-               workaroundMissingGroupsPath();
-
-               User user2 = null;
-               Group group1 = null;
-
-               try {
-                       // create a couple of test users
-                       user2 = createUser.createUser(adminSession, 
createUniqueName("group"), "testPwd", "testPwd", 
-                                       Collections.singletonMap("prop1", 
"value1"), new ArrayList<Modification>());
-                       assertNotNull("Expected user2 to not be null", user2);
-
-                       group1 = createGroup.createGroup(adminSession, 
createUniqueName("group"), 
-                                       Collections.singletonMap("prop1", 
"value1"), new ArrayList<Modification>());
-                       assertNotNull("Expected group1 to not be null", group1);
-
-                       String [] principalIds = new String[] { user2.getID(), 
group1.getID() };
-
-                       // initially user can't do the operation
-                       for (String pid : principalIds) {
-                               assertFalse("Should not be allowed to update 
properties for: " + pid, 
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid));
-                       }
-                       
-                       // start with only read rights
-                       Map<String, String> privileges = new HashMap<>();
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ), "granted");
-                       modifyAce.modifyAce(adminSession, user2.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       for (String pid : principalIds) {
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
-                       }
-
-                       // + grant user management rights
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
-                       modifyAce.modifyAce(adminSession, user2.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       for (String pid : principalIds) {
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
-                       }
-
-                       
-                       // grant rights to only remove properties
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ADD_PROPERTIES), "none");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ALTER_PROPERTIES), "none");
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_ADD_CHILD_NODES), "none");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_REMOVE_PROPERTIES), "granted");
-                       modifyAce.modifyAce(adminSession, user2.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       for (String pid : principalIds) {
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
-                       }
-                       
-                       // verify that the user can actually delete property
-                       Map<String, Object> propsMap = new HashMap<>();
-                       propsMap.put("prop1@Delete", "value1");
-                       try {
-                               updateUser.updateUser(user1Session, 
user2.getID(), propsMap, new ArrayList<>());
-                               updateGroup.updateGroup(user1Session, 
group1.getID(), propsMap, new ArrayList<>());
-                               assertTrue("Expected pending changes in the jcr 
session", user1Session.hasPendingChanges());
-                               user1Session.save();
-                       } catch (RepositoryException e) {
-                               logger.error("Did not expect 
RepositoryException when deleting property: " + e.getMessage(), e);
-                               fail("Did not expect RepositoryException when 
deleting property: " + e.getMessage());
-                       }
-                       // verify that the user can not add nested property
-                       propsMap = new HashMap<>();
-                       propsMap.put("nested/prop2", "value2");
-                       updateUser.updateUser(user1Session, user2.getID(), 
propsMap, new ArrayList<>());
-                       updateGroup.updateGroup(user1Session, group1.getID(), 
propsMap, new ArrayList<>());
-                       assertTrue("Expected pending changes in the jcr 
session", user1Session.hasPendingChanges());
-                       try {
-                               user1Session.save();
-                               fail("Expected AccessDeniedException when 
adding nested property");
-                       } catch (AccessDeniedException e) {
-                               // expected
-                               user1Session.refresh(false);
-                       }
-                       
-                       
-                       
-                       // grant rights to only alter (non-nested) properties
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ADD_PROPERTIES), "granted");
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_ADD_CHILD_NODES), "none");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_REMOVE_PROPERTIES), "none");
-                       modifyAce.modifyAce(adminSession, user2.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       for (String pid : principalIds) {
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
-                       }
-                       
-                       // verify that the user can actually add property
-                       propsMap = new HashMap<>();
-                       propsMap.put("prop1", "value1");
-                       try {
-                               updateUser.updateUser(user1Session, 
user2.getID(), propsMap, new ArrayList<>());
-                               updateGroup.updateGroup(user1Session, 
group1.getID(), propsMap, new ArrayList<>());
-                               assertTrue("Expected pending changes in the jcr 
session", user1Session.hasPendingChanges());
-                               user1Session.save();
-                       } catch (RepositoryException e) {
-                               logger.error("Did not expect 
RepositoryException when adding property: " + e.getMessage(), e);
-                               fail("Did not expect RepositoryException when 
adding property: " + e.getMessage());
-                       }
-                       // verify that the user can not add nested property
-                       propsMap.put("nested/prop2", "value2");
-                       updateUser.updateUser(user1Session, user2.getID(), 
propsMap, new ArrayList<>());
-                       updateGroup.updateGroup(user1Session, group1.getID(), 
propsMap, new ArrayList<>());
-                       assertTrue("Expected pending changes in the jcr 
session", user1Session.hasPendingChanges());
-                       try {
-                               user1Session.save();
-                               fail("Expected AccessDeniedException when 
adding nested property");
-                       } catch (AccessDeniedException e) {
-                               // expected
-                               user1Session.refresh(false);
-                       }
-                       
-                       
-                       
-                       // grant rights to alter (non-nested or nested) 
properties
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ADD_PROPERTIES), "granted");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ALTER_PROPERTIES), "granted");
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_ADD_CHILD_NODES), "granted");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_REMOVE_PROPERTIES), "none");
-                       modifyAce.modifyAce(adminSession, user2.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       for (String pid : principalIds) {
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
-                               assertFalse("Should not be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
-                       }
-                       
-                       // verify that the user can actually add property and 
nested property
-                       propsMap = new HashMap<>();
-                       propsMap.put("prop1", "value1");
-                       propsMap.put("nested/prop2", "value2");
-                       try {
-                               updateUser.updateUser(user1Session, 
user2.getID(), propsMap, new ArrayList<>());
-                               updateGroup.updateGroup(user1Session, 
group1.getID(), propsMap, new ArrayList<>());
-                               assertTrue("Expected pending changes in the jcr 
session", user1Session.hasPendingChanges());
-                               user1Session.save();
-                       } catch (RepositoryException e) {
-                               logger.error("Did not expect 
RepositoryException when adding properties: " + e.getMessage(), e);
-                               fail("Did not expect RepositoryException when 
adding properties: " + e.getMessage());
-                       }
-                       
-                       
-                       
-                       // grant rights to alter (non-nested or nested) 
properties and remove properties
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ADD_PROPERTIES), "granted");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ALTER_PROPERTIES), "granted");
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_ADD_CHILD_NODES), "granted");
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_REMOVE_PROPERTIES), "granted");
-                       modifyAce.modifyAce(adminSession, user2.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       for (String pid : principalIds) {
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
-                               assertTrue("Should be allowed to update 
properties for: " + pid,
-                                               
privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
-                       }                       
-                       
-                       // verify that the user can actually add property and 
nested property
-                       propsMap = new HashMap<>();
-                       propsMap.put("prop3", "value3");
-                       propsMap.put("nested/prop4", "value4");
-                       propsMap.put("prop1@Delete", "value1");
-                       propsMap.put("nested/prop2@Delete", "value1");
-                       try {
-                               updateUser.updateUser(user1Session, 
user2.getID(), propsMap, new ArrayList<>());
-                               updateGroup.updateGroup(user1Session, 
group1.getID(), propsMap, new ArrayList<>());
-                               assertTrue("Expected pending changes in the jcr 
session", user1Session.hasPendingChanges());
-                               user1Session.save();
-                       } catch (RepositoryException e) {
-                               logger.error("Did not expect 
RepositoryException when adding or deleting properties: " + e.getMessage(), e);
-                               fail("Did not expect RepositoryException when 
adding or deleting properties: " + e.getMessage());
-                       }
-               } finally {
-                       if (user2 != null) {
-                               deleteUser.deleteUser(adminSession, 
user2.getID(), new ArrayList<>());
-                       }
-               }
+        assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
+
+        workaroundMissingGroupsPath();
+
+        User user2 = null;
+        Group group1 = null;
+
+        try {
+            // create a couple of test users
+            user2 = createUser.createUser(adminSession, 
createUniqueName("user"), "testPwd", "testPwd",
+                    Collections.singletonMap("prop1", "value1"), new 
ArrayList<Modification>());
+            assertNotNull("Expected user2 to not be null", user2);
+
+            group1 = createGroup.createGroup(adminSession, 
createUniqueName("group"),
+                    Collections.singletonMap("prop1", "value1"), new 
ArrayList<Modification>());
+            assertNotNull("Expected group1 to not be null", group1);
+
+            String [] principalIds = new String[] { user2.getID(), 
group1.getID() };
+
+            // initially user can't do the operation
+            for (String pid : principalIds) {
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid));
+            }
+
+            // start with only read rights
+            Map<String, String> privileges = new HashMap<>();
+            privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            for (String pid : principalIds) {
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
+            }
+
+            // + grant user management rights
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            for (String pid : principalIds) {
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
+            }
+
+
+            // grant rights to only remove properties
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ADD_PROPERTIES), "none");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ALTER_PROPERTIES), "none");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_ADD_CHILD_NODES), "none");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_REMOVE_PROPERTIES), "granted");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            for (String pid : principalIds) {
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
+            }
+
+            // verify that the user can actually delete property
+            Map<String, Object> propsMap = new HashMap<>();
+            propsMap.put("prop1@Delete", "value1");
+            try {
+                updateUser.updateUser(user1Session, user2.getID(), propsMap, 
new ArrayList<>());
+                updateGroup.updateGroup(user1Session, group1.getID(), 
propsMap, new ArrayList<>());
+                assertTrue("Expected pending changes in the jcr session", 
user1Session.hasPendingChanges());
+                user1Session.save();
+            } catch (RepositoryException e) {
+                logger.error("Did not expect RepositoryException when deleting 
property: " + e.getMessage(), e);
+                fail("Did not expect RepositoryException when deleting 
property: " + e.getMessage());
+            }
+            // verify that the user can not add nested property
+            propsMap = new HashMap<>();
+            propsMap.put("nested/prop2", "value2");
+            updateUser.updateUser(user1Session, user2.getID(), propsMap, new 
ArrayList<>());
+            updateGroup.updateGroup(user1Session, group1.getID(), propsMap, 
new ArrayList<>());
+            assertTrue("Expected pending changes in the jcr session", 
user1Session.hasPendingChanges());
+            try {
+                user1Session.save();
+                fail("Expected AccessDeniedException when adding nested 
property");
+            } catch (AccessDeniedException e) {
+                // expected
+                user1Session.refresh(false);
+            }
+
+
+
+            // grant rights to only alter (non-nested) properties
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ADD_PROPERTIES), "granted");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_ADD_CHILD_NODES), "none");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_REMOVE_PROPERTIES), "none");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            for (String pid : principalIds) {
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
+            }
+
+            // verify that the user can actually add property
+            propsMap = new HashMap<>();
+            propsMap.put("prop1", "value1");
+            try {
+                updateUser.updateUser(user1Session, user2.getID(), propsMap, 
new ArrayList<>());
+                updateGroup.updateGroup(user1Session, group1.getID(), 
propsMap, new ArrayList<>());
+                assertTrue("Expected pending changes in the jcr session", 
user1Session.hasPendingChanges());
+                user1Session.save();
+            } catch (RepositoryException e) {
+                logger.error("Did not expect RepositoryException when adding 
property: " + e.getMessage(), e);
+                fail("Did not expect RepositoryException when adding property: 
" + e.getMessage());
+            }
+            // verify that the user can not add nested property
+            propsMap.put("nested/prop2", "value2");
+            updateUser.updateUser(user1Session, user2.getID(), propsMap, new 
ArrayList<>());
+            updateGroup.updateGroup(user1Session, group1.getID(), propsMap, 
new ArrayList<>());
+            assertTrue("Expected pending changes in the jcr session", 
user1Session.hasPendingChanges());
+            try {
+                user1Session.save();
+                fail("Expected AccessDeniedException when adding nested 
property");
+            } catch (AccessDeniedException e) {
+                // expected
+                user1Session.refresh(false);
+            }
+
+
+
+            // grant rights to alter (non-nested or nested) properties
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ADD_PROPERTIES), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ALTER_PROPERTIES), "granted");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_ADD_CHILD_NODES), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_REMOVE_PROPERTIES), "none");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            for (String pid : principalIds) {
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
+                assertFalse("Should not be allowed to update properties for: " 
+ pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
+            }
+
+            // verify that the user can actually add property and nested 
property
+            propsMap = new HashMap<>();
+            propsMap.put("prop1", "value1");
+            propsMap.put("nested/prop2", "value2");
+            try {
+                updateUser.updateUser(user1Session, user2.getID(), propsMap, 
new ArrayList<>());
+                updateGroup.updateGroup(user1Session, group1.getID(), 
propsMap, new ArrayList<>());
+                assertTrue("Expected pending changes in the jcr session", 
user1Session.hasPendingChanges());
+                user1Session.save();
+            } catch (RepositoryException e) {
+                logger.error("Did not expect RepositoryException when adding 
properties: " + e.getMessage(), e);
+                fail("Did not expect RepositoryException when adding 
properties: " + e.getMessage());
+            }
+
+
+
+            // grant rights to alter (non-nested or nested) properties and 
remove properties
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ADD_PROPERTIES), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ALTER_PROPERTIES), "granted");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_ADD_CHILD_NODES), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_REMOVE_PROPERTIES), "granted");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            for (String pid : principalIds) {
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty, 
PropertyUpdateTypes.removeProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.addNestedProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty, PropertyUpdateTypes.removeProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty, PropertyUpdateTypes.removeProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.addNestedProperty));
+                assertTrue("Should be allowed to update properties for: " + 
pid,
+                        privilegesInfo.canUpdateProperties(user1Session, pid, 
PropertyUpdateTypes.removeProperty));
+            }
+
+            // verify that the user can actually add property and nested 
property
+            propsMap = new HashMap<>();
+            propsMap.put("prop3", "value3");
+            propsMap.put("nested/prop4", "value4");
+            propsMap.put("prop1@Delete", "value1");
+            propsMap.put("nested/prop2@Delete", "value1");
+            try {
+                updateUser.updateUser(user1Session, user2.getID(), propsMap, 
new ArrayList<>());
+                updateGroup.updateGroup(user1Session, group1.getID(), 
propsMap, new ArrayList<>());
+                assertTrue("Expected pending changes in the jcr session", 
user1Session.hasPendingChanges());
+                user1Session.save();
+            } catch (RepositoryException e) {
+                logger.error("Did not expect RepositoryException when adding 
or deleting properties: " + e.getMessage(), e);
+                fail("Did not expect RepositoryException when adding or 
deleting properties: " + e.getMessage());
+            }
+        } finally {
+            if (user2 != null) {
+                deleteUser.deleteUser(adminSession, user2.getID(), new 
ArrayList<>());
+            }
+        }
     }
 
     /**
@@ -594,74 +602,74 @@ public class AuthorizablePrivilegesInfoIT extends 
UserManagerTestSupport {
      */
     @Test
     public void canRemove() throws Exception {
-               assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
-               
-               workaroundMissingGroupsPath();
-               
-               User user2 = null;
-               Group group1 = null;
-               try {
-                       user2 = createUser.createUser(adminSession, 
createUniqueName("user"), "testPwd", "testPwd", 
-                                       Collections.singletonMap("prop1", 
"value1"), new ArrayList<Modification>());
-                       assertNotNull("Expected user2 to not be null", user2);
-
-                       group1 = createGroup.createGroup(adminSession, 
createUniqueName("group"), 
-                                       Collections.singletonMap("prop1", 
"value1"), new ArrayList<Modification>());
-                       assertNotNull("Expected group1 to not be null", group1);
-
-                       // initially user can't do the operations
-                       assertFalse("Should not be allowed to remove user", 
-                                       privilegesInfo.canRemove(user1Session, 
user2.getID()));
-                       assertFalse("Should not be allowed to remove group", 
-                                       privilegesInfo.canRemove(user1Session, 
group1.getID()));
-                       
-                       // grant user1 rights to user2 profile
-                       Map<String, String> privileges = new HashMap<>();
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ), "granted");
-                       modifyAce.modifyAce(adminSession, user2.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       assertFalse("Should not be allowed to remove user", 
-                                       privilegesInfo.canRemove(user1Session, 
user2.getID()));
-                       assertFalse("Should not be allowed to remove group", 
-                                       privilegesInfo.canRemove(user1Session, 
group1.getID()));
-
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
-                       modifyAce.modifyAce(adminSession, user2.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       assertTrue("Should be allowed to remove user", 
-                                       privilegesInfo.canRemove(user1Session, 
user2.getID()));
-                       assertTrue("Should be allowed to remove group", 
-                                       privilegesInfo.canRemove(user1Session, 
group1.getID()));
-
-                       // verify that the user can actually delete the user
-                       String user2Id = user2.getID();
-                       deleteUser.deleteUser(user1Session, user2Id, new 
ArrayList<>());
-                       user2 = null;
-                       // verify the user is no longer there
-                       UserManager um = 
AccessControlUtil.getUserManager(user1Session);
-                       assertNull("Expected user to be gone: " + user2Id, 
um.getAuthorizable(user2Id));
-                       
-                       // verify that the user can actually delete the group
-                       String group1Id = group1.getID();
-                       deleteGroup.deleteGroup(user1Session, group1Id, new 
ArrayList<>());
-                       group1 = null;
-                       assertNull("Expected group to be gone: " + group1Id, 
um.getAuthorizable(group1Id));
-               } finally {
-                       if (user2 != null) {
-                               deleteUser.deleteUser(adminSession, 
user2.getID(), new ArrayList<>());
-                       }
-                       if (group1 != null) {
-                               deleteGroup.deleteGroup(adminSession, 
group1.getID(), new ArrayList<>());
-                       }
-               }
+        assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
+
+        workaroundMissingGroupsPath();
+
+        User user2 = null;
+        Group group1 = null;
+        try {
+            user2 = createUser.createUser(adminSession, 
createUniqueName("user"), "testPwd", "testPwd",
+                    Collections.singletonMap("prop1", "value1"), new 
ArrayList<Modification>());
+            assertNotNull("Expected user2 to not be null", user2);
+
+            group1 = createGroup.createGroup(adminSession, 
createUniqueName("group"),
+                    Collections.singletonMap("prop1", "value1"), new 
ArrayList<Modification>());
+            assertNotNull("Expected group1 to not be null", group1);
+
+            // initially user can't do the operations
+            assertFalse("Should not be allowed to remove user",
+                    privilegesInfo.canRemove(user1Session, user2.getID()));
+            assertFalse("Should not be allowed to remove group",
+                    privilegesInfo.canRemove(user1Session, group1.getID()));
+
+            // grant user1 rights to user2 profile
+            Map<String, String> privileges = new HashMap<>();
+            privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            assertFalse("Should not be allowed to remove user",
+                    privilegesInfo.canRemove(user1Session, user2.getID()));
+            assertFalse("Should not be allowed to remove group",
+                    privilegesInfo.canRemove(user1Session, group1.getID()));
+
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            assertTrue("Should be allowed to remove user",
+                    privilegesInfo.canRemove(user1Session, user2.getID()));
+            assertTrue("Should be allowed to remove group",
+                    privilegesInfo.canRemove(user1Session, group1.getID()));
+
+            // verify that the user can actually delete the user
+            String user2Id = user2.getID();
+            deleteUser.deleteUser(user1Session, user2Id, new ArrayList<>());
+            user2 = null;
+            // verify the user is no longer there
+            UserManager um = AccessControlUtil.getUserManager(user1Session);
+            assertNull("Expected user to be gone: " + user2Id, 
um.getAuthorizable(user2Id));
+
+            // verify that the user can actually delete the group
+            String group1Id = group1.getID();
+            deleteGroup.deleteGroup(user1Session, group1Id, new ArrayList<>());
+            group1 = null;
+            assertNull("Expected group to be gone: " + group1Id, 
um.getAuthorizable(group1Id));
+        } finally {
+            if (user2 != null) {
+                deleteUser.deleteUser(adminSession, user2.getID(), new 
ArrayList<>());
+            }
+            if (group1 != null) {
+                deleteGroup.deleteGroup(adminSession, group1.getID(), new 
ArrayList<>());
+            }
+        }
     }
 
     /**
@@ -670,52 +678,197 @@ public class AuthorizablePrivilegesInfoIT extends 
UserManagerTestSupport {
      */
     @Test
     public void canUpdateGroupMembers() throws Exception {
-               assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
-               
-               workaroundMissingGroupsPath();
-               
-               Group group1 = null;
-               try {
-                       group1 = createGroup.createGroup(adminSession, 
createUniqueName("group"), 
-                                       Collections.singletonMap("prop1", 
"value1"), new ArrayList<Modification>());
-                       assertNotNull("Expected group1 to not be null", group1);
-
-                       // initially user can't do the operations
-                       assertFalse("Should not be allowed to update group 
members", 
-                                       
privilegesInfo.canUpdateGroupMembers(user1Session, group1.getID()));
-                       
-                       // grant user1 rights to group1 profile
-                       Map<String, String> privileges = new HashMap<>();
-                       privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ), "granted");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       assertFalse("Should not be allowed to update group 
members", 
-                                       
privilegesInfo.canUpdateGroupMembers(user1Session, group1.getID()));
-
-                       privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
-                       modifyAce.modifyAce(adminSession, group1.getPath(), 
user1.getID(), 
-                                       privileges, 
-                                       "first");
-                       assertTrue("Should be allowed to update group members", 
-                                       
privilegesInfo.canUpdateGroupMembers(user1Session, group1.getID()));
-
-                       // verify that the user can actually change the group 
members
-                       try {
-                               Map<String, Object> propsMap = new HashMap<>();
-                               propsMap.put(":member", user1.getID());
-                               updateGroup.updateGroup(user1Session, 
group1.getID(), propsMap, new ArrayList<>());
-                               assertTrue("Expected pending changes in the jcr 
session", user1Session.hasPendingChanges());
-                               user1Session.save();
-                       } catch (RepositoryException e) {
-                               logger.error("Did not expect 
RepositoryException when adding member to group: " + e.getMessage(), e);
-                               fail("Did not expect RepositoryException when 
adding member to group: " + e.getMessage());
-                       }
-               } finally {
-                       if (group1 != null) {
-                               deleteGroup.deleteGroup(adminSession, 
group1.getID(), new ArrayList<>());
-                       }
-               }
+        assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
+
+        workaroundMissingGroupsPath();
+
+        Group group1 = null;
+        try {
+            group1 = createGroup.createGroup(adminSession, 
createUniqueName("group"),
+                    Collections.singletonMap("prop1", "value1"), new 
ArrayList<Modification>());
+            assertNotNull("Expected group1 to not be null", group1);
+
+            // initially user can't do the operations
+            assertFalse("Should not be allowed to update group members",
+                    privilegesInfo.canUpdateGroupMembers(user1Session, 
group1.getID()));
+
+            // grant user1 rights to group1 profile
+            Map<String, String> privileges = new HashMap<>();
+            privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            assertFalse("Should not be allowed to update group members",
+                    privilegesInfo.canUpdateGroupMembers(user1Session, 
group1.getID()));
+
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+            modifyAce.modifyAce(adminSession, group1.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            assertTrue("Should be allowed to update group members",
+                    privilegesInfo.canUpdateGroupMembers(user1Session, 
group1.getID()));
+
+            // verify that the user can actually change the group members
+            try {
+                Map<String, Object> propsMap = new HashMap<>();
+                propsMap.put(":member", user1.getID());
+                updateGroup.updateGroup(user1Session, group1.getID(), 
propsMap, new ArrayList<>());
+                assertTrue("Expected pending changes in the jcr session", 
user1Session.hasPendingChanges());
+                user1Session.save();
+            } catch (RepositoryException e) {
+                logger.error("Did not expect RepositoryException when adding 
member to group: " + e.getMessage(), e);
+                fail("Did not expect RepositoryException when adding member to 
group: " + e.getMessage());
+            }
+        } finally {
+            if (group1 != null) {
+                deleteGroup.deleteGroup(adminSession, group1.getID(), new 
ArrayList<>());
+            }
+        }
+    }
+
+    protected void configMinimumUserPrivileges(User user) throws 
RepositoryException {
+        //change the ACE for the user home folder to the minimum privileges
+        // and without rep:userManagement
+        deleteAces.deleteAces(adminSession, user.getPath(), new String[] 
{user.getID()});
+        Map<String, String> privileges = new HashMap<>();
+        privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
+        privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ALTER_PROPERTIES), "granted");
+        modifyAce.modifyAce(adminSession, user.getPath(), user.getID(),
+                privileges,
+                "first");
+        if (adminSession.hasPendingChanges()) {
+            adminSession.save();
+        }
+    }
+
+    /**
+     * SLING-9814 Checks whether the current user has been granted privileges
+     * to disable a user.
+     */
+    @Test
+    public void canDisableUser() throws Exception {
+        assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
+
+        User user2 = null;
+        try {
+            // setup the user privileges
+            configMinimumUserPrivileges(user1);
+
+            // create another test user
+            user2 = createUser.createUser(adminSession, 
createUniqueName("user"), "testPwd", "testPwd",
+                    Collections.singletonMap("prop1", "value1"), new 
ArrayList<Modification>());
+            assertNotNull("Expected user2 to not be null", user2);
+            // setup the user privileges
+            configMinimumUserPrivileges(user2);
+
+            // initially user can't do the operations
+            assertFalse("Should not be allowed to disable yourself",
+                    privilegesInfo.canDisable(user1Session, user1.getID()));
+            assertFalse("Should not be allowed to disable user",
+                    privilegesInfo.canDisable(user1Session, user2.getID()));
+
+            // grant user1 rights to user2 profile
+            Map<String, String> privileges = new HashMap<>();
+            privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            assertTrue("Should be allowed to disable user",
+                    privilegesInfo.canDisable(user1Session, user2.getID()));
+
+            // verify that the user can actually disable the other user
+            Map<String, Object> propsMap = new HashMap<>();
+            propsMap.put(":disabled", "true");
+            propsMap.put(":disabledReason", "Just a test");
+            try {
+                updateUser.updateUser(user1Session, user2.getID(), propsMap, 
new ArrayList<>());
+                assertTrue("Expected pending changes in the jcr session", 
user1Session.hasPendingChanges());
+                user1Session.save();
+            } catch (RepositoryException e) {
+                logger.error("Did not expect RepositoryException when 
disabling the user: " + e.getMessage(), e);
+                fail("Did not expect RepositoryException when disabling the 
user: " + e.getMessage());
+            }
+        } finally {
+            if (user2 != null) {
+                deleteUser.deleteUser(adminSession, user2.getID(), new 
ArrayList<>());
+            }
+        }
+    }
+
+    /**
+     * SLING-9814 Checks whether the current user has been granted privileges
+     * to change a user's password.
+     */
+    @Test
+    public void canChangePassword() throws Exception {
+        assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
+
+        User user2 = null;
+        try {
+            // create another test user
+            user2 = createUser.createUser(adminSession, 
createUniqueName("user"), "testPwd", "testPwd",
+                    Collections.singletonMap("prop1", "value1"), new 
ArrayList<Modification>());
+            assertNotNull("Expected user2 to not be null", user2);
+
+            // initially user can't do the operations
+            assertFalse("Should not be allowed to change the user password",
+                    privilegesInfo.canChangePassword(user1Session, 
user2.getID()));
+
+            // grant user1 rights to user2 profile
+            Map<String, String> privileges = new HashMap<>();
+            privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+            modifyAce.modifyAce(adminSession, user2.getPath(), user1.getID(),
+                    privileges,
+                    "first");
+            assertTrue("Should be allowed to change the user password user",
+                    privilegesInfo.canChangePassword(user1Session, 
user2.getID()));
+
+            // verify that the user can actually change the password of the 
other user
+            try {
+                changeUserPassword.changePassword(user1Session, user2.getID(), 
"testPwd", "newPassword", "newPassword", new ArrayList<>());
+                assertTrue("Expected pending changes in the jcr session", 
user1Session.hasPendingChanges());
+                user1Session.save();
+            } catch (RepositoryException e) {
+                logger.error("Did not expect RepositoryException when changing 
the user password: " + e.getMessage(), e);
+                fail("Did not expect RepositoryException when changing the 
user password: " + e.getMessage());
+            }
+        } finally {
+            if (user2 != null) {
+                deleteUser.deleteUser(adminSession, user2.getID(), new 
ArrayList<>());
+            }
+        }
     }
-    
+
+    /**
+     * SLING-9814 Checks whether the current user has been granted privileges
+     * to change the anonymous user's password.
+     */
+    @Test
+    public void cannotChangePassword_for_anonymous() throws Exception {
+        assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
+
+        // anonymous user has no password to change
+        assertFalse("Should not be allowed to change the user password",
+                privilegesInfo.canChangePassword(user1Session, "anonymous"));
+        assertFalse("Should not be allowed to change the user password",
+                privilegesInfo.canChangePassword(adminSession, "anonymous"));
+    }
+
+    /**
+     * SLING-9814 Checks whether the current user has been granted privileges
+     * to change the anonymous user's password.
+     */
+    @Test
+    public void cannotChangePassword_for_service_user() throws Exception {
+        assertNotNull("Expected privilegesInfo to not be null", 
privilegesInfo);
+
+        // service user has no password to change
+        assertFalse("Should not be allowed to change the user password",
+                privilegesInfo.canChangePassword(user1Session, 
"sling-jcr-usermanager"));
+        assertFalse("Should not be allowed to change the user password",
+                privilegesInfo.canChangePassword(adminSession, 
"sling-jcr-usermanager"));
+    }
+
 }
diff --git 
a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/ChangeUserPasswordIT.java
 
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/ChangeUserPasswordIT.java
index 007575f..782d683 100644
--- 
a/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/ChangeUserPasswordIT.java
+++ 
b/src/test/java/org/apache/sling/jcr/jackrabbit/usermanager/it/post/ChangeUserPasswordIT.java
@@ -19,6 +19,7 @@
 package org.apache.sling.jcr.jackrabbit.usermanager.it.post;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -41,7 +42,9 @@ import javax.jcr.security.Privilege;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo;
 import org.apache.sling.jackrabbit.usermanager.ChangeUserPassword;
+import org.apache.sling.jackrabbit.usermanager.CreateGroup;
 import org.apache.sling.jackrabbit.usermanager.CreateUser;
 import org.apache.sling.jackrabbit.usermanager.DeleteUser;
 import org.apache.sling.jcr.api.SlingRepository;
@@ -79,17 +82,20 @@ public class ChangeUserPasswordIT extends 
UserManagerTestSupport {
 
     @Inject
     protected BundleContext bundleContext;
-    
+
     @Inject
     protected SlingRepository repository;
 
     @Inject
     protected ConfigurationAdmin configAdmin;
-    
+
     @Inject
     private CreateUser createUser;
 
     @Inject
+    private CreateGroup createGroup;
+
+    @Inject
     private ModifyAce modifyAce;
 
     @Inject
@@ -98,6 +104,9 @@ public class ChangeUserPasswordIT extends 
UserManagerTestSupport {
     @Inject
     private DeleteUser deleteUser;
 
+    @Inject
+    private AuthorizablePrivilegesInfo privilegesInfo;
+
     @Rule
     public TestName testName = new TestName();
 
@@ -117,40 +126,40 @@ public class ChangeUserPasswordIT extends 
UserManagerTestSupport {
         adminSession = repository.login(new SimpleCredentials("admin", 
"admin".toCharArray()));
         assertNotNull("Expected adminSession to not be null", adminSession);
 
-        user1 = createUser.createUser(adminSession, createUniqueName("user"), 
"testPwd", "testPwd", 
+        user1 = createUser.createUser(adminSession, createUniqueName("user"), 
"testPwd", "testPwd",
                 Collections.emptyMap(), new ArrayList<Modification>());
         assertNotNull("Expected user1 to not be null", user1);
-        
+
         user1Session = repository.login(new SimpleCredentials(user1.getID(), 
"testPwd".toCharArray()));
         assertNotNull("Expected user1Session to not be null", user1Session);
-        
+
         //change the ACE for the user home folder to the minimum privileges
         // and without rep:userManagement
         deleteAces.deleteAces(adminSession, user1.getPath(), new String[] 
{user1.getID()});
         Map<String, String> privileges = new HashMap<>();
         privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
         privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_ALTER_PROPERTIES), "granted");
-        modifyAce.modifyAce(adminSession, user1.getPath(), user1.getID(), 
-                privileges, 
+        modifyAce.modifyAce(adminSession, user1.getPath(), user1.getID(),
+                privileges,
                 "first");
         if (adminSession.hasPendingChanges()) {
-               adminSession.save();
+            adminSession.save();
         }
     }
 
     @After
     public void teardown() {
-       try {
-                       adminSession.refresh(false);
-                       if (user1 != null) {
-                               deleteUser.deleteUser(adminSession, 
user1.getID(), new ArrayList<>());
-                       }
-                       if (adminSession.hasPendingChanges()) {
-                               adminSession.save();
-                       }
-               } catch (RepositoryException e) {
-                       logger.warn("Failed to delete user: " + e.getMessage(), 
e);
-               }
+        try {
+            adminSession.refresh(false);
+            if (user1 != null) {
+                deleteUser.deleteUser(adminSession, user1.getID(), new 
ArrayList<>());
+            }
+            if (adminSession.hasPendingChanges()) {
+                adminSession.save();
+            }
+        } catch (RepositoryException e) {
+            logger.warn("Failed to delete user: " + e.getMessage(), e);
+        }
 
         user1Session.logout();
         adminSession.logout();
@@ -172,18 +181,23 @@ public class ChangeUserPasswordIT extends 
UserManagerTestSupport {
             // update the service configuration to ensure the option is enabled
             Dictionary<String, Object> newServiceProps = 
replaceConfigProp(originalServiceProps, "alwaysAllowSelfChangePassword", 
Boolean.TRUE);
             configuration.update(newServiceProps);
-            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class, 
+            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class,
                     "alwaysAllowSelfChangePassword", Boolean.TRUE);
-            
+
             serviceReference = 
bundleContext.getServiceReference(ChangeUserPassword.class);
             assertEquals(Boolean.TRUE, 
serviceReference.getProperty("alwaysAllowSelfChangePassword"));
             ChangeUserPassword changeUserPassword = 
bundleContext.getService(serviceReference);
             assertNotNull(changeUserPassword);
-            changeUserPassword.changePassword(user1Session, 
-                    user1.getID(), 
-                    "testPwd", 
-                    "testPwdChanged", 
-                    "testPwdChanged", 
+
+            // user can do the operation
+            assertTrue("Should be allowed to change the user password",
+                    privilegesInfo.canChangePassword(user1Session, 
user1.getID()));
+
+            changeUserPassword.changePassword(user1Session,
+                    user1.getID(),
+                    "testPwd",
+                    "testPwdChanged",
+                    "testPwdChanged",
                     new ArrayList<>());
             try {
                 user1Session.save();
@@ -199,7 +213,7 @@ public class ChangeUserPasswordIT extends 
UserManagerTestSupport {
             
             //put the original config back
             configuration.update(originalServiceProps);
-            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class, "alwaysAllowSelfChangePassword", 
+            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class, "alwaysAllowSelfChangePassword",
                     originalServiceProps == null ? null 
:originalServiceProps.get("alwaysAllowSelfChangePassword"));
         }
     }
@@ -216,18 +230,23 @@ public class ChangeUserPasswordIT extends 
UserManagerTestSupport {
             // update the service configuration to ensure the option is 
disabled
             Dictionary<String, Object> newServiceProps = 
replaceConfigProp(originalServiceProps, "alwaysAllowSelfChangePassword", 
Boolean.FALSE);
             configuration.update(newServiceProps);
-            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class, 
+            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class,
                     "alwaysAllowSelfChangePassword", Boolean.FALSE);
-            
+
             serviceReference = 
bundleContext.getServiceReference(ChangeUserPassword.class);
             assertEquals(Boolean.FALSE, 
serviceReference.getProperty("alwaysAllowSelfChangePassword"));
             ChangeUserPassword changeUserPassword = 
bundleContext.getService(serviceReference);
             assertNotNull(changeUserPassword);
-            changeUserPassword.changePassword(user1Session, 
-                    user1.getID(), 
-                    "testPwd", 
-                    "testPwdChanged", 
-                    "testPwdChanged", 
+
+            // user can't do the operation
+            assertFalse("Should not be allowed to change the user password",
+                    privilegesInfo.canChangePassword(user1Session, 
user1.getID()));
+
+            changeUserPassword.changePassword(user1Session,
+                    user1.getID(),
+                    "testPwd",
+                    "testPwdChanged",
+                    "testPwdChanged",
                     new ArrayList<>());
             assertTrue(user1Session.hasPendingChanges());
             try {
@@ -244,12 +263,179 @@ public class ChangeUserPasswordIT extends 
UserManagerTestSupport {
                 // done with this.
                 bundleContext.ungetService(serviceReference);
             }
-            
+
             //put the original config back
             configuration.update(originalServiceProps);
-            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class, "alwaysAllowSelfChangePassword", 
+            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class, "alwaysAllowSelfChangePassword",
                     originalServiceProps == null ? null 
:originalServiceProps.get("alwaysAllowSelfChangePassword"));
         }
     }
 
+    /**
+     * test changing your own password without sending the old password is not 
allowed
+     */
+    @Test
+    public void changePasswordAsSelfWithoutOldPasswordFails() throws Exception 
{
+        org.osgi.service.cm.Configuration configuration = 
configAdmin.getConfiguration("org.apache.sling.jackrabbit.usermanager.impl.post.ChangeUserPasswordServlet",
 null);
+        Dictionary<String, Object> originalServiceProps = 
configuration.getProperties();
+        ServiceReference<ChangeUserPassword> serviceReference = null;
+        try {
+            // update the service configuration to ensure the option is enabled
+            Dictionary<String, Object> newServiceProps = 
replaceConfigProp(originalServiceProps, "alwaysAllowSelfChangePassword", 
Boolean.TRUE);
+            configuration.update(newServiceProps);
+            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class,
+                    "alwaysAllowSelfChangePassword", Boolean.TRUE);
+
+            serviceReference = 
bundleContext.getServiceReference(ChangeUserPassword.class);
+            assertEquals(Boolean.TRUE, 
serviceReference.getProperty("alwaysAllowSelfChangePassword"));
+            ChangeUserPassword changeUserPassword = 
bundleContext.getService(serviceReference);
+            assertNotNull(changeUserPassword);
+
+            // user can do the operation
+            assertTrue("Should be allowed to change the user password",
+                    privilegesInfo.canChangePassword(user1Session, 
user1.getID()));
+
+            // no oldPassword submitted
+            try {
+                changeUserPassword.changePassword(user1Session,
+                        user1.getID(),
+                        null,
+                        "testPwdChanged",
+                        "testPwdChanged",
+                        new ArrayList<>());
+                fail("Expected a RepositoryException when changing user 
passsword.");
+            } catch (RepositoryException e) {
+                assertEquals("Old Password was not submitted", e.getMessage());
+                user1Session.refresh(false);
+            }
+
+            // empty oldPassword submitted
+            try {
+                changeUserPassword.changePassword(user1Session,
+                        user1.getID(),
+                        "",
+                        "testPwdChanged2",
+                        "testPwdChanged2",
+                        new ArrayList<>());
+                fail("Expected a RepositoryException when changing user 
passsword.");
+            } catch (RepositoryException e) {
+                assertEquals("Old Password was not submitted", e.getMessage());
+                user1Session.refresh(false);
+            }
+
+        } finally {
+            if (serviceReference != null) {
+                // done with this.
+                bundleContext.ungetService(serviceReference);
+            }
+
+            //put the original config back
+            configuration.update(originalServiceProps);
+            new WaitForServiceUpdated(5000, 100, bundleContext, 
ChangeUserPassword.class, "alwaysAllowSelfChangePassword",
+                    originalServiceProps == null ? null 
:originalServiceProps.get("alwaysAllowSelfChangePassword"));
+        }
+    }
+
+    /**
+     * test changing a user's password without sending the old password is 
allowed if the current
+     * user is a member of the UserAdmin group.
+     */
+    @Test
+    public void changePasswordAsUserAdminMemberWithoutOldPassword() throws 
Exception {
+        User user2 = null;
+        ServiceReference<ChangeUserPassword> serviceReference = null;
+        try {
+            // create a second user to attempt the change password on
+            user2 = createUser.createUser(adminSession, 
createUniqueName("user"), "testPwd", "testPwd",
+                    Collections.emptyMap(), new ArrayList<Modification>());
+            if (adminSession.hasPendingChanges()) {
+                adminSession.save();
+            }
+
+            // figure out what the user admin group name has been configured as
+            serviceReference = 
bundleContext.getServiceReference(ChangeUserPassword.class);
+            String userAdminGroup = 
(String)serviceReference.getProperty("user.admin.group.name");
+            if (userAdminGroup == null || userAdminGroup.isEmpty()) {
+                userAdminGroup = "UserAdmin"; // fallback to the default
+            }
+
+            // add user1 to the UserAdmin group
+            createGroup.createGroup(adminSession, userAdminGroup,
+                    Collections.singletonMap(":member", user1.getID()), new 
ArrayList<>());
+            if (adminSession.hasPendingChanges()) {
+                adminSession.save();
+            }
+
+            //make sure the UserAdmin group has the expected privileges granted
+            Map<String, String> privileges = new HashMap<>();
+            privileges.put(String.format("privilege@%s", Privilege.JCR_READ), 
"granted");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_READ_ACCESS_CONTROL), "granted");
+            privileges.put(String.format("privilege@%s", 
Privilege.JCR_MODIFY_ACCESS_CONTROL), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_WRITE), "granted");
+            privileges.put(String.format("privilege@%s", 
PrivilegeConstants.REP_USER_MANAGEMENT), "granted");
+            modifyAce.modifyAce(adminSession, user2.getPath(), userAdminGroup,
+                    privileges,
+                    "first");
+
+
+            // create a fresh session so changes from the adminSession are 
picked up
+            user1Session.logout();
+            user1Session = repository.login(new 
SimpleCredentials(user1.getID(), "testPwd".toCharArray()));
+            assertNotNull("Expected user1Session to not be null", 
user1Session);
+
+            ChangeUserPassword changeUserPassword = 
bundleContext.getService(serviceReference);
+            assertNotNull(changeUserPassword);
+
+            // user can do the operation
+            assertTrue("Should be allowed to change the user password",
+                    privilegesInfo.canChangePassword(user1Session, 
user2.getID()));
+
+            // no oldPassword submitted
+            try {
+                changeUserPassword.changePassword(user1Session,
+                        user2.getID(),
+                        null,
+                        "testPwdChanged",
+                        "testPwdChanged",
+                        new ArrayList<>());
+            } catch (RepositoryException e) {
+                fail("Did not expect a RepositoryException when changing user 
passsword.");
+            }
+            try {
+                user1Session.save();
+            } catch (AccessDeniedException e) {
+                logger.error("Did not expect AccessDeniedException when 
changing user passsword: " + e.getMessage(), e);
+                fail("Did not expect AccessDeniedException when changing user 
passsword: " + e.getMessage());
+            }
+
+            // empty oldPassword submitted
+            try {
+                changeUserPassword.changePassword(user1Session,
+                        user2.getID(),
+                        "",
+                        "testPwdChanged2",
+                        "testPwdChanged2",
+                        new ArrayList<>());
+            } catch (RepositoryException e) {
+                fail("Did not expect a RepositoryException when changing user 
passsword.");
+            }
+            try {
+                user1Session.save();
+            } catch (AccessDeniedException e) {
+                logger.error("Did not expect AccessDeniedException when 
changing user passsword: " + e.getMessage(), e);
+                fail("Did not expect AccessDeniedException when changing user 
passsword: " + e.getMessage());
+            }
+
+        } finally {
+            if (user2 != null) {
+                deleteUser.deleteUser(adminSession, user2.getID(), new 
ArrayList<>());
+            }
+
+            if (serviceReference != null) {
+                // done with this.
+                bundleContext.ungetService(serviceReference);
+            }
+        }
+    }
+
 }

Reply via email to