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-testing-jcr-mock.git


The following commit(s) were added to refs/heads/master by this push:
     new a27a118  SLING-12190 provide more real MockUser password handling (#32)
a27a118 is described below

commit a27a11874d605793579a2f951a2097a5829d0073
Author: Eric Norman <enor...@apache.org>
AuthorDate: Sun Dec 10 13:48:18 2023 -0800

    SLING-12190 provide more real MockUser password handling (#32)
---
 .../apache/sling/testing/mock/jcr/MockUser.java    | 39 ++++++++++++++---
 .../sling/testing/mock/jcr/MockUserManager.java    |  6 ++-
 .../testing/mock/jcr/MockUserManagerTest.java      | 19 +++++++-
 .../sling/testing/mock/jcr/MockUserTest.java       | 51 ++++++++++++++++++++--
 4 files changed, 103 insertions(+), 12 deletions(-)

diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockUser.java 
b/src/main/java/org/apache/sling/testing/mock/jcr/MockUser.java
index ad0f2bb..88ed311 100644
--- a/src/main/java/org/apache/sling/testing/mock/jcr/MockUser.java
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockUser.java
@@ -16,8 +16,9 @@
  */
 package org.apache.sling.testing.mock.jcr;
 
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
-import java.util.Arrays;
 
 import javax.jcr.Credentials;
 import javax.jcr.Node;
@@ -27,6 +28,9 @@ import javax.jcr.SimpleCredentials;
 import org.apache.jackrabbit.api.security.user.Impersonation;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.oak.spi.security.principal.SystemUserPrincipal;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.security.user.UserIdCredentials;
+import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -37,7 +41,6 @@ import org.slf4j.LoggerFactory;
  */
 class MockUser extends MockAuthorizable implements User {
     private Logger logger = LoggerFactory.getLogger(getClass());
-    private char[] pwd = {};
     private boolean disabled;
     private String disabledReason;
 
@@ -68,7 +71,20 @@ class MockUser extends MockAuthorizable implements User {
 
     @Override
     public @NotNull Credentials getCredentials() throws RepositoryException {
-        return new SimpleCredentials(id, pwd);
+        char[] pwd;
+        if (homeNode.hasProperty(UserConstants.REP_PASSWORD)) {
+            pwd = 
homeNode.getProperty(UserConstants.REP_PASSWORD).getString().toCharArray();
+        } else {
+            pwd = null;
+        }
+
+        Credentials creds;
+        if (pwd == null) {
+            creds = new UserIdCredentials(id);
+        } else {
+            creds = new SimpleCredentials(id, pwd);
+        }
+        return creds;
     }
 
     @Override
@@ -81,13 +97,24 @@ class MockUser extends MockAuthorizable implements User {
         if (password == null) {
             throw new RepositoryException("Attempt to set 'null' password for 
user " + getID());
         }
-
-        pwd = password.toCharArray();
+        try {
+            char[] hashedPwd = 
PasswordUtil.buildPasswordHash(password).toCharArray();
+            homeNode.setProperty(UserConstants.REP_PASSWORD, 
String.valueOf(hashedPwd));
+        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
+            throw new RepositoryException("Failed to build the password hash", 
e);
+        }
     }
 
     @Override
     public void changePassword(@Nullable String password, @NotNull String 
oldPassword) throws RepositoryException {
-        if (Arrays.equals(pwd, oldPassword.toCharArray())) {
+        String hashedPwd;
+        if (homeNode.hasProperty(UserConstants.REP_PASSWORD)) {
+            String pwd = 
homeNode.getProperty(UserConstants.REP_PASSWORD).getString();
+            hashedPwd = String.valueOf(pwd);
+        } else {
+            hashedPwd = null;
+        }
+        if (PasswordUtil.isSame(hashedPwd, oldPassword.toCharArray())) {
             changePassword(password);
         } else {
             throw new RepositoryException("old password did not match");
diff --git 
a/src/main/java/org/apache/sling/testing/mock/jcr/MockUserManager.java 
b/src/main/java/org/apache/sling/testing/mock/jcr/MockUserManager.java
index f8bd17a..ca21b11 100644
--- a/src/main/java/org/apache/sling/testing/mock/jcr/MockUserManager.java
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockUserManager.java
@@ -306,7 +306,11 @@ public class MockUserManager implements UserManager {
             }
         }
         Node node = ensureAuthorizablePathExists(intermediatePath, 
principalName, authorizableNodeType);
-        return (User)authorizables.computeIfAbsent(userID, id -> new 
MockUser(id, principal, node, this));
+        User user = (User)authorizables.computeIfAbsent(userID, id -> new 
MockUser(id, principal, node, this));
+        if (password != null) {
+            user.changePassword(password);
+        }
+        return user;
     }
 
     @Override
diff --git 
a/src/test/java/org/apache/sling/testing/mock/jcr/MockUserManagerTest.java 
b/src/test/java/org/apache/sling/testing/mock/jcr/MockUserManagerTest.java
index bc03f2a..33278ea 100644
--- a/src/test/java/org/apache/sling/testing/mock/jcr/MockUserManagerTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockUserManagerTest.java
@@ -23,12 +23,15 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
 import java.util.Iterator;
 import java.util.Set;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.ValueFactory;
 
@@ -40,6 +43,7 @@ import org.apache.jackrabbit.api.security.user.Query;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil;
 import org.apache.jackrabbit.value.ValueFactoryImpl;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -87,13 +91,19 @@ public class MockUserManagerTest {
     }
 
     @Test
-    public void testLoadAlreadyExistingAuthorizables() throws 
RepositoryException {
+    public void testLoadAlreadyExistingAuthorizables() throws 
RepositoryException, NoSuchAlgorithmException, UnsupportedEncodingException {
         Node homeNode = session.getRootNode()
             .addNode("home", UserConstants.NT_REP_AUTHORIZABLE_FOLDER);
         Node usersNode = homeNode.addNode("users", 
UserConstants.NT_REP_AUTHORIZABLE_FOLDER);
         Node testuser1 = usersNode.addNode("testuser1", 
UserConstants.NT_REP_USER);
         testuser1.setProperty(UserConstants.REP_AUTHORIZABLE_ID, "testuser1");
         testuser1.setProperty(UserConstants.REP_PRINCIPAL_NAME, "testuser1");
+        testuser1.setProperty(UserConstants.REP_PASSWORD, 
PasswordUtil.buildPasswordHash("testPwd"));
+
+        // user with no password set - for code coverage
+        Node testuser2 = usersNode.addNode("testuser2", 
UserConstants.NT_REP_USER);
+        testuser2.setProperty(UserConstants.REP_AUTHORIZABLE_ID, "testuser2");
+        testuser2.setProperty(UserConstants.REP_PRINCIPAL_NAME, "testuser2");
 
         Node testsystemuser1 = usersNode.addNode("testsystemuser1", 
UserConstants.NT_REP_SYSTEM_USER);
         testsystemuser1.setProperty(UserConstants.REP_AUTHORIZABLE_ID, 
"testsystemuser1");
@@ -106,7 +116,12 @@ public class MockUserManagerTest {
 
         userManager.loadAlreadyExistingAuthorizables();
 
-        assertNotNull(userManager.getAuthorizable("testuser1"));
+        @Nullable Authorizable authorizable = 
userManager.getAuthorizable("testuser1");
+        assertTrue(authorizable instanceof User);
+        // verify password state was stored
+        SimpleCredentials creds = 
(SimpleCredentials)((User)authorizable).getCredentials();
+        assertTrue(PasswordUtil.isSame(String.valueOf(creds.getPassword()), 
"testPwd"));
+        assertNotNull(userManager.getAuthorizable("testuser2"));
         assertNotNull(userManager.getAuthorizable("testsystemuser1"));
         assertNotNull(userManager.getAuthorizable("testgroup1"));
     }
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockUserTest.java 
b/src/test/java/org/apache/sling/testing/mock/jcr/MockUserTest.java
index 519d5fc..a53ba15 100644
--- a/src/test/java/org/apache/sling/testing/mock/jcr/MockUserTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockUserTest.java
@@ -16,13 +16,15 @@
  */
 package org.apache.sling.testing.mock.jcr;
 
-import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+
 import javax.jcr.Credentials;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
@@ -31,8 +33,11 @@ import javax.jcr.UnsupportedRepositoryOperationException;
 
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.jackrabbit.oak.spi.security.user.UserIdCredentials;
+import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil;
 import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 
 import ch.qos.logback.classic.Level;
@@ -114,6 +119,15 @@ public class MockUserTest extends 
MockAuthorizableTest<User> {
         assertTrue(credentials instanceof SimpleCredentials);
         assertEquals(authorizable.getID(), 
((SimpleCredentials)credentials).getUserID());
     }
+    @Test
+    public void testGetCredentialsWithNullPassword() throws 
RepositoryException {
+        // create a user with no password
+        authorizable = userManager.createUser("user2", null);
+
+        @NotNull Credentials credentials = authorizable.getCredentials();
+        assertTrue(credentials instanceof UserIdCredentials);
+        assertEquals(authorizable.getID(), 
((UserIdCredentials)credentials).getUserId());
+    }
 
     /**
      * Test method for {@link 
org.apache.sling.testing.mock.jcr.MockUser#getImpersonation()}.
@@ -133,10 +147,33 @@ public class MockUserTest extends 
MockAuthorizableTest<User> {
 
         assertThrows(RepositoryException.class, () -> 
authorizable.changePassword(null));
     }
+    @Test
+    public void testChangePasswordStringFromNull() throws RepositoryException {
+        // start with a null password - for code coverage
+        authorizable = userManager.createUser("testuser2", null);
+        assertThrows(RepositoryException.class, () -> 
authorizable.changePassword("changed", "oldPwd"));
+    }
+
+    @Test
+    public void testChangePasswordStringWithCaughtNoSuchAlgorithmException() 
throws RepositoryException {
+        try (MockedStatic<PasswordUtil> passwordUtilMock = 
Mockito.mockStatic(PasswordUtil.class);) {
+            passwordUtilMock.when(() -> 
PasswordUtil.buildPasswordHash("changed"))
+                .thenThrow(NoSuchAlgorithmException.class);
+            assertThrows(RepositoryException.class, () -> 
authorizable.changePassword("changed"));
+        }
+    }
+    @Test
+    public void 
testChangePasswordStringWithCaughtUnsupportedEncodingExceptionException() 
throws RepositoryException {
+        try (MockedStatic<PasswordUtil> passwordUtilMock = 
Mockito.mockStatic(PasswordUtil.class);) {
+            passwordUtilMock.when(() -> 
PasswordUtil.buildPasswordHash("changed"))
+                .thenThrow(UnsupportedEncodingException.class);
+            assertThrows(RepositoryException.class, () -> 
authorizable.changePassword("changed"));
+        }
+    }
 
     protected void assertPassword(char [] expectedPwd) throws 
RepositoryException {
         SimpleCredentials creds = 
(SimpleCredentials)authorizable.getCredentials();
-        assertArrayEquals(expectedPwd, creds.getPassword());
+        assertTrue(PasswordUtil.isSame(new String(creds.getPassword()), 
expectedPwd));
     }
 
     /**
@@ -144,7 +181,7 @@ public class MockUserTest extends 
MockAuthorizableTest<User> {
      */
     @Test
     public void testChangePasswordStringString() throws RepositoryException {
-        authorizable.changePassword("changed", "");
+        authorizable.changePassword("changed", "pwd");
         assertPassword("changed".toCharArray());
 
         assertThrows(RepositoryException.class, () -> 
authorizable.changePassword("changed2", "wrong"));
@@ -194,4 +231,12 @@ public class MockUserTest extends 
MockAuthorizableTest<User> {
                 
node.getProperty(UserConstants.REP_PRINCIPAL_NAME).getString());
     }
 
+    @Test
+    @Override
+    public void testGetPropertyNames() throws RepositoryException {
+        // create a user with no password so there are no initial properties
+        authorizable = userManager.createUser("user2", null);
+        super.testGetPropertyNames();
+    }
+
 }

Reply via email to