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 abea574  SLING-12202 add canChangePasswordWithoutOldPassword (#24)
abea574 is described below

commit abea5742f5bd668685a0407f0ff7de686d6a733e
Author: Eric Norman <enor...@apache.org>
AuthorDate: Mon Dec 18 14:55:11 2023 -0800

    SLING-12202 add canChangePasswordWithoutOldPassword (#24)
---
 .../usermanager/AuthorizablePrivilegesInfo.java    | 16 +++++
 .../impl/AuthorizablePrivilegesInfoImpl.java       | 45 +++++++++++--
 .../sling/jackrabbit/usermanager/package-info.java |  2 +-
 .../AuthorizablePrivilegesInfoTest.java            | 75 ++++++++++++++++++++++
 .../it/AuthorizablePrivilegesInfoIT.java           | 44 +++++++++++++
 5 files changed, 177 insertions(+), 5 deletions(-)

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 19ffb7b..ff03cf8 100644
--- 
a/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
+++ 
b/src/main/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfo.java
@@ -18,6 +18,8 @@ package org.apache.sling.jackrabbit.usermanager;
 
 import javax.jcr.Session;
 
+import org.jetbrains.annotations.NotNull;
+
 public interface AuthorizablePrivilegesInfo {
 
     /**
@@ -171,4 +173,18 @@ public interface AuthorizablePrivilegesInfo {
         throw new UnsupportedOperationException();
     }
 
+    /**
+     * Checks whether the current user has been granted privileges
+     * to change the password of the specified user without knowing
+     * the current password of the 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 canChangePasswordWithoutOldPassword(@NotNull Session 
jcrSession,
+            @NotNull 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 3e0edbb..0b82fc8 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
@@ -37,6 +37,7 @@ 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.jetbrains.annotations.NotNull;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -98,6 +99,7 @@ public class AuthorizablePrivilegesInfoImpl implements 
AuthorizablePrivilegesInf
     private String groupsPath;
     private boolean selfRegistrationEnabled;
     private boolean allowSelfChangePassword = false;
+    private String userAdminGroupName;
 
     @Reference(cardinality=ReferenceCardinality.OPTIONAL, policy = 
ReferencePolicy.DYNAMIC)
     private void bindChangeUserPassword(ChangeUserPassword changeUserPassword, 
Map<String, Object> properties) {
@@ -108,6 +110,8 @@ public class AuthorizablePrivilegesInfoImpl implements 
AuthorizablePrivilegesInf
         } else {
             allowSelfChangePassword = 
OsgiUtil.toBoolean(properties.get("allowSelfChangePassword"), false);
         }
+
+        userAdminGroupName = 
OsgiUtil.toString(properties.get(PAR_USER_ADMIN_GROUP_NAME), 
DEFAULT_USER_ADMIN_GROUP_NAME);
     }
     @SuppressWarnings("unused")
     private void unbindChangeUserPassword(ChangeUserPassword 
changeUserPassword, Map<String, Object> properties) {
@@ -381,17 +385,50 @@ public class AuthorizablePrivilegesInfoImpl implements 
AuthorizablePrivilegesInf
         return hasRights;
     }
 
+    /* (non-Javadoc)
+     * @see 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canChangePasswordWithoutOldPassword(javax.jcr.Session,
 java.lang.String)
+     */
+    @Override
+    public boolean canChangePasswordWithoutOldPassword(@NotNull Session 
jcrSession, @NotNull String userId) {
+        boolean can = false;
+        try {
+            // can't change your own password without the old password
+            if (!jcrSession.getUserID().equals(userId)) {
+                UserManager um = AccessControlUtil.getUserManager(jcrSession);
+                Authorizable currentUser = 
um.getAuthorizable(jcrSession.getUserID());
+                if (currentUser instanceof User) {
+                    Authorizable targetUser = um.getAuthorizable(userId);
+                    //system users and anonymous have no passwords
+                    if (targetUser instanceof User && 
!((User)targetUser).isSystemUser() && !"anonymous".equals(targetUser.getID())) {
+                        if (((User)currentUser).isAdmin()) {
+                            can = true;
+                        } else if (userAdminGroupName != null) {
+                            Authorizable group = 
um.getAuthorizable(userAdminGroupName);
+                            if (group instanceof Group) {
+                                can = ((Group)group).isMember(currentUser);
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (RepositoryException e) {
+            log.warn("Failed to determine if {} is a user admin", userId);
+        }
+        return can;
+    }
+
+
     // ---------- SCR Integration 
----------------------------------------------
 
     @Activate
     protected void activate(BundleContext bundleContext, Map<String, Object> 
properties) {
-        String userAdminGroupName = 
OsgiUtil.toString(properties.get(PAR_USER_ADMIN_GROUP_NAME), null);
-        if ( userAdminGroupName != null && ! 
DEFAULT_USER_ADMIN_GROUP_NAME.equals(userAdminGroupName)) {
+        String deprecatedUserAdminGroupName = 
OsgiUtil.toString(properties.get(PAR_USER_ADMIN_GROUP_NAME), null);
+        if ( deprecatedUserAdminGroupName != null && ! 
DEFAULT_USER_ADMIN_GROUP_NAME.equals(deprecatedUserAdminGroupName)) {
             log.warn("Configuration setting for {} is deprecated and will not 
have any effect", PAR_USER_ADMIN_GROUP_NAME);
         }
 
-        String groupAdminGroupName = 
OsgiUtil.toString(properties.get(PAR_GROUP_ADMIN_GROUP_NAME), null);
-        if ( groupAdminGroupName != null && ! 
DEFAULT_GROUP_ADMIN_GROUP_NAME.equals(userAdminGroupName)) {
+        String deprecatedGroupAdminGroupName = 
OsgiUtil.toString(properties.get(PAR_GROUP_ADMIN_GROUP_NAME), null);
+        if ( deprecatedGroupAdminGroupName != null && ! 
DEFAULT_GROUP_ADMIN_GROUP_NAME.equals(deprecatedUserAdminGroupName)) {
             log.warn("Configuration setting for {} is deprecated and will not 
have any effect", PAR_GROUP_ADMIN_GROUP_NAME);
         }
     }
diff --git 
a/src/main/java/org/apache/sling/jackrabbit/usermanager/package-info.java 
b/src/main/java/org/apache/sling/jackrabbit/usermanager/package-info.java
index 003bd56..c3526b4 100644
--- a/src/main/java/org/apache/sling/jackrabbit/usermanager/package-info.java
+++ b/src/main/java/org/apache/sling/jackrabbit/usermanager/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@org.osgi.annotation.versioning.Version("2.5.0")
+@org.osgi.annotation.versioning.Version("2.6.0")
 package org.apache.sling.jackrabbit.usermanager;
 
 
diff --git 
a/src/test/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfoTest.java
 
b/src/test/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfoTest.java
index b53504a..fa08608 100644
--- 
a/src/test/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfoTest.java
+++ 
b/src/test/java/org/apache/sling/jackrabbit/usermanager/AuthorizablePrivilegesInfoTest.java
@@ -18,6 +18,8 @@ package org.apache.sling.jackrabbit.usermanager;
 
 import static org.junit.Assert.*;
 
+import javax.jcr.Session;
+
 import 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo.PropertyUpdateTypes;
 import org.junit.Test;
 
@@ -26,6 +28,38 @@ import org.junit.Test;
  */
 public class AuthorizablePrivilegesInfoTest {
 
+    /**
+     * An implementation to facilitate testing of default methods in the 
interface
+     */
+    public static class TestDefaultMethodsAuthorizablePrivlegesInfo implements 
AuthorizablePrivilegesInfo {
+
+        @Override
+        public boolean canAddUser(Session jcrSession) {
+            return false;
+        }
+
+        @Override
+        public boolean canAddGroup(Session jcrSession) {
+            return false;
+        }
+
+        @Override
+        public boolean canUpdateProperties(Session jcrSession, String 
principalId) {
+            return false;
+        }
+
+        @Override
+        public boolean canRemove(Session jcrSession, String principalId) {
+            return false;
+        }
+
+        @Override
+        public boolean canUpdateGroupMembers(Session jcrSession, String 
groupId) {
+            return false;
+        }
+
+    }
+
     @SuppressWarnings("deprecation")
     @Test
     public void testConvertDeprecated() {
@@ -38,4 +72,45 @@ public class AuthorizablePrivilegesInfoTest {
         assertEquals(PropertyUpdateTypes.REMOVE_PROPERTY, 
PropertyUpdateTypes.convertDeprecated(PropertyUpdateTypes.REMOVE_PROPERTY));
     }
 
+
+    /**
+     * Test method for {@link 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canUpdateProperties(javax.jcr.Session,
 java.lang.String, 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo.PropertyUpdateTypes[])}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testCanUpdatePropertiesSessionStringPropertyUpdateTypesArray() 
{
+        AuthorizablePrivilegesInfo api = new 
TestDefaultMethodsAuthorizablePrivlegesInfo();
+        Session jcrSession = null;
+        api.canUpdateProperties(jcrSession, "testuser1", 
PropertyUpdateTypes.ALTER_PROPERTY);
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canDisable(javax.jcr.Session,
 java.lang.String)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testCanDisable() {
+        AuthorizablePrivilegesInfo api = new 
TestDefaultMethodsAuthorizablePrivlegesInfo();
+        Session jcrSession = null;
+        api.canDisable(jcrSession, "testuser1");
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canChangePassword(javax.jcr.Session,
 java.lang.String)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testCanChangePassword() {
+        AuthorizablePrivilegesInfo api = new 
TestDefaultMethodsAuthorizablePrivlegesInfo();
+        Session jcrSession = null;
+        api.canChangePassword(jcrSession, "testuser1");
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.jackrabbit.usermanager.AuthorizablePrivilegesInfo#canChangePasswordWithoutOldPassword(javax.jcr.Session,
 java.lang.String)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testCanChangePasswordWithoutOldPassword() {
+        AuthorizablePrivilegesInfo api = new 
TestDefaultMethodsAuthorizablePrivlegesInfo();
+        Session jcrSession = null;
+        api.canChangePasswordWithoutOldPassword(jcrSession, "testuser1");
+    }
+
 }
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 f239503..29c2437 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
@@ -39,6 +39,7 @@ import javax.jcr.Session;
 import javax.jcr.SimpleCredentials;
 import javax.jcr.security.Privilege;
 
+import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.api.security.user.Group;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
@@ -1080,4 +1081,47 @@ public class AuthorizablePrivilegesInfoIT extends 
UserManagerTestSupport {
                 privilegesInfo.canChangePassword(adminSession, 
"sling-jcr-usermanager"));
     }
 
+    /**
+     * Tests for SLING-12202
+     */
+    @Test
+    public void canChangePasswordWithoutOldPasswordForSelf() throws 
RepositoryException {
+        
assertFalse(privilegesInfo.canChangePasswordWithoutOldPassword(adminSession, 
"admin"));
+    }
+    @Test
+    public void canChangePasswordWithoutOldPasswordForAdminUser() throws 
RepositoryException {
+        
assertTrue(privilegesInfo.canChangePasswordWithoutOldPassword(adminSession, 
user1.getID()));
+    }
+    @Test
+    public void canChangePasswordWithoutOldPasswordForAnonymousUser() throws 
RepositoryException {
+        
assertFalse(privilegesInfo.canChangePasswordWithoutOldPassword(adminSession, 
"anonymous"));
+    }
+    @Test
+    public void canChangePasswordWithoutOldPasswordForServiceUser() throws 
RepositoryException {
+        User systemuser1 = 
((JackrabbitSession)adminSession).getUserManager().createSystemUser("systemuser1",
 null);
+        
assertFalse(privilegesInfo.canChangePasswordWithoutOldPassword(adminSession, 
systemuser1.getID()));
+    }
+    @Test
+    public void canChangePasswordWithoutOldPasswordForUserAdminGroupMember() 
throws RepositoryException {
+        User testuser2 = 
((JackrabbitSession)adminSession).getUserManager().createUser("testuser2", 
"testPwd");
+        Group userAdmin = createGroup.createGroup(adminSession, "UserAdmin", 
new HashMap<>(), new ArrayList<>());
+        // 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, userAdmin.getPath(), user1.getID(),
+                privileges,
+                "first");
+        modifyAce.modifyAce(adminSession, testuser2.getPath(), user1.getID(),
+                privileges,
+                "first");
+        adminSession.save();
+        user1Session.refresh(true);
+        
assertFalse(privilegesInfo.canChangePasswordWithoutOldPassword(user1Session, 
testuser2.getID()));
+
+        userAdmin.addMember(user1);
+        adminSession.save();
+        user1Session.refresh(true);
+        
assertTrue(privilegesInfo.canChangePasswordWithoutOldPassword(user1Session, 
testuser2.getID()));
+    }
+
 }

Reply via email to