This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 84d5acca922e63ce1d85e655f54bfc88e70da31f
Author: Benoit Tellier <[email protected]>
AuthorDate: Fri Dec 3 14:07:34 2021 +0700

    JAMES-3674 Document PBKDF2 usage for usersrepository.xml
---
 .../ROOT/pages/configure/usersrepository.adoc      |   4 +-
 .../apache/james/user/lib/model/AlgorithmTest.java | 124 ++++++++++++++++++++-
 .../james/user/lib/model/DefaultUserTest.java      | 120 --------------------
 src/site/xdoc/server/config-users.xml              |   4 +-
 4 files changed, 128 insertions(+), 124 deletions(-)

diff --git 
a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/usersrepository.adoc
 
b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/usersrepository.adoc
index 637955e..1921afa 100644
--- 
a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/usersrepository.adoc
+++ 
b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/usersrepository.adoc
@@ -40,7 +40,9 @@ acting on the behalf of any user.
 | Delay after a failed authentication attempt with an invalid user name or 
password. Duration string defaulting to seconds, e.g. `2`, `2s`, `2000ms`. 
Default `0s` (disabled).
 
 | algorithm
-| use a specific hash algorithm to compute passwords, with optional mode 
`plain` (default) or `salted`; e.g. `SHA-512` (default),  `SHA-512/plain`, 
`SHA-512/salted`
+| use a specific hash algorithm to compute passwords, with optional mode 
`plain` (default) or `salted`; e.g. `SHA-512`,  `SHA-512/plain`, 
`SHA-512/salted`, `PBKDF2` (default).
+Note: When using `PBKDF2` one can specify the iteration count and the key size 
in bytes. You can specify it as part of the algorithm. EG: `PBKDF2-2000-512` 
will use
+2000 iterations with a key size of 512 bytes.
 
 | hashingMode
 | specify the hashing mode to use if there is none recorded in the database: 
`plain` (default) for newer installations or `legacy` for older ones
diff --git 
a/server/data/data-library/src/test/java/org/apache/james/user/lib/model/AlgorithmTest.java
 
b/server/data/data-library/src/test/java/org/apache/james/user/lib/model/AlgorithmTest.java
index 5da7daa..3e2805e 100644
--- 
a/server/data/data-library/src/test/java/org/apache/james/user/lib/model/AlgorithmTest.java
+++ 
b/server/data/data-library/src/test/java/org/apache/james/user/lib/model/AlgorithmTest.java
@@ -22,8 +22,13 @@ package org.apache.james.user.lib.model;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
+import java.util.stream.Stream;
+
 import org.assertj.core.api.SoftAssertions;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 
 import nl.jqno.equalsverifier.EqualsVerifier;
 
@@ -154,13 +159,13 @@ class AlgorithmTest {
     @Test
     void ofShouldParseIteration() {
         assertThat(Algorithm.of("PBKDF2-10", "plain").hasher())
-            .isEqualTo(new Algorithm.PBKDF2Hasher(10, 1024));
+            .isEqualTo(new Algorithm.PBKDF2Hasher(10, 512));
     }
 
     @Test
     void ofShouldAcceptDefaultPBKDF2() {
         assertThat(Algorithm.of("PBKDF2", "plain").hasher())
-            .isEqualTo(new Algorithm.PBKDF2Hasher(1000, 1024));
+            .isEqualTo(new Algorithm.PBKDF2Hasher(1000, 512));
     }
 
     @Test
@@ -210,4 +215,119 @@ class AlgorithmTest {
         assertThatThrownBy(() -> Algorithm.of("PBKDF2-1--1", "plain"))
             .isInstanceOf(IllegalArgumentException.class);
     }
+
+
+    private static Stream<Arguments> sha1LegacyTestBed() {
+        return Stream.of(
+            Arguments.of("myPassword", "VBPuJHI7uixaa6LQGWx4s+5G"),
+            Arguments.of("otherPassword", "ks40t+AjBnHsMaC1Is/6+mtb"),
+            Arguments.of("", "2jmj7l5rSw0yVb/vlWAYkK/Y"),
+            Arguments.of("a", "hvfkN/qlp/zhXR3cuerq6jd2"));
+    }
+
+    @ParameterizedTest
+    @MethodSource("sha1LegacyTestBed")
+    void testSha1Legacy(String password, String expectedHash) {
+        assertThat(Algorithm.of("SHA-1", "legacy").digest(password, "salt"))
+            .isEqualTo(expectedHash);
+    }
+
+    private static Stream<Arguments> sha512LegacyTestBed() {
+        return Stream.of(
+            Arguments.of("myPassword", 
"RQrQPbk5XfzLXgMGb9fxbPuith4j1RY3NxRHFFkFLskKmkvzoVHmAOqKrtNuO4who9OKsXBYOXSd\r\nEw2kOA8U"),
+            Arguments.of("otherPassword", 
"6S2kG/b6oHgWBXQjKDKTayXWu2cs9374lxFrL9uVpmYUlq0lw/ZFU9svMtYVDV5aVjJqRbLWZ/df\r\neaaJwYxk"),
+            Arguments.of("", 
"z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGl\r\nODJ6+Sfa"),
+            Arguments.of("a", 
"H0D8ktokFpR1CXnubPWC8tXX0o4YM13gWrxU0FYOD1MChgxlK/CNVgJSql50IQVG82n7u86MEs/H\r\nlXsmUv6a"));
+    }
+
+    @ParameterizedTest
+    @MethodSource("sha512LegacyTestBed")
+    void testSha512Legacy(String password, String expectedHash) {
+        assertThat(Algorithm.of("SHA-512", "legacy").digest(password, "salt"))
+            .isEqualTo(expectedHash);
+    }
+
+    private static Stream<Arguments> sha1TestBed() {
+        return Stream.of(
+            Arguments.of("myPassword", "VBPuJHI7uixaa6LQGWx4s+5GKNE=\r\n"),
+            Arguments.of("otherPassword", "ks40t+AjBnHsMaC1Is/6+mtb05s=\r\n"),
+            Arguments.of("", "2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\n"),
+            Arguments.of("a", "hvfkN/qlp/zhXR3cuerq6jd2Z7g=\r\n"));
+    }
+
+    @ParameterizedTest
+    @MethodSource("sha1TestBed")
+    void testSha1(String password, String expectedHash) {
+        assertThat(Algorithm.of("SHA-1").digest(password, "salt"))
+            .isEqualTo(expectedHash);
+    }
+
+    private static Stream<Arguments> sha512TestBed() {
+        return Stream.of(
+            Arguments.of("myPassword", 
"RQrQPbk5XfzLXgMGb9fxbPuith4j1RY3NxRHFFkFLskKmkvzoVHmAOqKrtNuO4who9OKsXBYOXSd\r\nEw2kOA8USA==\r\n"),
+            Arguments.of("otherPassword", 
"6S2kG/b6oHgWBXQjKDKTayXWu2cs9374lxFrL9uVpmYUlq0lw/ZFU9svMtYVDV5aVjJqRbLWZ/df\r\neaaJwYxkhQ==\r\n"),
+            Arguments.of("", 
"z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGl\r\nODJ6+SfaPg==\r\n"),
+            Arguments.of("a", 
"H0D8ktokFpR1CXnubPWC8tXX0o4YM13gWrxU0FYOD1MChgxlK/CNVgJSql50IQVG82n7u86MEs/H\r\nlXsmUv6adQ==\r\n"));
+    }
+
+    @ParameterizedTest
+    @MethodSource("sha512TestBed")
+    void testSha512(String password, String expectedHash) {
+        assertThat(Algorithm.of("SHA-512").digest(password, "salt"))
+            .isEqualTo(expectedHash);
+    }
+
+    private static Stream<Arguments> PBKDF2TestBed() {
+        return Stream.of(
+            Arguments.of("myPassword", 
"Ag2g49zxor0w11yguLUMQ7EKBokU81LkvLyDqubWtQq7R5V21HVqZ+CEjEQxBLGfi35RFyesJtxb\r\n"
 +
+                "L5/VRCpI3g==\r\n"),
+            Arguments.of("otherPassword", 
"4KFfGIjbZqhaqZfr1rKWcoY5vkeps3/+x5BwU342kUbGGoW30kaP98R5iY6SNGg0yOaPBcB8EWqJ\r\n"
 +
+                "96RtIMnIYQ==\r\n"),
+            Arguments.of("", 
"6grdNX1hpxA5wJPXhBUJhz4qUoUSRZE0F3rqoPR+PYedDklDomJ0LPRV5f1SMNAX0fRgmQ8WDe6k\r\n"
 +
+                "2qr1Nc/orA==\r\n"),
+            Arguments.of("a", 
"WxpwqV5V9L3QR8xi8D8INuH0UH5oLeq+ZuXb6J1bAfhHp3urVOtAr+bwksC3JQRyC7QHE9MLfn61\r\n"
 +
+                "nTXo5johrQ==\r\n"));
+    }
+
+    @ParameterizedTest
+    @MethodSource("PBKDF2TestBed")
+    void testPBKDF2(String password, String expectedHash) {
+        assertThat(Algorithm.of("PBKDF2").digest(password, "salt"))
+            .isEqualTo(expectedHash);
+    }
+
+    private static Stream<Arguments> PBKDF210IterationTestBed() {
+        return Stream.of(
+            Arguments.of("myPassword", 
"AoNuFZ7ZI6vHU8obYASuuLPaQcr8fGentKWsOawBNTIc7MNJMbo4yNjo0pcCVK6J/XAfbISEKugt\r\n"
 +
+                "HwDeSUA10A==\r\n"),
+            Arguments.of("otherPassword", 
"e4+swbwo1s3X665INoRVsENXrgJtC7SMws9G3Y0GBoLZBkqZQzE2aT2WLd+hOlf3s/wwQe10MA0Q\r\n"
 +
+                "xMJQIcIosQ==\r\n"),
+            Arguments.of("", 
"ZBXj9rrLc4L9hHXOBPpDd5ot9DDB6qaq1g2mbAMOivpZe3eYw1ehdFXbU9pwpI4y/+MZlLkG3E1S\r\n"
 +
+                "WRQXuUZqag==\r\n"),
+            Arguments.of("a", 
"i1iWZzuaqsFotT998+stRqyrcyUrZ0diBJf9RJ52mUo0a074ykh8joWdrxhEsyd2Fh2DNO38TWxC\r\n"
 +
+                "KkIK6taLxA==\r\n"));
+
+    }
+
+    @ParameterizedTest
+    @MethodSource("PBKDF210IterationTestBed")
+    void testPBKDF210Iteration(String password, String expectedHash) {
+        assertThat(Algorithm.of("PBKDF2-10").digest(password, "salt"))
+            .isEqualTo(expectedHash);
+    }
+
+    private static Stream<Arguments> PBKDF210Iteration128KeySizeTestBed() {
+        return Stream.of(
+            Arguments.of("myPassword", "AoNuFZ7ZI6vHU8obYASuuA==\r\n"),
+            Arguments.of("otherPassword", "e4+swbwo1s3X665INoRVsA==\r\n"),
+            Arguments.of("", "ZBXj9rrLc4L9hHXOBPpDdw==\r\n"),
+            Arguments.of("a", "i1iWZzuaqsFotT998+stRg==\r\n"));
+    }
+
+    @ParameterizedTest
+    @MethodSource("PBKDF210Iteration128KeySizeTestBed")
+    void testPBKDF210Iteration128KeySize(String password, String expectedHash) 
{
+        assertThat(Algorithm.of("PBKDF2-10-128").digest(password, "salt"))
+            .isEqualTo(expectedHash);
+    }
 }
\ No newline at end of file
diff --git 
a/server/data/data-library/src/test/java/org/apache/james/user/lib/model/DefaultUserTest.java
 
b/server/data/data-library/src/test/java/org/apache/james/user/lib/model/DefaultUserTest.java
index 98b37f1..d900ef6 100644
--- 
a/server/data/data-library/src/test/java/org/apache/james/user/lib/model/DefaultUserTest.java
+++ 
b/server/data/data-library/src/test/java/org/apache/james/user/lib/model/DefaultUserTest.java
@@ -23,15 +23,9 @@ import static 
org.apache.james.user.lib.model.Algorithm.HashingMode.LEGACY;
 import static org.apache.james.user.lib.model.Algorithm.HashingMode.PLAIN;
 import static org.assertj.core.api.Assertions.assertThat;
 
-import java.util.Optional;
-import java.util.stream.Stream;
-
 import org.apache.james.core.Username;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
 
 public class DefaultUserTest {
 
@@ -67,118 +61,4 @@ public class DefaultUserTest {
         assertThat(user.verifyPassword("secret2")).isTrue();
         assertThat(user.verifyPassword("secret")).isFalse();
     }
-
-    private static Stream<Arguments> sha1LegacyTestBed() {
-        return Stream.of(
-            Arguments.of("myPassword", "VBPuJHI7uixaa6LQGWx4s+5G"),
-            Arguments.of("otherPassword", "ks40t+AjBnHsMaC1Is/6+mtb"),
-            Arguments.of("", "2jmj7l5rSw0yVb/vlWAYkK/Y"),
-            Arguments.of("a", "hvfkN/qlp/zhXR3cuerq6jd2"));
-    }
-
-    @ParameterizedTest
-    @MethodSource("sha1LegacyTestBed")
-    void testSha1Legacy(String password, String expectedHash) throws Exception 
{
-        
assertThat(DefaultUser.digestString(Optional.ofNullable(password).orElse(""),
-            Algorithm.of("SHA-1", "legacy"), "salt"))
-            .isEqualTo(expectedHash);
-    }
-
-    private static Stream<Arguments> sha512LegacyTestBed() {
-        return Stream.of(
-            Arguments.of("myPassword", 
"RQrQPbk5XfzLXgMGb9fxbPuith4j1RY3NxRHFFkFLskKmkvzoVHmAOqKrtNuO4who9OKsXBYOXSd\r\nEw2kOA8U"),
-            Arguments.of("otherPassword", 
"6S2kG/b6oHgWBXQjKDKTayXWu2cs9374lxFrL9uVpmYUlq0lw/ZFU9svMtYVDV5aVjJqRbLWZ/df\r\neaaJwYxk"),
-            Arguments.of("", 
"z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGl\r\nODJ6+Sfa"),
-            Arguments.of("a", 
"H0D8ktokFpR1CXnubPWC8tXX0o4YM13gWrxU0FYOD1MChgxlK/CNVgJSql50IQVG82n7u86MEs/H\r\nlXsmUv6a"));
-    }
-
-    @ParameterizedTest
-    @MethodSource("sha512LegacyTestBed")
-    void testSha512Legacy(String password, String expectedHash) throws 
Exception {
-        assertThat(DefaultUser.digestString(password, Algorithm.of("SHA-512", 
"legacy"), "salt"))
-            .isEqualTo(expectedHash);
-    }
-
-    private static Stream<Arguments> sha1TestBed() {
-        return Stream.of(
-            Arguments.of("myPassword", "VBPuJHI7uixaa6LQGWx4s+5GKNE=\r\n"),
-            Arguments.of("otherPassword", "ks40t+AjBnHsMaC1Is/6+mtb05s=\r\n"),
-            Arguments.of("", "2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\n"),
-            Arguments.of("a", "hvfkN/qlp/zhXR3cuerq6jd2Z7g=\r\n"));
-    }
-
-    @ParameterizedTest
-    @MethodSource("sha1TestBed")
-    void testSha1(String password, String expectedHash) throws Exception {
-        assertThat(DefaultUser.digestString(password, Algorithm.of("SHA-1"), 
"salt"))
-            .isEqualTo(expectedHash);
-    }
-
-    private static Stream<Arguments> sha512TestBed() {
-        return Stream.of(
-            Arguments.of("myPassword", 
"RQrQPbk5XfzLXgMGb9fxbPuith4j1RY3NxRHFFkFLskKmkvzoVHmAOqKrtNuO4who9OKsXBYOXSd\r\nEw2kOA8USA==\r\n"),
-            Arguments.of("otherPassword", 
"6S2kG/b6oHgWBXQjKDKTayXWu2cs9374lxFrL9uVpmYUlq0lw/ZFU9svMtYVDV5aVjJqRbLWZ/df\r\neaaJwYxkhQ==\r\n"),
-            Arguments.of("", 
"z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGl\r\nODJ6+SfaPg==\r\n"),
-            Arguments.of("a", 
"H0D8ktokFpR1CXnubPWC8tXX0o4YM13gWrxU0FYOD1MChgxlK/CNVgJSql50IQVG82n7u86MEs/H\r\nlXsmUv6adQ==\r\n"));
-    }
-
-    @ParameterizedTest
-    @MethodSource("sha512TestBed")
-    void testSha512(String password, String expectedHash) throws Exception {
-        assertThat(DefaultUser.digestString(password, Algorithm.of("SHA-512"), 
"salt"))
-            .isEqualTo(expectedHash);
-    }
-
-    private static Stream<Arguments> PBKDF2TestBed() {
-        return Stream.of(
-            Arguments.of("myPassword", 
"Ag2g49zxor0w11yguLUMQ7EKBokU81LkvLyDqubWtQq7R5V21HVqZ+CEjEQxBLGfi35RFyesJtxb\r\n"
 +
-                "L5/VRCpI3g==\r\n"),
-            Arguments.of("otherPassword", 
"4KFfGIjbZqhaqZfr1rKWcoY5vkeps3/+x5BwU342kUbGGoW30kaP98R5iY6SNGg0yOaPBcB8EWqJ\r\n"
 +
-                "96RtIMnIYQ==\r\n"),
-            Arguments.of("", 
"6grdNX1hpxA5wJPXhBUJhz4qUoUSRZE0F3rqoPR+PYedDklDomJ0LPRV5f1SMNAX0fRgmQ8WDe6k\r\n"
 +
-                "2qr1Nc/orA==\r\n"),
-            Arguments.of("a", 
"WxpwqV5V9L3QR8xi8D8INuH0UH5oLeq+ZuXb6J1bAfhHp3urVOtAr+bwksC3JQRyC7QHE9MLfn61\r\n"
 +
-                "nTXo5johrQ==\r\n"));
-    }
-
-    @ParameterizedTest
-    @MethodSource("PBKDF2TestBed")
-    void testPBKDF2(String password, String expectedHash) {
-        assertThat(DefaultUser.digestString(password, Algorithm.of("PBKDF2"), 
"salt"))
-            .isEqualTo(expectedHash);
-    }
-
-    private static Stream<Arguments> PBKDF210IterationTestBed() {
-        return Stream.of(
-            Arguments.of("myPassword", 
"AoNuFZ7ZI6vHU8obYASuuLPaQcr8fGentKWsOawBNTIc7MNJMbo4yNjo0pcCVK6J/XAfbISEKugt\r\n"
 +
-                "HwDeSUA10A==\r\n"),
-            Arguments.of("otherPassword", 
"e4+swbwo1s3X665INoRVsENXrgJtC7SMws9G3Y0GBoLZBkqZQzE2aT2WLd+hOlf3s/wwQe10MA0Q\r\n"
 +
-                "xMJQIcIosQ==\r\n"),
-            Arguments.of("", 
"ZBXj9rrLc4L9hHXOBPpDd5ot9DDB6qaq1g2mbAMOivpZe3eYw1ehdFXbU9pwpI4y/+MZlLkG3E1S\r\n"
 +
-                "WRQXuUZqag==\r\n"),
-            Arguments.of("a", 
"i1iWZzuaqsFotT998+stRqyrcyUrZ0diBJf9RJ52mUo0a074ykh8joWdrxhEsyd2Fh2DNO38TWxC\r\n"
 +
-                "KkIK6taLxA==\r\n"));
-    }
-
-    @ParameterizedTest
-    @MethodSource("PBKDF210IterationTestBed")
-    void testPBKDF210Iteration(String password, String expectedHash) {
-        assertThat(DefaultUser.digestString(password, 
Algorithm.of("PBKDF2-10"), "salt"))
-            .isEqualTo(expectedHash);
-    }
-
-    private static Stream<Arguments> PBKDF210Iteration128KeySizeTestBed() {
-        return Stream.of(
-            Arguments.of("myPassword", "AoNuFZ7ZI6vHU8obYASuuA==\r\n"),
-            Arguments.of("otherPassword", "e4+swbwo1s3X665INoRVsA==\r\n"),
-            Arguments.of("", "ZBXj9rrLc4L9hHXOBPpDdw==\r\n"),
-            Arguments.of("a", "i1iWZzuaqsFotT998+stRg==\r\n"));
-    }
-
-    @ParameterizedTest
-    @MethodSource("PBKDF210Iteration128KeySizeTestBed")
-    void testPBKDF210Iteration128KeySize(String password, String expectedHash) 
{
-        assertThat(DefaultUser.digestString(password, 
Algorithm.of("PBKDF2-10-128"), "salt"))
-            .isEqualTo(expectedHash);
-    }
 }
diff --git a/src/site/xdoc/server/config-users.xml 
b/src/site/xdoc/server/config-users.xml
index 43dc856..58a7677 100644
--- a/src/site/xdoc/server/config-users.xml
+++ b/src/site/xdoc/server/config-users.xml
@@ -77,7 +77,9 @@
 
       <dl>
         <dt><strong>algorithm</strong></dt>
-        <dd>Algorithm to hash passwords. Supported password algorithm are: 
MD5, SHA-256, SHA-512, NONE(then SHA-1 will be used).</dd>
+        <dd>Algorithm to hash passwords. Supported password algorithm are: 
MD5, SHA-256, SHA-512, NONE(then SHA-1 will be used), `PBKDF2` (default).<br/>
+            <b>Note</b>: When using `PBKDF2` one can specify the iteration 
count and the key size in bytes. You can specify it as part of the algorithm. 
EG: `PBKDF2-2000-512` will use
+            2000 iterations with a key size of 512 bytes.</dd>
         <dd>MD5 and SHA-1 are deprecated.</dd>
         <dt><strong>enableVirtualHosting</strong></dt>
         <dd>true (default) or false. Defines if the usernames must (true) or 
may not contain (false) a domain part ([email protected]).</dd>

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to