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