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

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


The following commit(s) were added to refs/heads/master by this push:
     new 62f45bdfa3 JAMES-3680 XUserAuthenticationStrategy support list secret
62f45bdfa3 is described below

commit 62f45bdfa30731ca661f5ae74e3a344497ba48b9
Author: TungTV <vtt...@linagora.com>
AuthorDate: Thu Oct 17 12:45:00 2024 +0700

    JAMES-3680 XUserAuthenticationStrategy support list secret
---
 docs/modules/servers/partials/configure/jmap.adoc  |  2 +-
 ...eJmapRFC8621AuthenticationStrategyContract.java | 26 ++++++++++++++++++----
 .../jmap/http/XUserAuthenticationStrategy.java     | 17 +++++++++-----
 src/site/xdoc/server/config-jmap.xml               |  2 +-
 4 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/docs/modules/servers/partials/configure/jmap.adoc 
b/docs/modules/servers/partials/configure/jmap.adoc
index 87bcfa21fb..c5f697c53f 100644
--- a/docs/modules/servers/partials/configure/jmap.adoc
+++ b/docs/modules/servers/partials/configure/jmap.adoc
@@ -101,7 +101,7 @@ This allows to prevent users from using some specific JMAP 
extensions.
 | Optional, default value is 500. The max number of items for /set methods.
 
 | authentication.strategy.rfc8621.xUser.secret
-| Optional. Disabled by default. Secret-value used to validate the 
X-User-Secret header when using the XUserAuthenticationStrategy. Use of this 
configuration property is highly advised.
+| Optional. List[String] with delimiter ",". Disabled by default. Secret-value 
used to validate the X-User-Secret header when using the 
XUserAuthenticationStrategy. Use of this configuration property is highly 
advised.
 
 |===
 
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/custom/authentication/strategy/ModularizeJmapRFC8621AuthenticationStrategyContract.java
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/custom/authentication/strategy/ModularizeJmapRFC8621AuthenticationStrategyContract.java
index 5dd126da9a..a86d088a59 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/custom/authentication/strategy/ModularizeJmapRFC8621AuthenticationStrategyContract.java
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/custom/authentication/strategy/ModularizeJmapRFC8621AuthenticationStrategyContract.java
@@ -43,6 +43,7 @@ import static org.hamcrest.Matchers.equalTo;
 
 import java.util.List;
 import java.util.Optional;
+import java.util.concurrent.ThreadLocalRandom;
 
 import org.apache.james.GuiceJamesServer;
 import org.apache.james.jmap.JmapGuiceProbe;
@@ -243,7 +244,7 @@ public abstract class 
ModularizeJmapRFC8621AuthenticationStrategyContract {
     public void 
givenXUserStrategyWhenMissingXUserSecretHeaderShouldFail(GuiceJamesServer 
server) throws Throwable {
         // given a server with XUserAuthenticationStrategy
         // The XUserAuthenticationStrategy is configured with a secret: 
"secret1"
-        setupJamesServerWithXUserStrategy(server, Optional.of("secret1"));
+        setupJamesServerWithXUserStrategy(server, 
Optional.of(List.of("secret1")));
 
         // when a request is made without the X-User-Secret header
         // then the request should fail with a 401 status code
@@ -262,7 +263,7 @@ public abstract class 
ModularizeJmapRFC8621AuthenticationStrategyContract {
     public void 
givenXUserStrategyWhenInvalidateXUserSecretHeaderShouldFail(GuiceJamesServer 
server) throws Throwable {
         // given a server with XUserAuthenticationStrategy
         // The XUserAuthenticationStrategy is configured with a secret: 
"secret1"
-        setupJamesServerWithXUserStrategy(server, Optional.of("secret1"));
+        setupJamesServerWithXUserStrategy(server, 
Optional.of(List.of("secret1")));
 
         // when a request is made with an invalid X-User-Secret header
         // then the request should fail with a 401 status code
@@ -283,7 +284,7 @@ public abstract class 
ModularizeJmapRFC8621AuthenticationStrategyContract {
         // given a server with XUserAuthenticationStrategy
         // The XUserAuthenticationStrategy is configured with a secret: 
"secret1"
         String secret = "secret1";
-        setupJamesServerWithXUserStrategy(server, Optional.of(secret));
+        setupJamesServerWithXUserStrategy(server, 
Optional.of(List.of(secret)));
 
         // when a request is made with an invalid X-User-Secret header
         // then the request should fail with a 401 status code
@@ -314,7 +315,24 @@ public abstract class 
ModularizeJmapRFC8621AuthenticationStrategyContract {
             .body("username", equalTo(BOB().asString()));
     }
 
-    private void setupJamesServerWithXUserStrategy(GuiceJamesServer server, 
Optional<String> xUserSecret) throws Exception {
+    @Test
+    public void givenXUserStrategySupportListSecret(GuiceJamesServer server) 
throws Throwable {
+        List<String> validatedSecretList = List.of("secret1", "secret2", 
"secret3");
+        setupJamesServerWithXUserStrategy(server, 
Optional.of(validatedSecretList));
+
+        // when a request is made with an invalid X-User-Secret header
+        // then the request should fail with a 401 status code
+        given()
+            .header(new Header("X-User", BOB().asString()))
+            .header(new Header("X-User-Secret", 
validatedSecretList.get(ThreadLocalRandom.current().nextInt(3))))
+        .when()
+            .get("/session")
+        .then()
+            .statusCode(SC_OK)
+            .body("username", equalTo(BOB().asString()));
+    }
+
+    private void setupJamesServerWithXUserStrategy(GuiceJamesServer server, 
Optional<List<String>> xUserSecret) throws Exception {
         jmapServer = server
             .overrideWith(new AbstractModule() {
                 @Provides
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/XUserAuthenticationStrategy.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/XUserAuthenticationStrategy.java
index 7de77df0a1..d357e3edc7 100644
--- 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/XUserAuthenticationStrategy.java
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/XUserAuthenticationStrategy.java
@@ -20,6 +20,7 @@
 package org.apache.james.jmap.http;
 
 import java.io.FileNotFoundException;
+import java.util.List;
 import java.util.Optional;
 import java.util.function.Function;
 
@@ -35,6 +36,7 @@ import org.apache.james.utils.PropertiesProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 
 import reactor.core.publisher.Mono;
@@ -50,12 +52,15 @@ public class XUserAuthenticationStrategy implements 
AuthenticationStrategy {
         AuthenticationScheme.of("XUserHeader"),
         ImmutableMap.of());
 
-    private static Optional<String> 
extractXUserSecretFromConfig(PropertiesProvider propertiesProvider) throws 
ConfigurationException {
+    private static Optional<List<String>> 
extractXUserSecretFromConfig(PropertiesProvider propertiesProvider) throws 
ConfigurationException {
         try {
             return 
Optional.ofNullable(propertiesProvider.getConfiguration("jmap"))
-                .map(config -> 
config.getString(AUTHENTICATION_STRATEGY_XUSER_SECRET, null));
+                .map(config -> config.getList(String.class, 
AUTHENTICATION_STRATEGY_XUSER_SECRET, null))
+                .map(list -> {
+                    Preconditions.checkArgument(!list.isEmpty(), 
AUTHENTICATION_STRATEGY_XUSER_SECRET + " must not be empty");
+                    return list;
+                });
         } catch (FileNotFoundException e) {
-             LOGGER.warn("");
             return Optional.empty();
         }
     }
@@ -73,7 +78,7 @@ public class XUserAuthenticationStrategy implements 
AuthenticationStrategy {
 
     public XUserAuthenticationStrategy(UsersRepository usersRepository,
                                        MailboxManager mailboxManager,
-                                       Optional<String> xUserSecret) {
+                                       Optional<List<String>> xUserSecret) {
         this.usersRepository = usersRepository;
         this.mailboxManager = mailboxManager;
         this.usernameExtractor = xUserSecret
@@ -110,10 +115,10 @@ public class XUserAuthenticationStrategy implements 
AuthenticationStrategy {
             .map(Username::of);
     }
 
-    private Function<HttpServerRequest, Optional<Username>> 
createUsernameExtractorWithSecretValidation(String secret) {
+    private Function<HttpServerRequest, Optional<Username>> 
createUsernameExtractorWithSecretValidation(List<String> validatedSecretList) {
         return httpRequest -> 
createUsernameExtractorWithoutSecretValidation().apply(httpRequest)
             .filter(username -> 
Optional.ofNullable(httpRequest.requestHeaders().get(X_USER_SECRET_HEADER_NAME))
-                .map(secret::equals)
+                .map(validatedSecretList::contains)
                 .orElse(false));
     }
 }
diff --git a/src/site/xdoc/server/config-jmap.xml 
b/src/site/xdoc/server/config-jmap.xml
index e1b6c5cf71..23cea6c482 100644
--- a/src/site/xdoc/server/config-jmap.xml
+++ b/src/site/xdoc/server/config-jmap.xml
@@ -143,7 +143,7 @@
                     <dd>Optional, default value is 500. The max number of 
items for /set methods.</dd>
 
                     
<dt><string>authentication.strategy.rfc8621.xUser.secret</string></dt>
-                    <dd>Optional. Disabled by default. Secret-value used to 
validate the X-User-Secret header when using the XUserAuthenticationStrategy. 
Use of this configuration property is highly advised.</dd>
+                    <dd>Optional, List[String] with delimiter ",". Disabled by 
default. Secret-value used to validate the X-User-Secret header when using the 
XUserAuthenticationStrategy. Use of this configuration property is highly 
advised.</dd>
                 </dl>
 
             </subsection>


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to