You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2022/01/06 01:51:52 UTC

[james-project] branch master updated: JAMES-3680 Capability should be aware of request URL prefixes (#807)

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


The following commit(s) were added to refs/heads/master by this push:
     new 8607439  JAMES-3680 Capability should be aware of request URL prefixes (#807)
8607439 is described below

commit 8607439fb9bb4c309b5d3df5aba73a3b7a1902e5
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Thu Jan 6 08:51:44 2022 +0700

    JAMES-3680 Capability should be aware of request URL prefixes (#807)
---
 .../org/apache/james/jmap/draft/JMAPModule.java    | 34 ++++----
 .../rfc8621/contract/CustomMethodContract.scala    | 12 ++-
 .../org/apache/james/jmap/core/Capabilities.scala  | 20 +++--
 .../org/apache/james/jmap/core/Capability.scala    | 95 +++++++++++++++++++++-
 .../james/jmap/core/JmapRfc8621Configuration.scala |  3 +
 .../org/apache/james/jmap/routes/JmapApi.scala     | 10 +--
 .../apache/james/jmap/routes/SessionRoutes.scala   | 24 ++----
 .../apache/james/jmap/routes/SessionSupplier.scala | 63 +++++---------
 .../change/StateChangeEventSerializerTest.scala    |  1 -
 .../jmap/core/JmapUrlEndpointResolverTest.scala    | 76 +----------------
 .../james/jmap/routes/JMAPApiRoutesTest.scala      |  4 +-
 .../james/jmap/routes/SessionRoutesTest.scala      |  5 +-
 .../james/jmap/routes/SessionSupplierTest.scala    |  8 +-
 13 files changed, 179 insertions(+), 176 deletions(-)

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 438a7d0..2acbbb8 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
@@ -37,16 +37,22 @@ import org.apache.james.jmap.JMAPConfiguration;
 import org.apache.james.jmap.JMAPServer;
 import org.apache.james.jmap.Version;
 import org.apache.james.jmap.change.MailboxChangeListener;
-import org.apache.james.jmap.core.Capability;
-import org.apache.james.jmap.core.DefaultCapabilities;
+import org.apache.james.jmap.core.CapabilityFactory;
+import org.apache.james.jmap.core.CoreCapabilityFactory;
 import org.apache.james.jmap.core.JmapRfc8621Configuration;
+import org.apache.james.jmap.core.MDNCapabilityFactory$;
+import org.apache.james.jmap.core.MailCapabilityFactory$;
+import org.apache.james.jmap.core.QuotaCapabilityFactory$;
+import org.apache.james.jmap.core.SharesCapabilityFactory$;
+import org.apache.james.jmap.core.SubmissionCapabilityFactory$;
+import org.apache.james.jmap.core.VacationResponseCapabilityFactory$;
+import org.apache.james.jmap.core.WebSocketCapabilityFactory$;
 import org.apache.james.jmap.draft.methods.RequestHandler;
 import org.apache.james.jmap.draft.send.PostDequeueDecoratorFactory;
 import org.apache.james.jmap.draft.utils.JsoupHtmlTextExtractor;
 import org.apache.james.jmap.event.PropagateLookupRightListener;
 import org.apache.james.jmap.mailet.filter.JMAPFiltering;
 import org.apache.james.jmap.rfc8621.RFC8621MethodsModule;
-import org.apache.james.jmap.routes.JmapUrlEndpointResolver;
 import org.apache.james.jwt.JwtConfiguration;
 import org.apache.james.jwt.JwtTokenVerifier;
 import org.apache.james.lifecycle.api.StartUpCheck;
@@ -135,13 +141,13 @@ public class JMAPModule extends AbstractModule {
         supportedVersions.addBinding().toInstance(Version.DRAFT);
         supportedVersions.addBinding().toInstance(Version.RFC8621);
 
-        Multibinder<Capability> supportedCapabilities = Multibinder.newSetBinder(binder(), Capability.class);
-        supportedCapabilities.addBinding().toInstance(DefaultCapabilities.MAIL_CAPABILITY());
-        supportedCapabilities.addBinding().toInstance(DefaultCapabilities.QUOTA_CAPABILITY());
-        supportedCapabilities.addBinding().toInstance(DefaultCapabilities.SHARES_CAPABILITY());
-        supportedCapabilities.addBinding().toInstance(DefaultCapabilities.VACATION_RESPONSE_CAPABILITY());
-        supportedCapabilities.addBinding().toInstance(DefaultCapabilities.SUBMISSION_CAPABILITY());
-        supportedCapabilities.addBinding().toInstance(DefaultCapabilities.MDN_CAPABILITY());
+        Multibinder<CapabilityFactory> supportedCapabilities = Multibinder.newSetBinder(binder(), CapabilityFactory.class);
+        supportedCapabilities.addBinding().toInstance(MailCapabilityFactory$.MODULE$);
+        supportedCapabilities.addBinding().toInstance(QuotaCapabilityFactory$.MODULE$);
+        supportedCapabilities.addBinding().toInstance(SharesCapabilityFactory$.MODULE$);
+        supportedCapabilities.addBinding().toInstance(VacationResponseCapabilityFactory$.MODULE$);
+        supportedCapabilities.addBinding().toInstance(SubmissionCapabilityFactory$.MODULE$);
+        supportedCapabilities.addBinding().toInstance(MDNCapabilityFactory$.MODULE$);
     }
 
     @ProvidesIntoSet
@@ -161,13 +167,13 @@ public class JMAPModule extends AbstractModule {
     }
 
     @ProvidesIntoSet
-    Capability coreCapability(JmapRfc8621Configuration configuration) {
-        return DefaultCapabilities.coreCapability(configuration.maxUploadSize());
+    CapabilityFactory coreCapability(JmapRfc8621Configuration configuration) {
+        return new CoreCapabilityFactory(configuration.maxUploadSize());
     }
 
     @ProvidesIntoSet
-    Capability webSocketCapability(JmapRfc8621Configuration configuration) {
-        return DefaultCapabilities.webSocketCapability(JmapUrlEndpointResolver.from(configuration).webSocketUrl());
+    CapabilityFactory webSocketCapability(JmapRfc8621Configuration configuration) {
+        return WebSocketCapabilityFactory$.MODULE$;
     }
 
     @Provides
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/CustomMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/CustomMethodContract.scala
index 84a813d..d2e5487 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/CustomMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/CustomMethodContract.scala
@@ -43,7 +43,7 @@ import org.apache.james.jmap.change.{AccountIdRegistrationKey, StateChangeEvent}
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE}
 import org.apache.james.jmap.core.Invocation.MethodName
 import org.apache.james.jmap.core.ResponseObject.SESSION_STATE
-import org.apache.james.jmap.core.{Capability, CapabilityProperties}
+import org.apache.james.jmap.core.{Capability, CapabilityFactory, CapabilityProperties, UrlPrefixes}
 import org.apache.james.jmap.draft.JmapGuiceProbe
 import org.apache.james.jmap.http.UserCredential
 import org.apache.james.jmap.mail
@@ -177,10 +177,16 @@ case class CustomCapabilityProperties() extends CapabilityProperties {
 
 case class CustomCapability(properties: CustomCapabilityProperties = CustomCapabilityProperties(), identifier: CapabilityIdentifier = CUSTOM) extends Capability
 
+case object CustomCapabilityFactory extends CapabilityFactory {
+  override def create(urlPrefixes: UrlPrefixes): Capability = CustomCapability()
+
+  override def id(): CapabilityIdentifier = CUSTOM
+}
+
 class CustomMethodModule extends AbstractModule {
   override protected def configure(): Unit = {
-    val supportedCapabilities: Multibinder[Capability] = Multibinder.newSetBinder(binder, classOf[Capability])
-    supportedCapabilities.addBinding.toInstance(CustomCapability())
+    val supportedCapabilities: Multibinder[CapabilityFactory] = Multibinder.newSetBinder(binder, classOf[CapabilityFactory])
+    supportedCapabilities.addBinding.toInstance(CustomCapabilityFactory)
     Multibinder.newSetBinder(binder(), classOf[Method])
       .addBinding()
       .to(classOf[CustomMethod])
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
index 229e917..ae113f3 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
@@ -20,9 +20,9 @@ package org.apache.james.jmap.core
 
 import java.net.URI
 
+import com.google.common.annotations.VisibleForTesting
 import eu.timepit.refined.auto._
 import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.routes.JmapUrlEndpointResolver
 
 object DefaultCapabilities {
   def coreCapability(maxUploadSize: MaxSizeUpload) = CoreCapability(
@@ -53,14 +53,16 @@ object DefaultCapabilities {
   val VACATION_RESPONSE_CAPABILITY = VacationResponseCapability()
   val SUBMISSION_CAPABILITY = SubmissionCapability()
 
-  def supported(configuration: JmapRfc8621Configuration): Capabilities = Capabilities.of(
-    coreCapability(configuration.maxUploadSize),
-    MAIL_CAPABILITY,
-    QUOTA_CAPABILITY,
-    SHARES_CAPABILITY,
-    VACATION_RESPONSE_CAPABILITY,
-    SUBMISSION_CAPABILITY,
-    webSocketCapability(JmapUrlEndpointResolver.from(configuration).webSocketUrl))
+  @VisibleForTesting
+  def supported(configuration: JmapRfc8621Configuration): Set[CapabilityFactory] = Set(
+    CoreCapabilityFactory(configuration.maxUploadSize),
+    MailCapabilityFactory,
+    QuotaCapabilityFactory,
+    SharesCapabilityFactory,
+    VacationResponseCapabilityFactory,
+    SharesCapabilityFactory,
+    SubmissionCapabilityFactory,
+    WebSocketCapabilityFactory)
 }
 
 object Capabilities {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
index 920d087..d8ceafd 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
@@ -19,7 +19,7 @@
 
 package org.apache.james.jmap.core
 
-import java.net.URI
+import java.net.{URI, URL}
 
 import eu.timepit.refined
 import eu.timepit.refined.api.Refined
@@ -33,6 +33,7 @@ import org.apache.james.jmap.core.UnsignedInt.{UnsignedInt, UnsignedIntConstrain
 import org.apache.james.jmap.json.ResponseSerializer
 import org.apache.james.util.Size
 import play.api.libs.json.{JsObject, Json}
+import reactor.netty.http.server.HttpServerRequest
 
 import scala.util.{Failure, Success, Try}
 
@@ -60,11 +61,61 @@ trait Capability {
   def properties(): CapabilityProperties
 }
 
+object UrlPrefixes {
+  private val JMAP_PREFIX_HEADER: String = "X-JMAP-PREFIX"
+  private val JMAP_WEBSOCKET_PREFIX_HEADER: String = "X-JMAP-WEBSOCKET-PREFIX"
+
+  def from(jmapRfc8621Configuration: JmapRfc8621Configuration, request: HttpServerRequest): UrlPrefixes =
+    if (jmapRfc8621Configuration.dynamicJmapPrefixResolutionEnabled) {
+      UrlPrefixes(
+        safeURL(request.requestHeaders().get(JMAP_PREFIX_HEADER))
+          .map(_.toURI)
+          .getOrElse(new URI(jmapRfc8621Configuration.urlPrefixString)),
+        safeURI(request.requestHeaders().get(JMAP_WEBSOCKET_PREFIX_HEADER))
+          .getOrElse(new URI(jmapRfc8621Configuration.websocketPrefixString)))
+    } else {
+      jmapRfc8621Configuration.urlPrefixes()
+    }
+
+  private def safeURI(string: String): Option[URI] = Option(string).flatMap(s => Try(new URI(s)).toOption)
+
+  private def safeURL(string: String): Option[URL] = Option(string).flatMap(s => Try(new URL(s)).toOption)
+}
+
+final case class UrlPrefixes(httpUrlPrefix: URI, webSocketURLPrefix: URI)
+
+trait CapabilityFactory {
+  def create(urlPrefixes: UrlPrefixes): Capability
+
+  def id(): CapabilityIdentifier
+}
+
 final case class CoreCapability(properties: CoreCapabilityProperties,
                                 identifier: CapabilityIdentifier = JMAP_CORE) extends Capability
 
+final case class CoreCapabilityFactory(maxUploadSize: MaxSizeUpload) extends CapabilityFactory {
+  override def id(): CapabilityIdentifier = JMAP_CORE
+
+  override def create(urlPrefixes: UrlPrefixes): Capability = CoreCapability(CoreCapabilityProperties(
+    maxUploadSize,
+    MaxConcurrentUpload(4L),
+    MaxSizeRequest(10_000_000L),
+    MaxConcurrentRequests(4L),
+    MaxCallsInRequest(16L),
+    MaxObjectsInGet(500L),
+    MaxObjectsInSet(500L),
+    collationAlgorithms = List("i;unicode-casemap")))
+}
+
 case class WebSocketCapability(properties: WebSocketCapabilityProperties, identifier: CapabilityIdentifier = JMAP_WEBSOCKET) extends Capability
 
+case object WebSocketCapabilityFactory extends CapabilityFactory {
+  override def id(): CapabilityIdentifier = JMAP_WEBSOCKET
+
+  override def create(urlPrefixes: UrlPrefixes): Capability = WebSocketCapability(
+    WebSocketCapabilityProperties(SupportsPush(true), new URI(urlPrefixes.webSocketURLPrefix + "/jmap/ws")))
+}
+
 object MaxSizeUpload {
   def of(size: Size): Try[MaxSizeUpload] = refined.refineV[UnsignedIntConstraint](size.asBytes()) match {
     case Right(value) => Success(MaxSizeUpload(value))
@@ -108,6 +159,12 @@ final case class EhloArgs(value: String) extends AnyVal
 final case class SubmissionCapability(identifier: CapabilityIdentifier = EMAIL_SUBMISSION,
                                       properties: SubmissionProperties = SubmissionProperties()) extends Capability
 
+case object SubmissionCapabilityFactory extends CapabilityFactory {
+  override def id(): CapabilityIdentifier = EMAIL_SUBMISSION
+
+  override def create(urlPrefixes: UrlPrefixes): Capability = SubmissionCapability()
+}
+
 final case class SubmissionProperties(maxDelayedSend: MaxDelayedSend = MaxDelayedSend(0),
                                       submissionExtensions: Map[EhloName, List[EhloArgs]] = Map()) extends CapabilityProperties {
   override def jsonify(): JsObject = ResponseSerializer.submissionPropertiesWrites.writes(this)
@@ -120,6 +177,18 @@ object MailCapability {
 final case class MailCapability(properties: MailCapabilityProperties,
                                 identifier: CapabilityIdentifier = JMAP_MAIL) extends Capability
 
+case object MailCapabilityFactory extends CapabilityFactory {
+  override def id(): CapabilityIdentifier = JMAP_MAIL
+
+  override def create(urlPrefixes: UrlPrefixes): Capability = MailCapability(MailCapabilityProperties(
+    MaxMailboxesPerEmail(Some(10_000_000L)),
+    MaxMailboxDepth(None),
+    MaxSizeMailboxName(200L),
+    MaxSizeAttachmentsPerEmail(20_000_000L),
+    emailQuerySortOptions = List("receivedAt", "sentAt", "size", "from", "to", "subject"),
+    MayCreateTopLevelMailbox(true)))
+}
+
 case class MaxMailboxesPerEmail(value: Option[UnsignedInt])
 case class MaxMailboxDepth(value: Option[UnsignedInt])
 case class MaxSizeMailboxName(value: UnsignedInt)
@@ -142,10 +211,22 @@ final case class QuotaCapabilityProperties() extends CapabilityProperties {
 final case class QuotaCapability(properties: QuotaCapabilityProperties = QuotaCapabilityProperties(),
                                  identifier: CapabilityIdentifier = JAMES_QUOTA) extends Capability
 
+case object QuotaCapabilityFactory extends CapabilityFactory {
+  override def id(): CapabilityIdentifier = JAMES_QUOTA
+
+  override def create(urlPrefixes: UrlPrefixes): Capability = QuotaCapability()
+}
+
 final case class SharesCapabilityProperties() extends CapabilityProperties {
   override def jsonify(): JsObject = Json.obj()
 }
 
+case object SharesCapabilityFactory extends CapabilityFactory {
+  override def id(): CapabilityIdentifier = JAMES_SHARES
+
+  override def create(urlPrefixes: UrlPrefixes): Capability = SharesCapability()
+}
+
 final case class SharesCapability(properties: SharesCapabilityProperties = SharesCapabilityProperties(),
                                   identifier: CapabilityIdentifier = JAMES_SHARES) extends Capability
 
@@ -153,6 +234,12 @@ final case class MDNCapabilityProperties() extends CapabilityProperties {
   override def jsonify(): JsObject = Json.obj()
 }
 
+case object MDNCapabilityFactory extends CapabilityFactory {
+  override def id(): CapabilityIdentifier = JMAP_MDN
+
+  override def create(urlPrefixes: UrlPrefixes): Capability = MDNCapability()
+}
+
 final case class MDNCapability(properties: MDNCapabilityProperties = MDNCapabilityProperties(),
                                identifier: CapabilityIdentifier = JMAP_MDN) extends Capability
 
@@ -160,5 +247,11 @@ final case class VacationResponseCapabilityProperties() extends CapabilityProper
   override def jsonify(): JsObject = Json.obj()
 }
 
+case object VacationResponseCapabilityFactory extends CapabilityFactory {
+  override def id(): CapabilityIdentifier = JMAP_VACATION_RESPONSE
+
+  override def create(urlPrefixes: UrlPrefixes): Capability = VacationResponseCapability()
+}
+
 final case class VacationResponseCapability(properties: VacationResponseCapabilityProperties = VacationResponseCapabilityProperties(),
                                             identifier: CapabilityIdentifier = JMAP_VACATION_RESPONSE) extends Capability
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala
index 70b29ae..c8f0651 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala
@@ -19,6 +19,7 @@
 
 package org.apache.james.jmap.core
 
+import java.net.{URI, URL}
 import java.util.Optional
 
 import org.apache.commons.configuration2.Configuration
@@ -75,6 +76,8 @@ case class JmapRfc8621Configuration(urlPrefixString: String,
     maxTimeoutSeconds = maxTimeoutSeconds,
     maxConnections = maxConnections)
 
+  def urlPrefixes(): UrlPrefixes = UrlPrefixes(new URI(urlPrefixString), new URI(websocketPrefixString))
+
   def getAuthenticationStrategiesAsJava(): Optional[java.util.List[String]] = authenticationStrategies.toJava
 
   def withAuthenticationStrategies(list: Optional[java.util.List[String]]): JmapRfc8621Configuration =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JmapApi.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JmapApi.scala
index 3d19067..32f0f33 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JmapApi.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JmapApi.scala
@@ -21,7 +21,7 @@ package org.apache.james.jmap.routes
 import javax.inject.Inject
 import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.core.Invocation.MethodName
-import org.apache.james.jmap.core.{Capability, ErrorCode, Invocation, MissingCapabilityException, RequestObject, ResponseObject}
+import org.apache.james.jmap.core.{CapabilityFactory, ErrorCode, Invocation, MissingCapabilityException, RequestObject, ResponseObject}
 import org.apache.james.jmap.method.{InvocationWithContext, Method}
 import org.apache.james.mailbox.MailboxSession
 import org.slf4j.{Logger, LoggerFactory}
@@ -34,19 +34,19 @@ object JMAPApi {
   val LOGGER: Logger = LoggerFactory.getLogger(classOf[JMAPApi])
 }
 
-class JMAPApi (methods: Set[Method], defaultCapabilities: Set[Capability]) {
+class JMAPApi (methods: Set[Method], defaultCapabilities: Set[CapabilityIdentifier]) {
 
   private val methodsByName: Map[MethodName, Method] = methods.map(method => method.methodName -> method).toMap
 
   @Inject
-  def this(javaMethods: java.util.Set[Method], supportedCapabilities: java.util.Set[Capability]) {
-    this(javaMethods.asScala.toSet, supportedCapabilities.asScala.toSet)
+  def this(javaMethods: java.util.Set[Method], supportedCapabilities: java.util.Set[CapabilityFactory]) {
+    this(javaMethods.asScala.toSet, supportedCapabilities.asScala.map(x => x.id()).toSet)
   }
 
   def process(requestObject: RequestObject,
               mailboxSession: MailboxSession): SMono[ResponseObject] = {
     val processingContext: ProcessingContext = ProcessingContext(Map.empty, Map.empty)
-    val unsupportedCapabilities = requestObject.using.toSet -- defaultCapabilities.map(_.identifier())
+    val unsupportedCapabilities = requestObject.using.toSet -- defaultCapabilities
     val capabilities: Set[CapabilityIdentifier] = requestObject.using.toSet
 
     if (unsupportedCapabilities.nonEmpty) {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
index 01d1424..f34b04d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionRoutes.scala
@@ -19,7 +19,6 @@
 
 package org.apache.james.jmap.routes
 
-import java.net.{URI, URL}
 import java.nio.charset.StandardCharsets
 import java.util.stream.Stream
 
@@ -29,38 +28,35 @@ import io.netty.handler.codec.http.{HttpMethod, HttpResponseStatus}
 import javax.inject.{Inject, Named}
 import org.apache.james.jmap.HttpConstants.{JSON_CONTENT_TYPE, JSON_CONTENT_TYPE_UTF8}
 import org.apache.james.jmap.JMAPRoutes.CORS_CONTROL
-import org.apache.james.jmap.core.{ProblemDetails, Session}
+import org.apache.james.jmap.core.{JmapRfc8621Configuration, ProblemDetails, Session, UrlPrefixes}
 import org.apache.james.jmap.exceptions.UnauthorizedException
 import org.apache.james.jmap.http.Authenticator
 import org.apache.james.jmap.http.rfc8621.InjectionKeys
 import org.apache.james.jmap.json.ResponseSerializer
-import org.apache.james.jmap.routes.SessionRoutes.{JMAP_PREFIX_HEADER, JMAP_SESSION, JMAP_WEBSOCKET_PREFIX_HEADER, LOGGER, WELL_KNOWN_JMAP}
+import org.apache.james.jmap.routes.SessionRoutes.{JMAP_SESSION, LOGGER, WELL_KNOWN_JMAP}
 import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes}
 import org.slf4j.{Logger, LoggerFactory}
 import play.api.libs.json.Json
 import reactor.core.publisher.Mono
 import reactor.core.scala.publisher.SMono
 import reactor.core.scheduler.Schedulers
-import reactor.netty.http.server.{HttpServerRequest, HttpServerResponse}
-
-import scala.util.Try
+import reactor.netty.http.server.HttpServerResponse
 
 object SessionRoutes {
   private val JMAP_SESSION: String = "/jmap/session"
   private val WELL_KNOWN_JMAP: String = "/.well-known/jmap"
   private val LOGGER: Logger = LoggerFactory.getLogger(classOf[SessionRoutes])
-  private val JMAP_PREFIX_HEADER: String = "X-JMAP-PREFIX"
-  private val JMAP_WEBSOCKET_PREFIX_HEADER: String = "X-JMAP-WEBSOCKET-PREFIX"
 }
 
 class SessionRoutes @Inject() (@Named(InjectionKeys.RFC_8621) val authenticator: Authenticator,
-                               val sessionSupplier: SessionSupplier) extends JMAPRoutes {
+                               val sessionSupplier: SessionSupplier,
+                               val jmapRfc8621Configuration: JmapRfc8621Configuration) extends JMAPRoutes {
 
   private val generateSession: JMAPRoute.Action =
     (request, response) => SMono.fromPublisher(authenticator.authenticate(request))
       .map(_.getUser)
       .handle[Session] {
-        case (username, sink) => sessionSupplier.generate(username, extractJMAPPrefix(request), extractJMAPWebSocketPrefix(request))
+        case (username, sink) => sessionSupplier.generate(username, UrlPrefixes.from(jmapRfc8621Configuration, request))
           .fold(sink.error, session => sink.next(session))
       }
       .flatMap(session => sendRespond(session, response))
@@ -68,14 +64,6 @@ class SessionRoutes @Inject() (@Named(InjectionKeys.RFC_8621) val authenticator:
       .subscribeOn(Schedulers.elastic())
       .asJava()
 
-  private def extractJMAPPrefix(request: HttpServerRequest): Option[URL] =
-    Option(request.requestHeaders().get(JMAP_PREFIX_HEADER))
-      .flatMap(value => Try(new URL(value)).toOption)
-
-  private def extractJMAPWebSocketPrefix(request: HttpServerRequest): Option[URI] =
-    Option(request.requestHeaders().get(JMAP_WEBSOCKET_PREFIX_HEADER))
-      .flatMap(value => Try(new URI(value)).toOption)
-
   private val redirectToSession: JMAPRoute.Action = JMAPRoutes.redirectTo(JMAP_SESSION)
 
   override def routes: Stream[JMAPRoute] =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
index d9d4b8e..e5ceb1f 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/SessionSupplier.scala
@@ -19,19 +19,19 @@
 
 package org.apache.james.jmap.routes
 
-import java.net.{URI, URL}
+import java.net.URL
 
 import javax.inject.Inject
 import org.apache.james.core.Username
 import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.core.{Account, AccountId, Capabilities, Capability, IsPersonal, IsReadOnly, JmapRfc8621Configuration, Session, WebSocketCapability}
+import org.apache.james.jmap.core.{Account, AccountId, Capabilities, Capability, CapabilityFactory, IsPersonal, IsReadOnly, Session, UrlPrefixes}
 
 import scala.jdk.CollectionConverters._
 
-class SessionSupplier(val configuration: JmapRfc8621Configuration, defaultCapabilities: Set[Capability]) {
+class SessionSupplier(capabilityFactories: Set[CapabilityFactory]) {
   @Inject
-  def this(configuration: JmapRfc8621Configuration, defaultCapabilities: java.util.Set[Capability]) {
-    this(configuration, defaultCapabilities.asScala.toSet)
+  def this(defaultCapabilities: java.util.Set[CapabilityFactory]) {
+    this(defaultCapabilities.asScala.toSet)
   }
 
   def validate(username: Username, accountId: AccountId): Boolean = AccountId.from(username)
@@ -39,19 +39,15 @@ class SessionSupplier(val configuration: JmapRfc8621Configuration, defaultCapabi
     .toOption
     .getOrElse(false)
 
-  def generate(username: Username, urlPrefix: Option[URL] = None, webSocketUriPrefix: Option[URI] = None): Either[IllegalArgumentException, Session] = {
-    val urlEndpointResolver: JmapUrlEndpointResolver = new JmapUrlEndpointResolver(configuration, urlPrefix, webSocketUriPrefix)
-    val capabilities: Set[Capability] = defaultCapabilities
-      .map {
-        case websocketCapability: WebSocketCapability =>
-          websocketCapability.copy(properties = websocketCapability.properties.copy(url = urlEndpointResolver.webSocketUrl))
-        case another => another
-      }
-    accounts(username)
+  def generate(username: Username, urlPrefixes: UrlPrefixes): Either[IllegalArgumentException, Session] = {
+    val urlEndpointResolver: JmapUrlEndpointResolver = new JmapUrlEndpointResolver(urlPrefixes)
+    val capabilities: Set[Capability] = capabilityFactories
+      .map(cf => cf.create(urlPrefixes))
+    accounts(username, capabilities)
       .map(account => Session(
         Capabilities(capabilities),
         List(account),
-        primaryAccounts(account.accountId),
+        primaryAccounts(account.accountId, capabilities),
         username,
         apiUrl = urlEndpointResolver.apiUrl,
         downloadUrl = urlEndpointResolver.downloadUrl,
@@ -59,40 +55,21 @@ class SessionSupplier(val configuration: JmapRfc8621Configuration, defaultCapabi
         eventSourceUrl = urlEndpointResolver.eventSourceUrl))
   }
 
-  private def accounts(username: Username): Either[IllegalArgumentException, Account] =
-    Account.from(username, IsPersonal(true), IsReadOnly(false), defaultCapabilities)
+  private def accounts(username: Username, capabilities: Set[Capability]): Either[IllegalArgumentException, Account] =
+    Account.from(username, IsPersonal(true), IsReadOnly(false), capabilities)
 
-  private def primaryAccounts(accountId: AccountId): Map[CapabilityIdentifier, AccountId] =
-    defaultCapabilities
+  private def primaryAccounts(accountId: AccountId, capabilities: Set[Capability]): Map[CapabilityIdentifier, AccountId] =
+    capabilities
       .map(capability => (capability.identifier(), accountId))
       .toMap
 }
 
-object JmapUrlEndpointResolver {
-  def from(configuration: JmapRfc8621Configuration): JmapUrlEndpointResolver = new JmapUrlEndpointResolver(configuration)
-}
-
-class JmapUrlEndpointResolver(val configuration: JmapRfc8621Configuration,
-                              urlPrefixRequest: Option[URL] = None,
-                              uriWebSocketPrefixRequest: Option[URI] = None) {
-
-  val urlPrefix: String = Some(configuration.dynamicJmapPrefixResolutionEnabled)
-    .filter(enabled => enabled)
-    .flatMap(_ => urlPrefixRequest.map(_.toString))
-    .getOrElse(configuration.urlPrefixString)
-
-  val uriWebSocketPrefix: String = Some(configuration.dynamicJmapPrefixResolutionEnabled)
-    .filter(enabled => enabled)
-    .flatMap(_ => uriWebSocketPrefixRequest.map(_.toString))
-    .getOrElse(configuration.websocketPrefixString)
-
-  val apiUrl: URL = new URL(s"$urlPrefix/jmap")
-
-  val downloadUrl: URL = new URL(s"$urlPrefix/download/{accountId}/{blobId}?type={type}&name={name}")
+class JmapUrlEndpointResolver(val urlPrefixes: UrlPrefixes) {
+  val apiUrl: URL = new URL(urlPrefixes.httpUrlPrefix.toString + "/jmap")
 
-  val uploadUrl: URL = new URL(s"$urlPrefix/upload/{accountId}")
+  val downloadUrl: URL = new URL(urlPrefixes.httpUrlPrefix.toString + "/download/{accountId}/{blobId}?type={type}&name={name}")
 
-  val webSocketUrl: URI = new URI(s"$uriWebSocketPrefix/jmap/ws")
+  val uploadUrl: URL = new URL(urlPrefixes.httpUrlPrefix.toString + "/upload/{accountId}")
 
-  val eventSourceUrl: URL = new URL(s"$urlPrefix/eventSource?types={types}&closeAfter={closeafter}&ping={ping}")
+  val eventSourceUrl: URL = new URL(urlPrefixes.httpUrlPrefix.toString + "/eventSource?types={types}&closeAfter={closeafter}&ping={ping}")
 }
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/change/StateChangeEventSerializerTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/change/StateChangeEventSerializerTest.scala
index 08f0010..cc30918 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/change/StateChangeEventSerializerTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/change/StateChangeEventSerializerTest.scala
@@ -21,7 +21,6 @@ package org.apache.james.jmap.change
 import org.apache.james.JsonSerializationVerifier
 import org.apache.james.core.Username
 import org.apache.james.events.Event.EventId
-import org.apache.james.jmap.api.change
 import org.apache.james.jmap.api.change.TypeStateFactory
 import org.apache.james.jmap.api.model.TypeName
 import org.apache.james.jmap.change.StateChangeEventSerializerTest.{EVENT, EVENT_EMPTY_TYPE_STATE_MAP, EVENT_ID, EVENT_JSON, EVENT_JSON_EMPTY_TYPE_STATE_MAP, EVENT_JSON_NO_DELIVERY, EVENT_NO_DELIVERY, USERNAME}
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/JmapUrlEndpointResolverTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/JmapUrlEndpointResolverTest.scala
index 40aa9e3..325c32d 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/JmapUrlEndpointResolverTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/core/JmapUrlEndpointResolverTest.scala
@@ -22,8 +22,7 @@ package org.apache.james.jmap.core
 import java.net.{URI, URL}
 
 import org.apache.commons.configuration2.{Configuration, PropertiesConfiguration}
-import org.apache.james.jmap.core.JmapConfigProperties.{DYNAMIC_JMAP_PREFIX_RESOLUTION_ENABLED_PROPERTY, URL_PREFIX_PROPERTY, WEBSOCKET_URL_PREFIX_PROPERTY}
-import org.apache.james.jmap.core.JmapUrlEndpointResolverTest.{emptyConfiguration, providedConfiguration}
+import org.apache.james.jmap.core.JmapConfigProperties.{URL_PREFIX_PROPERTY, WEBSOCKET_URL_PREFIX_PROPERTY}
 import org.apache.james.jmap.routes.JmapUrlEndpointResolver
 import org.scalatest.matchers.must.Matchers
 import org.scalatest.wordspec.AnyWordSpec
@@ -41,83 +40,12 @@ object JmapUrlEndpointResolverTest {
 class JmapUrlEndpointResolverTest extends AnyWordSpec with Matchers {
   "JmapUrlEndpointResolverTest" should {
     "succeed to configuration urlPrefix when provided" in {
-      val jmapRfc8621Configuration: JmapRfc8621Configuration = JmapRfc8621Configuration.from(providedConfiguration())
-      val testee : JmapUrlEndpointResolver= new JmapUrlEndpointResolver(jmapRfc8621Configuration)
+      val testee : JmapUrlEndpointResolver= new JmapUrlEndpointResolver(UrlPrefixes(new URI("http://random-domain.com"), new URI("ws://random-domain.com")))
 
       testee.apiUrl must be(new URL("http://random-domain.com/jmap"))
       testee.downloadUrl must be(new URL("http://random-domain.com/download/{accountId}/{blobId}?type={type}&name={name}"))
       testee.uploadUrl must be(new URL("http://random-domain.com/upload/{accountId}"))
       testee.eventSourceUrl must be(new URL("http://random-domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}"))
-      testee.webSocketUrl must be(new URI("ws://random-domain.com/jmap/ws"))
-    }
-
-    "load default config for urlPrefix when no configuration provided" in {
-      val jmapRfc8621Configuration: JmapRfc8621Configuration = JmapRfc8621Configuration.from(emptyConfiguration)
-      val testee : JmapUrlEndpointResolver= new JmapUrlEndpointResolver(jmapRfc8621Configuration)
-
-      testee.apiUrl must be(new URL("http://localhost/jmap"))
-      testee.downloadUrl must be(new URL("http://localhost/download/{accountId}/{blobId}?type={type}&name={name}"))
-      testee.uploadUrl must be(new URL("http://localhost/upload/{accountId}"))
-      testee.eventSourceUrl must be(new URL("http://localhost/eventSource?types={types}&closeAfter={closeafter}&ping={ping}"))
-      testee.webSocketUrl must be(new URI("ws://localhost/jmap/ws"))
-    }
-
-    "dynamic jmap prefix should be set when has prefix request and configuration provided is true" in {
-      val configuration: Configuration = providedConfiguration()
-      configuration.addProperty(DYNAMIC_JMAP_PREFIX_RESOLUTION_ENABLED_PROPERTY, true)
-      val jmapRfc8621Configuration: JmapRfc8621Configuration = JmapRfc8621Configuration.from(configuration)
-      val testee : JmapUrlEndpointResolver= new JmapUrlEndpointResolver(jmapRfc8621Configuration,
-        Some(new URL("http://dynamic.tld/prefix")),
-        Some(new URI("ws://dynamic.tld/prefix")))
-
-      testee.apiUrl must be(new URL("http://dynamic.tld/prefix/jmap"))
-      testee.downloadUrl must be(new URL("http://dynamic.tld/prefix/download/{accountId}/{blobId}?type={type}&name={name}"))
-      testee.uploadUrl must be(new URL("http://dynamic.tld/prefix/upload/{accountId}"))
-      testee.eventSourceUrl must be(new URL("http://dynamic.tld/prefix/eventSource?types={types}&closeAfter={closeafter}&ping={ping}"))
-      testee.webSocketUrl must be(new URI("ws://dynamic.tld/prefix/jmap/ws"))
-    }
-
-    "dynamic jmap prefix should NOT be set when has prefix request and configuration provided is false" in {
-      val configuration: Configuration = providedConfiguration()
-      configuration.addProperty(DYNAMIC_JMAP_PREFIX_RESOLUTION_ENABLED_PROPERTY, false)
-      val jmapRfc8621Configuration: JmapRfc8621Configuration = JmapRfc8621Configuration.from(configuration)
-      val testee : JmapUrlEndpointResolver= new JmapUrlEndpointResolver(jmapRfc8621Configuration,
-        Some(new URL("http://dynamic.tld/prefix")),
-        Some(new URI("ws://dynamic.tld/prefix")))
-
-      testee.apiUrl must be(new URL("http://random-domain.com/jmap"))
-      testee.downloadUrl must be(new URL("http://random-domain.com/download/{accountId}/{blobId}?type={type}&name={name}"))
-      testee.uploadUrl must be(new URL("http://random-domain.com/upload/{accountId}"))
-      testee.eventSourceUrl must be(new URL("http://random-domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}"))
-      testee.webSocketUrl must be(new URI("ws://random-domain.com/jmap/ws"))
-    }
-
-    "dynamic jmap prefix should NOT be set when has prefix request and no configuration provided" in {
-      val configuration: Configuration = providedConfiguration()
-      val jmapRfc8621Configuration: JmapRfc8621Configuration = JmapRfc8621Configuration.from(configuration)
-      val testee : JmapUrlEndpointResolver= new JmapUrlEndpointResolver(jmapRfc8621Configuration,
-        Some(new URL("http://dynamic.tld/prefix")),
-        Some(new URI("ws://dynamic.tld/prefix")))
-
-      testee.apiUrl must be(new URL("http://random-domain.com/jmap"))
-      testee.downloadUrl must be(new URL("http://random-domain.com/download/{accountId}/{blobId}?type={type}&name={name}"))
-      testee.uploadUrl must be(new URL("http://random-domain.com/upload/{accountId}"))
-      testee.eventSourceUrl must be(new URL("http://random-domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}"))
-      testee.webSocketUrl must be(new URI("ws://random-domain.com/jmap/ws"))
-    }
-
-    "dynamic jmap prefix should NOT be set when has not prefix request" in {
-      val configuration: Configuration = providedConfiguration()
-      configuration.addProperty(DYNAMIC_JMAP_PREFIX_RESOLUTION_ENABLED_PROPERTY, true)
-
-      val jmapRfc8621Configuration: JmapRfc8621Configuration = JmapRfc8621Configuration.from(configuration)
-      val testee : JmapUrlEndpointResolver= new JmapUrlEndpointResolver(jmapRfc8621Configuration, None, None)
-
-      testee.apiUrl must be(new URL("http://random-domain.com/jmap"))
-      testee.downloadUrl must be(new URL("http://random-domain.com/download/{accountId}/{blobId}?type={type}&name={name}"))
-      testee.uploadUrl must be(new URL("http://random-domain.com/upload/{accountId}"))
-      testee.eventSourceUrl must be(new URL("http://random-domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}"))
-      testee.webSocketUrl must be(new URI("ws://random-domain.com/jmap/ws"))
     }
   }
 }
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala
index 84c7d47..4586488 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/JMAPApiRoutesTest.scala
@@ -78,7 +78,7 @@ object JMAPApiRoutesTest {
   private val userProvisionner: UserProvisioning = new UserProvisioning(usersRepository, new RecordingMetricFactory)
   private val JMAP_METHODS: Set[Method] = Set(new CoreEchoMethod)
 
-  private val JMAP_API_ROUTE: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, userProvisionner, new JMAPApi(JMAP_METHODS, DefaultCapabilities.supported(JmapRfc8621Configuration("http://127.0.0.1", "ws://127.0.0.1")).capabilities.toSet))
+  private val JMAP_API_ROUTE: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, userProvisionner, new JMAPApi(JMAP_METHODS, DefaultCapabilities.supported(JmapRfc8621Configuration("http://127.0.0.1", "ws://127.0.0.1")).map(_.id())))
   private val ROUTES_HANDLER: ImmutableSet[JMAPRoutesHandler] = ImmutableSet.of(new JMAPRoutesHandler(Version.RFC8621, JMAP_API_ROUTE))
 
   private val userBase64String: String = Base64.getEncoder.encodeToString("user1:password".getBytes(StandardCharsets.UTF_8))
@@ -442,7 +442,7 @@ class JMAPApiRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
     when(mockCoreEchoMethod.requiredCapabilities).thenReturn(Set(JMAP_CORE))
 
     val methods: Set[Method] = Set(mockCoreEchoMethod)
-    val apiRoute: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, userProvisionner, new JMAPApi(methods, DefaultCapabilities.supported(JmapRfc8621Configuration("http://127.0.0.1", "ws://127.0.0.1")).capabilities.toSet))
+    val apiRoute: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, userProvisionner, new JMAPApi(methods, DefaultCapabilities.supported(JmapRfc8621Configuration("http://127.0.0.1", "ws://127.0.0.1")).map(_.id())))
     val routesHandler: ImmutableSet[JMAPRoutesHandler] = ImmutableSet.of(new JMAPRoutesHandler(Version.RFC8621, apiRoute))
 
     val versionParser: VersionParser = new VersionParser(SUPPORTED_VERSIONS, JMAPConfiguration.DEFAULT)
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
index 1162fdc..0061236 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
@@ -66,8 +66,9 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
       .thenReturn(Mono.just(mockedSession))
 
     val sessionRoutes = new SessionRoutes(
-      sessionSupplier = new SessionSupplier(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION, DefaultCapabilities.supported(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION).capabilities),
-      authenticator = mockedAuthFilter)
+      sessionSupplier = new SessionSupplier(DefaultCapabilities.supported(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION)),
+      authenticator = mockedAuthFilter,
+      jmapRfc8621Configuration = JmapRfc8621Configuration.LOCALHOST_CONFIGURATION)
     jmapServer = new JMAPServer(
       TEST_CONFIGURATION,
       Set(new JMAPRoutesHandler(Version.RFC8621, sessionRoutes)).asJava,
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala
index 0b0eed7..4c463b3 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionSupplierTest.scala
@@ -33,13 +33,13 @@ class SessionSupplierTest extends AnyWordSpec with Matchers {
 
   "generate" should {
     "return correct username" in {
-      new SessionSupplier(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION, DefaultCapabilities.supported(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION).capabilities)
-        .generate(USERNAME).toOption.get.username should equal(USERNAME)
+      new SessionSupplier(DefaultCapabilities.supported(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION))
+        .generate(USERNAME, JmapRfc8621Configuration.LOCALHOST_CONFIGURATION.urlPrefixes()).toOption.get.username should equal(USERNAME)
     }
 
     "return correct account" which {
-      val accounts = new SessionSupplier(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION, DefaultCapabilities.supported(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION).capabilities)
-        .generate(USERNAME).toOption.get.accounts
+      val accounts = new SessionSupplier(DefaultCapabilities.supported(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION))
+        .generate(USERNAME, JmapRfc8621Configuration.LOCALHOST_CONFIGURATION.urlPrefixes()).toOption.get.accounts
 
       "has size" in {
         accounts should have size 1

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