You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ba...@apache.org on 2015/09/21 19:26:12 UTC

svn commit: r1704373 [2/2] - in /jackrabbit/branches/2.4: ./ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/ jackrabbit-core/src/main/java/org/apache/jackrabbit...

Modified: jackrabbit/branches/2.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/action/PasswordValidationAction.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/action/PasswordValidationAction.java?rev=1704373&r1=1704372&r2=1704373&view=diff
==============================================================================
--- jackrabbit/branches/2.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/action/PasswordValidationAction.java (original)
+++ jackrabbit/branches/2.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/action/PasswordValidationAction.java Mon Sep 21 17:25:57 2015
@@ -17,7 +17,7 @@
 package org.apache.jackrabbit.core.security.user.action;
 
 import org.apache.jackrabbit.api.security.user.User;
-import org.apache.jackrabbit.core.security.authentication.CryptedSimpleCredentials;
+import org.apache.jackrabbit.core.security.user.PasswordUtility;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -70,12 +70,12 @@ public class PasswordValidationAction ex
     //-------------------------------------------------< AuthorizableAction >---
     @Override
     public void onCreate(User user, String password, Session session) throws RepositoryException {
-        validatePassword(password);
+        validatePassword(password, false); // don't force validation of hashed passwords.
     }
 
     @Override
     public void onPasswordChange(User user, String newPassword, Session session) throws RepositoryException {
-        validatePassword(newPassword);
+        validatePassword(newPassword, true); // force validation of all passwords
     }
 
     //---------------------------------------------------------< BeanConfig >---
@@ -97,23 +97,16 @@ public class PasswordValidationAction ex
      * Validate the specified password.
      *
      * @param password The password to be validated
+     * @param forceMatch If true the specified password is always validated;
+     *                   otherwise only if it is a plain text password
      * @throws RepositoryException If the specified password is too short or
      * doesn't match the specified password pattern.
      */
-    private void validatePassword(String password) throws RepositoryException {
-        if (password != null && isPlainText(password)) {
+    private void validatePassword(String password, boolean forceMatch) throws RepositoryException {
+        if (password != null && (forceMatch || PasswordUtility.isPlainTextPassword(password))) {
             if (pattern != null && !pattern.matcher(password).matches()) {
                 throw new ConstraintViolationException("Password violates password constraint (" + pattern.pattern() + ").");
             }
         }
     }
-
-    private static boolean isPlainText(String password) {
-        try {
-            return !CryptedSimpleCredentials.buildPasswordHash(password).equals(password);
-        } catch (RepositoryException e) {
-            // failed to build hash from pw -> proceed with the validation.
-            return true;
-        }
-    }
 }
\ No newline at end of file

Modified: jackrabbit/branches/2.4/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.4/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd?rev=1704373&r1=1704372&r2=1704373&view=diff
==============================================================================
--- jackrabbit/branches/2.4/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd (original)
+++ jackrabbit/branches/2.4/jackrabbit-core/src/main/resources/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.cnd Mon Sep 21 17:25:57 2015
@@ -627,3 +627,12 @@
   mixin
   - rep:hold (UNDEFINED) protected  multiple IGNORE
   - rep:retentionPolicy (UNDEFINED) protected IGNORE
+
+// -----------------------------------------------------------------------------
+// Token Management (backported from oak)
+// -----------------------------------------------------------------------------
+[rep:Token] > mix:referenceable
+  - rep:token.key (STRING) protected mandatory
+  - rep:token.exp (DATE) protected mandatory
+  - * (UNDEFINED) protected
+  - * (UNDEFINED) multiple protected

Modified: jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java?rev=1704373&r1=1704372&r2=1704373&view=diff
==============================================================================
--- jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java (original)
+++ jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TestAll.java Mon Sep 21 17:25:57 2015
@@ -32,8 +32,10 @@ public class TestAll extends TestCase {
     public static Test suite() {
         TestSuite suite = new TestSuite("org.apache.jackrabbit.core.security.authentication.token tests");
 
+        suite.addTestSuite(TokenBasedAuthenticationCompatTest.class);
         suite.addTestSuite(TokenBasedAuthenticationTest.class);
         suite.addTestSuite(TokenBasedLoginTest.class);
+        suite.addTestSuite(TokenProviderTest.class);
 
         return suite;
     }

Modified: jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java?rev=1704373&r1=1704372&r2=1704373&view=diff
==============================================================================
--- jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java (original)
+++ jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedAuthenticationTest.java Mon Sep 21 17:25:57 2015
@@ -16,9 +16,7 @@
  */
 package org.apache.jackrabbit.core.security.authentication.token;
 
-import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
-import org.apache.jackrabbit.test.AbstractJCRTest;
-
+import java.util.UUID;
 import javax.jcr.Credentials;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
@@ -27,45 +25,82 @@ import javax.jcr.SimpleCredentials;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.version.VersionException;
-import java.util.Calendar;
-import java.util.Date;
+
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.test.AbstractJCRTest;
 
 /**
- * <code>TokenBasedAuthenticationTest</code>...
+ * <code>TokenBasedAuthenticationOakTest</code>...
  */
 public class TokenBasedAuthenticationTest extends AbstractJCRTest {
 
-    Node tokenNode;
+    private SessionImpl adminSession;
+    private User testUser;
+
+    private String token;
+    private Node tokenNode;
+    private TokenCredentials tokenCreds;
 
-    TokenBasedAuthentication nullTokenAuth;
-    TokenBasedAuthentication validTokenAuth;
+    private String expiredToken;
+    private Node expiredNode;
+    private TokenCredentials expiredCreds;
 
-    TokenCredentials tokenCreds;
-    Credentials simpleCreds = new SimpleCredentials("uid", "pw".toCharArray());
-    Credentials creds = new Credentials() {};
+
+    private TokenBasedAuthentication nullTokenAuth;
+    private TokenBasedAuthentication validTokenAuth;
+
+    private Credentials simpleCreds = new SimpleCredentials("uid", "pw".toCharArray());
+    private Credentials creds = new Credentials() {};
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
-        tokenNode = testRootNode.addNode(nodeName1, "nt:unstructured");
-        tokenNode.setProperty(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".exp", new Date().getTime()+TokenBasedAuthentication.TOKEN_EXPIRATION);
-        superuser.save();
+        adminSession = (SessionImpl) getHelper().getSuperuserSession("security");
+        testUser = adminSession.getUserManager().createUser(UUID.randomUUID().toString(), "pw");
+        adminSession.save();
+
+        SimpleCredentials sc = new SimpleCredentials(testUser.getID(), "pw".toCharArray());
+        sc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+        sc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE+".any", "correct");
+        sc.setAttribute("informative", "value");
+
+        TokenProvider tp = new TokenProvider(adminSession, TokenBasedAuthentication.TOKEN_EXPIRATION);
+        TokenInfo ti = tp.createToken(testUser, sc);
+        tokenCreds = ti.getCredentials();
+        token = tokenCreds.getToken();
+        tokenNode = TokenProvider.getTokenNode(token, adminSession);
+
+        tp = new TokenProvider(adminSession, 1);
+        TokenInfo expired = tp.createToken(testUser, sc);
+        expiredCreds = expired.getCredentials();
+        expiredToken = expiredCreds.getToken();
+        expiredNode = TokenProvider.getTokenNode(expiredToken, adminSession);
+
+        nullTokenAuth = new TokenBasedAuthentication(null, -1, adminSession);
+        validTokenAuth = new TokenBasedAuthentication(token, 7200, adminSession);
 
-        String token = tokenNode.getIdentifier();
+    }
 
-        nullTokenAuth = new TokenBasedAuthentication(null, -1, superuser);
-        validTokenAuth = new TokenBasedAuthentication(token, 7200, superuser);
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            testUser.remove();
+            adminSession.save();
+            adminSession.logout();
+        } finally {
+            super.tearDown();
+        }
+    }
 
-        tokenCreds = new TokenCredentials(token);
+    private TokenBasedAuthentication createAuthenticationForExpiredToken() throws RepositoryException, LockException, ConstraintViolationException, VersionException {
+        return new TokenBasedAuthentication(expiredToken, TokenBasedAuthentication.TOKEN_EXPIRATION, adminSession);
     }
 
-    private TokenBasedAuthentication expiredToken() throws RepositoryException, LockException, ConstraintViolationException, VersionException {
-        Calendar cal = Calendar.getInstance();
-        cal.setTimeInMillis(new Date().getTime()-100);
-        tokenNode.setProperty(".token.exp", cal);
-        superuser.save();
-        return new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+    private TokenBasedAuthentication createAuthentication() throws RepositoryException {
+        return new TokenBasedAuthentication(token, TokenBasedAuthentication.TOKEN_EXPIRATION, adminSession);
     }
 
     public void testCanHandle() throws RepositoryException {
@@ -77,23 +112,23 @@ public class TokenBasedAuthenticationTes
 
         assertFalse(validTokenAuth.canHandle(creds));
         assertFalse(nullTokenAuth.canHandle(creds));
+    }
 
-        TokenBasedAuthentication expiredToken = expiredToken();
-        assertTrue(expiredToken.canHandle(tokenCreds));
+    public void testCanHandleExpiredToken() throws RepositoryException {
+        TokenBasedAuthentication expiredToken = createAuthenticationForExpiredToken();
+        assertTrue(expiredToken.canHandle(expiredCreds));
     }
 
     public void testExpiry() throws RepositoryException {
-        assertTrue(validTokenAuth.authenticate(tokenCreds));
-
-        TokenBasedAuthentication expiredToken = expiredToken();
-        assertFalse(expiredToken.authenticate(tokenCreds));
+        TokenBasedAuthentication expiredToken = createAuthenticationForExpiredToken();
+        assertFalse(expiredToken.authenticate(expiredCreds));
     }
 
     public void testRemoval() throws RepositoryException {
-        String identifier = tokenNode.getIdentifier();
+        String identifier = expiredNode.getIdentifier();
 
-        TokenBasedAuthentication expiredToken = expiredToken();
-        assertFalse(expiredToken.authenticate(tokenCreds));
+        TokenBasedAuthentication expiredToken = createAuthenticationForExpiredToken();
+        assertFalse(expiredToken.authenticate(expiredCreds));
 
         try {
             superuser.getNodeByIdentifier(identifier);
@@ -120,57 +155,48 @@ public class TokenBasedAuthenticationTes
     }
 
     public void testAttributes() throws RepositoryException {
-        tokenNode.setProperty(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
-        superuser.save();
-        TokenBasedAuthentication auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+        TokenBasedAuthentication auth = createAuthentication();
+        assertFalse(auth.authenticate(new TokenCredentials(token)));
 
-        assertFalse(auth.authenticate(tokenCreds));
-
-        tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "wrong");
-        assertFalse(auth.authenticate(tokenCreds));
-
-        tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
-        assertTrue(auth.authenticate(tokenCreds));
-
-        // add informative property
-        tokenNode.setProperty("noMatchRequired", "abc");
-        superuser.save();
-        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
+        TokenCredentials tc = new TokenCredentials(token);
+        tc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "wrong");
+        assertFalse(auth.authenticate(tc));
 
+        tc = new TokenCredentials(token);
+        tc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
         assertTrue(auth.authenticate(tokenCreds));
     }
 
     public void testUpdateAttributes() throws RepositoryException {
-        tokenNode.setProperty(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
-        tokenNode.setProperty("informative","value");
-        superuser.save();
-
         // token credentials must be updated to contain the additional attribute
         // present on the token node.
-        TokenBasedAuthentication auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
-        tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
-        assertTrue(auth.authenticate(tokenCreds));               
-        assertEquals("value", tokenCreds.getAttribute("informative"));
+        TokenBasedAuthentication auth = createAuthentication();
+
+        TokenCredentials tc = new TokenCredentials(token);
+        tc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".any", "correct");
+
+        assertTrue(auth.authenticate(tc));
+        assertEquals("value", tc.getAttribute("informative"));
 
         // additional informative property present on credentials upon subsequent
         // authentication -> the node must not be updated
-        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
-        tokenCreds.setAttribute("informative2", "value2");
-        assertTrue(auth.authenticate(tokenCreds));
+        auth = createAuthentication();
+        tc.setAttribute("informative2", "value2");
+        assertTrue(auth.authenticate(tc));
         assertFalse(tokenNode.hasProperty("informative2"));
 
         // modified informative property present on credentials upon subsequent
         // authentication -> the node must not be updated
-        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);
-        tokenCreds.setAttribute("informative", "otherValue");
-        assertTrue(auth.authenticate(tokenCreds));
+        auth = createAuthentication();
+        tc.setAttribute("informative", "otherValue");
+        assertTrue(auth.authenticate(tc));
         assertTrue(tokenNode.hasProperty("informative"));
         assertEquals("value", tokenNode.getProperty("informative").getString());
 
         // additional mandatory property on the credentials upon subsequent
         // authentication -> must be ignored
-        auth = new TokenBasedAuthentication(tokenNode.getIdentifier(), TokenBasedAuthentication.TOKEN_EXPIRATION, superuser);        
-        tokenCreds.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".toIgnore", "ignore");
+        auth = createAuthentication();
+        tc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".toIgnore", "ignore");
         assertTrue(auth.authenticate(tokenCreds));
         assertFalse(tokenNode.hasProperty(TokenBasedAuthentication.TOKEN_ATTRIBUTE +".toIgnore"));
     }

Modified: jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableActionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableActionTest.java?rev=1704373&r1=1704372&r2=1704373&view=diff
==============================================================================
--- jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableActionTest.java (original)
+++ jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/AuthorizableActionTest.java Mon Sep 21 17:25:57 2015
@@ -230,7 +230,27 @@ public class AuthorizableActionTest exte
         }
     }
 
-    public void testPasswordValidationActionIgnoresHashedPwString() throws Exception {
+    public void testPasswordValidationActionIgnoresHashedPwStringOnCreate() throws Exception {
+        User u = null;
+
+        try {
+            PasswordValidationAction pwAction = new PasswordValidationAction();
+            pwAction.setConstraint("^.*(?=.{8,})(?=.*[a-z])(?=.*[A-Z]).*");
+            setActions(pwAction);
+
+            String uid = getTestPrincipal().getName();
+            String hashed = PasswordUtility.buildPasswordHash("DWkej32H");
+            u = impl.createUser(uid, hashed);
+
+        } finally {
+            if (u != null) {
+                u.remove();
+            }
+            save(superuser);
+        }
+    }
+
+    public void testPasswordValidationActionOnChange() throws Exception {
         User u = null;
 
         try {
@@ -238,12 +258,16 @@ public class AuthorizableActionTest exte
             u = impl.createUser(uid, buildPassword(uid));
 
             PasswordValidationAction pwAction = new PasswordValidationAction();
-            pwAction.setConstraint("^.*(?=.{8,})(?=.*[a-z])(?=.*[A-Z]).*");
+            pwAction.setConstraint("abc");
             setActions(pwAction);
 
-            String hashed = ((UserImpl) u).buildPasswordValue("DWkej32H");
+            String hashed = PasswordUtility.buildPasswordHash("abc");
             u.changePassword(hashed);
 
+            fail("Password change must always enforce password validation.");
+
+        } catch (ConstraintViolationException e) {
+            // success
         } finally {
             if (u != null) {
                 u.remove();
@@ -262,4 +286,4 @@ public class AuthorizableActionTest exte
             called++;
         }
     }
-}
\ No newline at end of file
+}

Added: jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/PasswordUtilityTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/PasswordUtilityTest.java?rev=1704373&view=auto
==============================================================================
--- jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/PasswordUtilityTest.java (added)
+++ jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/PasswordUtilityTest.java Mon Sep 21 17:25:57 2015
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.core.security.user;
+
+import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.test.JUnitTest;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <code>PasswordUtilityTest</code>...
+ */
+public class PasswordUtilityTest extends JUnitTest {
+
+    private static List<String> PLAIN_PWDS = new ArrayList<String>();
+    static {
+        PLAIN_PWDS.add("pw");
+        PLAIN_PWDS.add("PassWord123");
+        PLAIN_PWDS.add("_");
+        PLAIN_PWDS.add("{invalidAlgo}");
+        PLAIN_PWDS.add("{invalidAlgo}Password");
+        PLAIN_PWDS.add("{SHA-256}");
+        PLAIN_PWDS.add("pw{SHA-256}");
+        PLAIN_PWDS.add("p{SHA-256}w");
+        PLAIN_PWDS.add("");
+    }
+
+    private static Map<String, String> HASHED_PWDS = new HashMap<String, String>();
+    static {
+        for (String pw : PLAIN_PWDS) {
+            try {
+                HASHED_PWDS.put(pw, PasswordUtility.buildPasswordHash(pw));
+            } catch (Exception e) {
+                // should not get here
+            }
+        }
+    }
+
+    public void testBuildPasswordHash() throws Exception {
+        for (String pw : PLAIN_PWDS) {
+            String pwHash = PasswordUtility.buildPasswordHash(pw);
+            assertFalse(pw.equals(pwHash));
+        }
+
+        List<Integer[]> l = new ArrayList<Integer[]>();
+        l.add(new Integer[] {0, 1000});
+        l.add(new Integer[] {1, 10});
+        l.add(new Integer[] {8, 50});
+        l.add(new Integer[] {10, 5});
+        l.add(new Integer[] {-1, -1});
+        for (Integer[] params : l) {
+            for (String pw : PLAIN_PWDS) {
+                int saltsize = params[0];
+                int iterations = params[1];
+
+                String pwHash = PasswordUtility.buildPasswordHash(pw, PasswordUtility.DEFAULT_ALGORITHM, saltsize, iterations);
+                assertFalse(pw.equals(pwHash));
+            }
+        }
+    }
+
+    public void testBuildPasswordHashInvalidAlgorithm() throws Exception {
+        List<String> invalidAlgorithms = new ArrayList<String>();
+        invalidAlgorithms.add("");
+        invalidAlgorithms.add("+");
+        invalidAlgorithms.add("invalid");
+
+        for (String invalid : invalidAlgorithms) {
+            try {
+                String pwHash = PasswordUtility.buildPasswordHash("pw", invalid, PasswordUtility.DEFAULT_SALT_SIZE, PasswordUtility.DEFAULT_ITERATIONS);
+                fail("Invalid algorithm " + invalid);
+            } catch (NoSuchAlgorithmException e) {
+                // success
+            }
+        }
+
+    }
+
+    public void testIsPlainTextPassword() throws Exception {
+        for (String pw : PLAIN_PWDS) {
+            assertTrue(pw + " should be plain text.", PasswordUtility.isPlainTextPassword(pw));
+        }
+    }
+
+    public void testIsPlainTextForNull() throws Exception {
+        assertTrue(PasswordUtility.isPlainTextPassword(null));
+    }
+
+    public void testIsPlainTextForPwHash() throws Exception {
+        for (String pwHash : HASHED_PWDS.values()) {
+            assertFalse(pwHash + " should not be plain text.", PasswordUtility.isPlainTextPassword(pwHash));
+        }
+    }
+
+    public void testIsSame() throws Exception {
+        for (String pw : HASHED_PWDS.keySet()) {
+            String pwHash = HASHED_PWDS.get(pw);
+            assertTrue("Not the same " + pw + ", " + pwHash, PasswordUtility.isSame(pwHash, pw));
+        }
+
+        String pw = "password";
+        String pwHash = PasswordUtility.buildPasswordHash(pw, SecurityConstants.DEFAULT_DIGEST, 4, 50);
+        assertTrue("Not the same '" + pw + "', " + pwHash, PasswordUtility.isSame(pwHash, pw));
+
+        pwHash = PasswordUtility.buildPasswordHash(pw, "md5", 0, 5);
+        assertTrue("Not the same '" + pw + "', " + pwHash, PasswordUtility.isSame(pwHash, pw));
+
+        pwHash = PasswordUtility.buildPasswordHash(pw, "md5", -1, -1);
+        assertTrue("Not the same '" + pw + "', " + pwHash, PasswordUtility.isSame(pwHash, pw));
+    }
+
+    public void testIsNotSame() throws Exception {
+        String previous = null;
+        for (String pw : HASHED_PWDS.keySet()) {
+            String pwHash = HASHED_PWDS.get(pw);
+            assertFalse(pw, PasswordUtility.isSame(pw, pw));
+            assertFalse(pwHash, PasswordUtility.isSame(pwHash, pwHash));
+            if (previous != null) {
+                assertFalse(previous, PasswordUtility.isSame(pwHash, previous));
+            }
+            previous = pw;
+        }
+    }
+
+    public void testExtractAlgorithmFromPlainPw() throws Exception {
+        for (String pw : PLAIN_PWDS) {
+            assertNull(pw + " is no pw-hash -> no algorithm expected.", PasswordUtility.extractAlgorithm(pw));
+        }
+    }
+
+    public void testExtractAlgorithmFromNull() throws Exception {
+        assertNull("null pw -> no algorithm expected.", PasswordUtility.extractAlgorithm(null));
+    }
+
+    public void testExtractAlgorithmFromPwHash() throws Exception {
+        for (String pwHash : HASHED_PWDS.values()) {
+            String algorithm = PasswordUtility.extractAlgorithm(pwHash);
+            assertNotNull(pwHash + " is pw-hash -> algorithm expected.", algorithm);
+            assertEquals("Wrong algorithm extracted from " + pwHash, PasswordUtility.DEFAULT_ALGORITHM, algorithm);
+        }
+
+        String pwHash = PasswordUtility.buildPasswordHash("pw", SecurityConstants.DEFAULT_DIGEST, 4, 50);
+        assertEquals(SecurityConstants.DEFAULT_DIGEST, PasswordUtility.extractAlgorithm(pwHash));
+
+        pwHash = PasswordUtility.buildPasswordHash("pw", "md5", 0, 5);
+        assertEquals("md5", PasswordUtility.extractAlgorithm(pwHash));
+
+        pwHash = PasswordUtility.buildPasswordHash("pw", "md5", -1, -1);
+        assertEquals("md5", PasswordUtility.extractAlgorithm(pwHash));
+    }
+}

Modified: jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TestAll.java?rev=1704373&r1=1704372&r2=1704373&view=diff
==============================================================================
--- jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TestAll.java (original)
+++ jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/TestAll.java Mon Sep 21 17:25:57 2015
@@ -54,6 +54,7 @@ public class TestAll extends TestCase {
         suite.addTestSuite(UserAccessControlProviderTest.class);
         suite.addTestSuite(DefaultPrincipalProviderTest.class);        
 
+        suite.addTestSuite(PasswordUtilityTest.class);
         return suite;
     }
 }

Modified: jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImplTest.java?rev=1704373&r1=1704372&r2=1704373&view=diff
==============================================================================
--- jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImplTest.java (original)
+++ jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImplTest.java Mon Sep 21 17:25:57 2015
@@ -142,9 +142,11 @@ public class UserImplTest extends Abstra
         // plain text passwords
         pwds.put("abc", "abc");
         pwds.put("{a}password", "{a}password");
-        // passwords already in hashed format.
-        pwds.put(sha1Hash, "abc");
-        pwds.put(md5Hash, "abc");
+        // passwords with hash-like char-sequence -> must still be hashed.
+        pwds.put(sha1Hash, sha1Hash);
+        pwds.put(md5Hash, md5Hash);
+        pwds.put("{"+SecurityConstants.DEFAULT_DIGEST+"}any", "{"+SecurityConstants.DEFAULT_DIGEST+"}any");
+        pwds.put("{"+SecurityConstants.DEFAULT_DIGEST+"}", "{"+SecurityConstants.DEFAULT_DIGEST+"}");
 
         for (String pw : pwds.keySet()) {
             u.changePassword(pw);
@@ -159,11 +161,9 @@ public class UserImplTest extends Abstra
         // valid passwords, non-matching plain text
         Map<String, String>noMatch = new HashMap<String, String>();
         noMatch.put("{"+SecurityConstants.DEFAULT_DIGEST+"}", "");
-        noMatch.put("{"+SecurityConstants.DEFAULT_DIGEST+"}", "{"+SecurityConstants.DEFAULT_DIGEST+"}");
         noMatch.put("{"+SecurityConstants.DEFAULT_DIGEST+"}any", "any");
-        noMatch.put("{"+SecurityConstants.DEFAULT_DIGEST+"}any", "{"+SecurityConstants.DEFAULT_DIGEST+"}any");
-        noMatch.put(sha1Hash, sha1Hash);
-        noMatch.put(md5Hash, md5Hash);
+        noMatch.put(sha1Hash, "abc");
+        noMatch.put(md5Hash, "abc");
 
         for (String pw : noMatch.keySet()) {
             u.changePassword(pw);
@@ -172,10 +172,14 @@ public class UserImplTest extends Abstra
             SimpleCredentials sc = new SimpleCredentials(u.getID(), plain.toCharArray());
             CryptedSimpleCredentials cc = (CryptedSimpleCredentials) u.getCredentials();
 
-            assertFalse(cc.matches(sc));
+            assertFalse(pw, cc.matches(sc));
         }
+    }
 
-        // invalid pw string
+    public void testChangePasswordNull() throws RepositoryException {
+        User u = (User) userMgr.getAuthorizable(uID);
+
+        // invalid 'null' pw string
         try {
             u.changePassword(null);
             fail("invalid pw null");

Modified: jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java?rev=1704373&r1=1704372&r2=1704373&view=diff
==============================================================================
--- jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java (original)
+++ jackrabbit/branches/2.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/UserImporterTest.java Mon Sep 21 17:25:57 2015
@@ -860,7 +860,7 @@ public class UserImporterTest extends Ab
     }
 
     public void testImportNonExistingMemberBestEffort() throws IOException, RepositoryException, SAXException, NotExecutableException {
-        if (umgr.getGroupMembershipSplitSize() > 0) {
+        if (umgr.hasMemberSplitSize()) {
             throw new NotExecutableException();
         }
 
@@ -914,7 +914,7 @@ public class UserImporterTest extends Ab
 
         String g1Id = "0120a4f9-196a-3f9e-b9f5-23f31f914da7";
         String nonExistingId = "b2f5ff47-4366-31b6-a533-d8dc3614845d"; // groupId of 'g' group.
-        if (umgr.getAuthorizable("g") != null || umgr.getGroupMembershipSplitSize() > 0) {
+        if (umgr.getAuthorizable("g") != null || umgr.hasMemberSplitSize()) {
             throw new NotExecutableException();
         }