You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2020/10/14 02:31:33 UTC

[james-project] 07/22: JAMES-3423 Specify different JWT public keys for JMAP and WebAdmin

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

commit 49df4abb3246886e69a8ee486d18747c7d4fbda5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sun Oct 11 09:27:36 2020 +0700

    JAMES-3423 Specify different JWT public  keys for JMAP and WebAdmin
    
    Default to previous behaviour (webadmin uses JMAP JWT key) when to key is specified
    in webadmin configuration, in order to avoid breaking changes.
---
 .../destination/conf/webadmin.properties           |  6 +++++
 .../destination/conf/webadmin.properties           |  6 +++++
 .../destination/conf/webadmin.properties           |  6 +++++
 .../cassandra/destination/conf/webadmin.properties |  6 +++++
 .../memory/destination/conf/webadmin.properties    |  6 +++++
 .../pages/distributed/configure/webadmin.adoc      |  5 +++++
 .../org/apache/james/MemoryJamesServerMain.java    |  3 ++-
 .../org/apache/james/jmap/draft/JMAPModule.java    |  9 ++++++--
 .../apache/james/modules/server/NoJwtModule.java   | 15 ++++++++-----
 .../james/modules/server/WebAdminServerModule.java | 26 ++++++++++++++++++++--
 .../james/jmap/jwt/JWTAuthenticationStrategy.java  |  5 ++++-
 .../org/apache/james/jwt/JwtTokenVerifier.java     | 18 +++++++++------
 .../org/apache/james/jwt/PublicKeyProvider.java    |  8 +------
 .../rabbitmq/RabbitMQJwtFilterIntegrationTest.java |  8 +++++--
 .../memory/MemoryJwtFilterIntegrationTest.java     |  8 +++++--
 .../james/webadmin/WebAdminConfiguration.java      | 25 ++++++++++++++++++---
 .../james/webadmin/authentication/JwtFilter.java   |  5 +++--
 .../webadmin/authentication/JwtFilterTest.java     |  2 +-
 18 files changed, 131 insertions(+), 36 deletions(-)

diff --git a/dockerfiles/run/guice/cassandra-ldap/destination/conf/webadmin.properties b/dockerfiles/run/guice/cassandra-ldap/destination/conf/webadmin.properties
index ec014ea..ab7b2e4 100644
--- a/dockerfiles/run/guice/cassandra-ldap/destination/conf/webadmin.properties
+++ b/dockerfiles/run/guice/cassandra-ldap/destination/conf/webadmin.properties
@@ -37,6 +37,12 @@ https.enabled=false
 
 # Defaults to false
 #jwt.enabled=true
+#
+## If you wish to use OAuth authentication, you should provide a valid JWT public key.
+## The following entry specify the link to the URL of the public key file,
+## which should be a PEM format file.
+##
+#jwt.publickeypem.url=file://conf/jwt_publickey
 
 # Defaults to false
 #cors.enable=true
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/webadmin.properties b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/webadmin.properties
index ec014ea..ab7b2e4 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/webadmin.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/webadmin.properties
@@ -37,6 +37,12 @@ https.enabled=false
 
 # Defaults to false
 #jwt.enabled=true
+#
+## If you wish to use OAuth authentication, you should provide a valid JWT public key.
+## The following entry specify the link to the URL of the public key file,
+## which should be a PEM format file.
+##
+#jwt.publickeypem.url=file://conf/jwt_publickey
 
 # Defaults to false
 #cors.enable=true
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/webadmin.properties b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/webadmin.properties
index ec014ea..ab7b2e4 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/webadmin.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/webadmin.properties
@@ -37,6 +37,12 @@ https.enabled=false
 
 # Defaults to false
 #jwt.enabled=true
+#
+## If you wish to use OAuth authentication, you should provide a valid JWT public key.
+## The following entry specify the link to the URL of the public key file,
+## which should be a PEM format file.
+##
+#jwt.publickeypem.url=file://conf/jwt_publickey
 
 # Defaults to false
 #cors.enable=true
diff --git a/dockerfiles/run/guice/cassandra/destination/conf/webadmin.properties b/dockerfiles/run/guice/cassandra/destination/conf/webadmin.properties
index ec014ea..ab7b2e4 100644
--- a/dockerfiles/run/guice/cassandra/destination/conf/webadmin.properties
+++ b/dockerfiles/run/guice/cassandra/destination/conf/webadmin.properties
@@ -37,6 +37,12 @@ https.enabled=false
 
 # Defaults to false
 #jwt.enabled=true
+#
+## If you wish to use OAuth authentication, you should provide a valid JWT public key.
+## The following entry specify the link to the URL of the public key file,
+## which should be a PEM format file.
+##
+#jwt.publickeypem.url=file://conf/jwt_publickey
 
 # Defaults to false
 #cors.enable=true
diff --git a/dockerfiles/run/guice/memory/destination/conf/webadmin.properties b/dockerfiles/run/guice/memory/destination/conf/webadmin.properties
index 52cfffd..340315e 100644
--- a/dockerfiles/run/guice/memory/destination/conf/webadmin.properties
+++ b/dockerfiles/run/guice/memory/destination/conf/webadmin.properties
@@ -37,6 +37,12 @@ https.enabled=false
 
 # Defaults to false
 #jwt.enabled=true
+#
+## If you wish to use OAuth authentication, you should provide a valid JWT public key.
+## The following entry specify the link to the URL of the public key file,
+## which should be a PEM format file.
+##
+#jwt.publickeypem.url=file://conf/jwt_publickey
 
 # Defaults to false
 #cors.enable=true
diff --git a/docs/modules/servers/pages/distributed/configure/webadmin.adoc b/docs/modules/servers/pages/distributed/configure/webadmin.adoc
index 8e4b5c5..7b673e1 100644
--- a/docs/modules/servers/pages/distributed/configure/webadmin.adoc
+++ b/docs/modules/servers/pages/distributed/configure/webadmin.adoc
@@ -53,6 +53,11 @@ to get some examples and hints.
 | https.trust.password
 | Specify the truststore password (default: null)
 
+| jwt.publickeypem.url
+| Optional. JWT tokens allow request to bypass authentication. Path to the JWT public key.
+Defaults to the `jwt.publickeypem.url` value of `jmap.properties` file if unspecified
+(legacy behaviour)
+
 | extensions.routes
 | List of Routes specified as fully qualified class name that should be loaded in addition to your product routes list. Routes
 needs to be on the classpath or in the ./extensions-jars folder. Read mode about
diff --git a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
index 32256c5..3fcbe09 100644
--- a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
+++ b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
@@ -48,6 +48,7 @@ import org.apache.james.modules.server.MailRepositoriesRoutesModule;
 import org.apache.james.modules.server.MailboxRoutesModule;
 import org.apache.james.modules.server.MailboxesExportRoutesModule;
 import org.apache.james.modules.server.MemoryMailQueueModule;
+import org.apache.james.modules.server.NoJwtModule;
 import org.apache.james.modules.server.RawPostDequeueDecoratorModule;
 import org.apache.james.modules.server.SieveRoutesModule;
 import org.apache.james.modules.server.SwaggerRoutesModule;
@@ -86,7 +87,7 @@ public class MemoryJamesServerMain implements JamesServerMain {
         binder -> binder.bind(WebAdminConfiguration.class).toInstance(WebAdminConfiguration.TEST_CONFIGURATION));
 
     public static final Module WEBADMIN_TESTING = Modules.override(WEBADMIN)
-        .with(WEBADMIN_NO_AUTH_MODULE);
+        .with(WEBADMIN_NO_AUTH_MODULE, new NoJwtModule());
 
     public static final Module PROTOCOLS = Modules.combine(
         new IMAPServerModule(),
diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/JMAPModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/JMAPModule.java
index f31b10d..7acbe92 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/JMAPModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/JMAPModule.java
@@ -25,6 +25,8 @@ import java.util.EnumSet;
 import java.util.Optional;
 import java.util.stream.Stream;
 
+import javax.inject.Named;
+
 import org.apache.commons.configuration2.Configuration;
 import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.commons.io.FileUtils;
@@ -40,6 +42,7 @@ import org.apache.james.jmap.mailet.VacationMailet;
 import org.apache.james.jmap.mailet.filter.JMAPFiltering;
 import org.apache.james.jmap.rfc8621.RFC8621MethodsModule;
 import org.apache.james.jwt.JwtConfiguration;
+import org.apache.james.jwt.JwtTokenVerifier;
 import org.apache.james.lifecycle.api.StartUpCheck;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxManager.SearchCapabilities;
@@ -152,8 +155,10 @@ public class JMAPModule extends AbstractModule {
 
     @Provides
     @Singleton
-    JwtConfiguration providesJwtConfiguration(JMAPDraftConfiguration jmapConfiguration) {
-        return new JwtConfiguration(jmapConfiguration.getJwtPublicKeyPem());
+    @Named("jmap")
+    JwtTokenVerifier providesJwtTokenVerifier(JMAPDraftConfiguration jmapConfiguration) {
+        JwtConfiguration jwtConfiguration = new JwtConfiguration(jmapConfiguration.getJwtPublicKeyPem());
+        return JwtTokenVerifier.create(jwtConfiguration);
     }
 
     private Optional<String> loadPublicKey(FileSystem fileSystem, Optional<String> jwtPublickeyPemUrl) {
diff --git a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/NoJwtModule.java b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/NoJwtModule.java
index a500595..a64a2b2 100644
--- a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/NoJwtModule.java
+++ b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/NoJwtModule.java
@@ -19,16 +19,19 @@
 
 package org.apache.james.modules.server;
 
-import java.util.Optional;
+import javax.inject.Named;
 
-import org.apache.james.jwt.JwtConfiguration;
+import org.apache.james.jwt.JwtTokenVerifier;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
 
 public class NoJwtModule extends AbstractModule {
-
-    @Override
-    protected void configure() {
-        bind(JwtConfiguration.class).toInstance(new JwtConfiguration(Optional.empty()));
+    @Provides
+    @Singleton
+    @Named("jmap")
+    JwtTokenVerifier failWhenFallbackToJMAPConfiguration() {
+        throw new RuntimeException("JMAP is not enabled thus we can not fallback to its JWT configuration");
     }
 }
diff --git a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java
index a2625be..fb7a7d0 100644
--- a/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java
+++ b/server/container/guice/protocols/webadmin/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java
@@ -22,6 +22,7 @@ package org.apache.james.modules.server;
 import static org.apache.james.webadmin.WebAdminConfiguration.DISABLED_CONFIGURATION;
 
 import java.io.FileNotFoundException;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
@@ -29,6 +30,9 @@ import java.util.Set;
 import javax.inject.Named;
 
 import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.io.FileUtils;
+import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.jwt.JwtConfiguration;
 import org.apache.james.jwt.JwtTokenVerifier;
 import org.apache.james.utils.ClassName;
 import org.apache.james.utils.GuiceGenericLoader;
@@ -56,6 +60,7 @@ import com.github.fge.lambdas.Throwing;
 import com.github.steveash.guavate.Guavate;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.AbstractModule;
+import com.google.inject.Provider;
 import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.Singleton;
@@ -108,7 +113,7 @@ public class WebAdminServerModule extends AbstractModule {
 
     @Provides
     @Singleton
-    public WebAdminConfiguration provideWebAdminConfiguration(PropertiesProvider propertiesProvider) throws Exception {
+    public WebAdminConfiguration provideWebAdminConfiguration(FileSystem fileSystem, PropertiesProvider propertiesProvider) throws Exception {
         try {
             Configuration configurationFile = propertiesProvider.getConfiguration("webadmin");
 
@@ -124,6 +129,8 @@ public class WebAdminServerModule extends AbstractModule {
                 .urlCORSOrigin(configurationFile.getString("cors.origin", DEFAULT_NO_CORS_ORIGIN))
                 .host(configurationFile.getString("host", WebAdminConfiguration.DEFAULT_HOST))
                 .additionalRoutes(additionalRoutes)
+                .jwtPublicKeyPEM(loadPublicKey(fileSystem,
+                    Optional.ofNullable(configurationFile.getString("jwt.publickeypem.url", null))))
                 .build();
         } catch (FileNotFoundException e) {
             LOGGER.info("No webadmin.properties file. Disabling WebAdmin interface.");
@@ -131,10 +138,14 @@ public class WebAdminServerModule extends AbstractModule {
         }
     }
 
+    private Optional<String> loadPublicKey(FileSystem fileSystem, Optional<String> jwtPublickeyPemUrl) {
+        return jwtPublickeyPemUrl.map(Throwing.function(url -> FileUtils.readFileToString(fileSystem.getFile(url), StandardCharsets.US_ASCII)));
+    }
+
     @Provides
     @Singleton
     public AuthenticationFilter providesAuthenticationFilter(PropertiesProvider propertiesProvider,
-                                                             JwtTokenVerifier jwtTokenVerifier) throws Exception {
+                                                             @Named("webadmin") JwtTokenVerifier.Factory jwtTokenVerifier) throws Exception {
         try {
             Configuration configurationFile = propertiesProvider.getConfiguration("webadmin");
             if (configurationFile.getBoolean("jwt.enabled", DEFAULT_JWT_DISABLED)) {
@@ -146,6 +157,17 @@ public class WebAdminServerModule extends AbstractModule {
         }
     }
 
+    @Provides
+    @Singleton
+    @Named("webadmin")
+    JwtTokenVerifier.Factory providesJwtTokenVerifier(WebAdminConfiguration webAdminConfiguration,
+                                              @Named("jmap") Provider<JwtTokenVerifier> jmapTokenVerifier) {
+        return () -> webAdminConfiguration.getJwtPublicKey()
+            .map(keyPath -> new JwtConfiguration(Optional.of(keyPath)))
+            .map(JwtTokenVerifier::create)
+            .orElseGet(jmapTokenVerifier::get);
+    }
+
     private Optional<TlsConfiguration> readHttpsConfiguration(Configuration configurationFile) {
         boolean enabled = configurationFile.getBoolean("https.enabled", DEFAULT_HTTPS_DISABLED);
         if (enabled) {
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/jwt/JWTAuthenticationStrategy.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/jwt/JWTAuthenticationStrategy.java
index 5ffbec0..2dc0c8c 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/jwt/JWTAuthenticationStrategy.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/jwt/JWTAuthenticationStrategy.java
@@ -19,6 +19,7 @@
 package org.apache.james.jmap.jwt;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 import org.apache.james.core.Username;
 import org.apache.james.jmap.exceptions.UnauthorizedException;
@@ -44,7 +45,9 @@ public class JWTAuthenticationStrategy implements AuthenticationStrategy {
 
     @Inject
     @VisibleForTesting
-    public JWTAuthenticationStrategy(JwtTokenVerifier tokenManager, MailboxManager mailboxManager, UsersRepository usersRepository) {
+    public JWTAuthenticationStrategy(@Named("jmap") JwtTokenVerifier tokenManager,
+                                     MailboxManager mailboxManager,
+                                     UsersRepository usersRepository) {
         this.tokenManager = tokenManager;
         this.mailboxManager = mailboxManager;
         this.usersRepository = usersRepository;
diff --git a/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java b/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java
index 1516295..f7f833c 100644
--- a/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java
+++ b/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java
@@ -18,12 +18,9 @@
  ****************************************************************/
 package org.apache.james.jwt;
 
-import javax.inject.Inject;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
 
 import io.jsonwebtoken.Claims;
@@ -34,12 +31,19 @@ import io.jsonwebtoken.MalformedJwtException;
 
 public class JwtTokenVerifier {
 
-    private static Logger LOGGER = LoggerFactory.getLogger(JwtTokenVerifier.class);
+    public interface Factory {
+        JwtTokenVerifier create();
+    }
+
+    public static JwtTokenVerifier create(JwtConfiguration jwtConfiguration) {
+        PublicKeyProvider publicKeyProvider = new PublicKeyProvider(jwtConfiguration, new PublicKeyReader());
+        return new JwtTokenVerifier(publicKeyProvider);
+    }
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenVerifier.class);
     private final PublicKeyProvider pubKeyProvider;
 
-    @Inject
-    @VisibleForTesting
-    JwtTokenVerifier(PublicKeyProvider pubKeyProvider) {
+    public JwtTokenVerifier(PublicKeyProvider pubKeyProvider) {
         this.pubKeyProvider = pubKeyProvider;
     }
 
diff --git a/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyProvider.java b/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyProvider.java
index 9ce7792..4f102eb 100644
--- a/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyProvider.java
+++ b/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyProvider.java
@@ -20,18 +20,12 @@ package org.apache.james.jwt;
 
 import java.security.PublicKey;
 
-import javax.inject.Inject;
-
-import com.google.common.annotations.VisibleForTesting;
-
 public class PublicKeyProvider {
 
     private final JwtConfiguration jwtConfiguration;
     private final PublicKeyReader reader;
 
-    @Inject
-    @VisibleForTesting
-    PublicKeyProvider(JwtConfiguration jwtConfiguration, PublicKeyReader reader) {
+    public PublicKeyProvider(JwtConfiguration jwtConfiguration, PublicKeyReader reader) {
         this.jwtConfiguration = jwtConfiguration;
         this.reader = reader;
     }
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java
index a96b4a3..e9c190d 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java
@@ -26,7 +26,7 @@ import org.apache.james.DockerElasticSearchExtension;
 import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
 import org.apache.james.SearchConfiguration;
-import org.apache.james.jwt.JwtConfiguration;
+import org.apache.james.jwt.JwtTokenVerifier;
 import org.apache.james.modules.AwsS3BlobStoreExtension;
 import org.apache.james.modules.RabbitMQExtension;
 import org.apache.james.modules.blobstore.BlobStoreConfiguration;
@@ -36,6 +36,8 @@ import org.apache.james.webadmin.integration.JwtFilterIntegrationTest;
 import org.apache.james.webadmin.integration.WebadminIntegrationTestModule;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
+import com.google.inject.name.Names;
+
 class RabbitMQJwtFilterIntegrationTest extends JwtFilterIntegrationTest {
     @RegisterExtension
     static JamesServerExtension testExtension = new JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir ->
@@ -54,7 +56,9 @@ class RabbitMQJwtFilterIntegrationTest extends JwtFilterIntegrationTest {
         .extension(new RabbitMQExtension())
         .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
             .overrideWith(binder -> binder.bind(AuthenticationFilter.class).to(JwtFilter.class))
-            .overrideWith(binder -> binder.bind(JwtConfiguration.class).toInstance(jwtConfiguration()))
+            .overrideWith(binder -> binder.bind(JwtTokenVerifier.Factory.class)
+                .annotatedWith(Names.named("webadmin"))
+                .toInstance(() -> JwtTokenVerifier.create(jwtConfiguration())))
             .overrideWith(new WebadminIntegrationTestModule()))
         .build();
 }
diff --git a/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/MemoryJwtFilterIntegrationTest.java b/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/MemoryJwtFilterIntegrationTest.java
index c1aeca7..770b596 100644
--- a/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/MemoryJwtFilterIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/MemoryJwtFilterIntegrationTest.java
@@ -22,13 +22,15 @@ package org.apache.james.webadmin.integration.memory;
 import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
 import org.apache.james.MemoryJamesServerMain;
-import org.apache.james.jwt.JwtConfiguration;
+import org.apache.james.jwt.JwtTokenVerifier;
 import org.apache.james.webadmin.authentication.AuthenticationFilter;
 import org.apache.james.webadmin.authentication.JwtFilter;
 import org.apache.james.webadmin.integration.JwtFilterIntegrationTest;
 import org.apache.james.webadmin.integration.WebadminIntegrationTestModule;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
+import com.google.inject.name.Names;
+
 class MemoryJwtFilterIntegrationTest extends JwtFilterIntegrationTest {
 
     @RegisterExtension
@@ -36,6 +38,8 @@ class MemoryJwtFilterIntegrationTest extends JwtFilterIntegrationTest {
         .server(configuration -> MemoryJamesServerMain.createServer(configuration)
             .overrideWith(new WebadminIntegrationTestModule())
             .overrideWith(binder -> binder.bind(AuthenticationFilter.class).to(JwtFilter.class))
-            .overrideWith(binder -> binder.bind(JwtConfiguration.class).toInstance(jwtConfiguration())))
+            .overrideWith(binder -> binder.bind(JwtTokenVerifier.Factory.class)
+                .annotatedWith(Names.named("webadmin"))
+                .toInstance(() -> JwtTokenVerifier.create(jwtConfiguration()))))
         .build();
 }
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminConfiguration.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminConfiguration.java
index ecb5c1b..f5d2279 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminConfiguration.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminConfiguration.java
@@ -58,6 +58,17 @@ public class WebAdminConfiguration {
         private Optional<String> urlCORSOrigin = Optional.empty();
         private Optional<String> host = Optional.empty();
         private ImmutableList.Builder<String> additionalRoutes = ImmutableList.builder();
+        private Optional<String> jwtPublicKey = Optional.empty();
+
+        public Builder jwtPublicKeyPEM(String jwtPublicKeyPEM) {
+            this.jwtPublicKey = Optional.of(jwtPublicKeyPEM);
+            return this;
+        }
+
+        public Builder jwtPublicKeyPEM(Optional<String> jwtPublicKeyPEM) {
+            jwtPublicKeyPEM.ifPresent(this::jwtPublicKeyPEM);
+            return this;
+        }
 
         public Builder tls(TlsConfiguration tlsConfiguration) {
             this.tlsConfiguration = Optional.of(tlsConfiguration);
@@ -130,7 +141,8 @@ public class WebAdminConfiguration {
                 enableCORS.orElse(DEFAULT_CORS_DISABLED),
                 urlCORSOrigin.orElse(CORS_ALL_ORIGINS),
                 host.orElse(DEFAULT_HOST),
-                additionalRoutes.build());
+                additionalRoutes.build(),
+                jwtPublicKey);
         }
     }
 
@@ -141,10 +153,11 @@ public class WebAdminConfiguration {
     private final String urlCORSOrigin;
     private final String host;
     private final List<String> additionalRoutes;
+    private final Optional<String> jwtPublicKey;
 
     @VisibleForTesting
     WebAdminConfiguration(boolean enabled, Optional<PortSupplier> port, Optional<TlsConfiguration> tlsConfiguration,
-                          boolean enableCORS, String urlCORSOrigin, String host, List<String> additionalRoutes) {
+                          boolean enableCORS, String urlCORSOrigin, String host, List<String> additionalRoutes, Optional<String> jwtPublicKey) {
         this.enabled = enabled;
         this.port = port;
         this.tlsConfiguration = tlsConfiguration;
@@ -152,6 +165,11 @@ public class WebAdminConfiguration {
         this.urlCORSOrigin = urlCORSOrigin;
         this.host = host;
         this.additionalRoutes = additionalRoutes;
+        this.jwtPublicKey = jwtPublicKey;
+    }
+
+    public Optional<String> getJwtPublicKey() {
+        return jwtPublicKey;
     }
 
     public List<String> getAdditionalRoutes() {
@@ -195,6 +213,7 @@ public class WebAdminConfiguration {
                 && Objects.equals(this.port, that.port)
                 && Objects.equals(this.tlsConfiguration, that.tlsConfiguration)
                 && Objects.equals(this.enableCORS, that.enableCORS)
+                && Objects.equals(this.jwtPublicKey, that.jwtPublicKey)
                 && Objects.equals(this.urlCORSOrigin, that.urlCORSOrigin)
                 && Objects.equals(this.host, that.host)
                 && Objects.equals(this.additionalRoutes, that.additionalRoutes);
@@ -204,6 +223,6 @@ public class WebAdminConfiguration {
 
     @Override
     public final int hashCode() {
-        return Objects.hash(enabled, port, tlsConfiguration, enableCORS, urlCORSOrigin, host, additionalRoutes);
+        return Objects.hash(enabled, port, tlsConfiguration, enableCORS, jwtPublicKey, urlCORSOrigin, host, additionalRoutes);
     }
 }
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java
index 28d8cd6..972569b 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java
@@ -24,6 +24,7 @@ import static spark.Spark.halt;
 import java.util.Optional;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 import org.apache.james.jwt.JwtTokenVerifier;
 import org.eclipse.jetty.http.HttpStatus;
@@ -39,8 +40,8 @@ public class JwtFilter implements AuthenticationFilter {
     private final JwtTokenVerifier jwtTokenVerifier;
 
     @Inject
-    public JwtFilter(JwtTokenVerifier jwtTokenVerifier) {
-        this.jwtTokenVerifier = jwtTokenVerifier;
+    public JwtFilter(@Named("webadmin") JwtTokenVerifier.Factory jwtTokenVerifierFactory) {
+        this.jwtTokenVerifier = jwtTokenVerifierFactory.create();
     }
 
     @Override
diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/authentication/JwtFilterTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/authentication/JwtFilterTest.java
index 05cc9da..d2d46a4 100644
--- a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/authentication/JwtFilterTest.java
+++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/authentication/JwtFilterTest.java
@@ -66,7 +66,7 @@ public class JwtFilterTest {
     @Before
     public void setUp() {
         jwtTokenVerifier = mock(JwtTokenVerifier.class);
-        jwtFilter = new JwtFilter(jwtTokenVerifier);
+        jwtFilter = new JwtFilter(() -> jwtTokenVerifier);
     }
 
     @Test


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