You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2013/09/04 11:51:37 UTC

svn commit: r1519962 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/spi/security/user/util/ test/java/org/apache/jackrabbit/oak/security/user/ test/java/org/apache/jackrabbit/oak/spi/security/user/util/

Author: angela
Date: Wed Sep  4 09:51:36 2013
New Revision: 1519962

URL: http://svn.apache.org/r1519962
Log:
OAK-50: user mgt

PasswordUtil:
- revert internal methods being made public for test cases
- add annotations

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtil.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserManagerImplTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtil.java?rev=1519962&r1=1519961&r2=1519962&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtil.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtil.java Wed Sep  4 09:51:36 2013
@@ -23,7 +23,8 @@ import java.security.NoSuchAlgorithmExce
 import java.security.SecureRandom;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
-
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;
@@ -34,6 +35,8 @@ import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * Utility to generate and compare password hashes.
  */
@@ -68,7 +71,7 @@ public final class PasswordUtil {
      * @throws NoSuchAlgorithmException If {@link #DEFAULT_ALGORITHM} is not supported.
      * @throws UnsupportedEncodingException If utf-8 is not supported.
      */
-    public static String buildPasswordHash(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
+    public static String buildPasswordHash(@Nonnull String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
         return buildPasswordHash(password, DEFAULT_ALGORITHM, DEFAULT_SALT_SIZE, DEFAULT_ITERATIONS);
     }
 
@@ -77,7 +80,8 @@ public final class PasswordUtil {
      * salt size and number of iterations into account.
      *
      * @param password The password to be hashed.
-     * @param algorithm The desired hash algorithm.
+     * @param algorithm The desired hash algorithm. If the algorith is
+     * {@code null} the {@link #DEFAULT_ALGORITHM} will be used.
      * @param saltSize The desired salt size. If the specified integer is lower
      * that {@link #DEFAULT_SALT_SIZE} the default is used.
      * @param iterations The desired number of iterations. If the specified
@@ -86,11 +90,10 @@ public final class PasswordUtil {
      * @throws NoSuchAlgorithmException If the specified algorithm is not supported.
      * @throws UnsupportedEncodingException If utf-8 is not supported.
      */
-    public static String buildPasswordHash(String password, String algorithm,
+    public static String buildPasswordHash(@Nonnull String password,
+                                           @Nullable String algorithm,
                                            int saltSize, int iterations) throws NoSuchAlgorithmException, UnsupportedEncodingException {
-        if (password == null) {
-            throw new IllegalArgumentException("Password may not be null.");
-        }
+        checkNotNull(password);
         if (iterations < NO_ITERATIONS) {
             iterations = DEFAULT_ITERATIONS;
         }
@@ -112,10 +115,9 @@ public final class PasswordUtil {
      * @throws NoSuchAlgorithmException If the specified algorithm is not supported.
      * @throws UnsupportedEncodingException If utf-8 is not supported.
      */
-    public static String buildPasswordHash(String password, ConfigurationParameters config) throws NoSuchAlgorithmException, UnsupportedEncodingException {
-        if (config == null) {
-            throw new IllegalArgumentException("UserConfig must not be null");
-        }
+    public static String buildPasswordHash(@Nonnull String password,
+                                           @Nonnull ConfigurationParameters config) throws NoSuchAlgorithmException, UnsupportedEncodingException {
+        checkNotNull(config);
         String algorithm = config.getConfigValue(UserConstants.PARAM_PASSWORD_HASH_ALGORITHM, DEFAULT_ALGORITHM);
         int iterations = config.getConfigValue(UserConstants.PARAM_PASSWORD_HASH_ITERATIONS, DEFAULT_ITERATIONS);
         int saltSize = config.getConfigValue(UserConstants.PARAM_PASSWORD_SALT_SIZE, DEFAULT_SALT_SIZE);
@@ -144,7 +146,7 @@ public final class PasswordUtil {
      * @return If the hash created from the specified {@code password} equals
      * the given {@code hashedPassword} string.
      */
-    public static boolean isSame(String hashedPassword, char[] password) {
+    public static boolean isSame(@Nullable String hashedPassword, @Nonnull char[] password) {
         return isSame(hashedPassword, String.valueOf(password));
     }
 
@@ -157,7 +159,10 @@ public final class PasswordUtil {
      * @return If the hash created from the specified {@code password} equals
      * the given {@code hashedPassword} string.
      */
-    public static boolean isSame(String hashedPassword, String password) {
+    public static boolean isSame(@Nullable String hashedPassword, @Nonnull String password) {
+        if (hashedPassword == null) {
+            return false;
+        }
         try {
             String algorithm = extractAlgorithm(hashedPassword);
             if (algorithm != null) {
@@ -182,7 +187,8 @@ public final class PasswordUtil {
 
     //------------------------------------------------------------< private >---
 
-    public static String generateHash(String pwd, String algorithm, String salt, int iterations) throws NoSuchAlgorithmException, UnsupportedEncodingException {
+    private static String generateHash(@Nonnull String pwd, @Nonnull String algorithm,
+                                       @Nullable String salt, int iterations) throws NoSuchAlgorithmException, UnsupportedEncodingException {
         StringBuilder passwordHash = new StringBuilder();
         passwordHash.append('{').append(algorithm).append('}');
         if (salt != null && !salt.isEmpty()) {
@@ -207,6 +213,7 @@ public final class PasswordUtil {
         return passwordHash.toString();
     }
 
+    @Nonnull
     private static String generateSalt(int saltSize) {
         SecureRandom random = new SecureRandom();
         byte[] salt = new byte[saltSize];
@@ -221,7 +228,8 @@ public final class PasswordUtil {
      * @param bytes the byte array
      * @return the hex encoded string
      */
-    public static String convertBytesToHex(byte[] bytes) {
+    @Nonnull
+    private static String convertBytesToHex(byte[] bytes) {
         StringBuilder res = new StringBuilder(bytes.length * 2);
         for (byte b : bytes) {
             res.append(Text.hexTable[(b >> 4) & 15]);
@@ -236,7 +244,8 @@ public final class PasswordUtil {
      * @param s the hex encoded string
      * @return the byte array
      */
-    public static byte[] convertHexToBytes(String s) {
+    @Nonnull
+    private static byte[] convertHexToBytes(String s) {
         int len = s.length();
         if (len % 2 != 0) {
             throw new IllegalArgumentException("Not a hex encoded byte array: " + s);
@@ -249,8 +258,10 @@ public final class PasswordUtil {
         }
         return bytes;
     }
-    
-    private static String generatePBKDF2(String pwd, String salt, String algorithm, int iterations, int keyLength) throws NoSuchAlgorithmException {
+
+    @Nonnull
+    private static String generatePBKDF2(@Nonnull String pwd, @Nonnull String salt,
+                                         @Nonnull String algorithm, int iterations, int keyLength) throws NoSuchAlgorithmException {
         // for example PBKDF2WithHmacSHA1
         SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm);
         byte[] saltBytes = convertHexToBytes(salt);
@@ -264,7 +275,8 @@ public final class PasswordUtil {
         }  
     }
 
-    private static String generateDigest(String data, String algorithm, int iterations) throws UnsupportedEncodingException, NoSuchAlgorithmException {
+    @Nonnull
+    private static String generateDigest(@Nonnull String data, @Nonnull String algorithm, int iterations) throws UnsupportedEncodingException, NoSuchAlgorithmException {
         byte[] bytes = data.getBytes(ENCODING);
         MessageDigest md = MessageDigest.getInstance(algorithm);
 
@@ -287,7 +299,8 @@ public final class PasswordUtil {
      * leading {@code algorithm} such as created by {@code buildPasswordHash}
      * or if the extracted string isn't a supported algorithm.
      */
-    private static String extractAlgorithm(String hashedPwd) {
+    @CheckForNull
+    private static String extractAlgorithm(@Nullable String hashedPwd) {
         if (hashedPwd != null && !hashedPwd.isEmpty()) {
             int end = hashedPwd.indexOf('}');
             if (hashedPwd.charAt(0) == '{' && end > 0 && end < hashedPwd.length()-1) {
@@ -296,7 +309,7 @@ public final class PasswordUtil {
                     MessageDigest.getInstance(algorithm);
                     return algorithm;
                 } catch (NoSuchAlgorithmException e) {
-                    log.debug("Invalid algorithm detected " + algorithm);
+                    log.debug("Invalid algorithm detected " + algorithm, e);
                 }
             }
         }
@@ -305,23 +318,28 @@ public final class PasswordUtil {
         return null;
     }
 
-    private static String extractSalt(String hashedPwd, int start) {
-        int end = hashedPwd.indexOf(DELIMITER, start);
-        if (end > -1) {
-            return hashedPwd.substring(start, end);
+    @CheckForNull
+    private static String extractSalt(@Nullable String hashedPwd, int start) {
+        if (hashedPwd != null) {
+            int end = hashedPwd.indexOf(DELIMITER, start);
+            if (end > -1) {
+                return hashedPwd.substring(start, end);
+            }
         }
         // no salt
         return null;
     }
 
-    private static int extractIterations(String hashedPwd, int start) {
-        int end = hashedPwd.indexOf(DELIMITER, start);
-        if (end > -1) {
-            String str = hashedPwd.substring(start, end);
-            try {
-                return Integer.parseInt(str);
-            } catch (NumberFormatException e) {
-                log.debug("Expected number of iterations. Found: " + str);
+    private static int extractIterations(@Nullable String hashedPwd, int start) {
+        if (hashedPwd != null) {
+            int end = hashedPwd.indexOf(DELIMITER, start);
+            if (end > -1) {
+                String str = hashedPwd.substring(start, end);
+                try {
+                    return Integer.parseInt(str);
+                } catch (NumberFormatException e) {
+                    log.debug("Expected number of iterations. Found: " + str, e);
+                }
             }
         }
 

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserManagerImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserManagerImplTest.java?rev=1519962&r1=1519961&r2=1519962&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserManagerImplTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserManagerImplTest.java Wed Sep  4 09:51:36 2013
@@ -16,18 +16,10 @@
  */
 package org.apache.jackrabbit.oak.security.user;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
 
@@ -46,6 +38,13 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
 /**
  * @since OAK 1.0
  */
@@ -114,14 +113,14 @@ public class UserManagerImplTest extends
         try {
             userMgr.setPassword(userTree, null, true);
             fail("setting null password should fail");
-        } catch (IllegalArgumentException e) {
+        } catch (NullPointerException e) {
             // expected
         }
 
         try {
             userMgr.setPassword(userTree, null, false);
             fail("setting null password should fail");
-        } catch (IllegalArgumentException e) {
+        } catch (NullPointerException e) {
             // expected
         }
     }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilTest.java?rev=1519962&r1=1519961&r2=1519962&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/user/util/PasswordUtilTest.java Wed Sep  4 09:51:36 2013
@@ -16,14 +16,16 @@
  */
 package org.apache.jackrabbit.oak.spi.security.user.util;
 
-import org.junit.Test;
-
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import com.google.common.collect.ImmutableList;
+import org.junit.Before;
+import org.junit.Test;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -31,33 +33,32 @@ import static org.junit.Assert.fail;
 
 public class PasswordUtilTest {
 
-    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, PasswordUtil.buildPasswordHash(pw));
-            } catch (Exception e) {
-                // should not get here
-            }
+    private List<String> plainPasswords;
+
+    private static Map<String, String> hashedPasswords;
+
+    @Before
+    public void before() throws Exception {
+        plainPasswords = ImmutableList.of(
+                "pw",
+                "PassWord123",
+                "_",
+                "{invalidAlgo}",
+                "{invalidAlgo}Password",
+                "{SHA-256}",
+                "pw{SHA-256}",
+                "p{SHA-256}w",
+                "");
+
+        hashedPasswords = new HashMap<String, String>();
+        for (String pw : plainPasswords) {
+            hashedPasswords.put(pw, PasswordUtil.buildPasswordHash(pw));
         }
     }
 
     @Test
     public void testBuildPasswordHash() throws Exception {
-        for (String pw : PLAIN_PWDS) {
+        for (String pw : plainPasswords) {
             String pwHash = PasswordUtil.buildPasswordHash(pw);
             assertFalse(pw.equals(pwHash));
         }
@@ -69,7 +70,7 @@ public class PasswordUtilTest {
         l.add(new Integer[] {10, 5});
         l.add(new Integer[] {-1, -1});
         for (Integer[] params : l) {
-            for (String pw : PLAIN_PWDS) {
+            for (String pw : plainPasswords) {
                 int saltsize = params[0];
                 int iterations = params[1];
 
@@ -88,7 +89,7 @@ public class PasswordUtilTest {
 
         for (String invalid : invalidAlgorithms) {
             try {
-                String pwHash = PasswordUtil.buildPasswordHash("pw", invalid, PasswordUtil.DEFAULT_SALT_SIZE, PasswordUtil.DEFAULT_ITERATIONS);
+                PasswordUtil.buildPasswordHash("pw", invalid, PasswordUtil.DEFAULT_SALT_SIZE, PasswordUtil.DEFAULT_ITERATIONS);
                 fail("Invalid algorithm " + invalid);
             } catch (NoSuchAlgorithmException e) {
                 // success
@@ -99,7 +100,7 @@ public class PasswordUtilTest {
 
     @Test
     public void testIsPlainTextPassword() throws Exception {
-        for (String pw : PLAIN_PWDS) {
+        for (String pw : plainPasswords) {
             assertTrue(pw + " should be plain text.", PasswordUtil.isPlainTextPassword(pw));
         }
     }
@@ -111,15 +112,15 @@ public class PasswordUtilTest {
 
     @Test
     public void testIsPlainTextForPwHash() throws Exception {
-        for (String pwHash : HASHED_PWDS.values()) {
+        for (String pwHash : hashedPasswords.values()) {
             assertFalse(pwHash + " should not be plain text.", PasswordUtil.isPlainTextPassword(pwHash));
         }
     }
 
     @Test
     public void testIsSame() throws Exception {
-        for (String pw : HASHED_PWDS.keySet()) {
-            String pwHash = HASHED_PWDS.get(pw);
+        for (String pw : hashedPasswords.keySet()) {
+            String pwHash = hashedPasswords.get(pw);
             assertTrue("Not the same " + pw + ", " + pwHash, PasswordUtil.isSame(pwHash, pw));
         }
 
@@ -137,8 +138,8 @@ public class PasswordUtilTest {
     @Test
     public void testIsNotSame() throws Exception {
         String previous = null;
-        for (String pw : HASHED_PWDS.keySet()) {
-            String pwHash = HASHED_PWDS.get(pw);
+        for (String pw : hashedPasswords.keySet()) {
+            String pwHash = hashedPasswords.get(pw);
             assertFalse(pw, PasswordUtil.isSame(pw, pw));
             assertFalse(pwHash, PasswordUtil.isSame(pwHash, pwHash));
             if (previous != null) {
@@ -152,10 +153,13 @@ public class PasswordUtilTest {
     public void testPBKDF2WithHmacSHA1() throws Exception {
         String algo = "PBKDF2WithHmacSHA1";
         // test vector from http://tools.ietf.org/html/rfc6070
-        String hash = PasswordUtil.generateHash(
-                "pass\0word", algo,
-                PasswordUtil.convertBytesToHex("sa\0lt".getBytes()), 4096);
-        assertEquals("{PBKDF2WithHmacSHA1}7361006c74-4096-56fa6aa75548099dcc37d7f03425e0c3", hash);
+        String pw = "pass\0word";
+        int iterations = 4096;
+
+        String hash = PasswordUtil.buildPasswordHash(pw, algo, 5, iterations);
+        assertTrue(hash.startsWith("{PBKDF2WithHmacSHA1}"));
+        int cntOctets = hash.substring(hash.lastIndexOf('-')+1).length() / 2;
+        assertEquals(16, cntOctets);
     }
     
 }
\ No newline at end of file