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())); + } + }