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/09/28 03:10:06 UTC

[james-project] branch master updated (08c46e3 -> d124e2b)

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

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


    from 08c46e3  James-3385: update Email/Query documentation
     new 264f73d  JAMES-3387 require accountId to be one of the accounts of the authenticated user
     new 5e0d014  JAMES-3387 validate accountId for email/get
     new 4b789e3  JAMES-3387 validate accountId for email/query
     new a667ad5  JAMES-3387 refactor extract InvocationWithContext
     new 60732e1  JAMES-3387 validate accountId for mailbox/get
     new 5065639  JAMES-3387 validate accountId form mailbox/query
     new c532fad  JAMES-3387 validate accountId for vacationResponse/get
     new d124e2b  JAMES-3387 validate accountId for vacationResponse/set

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../rfc8621/contract/EmailGetMethodContract.scala  |  39 ++++
 .../contract/EmailQueryMethodContract.scala        | 243 +++++++--------------
 .../contract/MailboxGetMethodContract.scala        |  41 ++++
 .../contract/MailboxQueryMethodContract.scala      |  40 ++++
 .../contract/MailboxSetMethodContract.scala        |  47 ++++
 .../VacationResponseGetMethodContract.scala        |  41 ++++
 .../VacationResponseSetMethodContract.scala        |  50 +++++
 .../org/apache/james/jmap/mail/EmailGet.scala      |   3 +-
 .../org/apache/james/jmap/mail/EmailQuery.scala    |   3 +-
 .../org/apache/james/jmap/mail/MailboxGet.scala    |   3 +-
 .../org/apache/james/jmap/mail/MailboxQuery.scala  |   3 +-
 .../org/apache/james/jmap/mail/MailboxSet.scala    |   4 +-
 .../james/jmap/mail/VacationResponseGet.scala      |   3 +-
 .../apache/james/jmap/method/CoreEchoMethod.scala  |   5 +-
 .../apache/james/jmap/method/EmailGetMethod.scala  |  23 +-
 .../james/jmap/method/EmailQueryMethod.scala       |  44 ++--
 .../james/jmap/method/MailboxGetMethod.scala       |  46 ++--
 .../james/jmap/method/MailboxQueryMethod.scala     |  33 +--
 .../james/jmap/method/MailboxSetMethod.scala       |  22 +-
 .../org/apache/james/jmap/method/Method.scala      |  59 ++++-
 .../jmap/method/VacationResponseGetMethod.scala    |  59 +++--
 .../jmap/method/VacationResponseSetMethod.scala    |  25 +--
 .../apache/james/jmap/routes/JMAPApiRoutes.scala   |  50 ++---
 .../james/jmap/vacation/VacationResponseSet.scala  |   3 +-
 .../james/jmap/method/CoreEchoMethodTest.scala     |   8 +-
 .../james/jmap/routes/JMAPApiRoutesTest.scala      |  12 +-
 26 files changed, 560 insertions(+), 349 deletions(-)


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


[james-project] 02/08: JAMES-3387 validate accountId for email/get

Posted by rc...@apache.org.
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 5e0d014f137fd67d353cc210920ea032d253acb2
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Wed Sep 23 15:59:40 2020 +0200

    JAMES-3387 validate accountId for email/get
---
 .../rfc8621/contract/EmailGetMethodContract.scala  | 39 ++++++++++++++++++++++
 .../org/apache/james/jmap/mail/EmailGet.scala      |  3 +-
 .../apache/james/jmap/method/EmailGetMethod.scala  | 22 ++++++------
 3 files changed, 51 insertions(+), 13 deletions(-)

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/EmailGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
index 61fb12f..0821e63 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
@@ -73,6 +73,45 @@ trait EmailGetMethodContract {
   def randomMessageId: MessageId
 
   @Test
+  def emailGetShouldFailWhenWrongAccountId(): Unit = {
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(
+        s"""{
+           |  "using": [
+           |    "urn:ietf:params:jmap:core",
+           |    "urn:ietf:params:jmap:mail"],
+           |  "methodCalls": [[
+           |    "Email/get",
+           |    {
+           |      "accountId": "unknownAccountId",
+           |      "ids": []
+           |    },
+           |    "c1"]]
+           |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |    "sessionState": "75128aab4b1b",
+         |    "methodResponses": [[
+         |            "error",
+         |            {
+         |                "type": "accountNotFound"
+         |            },
+         |            "c1"
+         |        ]]
+         |}""".stripMargin)
+  }
+
+  @Test
   def idsShouldBeMandatory(): Unit = {
     val response = `given`
       .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
index cd4ae81..96f3ea7 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
@@ -28,6 +28,7 @@ import eu.timepit.refined.types.string.NonEmptyString
 import org.apache.james.jmap.mail.Email.UnparsedEmailId
 import org.apache.james.jmap.mail.EmailGetRequest.MaxBodyValueBytes
 import org.apache.james.jmap.mail.EmailHeaders.SPECIFIC_HEADER_PREFIX
+import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.jmap.model.State.State
 import org.apache.james.jmap.model.{AccountId, Properties}
 import org.apache.james.mime4j.dom.Message
@@ -74,7 +75,7 @@ case class EmailGetRequest(accountId: AccountId,
                            fetchHTMLBodyValues: Option[FetchHTMLBodyValues],
                            maxBodyValueBytes: Option[MaxBodyValueBytes],
                            properties: Option[Properties],
-                           bodyProperties: Option[Properties])
+                           bodyProperties: Option[Properties]) extends WithAccountId
 
 case class EmailNotFound(value: Set[UnparsedEmailId]) {
   def merge(other: EmailNotFound): EmailNotFound = EmailNotFound(this.value ++ other.value)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
index ecc4663..0dbae7e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
@@ -37,7 +37,6 @@ import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.model.MessageId
 import org.apache.james.metrics.api.MetricFactory
-import org.reactivestreams.Publisher
 import org.slf4j.{Logger, LoggerFactory}
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
 import reactor.core.scala.publisher.{SFlux, SMono}
@@ -83,20 +82,19 @@ class EmailGetMethod @Inject() (readerFactory: EmailViewReaderFactory,
                                 messageIdFactory: MessageId.Factory,
                                 zoneIdProvider: ZoneIdProvider,
                                 previewFactory: Preview.Factory,
-                                metricFactory: MetricFactory,
-                                sessionSupplier: SessionSupplier) extends Method {
+                                val metricFactory: MetricFactory,
+                                val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[EmailGetRequest] {
   override val methodName = MethodName("Email/get")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
-  override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, sessionSupplier: SessionSupplier): Publisher[(Invocation, ProcessingContext)] =
-    metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-      asEmailGetRequest(invocation.arguments)
-        .flatMap(computeResponseInvocation(_, invocation, mailboxSession))
-        .onErrorResume({
-          case e: IllegalArgumentException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId))
-          case e: Throwable => SMono.raiseError(e)
-        })
-        .map(invocationResult => (invocationResult, processingContext)))
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, request: EmailGetRequest): SMono[(Invocation, ProcessingContext)] = {
+    computeResponseInvocation(request, invocation, mailboxSession).onErrorResume({
+      case e: IllegalArgumentException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId))
+      case e: Throwable => SMono.raiseError(e)
+    }).map(invocationResult => (invocationResult, processingContext))
+  }
+
+  override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[EmailGetRequest] = asEmailGetRequest(invocation.arguments)
 
   private def computeResponseInvocation(request: EmailGetRequest, invocation: Invocation, mailboxSession: MailboxSession): SMono[Invocation] =
     validateProperties(request)


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


[james-project] 01/08: JAMES-3387 require accountId to be one of the accounts of the authenticated user

Posted by rc...@apache.org.
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 264f73da547e952c7eba942494acbbb0dcd50dbf
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Wed Sep 23 15:52:34 2020 +0200

    JAMES-3387 require accountId to be one of the accounts of the authenticated user
    
    and validate it for mailbox/set
---
 .../contract/MailboxSetMethodContract.scala        | 47 ++++++++++++++++++++++
 .../org/apache/james/jmap/mail/MailboxSet.scala    |  4 +-
 .../apache/james/jmap/method/CoreEchoMethod.scala  |  1 +
 .../apache/james/jmap/method/EmailGetMethod.scala  |  6 ++-
 .../james/jmap/method/EmailQueryMethod.scala       |  4 +-
 .../james/jmap/method/MailboxGetMethod.scala       |  4 +-
 .../james/jmap/method/MailboxQueryMethod.scala     |  4 +-
 .../james/jmap/method/MailboxSetMethod.scala       | 22 +++++-----
 .../org/apache/james/jmap/method/Method.scala      | 40 +++++++++++++++++-
 .../jmap/method/VacationResponseGetMethod.scala    |  1 +
 .../jmap/method/VacationResponseSetMethod.scala    |  1 +
 .../apache/james/jmap/routes/JMAPApiRoutes.scala   | 10 +++--
 .../james/jmap/method/CoreEchoMethodTest.scala     |  3 +-
 .../james/jmap/routes/JMAPApiRoutesTest.scala      | 10 ++---
 14 files changed, 127 insertions(+), 30 deletions(-)

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/MailboxSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
index 4434d3f..106194c 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
@@ -62,6 +62,53 @@ trait MailboxSetMethodContract {
   def errorInvalidMailboxIdMessage(value: String): String
 
   @Test
+  def mailboxSetShouldFailWhenWrongAccountId(server: GuiceJamesServer): Unit = {
+    val request =
+      """
+        |{
+        |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+        |   "methodCalls": [
+        |       [
+        |           "Mailbox/set",
+        |           {
+        |                "accountId": "unknownAccountId",
+        |                "create": {
+        |                    "C42": {
+        |                      "name": "myMailbox"
+        |                    }
+        |                }
+        |           },
+        |    "c1"
+        |       ]
+        |   ]
+        |}
+        |""".stripMargin
+
+   val response =  `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      """{
+        |  "sessionState": "75128aab4b1b",
+        |  "methodResponses": [
+        |    ["error", {
+        |      "type": "accountNotFound"
+        |    }, "c1"]
+        |  ]
+        |}""".stripMargin)
+  }
+
+  @Test
   def updateShouldFailWhenModifyingRole(server: GuiceJamesServer): Unit = {
     val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
       .createMailbox(MailboxPath.forUser(BOB, "INBOX"))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
index 7cf5355..cbd034e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
@@ -30,7 +30,7 @@ import org.apache.james.jmap.json.MailboxSerializer
 import org.apache.james.jmap.mail.MailboxName.MailboxName
 import org.apache.james.jmap.mail.MailboxPatchObject.MailboxPatchObjectKey
 import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId, UnparsedMailboxIdConstraint}
-import org.apache.james.jmap.method.MailboxCreationParseException
+import org.apache.james.jmap.method.{MailboxCreationParseException, WithAccountId}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.SetError.{SetErrorDescription, SetErrorType}
 import org.apache.james.jmap.model.State.State
@@ -45,7 +45,7 @@ case class MailboxSetRequest(accountId: AccountId,
                              create: Option[Map[MailboxCreationId, JsObject]],
                              update: Option[Map[UnparsedMailboxId, MailboxPatchObject]],
                              destroy: Option[Seq[UnparsedMailboxId]],
-                             onDestroyRemoveEmails: Option[RemoveEmailsOnDestroy])
+                             onDestroyRemoveEmails: Option[RemoveEmailsOnDestroy]) extends WithAccountId
 
 object MailboxSetRequest {
   type UnparsedMailboxIdConstraint = NonEmpty
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
index a7fd705..c956a81 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
@@ -20,6 +20,7 @@ package org.apache.james.jmap.method
 
 
 import eu.timepit.refined.auto._
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.CORE_CAPABILITY
 import org.apache.james.jmap.model.Invocation.MethodName
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
index 361a49f..ecc4663 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
@@ -24,6 +24,7 @@ import eu.timepit.refined.auto._
 import eu.timepit.refined.types.string.NonEmptyString
 import javax.inject.Inject
 import org.apache.james.jmap.api.model.Preview
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{EmailGetSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.Email.UnparsedEmailId
 import org.apache.james.jmap.mail.{Email, EmailBodyPart, EmailGetRequest, EmailGetResponse, EmailIds, EmailNotFound, EmailView, EmailViewReaderFactory, SpecificHeaderRequest}
@@ -82,11 +83,12 @@ class EmailGetMethod @Inject() (readerFactory: EmailViewReaderFactory,
                                 messageIdFactory: MessageId.Factory,
                                 zoneIdProvider: ZoneIdProvider,
                                 previewFactory: Preview.Factory,
-                                metricFactory: MetricFactory) extends Method {
+                                metricFactory: MetricFactory,
+                                sessionSupplier: SessionSupplier) extends Method {
   override val methodName = MethodName("Email/get")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
-  override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] =
+  override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, sessionSupplier: SessionSupplier): Publisher[(Invocation, ProcessingContext)] =
     metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
       asEmailGetRequest(invocation.arguments)
         .flatMap(computeResponseInvocation(_, invocation, mailboxSession))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
index 4feb97e..0cf6810 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
@@ -21,6 +21,7 @@ package org.apache.james.jmap.method
 import cats.implicits._
 import eu.timepit.refined.auto._
 import javax.inject.Inject
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{EmailQuerySerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, EmailQueryResponse, UnsupportedFilterException, UnsupportedRequestParameterException, UnsupportedSortException}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
@@ -44,7 +45,8 @@ import scala.jdk.CollectionConverters._
 
 class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
                                   mailboxManager: MailboxManager,
-                                  metricFactory: MetricFactory) extends Method {
+                                  metricFactory: MetricFactory,
+                                  sessionSupplier: SessionSupplier) extends Method {
   override val methodName: MethodName = MethodName("Email/query")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index e1a6a57..6e660c8 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -21,6 +21,7 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import javax.inject.Inject
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
 import org.apache.james.jmap.mail._
@@ -49,7 +50,8 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
                                   quotaFactory : QuotaLoaderWithPreloadedDefaultFactory,
                                   mailboxIdFactory: MailboxId.Factory,
                                   mailboxFactory: MailboxFactory,
-                                  metricFactory: MetricFactory) extends Method {
+                                  metricFactory: MetricFactory,
+                                  val sessionSupplier: SessionSupplier) extends Method {
   override val methodName: MethodName = MethodName("Mailbox/get")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
index 46e9417..268dc1d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
@@ -20,6 +20,7 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import javax.inject.Inject
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{MailboxQuerySerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.{MailboxQueryRequest, MailboxQueryResponse}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
@@ -35,7 +36,8 @@ import reactor.core.scala.publisher.{SFlux, SMono}
 import reactor.core.scheduler.Schedulers
 
 class MailboxQueryMethod @Inject()(systemMailboxesProvider: SystemMailboxesProvider,
-                                   metricFactory: MetricFactory) extends Method {
+                                   metricFactory: MetricFactory,
+                                   val sessionSupplier: SessionSupplier) extends Method {
   override val methodName = MethodName("Mailbox/query")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
index 465b22c..dc772c5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
@@ -21,6 +21,7 @@ package org.apache.james.jmap.method
 
 import eu.timepit.refined.auto._
 import javax.inject.Inject
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
 import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId}
 import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, RemoveEmailsOnDestroy, ServerSetPropertyException, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, UnsupportedPropertyUpdatedException, ValidatedMailboxPatchObject}
@@ -147,21 +148,18 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
                                  subscriptionManager: SubscriptionManager,
                                  mailboxIdFactory: MailboxId.Factory,
                                  quotaFactory : QuotaLoaderWithPreloadedDefaultFactory,
-                                 metricFactory: MetricFactory) extends Method {
+                                 val metricFactory: MetricFactory,
+                                 val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[MailboxSetRequest] {
   override val methodName: MethodName = MethodName("Mailbox/set")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
-  override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] = {
-    metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-      asMailboxSetRequest(invocation.arguments)
-        .flatMap(mailboxSetRequest => {
-          for {
-            creationResultsWithUpdatedProcessingContext <- createMailboxes(mailboxSession, mailboxSetRequest, processingContext)
-            deletionResults <- deleteMailboxes(mailboxSession, mailboxSetRequest, processingContext)
-            updateResults <- updateMailboxes(mailboxSession, mailboxSetRequest, processingContext, capabilities)
-          } yield (createResponse(capabilities, invocation, mailboxSetRequest, creationResultsWithUpdatedProcessingContext._1, deletionResults, updateResults), creationResultsWithUpdatedProcessingContext._2)
-        }))
-  }
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, request: MailboxSetRequest): SMono[(Invocation, ProcessingContext)] = for {
+    creationResultsWithUpdatedProcessingContext <- createMailboxes(mailboxSession, request, processingContext)
+    deletionResults <- deleteMailboxes(mailboxSession, request, processingContext)
+    updateResults <- updateMailboxes(mailboxSession, request, processingContext, capabilities)
+  } yield (createResponse(capabilities, invocation, request, creationResultsWithUpdatedProcessingContext._1, deletionResults, updateResults), creationResultsWithUpdatedProcessingContext._2)
+
+  override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[MailboxSetRequest] = asMailboxSetRequest(invocation.arguments)
 
   private def updateMailboxes(mailboxSession: MailboxSession,
                               mailboxSetRequest: MailboxSetRequest,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
index e6dde8c..f4a40a5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
@@ -19,12 +19,16 @@
 package org.apache.james.jmap.method
 
 
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.Invocation.MethodName
-import org.apache.james.jmap.model.{Capabilities, Invocation}
+import org.apache.james.jmap.model.{AccountId, Capabilities, ErrorCode, Invocation, Session}
 import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.MailboxSession
+import org.apache.james.mailbox.exception.MailboxNotFoundException
+import org.apache.james.metrics.api.MetricFactory
 import org.reactivestreams.Publisher
+import reactor.core.scala.publisher.SMono
 
 trait Method {
   val JMAP_RFC8621_PREFIX: String = "JMAP-RFC8621-"
@@ -36,3 +40,37 @@ trait Method {
   def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)]
 }
 
+trait WithAccountId {
+  def accountId: AccountId
+}
+trait MethodRequiringAccountId[REQUEST <: WithAccountId] extends Method {
+  def metricFactory: MetricFactory
+  def sessionSupplier: SessionSupplier
+
+  override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] = {
+    val result = (for {
+      request <- getRequest(mailboxSession, invocation)
+      response <- validateAccountId(request.accountId, mailboxSession, sessionSupplier, invocation).flatMap{
+        case Right(_) =>  doProcess(capabilities, invocation, mailboxSession, processingContext, request)
+        case Left(invocation) => SMono.just((invocation, processingContext))
+      }
+    } yield response)
+      .onErrorResume {
+      case e: IllegalArgumentException => SMono.just((Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId), processingContext))
+      case e: Throwable => SMono.raiseError(e)
+    }
+
+    metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value, result)
+  }
+
+  private def validateAccountId(accountId: AccountId, mailboxSession: MailboxSession, sessionSupplier: SessionSupplier, invocation: Invocation): SMono[Either[Invocation, Session]] = {
+    sessionSupplier.generate(mailboxSession.getUser)
+      .filter(session => session.accounts.map(_.accountId).contains(accountId))
+      .map(session => Right[Invocation, Session](session).asInstanceOf[Either[Invocation, Session]])
+      .switchIfEmpty(SMono.just(Left[Invocation, Session](Invocation.error(ErrorCode.AccountNotFound, invocation.methodCallId))))
+  }
+
+  def doProcess(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, request: REQUEST): SMono[(Invocation, ProcessingContext)]
+
+  def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[REQUEST]
+}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
index 4652add..a3c3e8b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
@@ -22,6 +22,7 @@ package org.apache.james.jmap.method
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.api.vacation.{VacationRepository, AccountId => JavaAccountId}
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
 import org.apache.james.jmap.mail.VacationResponse.UnparsedVacationResponseId
 import org.apache.james.jmap.mail.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseNotFound}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
index bd34d88..4b24625 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
@@ -22,6 +22,7 @@ package org.apache.james.jmap.method
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.api.vacation.{AccountId, VacationPatch, VacationRepository}
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
 import org.apache.james.jmap.method.VacationResponseSetMethod.VACATION_RESPONSE_PATCH_OBJECT_KEY
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
index 6833db8..ab870e1 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
@@ -33,7 +33,7 @@ import org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE
 import org.apache.james.jmap.JMAPUrls.JMAP
 import org.apache.james.jmap.exceptions.UnauthorizedException
 import org.apache.james.jmap.http.rfc8621.InjectionKeys
-import org.apache.james.jmap.http.{Authenticator, MailboxesProvisioner, UserProvisioning}
+import org.apache.james.jmap.http.{Authenticator, MailboxesProvisioner, SessionSupplier, UserProvisioning}
 import org.apache.james.jmap.json.ResponseSerializer
 import org.apache.james.jmap.method.Method
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
@@ -58,7 +58,8 @@ object JMAPApiRoutes {
 class JMAPApiRoutes (val authenticator: Authenticator,
                      userProvisioner: UserProvisioning,
                      mailboxesProvisioner: MailboxesProvisioner,
-                     methods: Set[Method]) extends JMAPRoutes {
+                     methods: Set[Method],
+                     sessionSupplier: SessionSupplier) extends JMAPRoutes {
 
   private val methodsByName: Map[MethodName, Method] = methods.map(method => method.methodName -> method).toMap
 
@@ -66,8 +67,9 @@ class JMAPApiRoutes (val authenticator: Authenticator,
   def this(@Named(InjectionKeys.RFC_8621) authenticator: Authenticator,
            userProvisioner: UserProvisioning,
            mailboxesProvisioner: MailboxesProvisioner,
-           javaMethods: java.util.Set[Method]) {
-    this(authenticator, userProvisioner, mailboxesProvisioner, javaMethods.asScala.toSet)
+           javaMethods: java.util.Set[Method],
+           sessionSupplier: SessionSupplier) {
+    this(authenticator, userProvisioner, mailboxesProvisioner, javaMethods.asScala.toSet, sessionSupplier)
   }
 
   override def routes(): stream.Stream[JMAPRoute] = Stream.of(
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
index ff24174..77da9e6 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
@@ -18,9 +18,10 @@
  * ***************************************************************/
 package org.apache.james.jmap.method
 
+import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.Fixture.{invocation1, invocation2}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.{CapabilityIdentifier, Invocation}
+import org.apache.james.jmap.model.{CapabilityIdentifier, Invocation, JmapRfc8621Configuration}
 import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.MailboxSession
 import org.mockito.Mockito.mock
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 069c3d6..7e5298f 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
@@ -36,12 +36,12 @@ import org.apache.james.dnsservice.api.DNSService
 import org.apache.james.domainlist.memory.MemoryDomainList
 import org.apache.james.jmap.JMAPUrls.JMAP
 import org.apache.james.jmap._
-import org.apache.james.jmap.http.{Authenticator, BasicAuthenticationStrategy, MailboxesProvisioner, UserProvisioning}
+import org.apache.james.jmap.http.{Authenticator, BasicAuthenticationStrategy, MailboxesProvisioner, SessionSupplier, UserProvisioning}
 import org.apache.james.jmap.method.{CoreEchoMethod, Method}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.CORE_CAPABILITY
 import org.apache.james.jmap.model.Invocation.MethodName
-import org.apache.james.jmap.model.{Capabilities, RequestLevelErrorType}
+import org.apache.james.jmap.model.{Capabilities, JmapRfc8621Configuration, RequestLevelErrorType}
 import org.apache.james.jmap.routes.JMAPApiRoutesTest._
 import org.apache.james.mailbox.extension.PreDeletionHook
 import org.apache.james.mailbox.inmemory.{InMemoryMailboxManager, MemoryMailboxManagerProvider}
@@ -76,10 +76,10 @@ object JMAPApiRoutesTest {
   private val userProvisionner: UserProvisioning = new UserProvisioning(usersRepository, new RecordingMetricFactory)
   private val subscriptionManager: StoreSubscriptionManager = new StoreSubscriptionManager(mailboxManager.getMapperFactory)
   private val mailboxesProvisioner: MailboxesProvisioner = new MailboxesProvisioner(mailboxManager, subscriptionManager, new RecordingMetricFactory)
-
+  private val sessionSupplier: SessionSupplier = new SessionSupplier(JmapRfc8621Configuration(JmapRfc8621Configuration.LOCALHOST_URL_PREFIX))
   private val JMAP_METHODS: Set[Method] = Set(new CoreEchoMethod)
 
-  private val JMAP_API_ROUTE: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, userProvisionner, mailboxesProvisioner, JMAP_METHODS)
+  private val JMAP_API_ROUTE: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, userProvisionner, mailboxesProvisioner, JMAP_METHODS, sessionSupplier)
   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))
@@ -443,7 +443,7 @@ class JMAPApiRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
     when(mockCoreEchoMethod.requiredCapabilities).thenReturn(Capabilities(CORE_CAPABILITY))
 
     val methods: Set[Method] = Set(mockCoreEchoMethod)
-    val apiRoute: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, userProvisionner, mailboxesProvisioner, methods)
+    val apiRoute: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, userProvisionner, mailboxesProvisioner, methods, sessionSupplier)
     val routesHandler: ImmutableSet[JMAPRoutesHandler] = ImmutableSet.of(new JMAPRoutesHandler(Version.RFC8621, apiRoute))
 
     val versionParser: VersionParser = new VersionParser(SUPPORTED_VERSIONS)


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


[james-project] 03/08: JAMES-3387 validate accountId for email/query

Posted by rc...@apache.org.
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 4b789e36a777b8c9c10ea004a7ae8666a2368eef
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Wed Sep 23 16:35:14 2020 +0200

    JAMES-3387 validate accountId for email/query
---
 .../contract/EmailQueryMethodContract.scala        | 243 +++++++--------------
 .../org/apache/james/jmap/mail/EmailQuery.scala    |   3 +-
 .../james/jmap/method/EmailQueryMethod.scala       |  56 ++---
 3 files changed, 114 insertions(+), 188 deletions(-)

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/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index dcd7a7e..4cfac72 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -66,7 +66,6 @@ object EmailQueryMethodContract {
   }
 }
 
-
 trait EmailQueryMethodContract {
 
   private lazy val slowPacedPollInterval = ONE_HUNDRED_MILLISECONDS
@@ -92,17 +91,63 @@ trait EmailQueryMethodContract {
   }
 
   @Test
+  def emailQueryShouldFailWhenWrongAccountId(server: GuiceJamesServer): Unit = {
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "unknownAccountId"
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response).isEqualTo(
+        s"""{
+           |    "sessionState": "75128aab4b1b",
+           |    "methodResponses": [[
+           |            "error",
+           |            {
+           |                "type": "accountNotFound"
+           |            },
+           |            "c1"
+           |        ]]
+           |}""".stripMargin)
+    }
+  }
+
+  private def buildTestMessage = {
+    Message.Builder
+      .of
+      .setSubject("test")
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+  }
+
+  @Test
   def hasAttachmentShouldKeepMessageWithAttachmentWhenTrue(server: GuiceJamesServer): Unit = {
     val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
     mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
     val messageId1: MessageId = mailboxProbe
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
         AppendCommand.from(
-          Message.Builder
-            .of
-            .setSubject("test")
-            .setBody("testmail", StandardCharsets.UTF_8)
-            .build))
+          buildTestMessage))
       .getMessageId
 
     val messageId2: MessageId = mailboxProbe
@@ -552,11 +597,7 @@ trait EmailQueryMethodContract {
     val messageId1: MessageId = mailboxProbe
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
         AppendCommand.from(
-          Message.Builder
-            .of
-            .setSubject("test")
-            .setBody("testmail", StandardCharsets.UTF_8)
-            .build))
+          buildTestMessage))
       .getMessageId
 
     val messageId2: MessageId = mailboxProbe
@@ -621,11 +662,7 @@ trait EmailQueryMethodContract {
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
         AppendCommand.builder()
           .withInternalDate(beforeRequestDate)
-          .build(Message.Builder
-            .of
-            .setSubject("test")
-            .setBody("testmail", StandardCharsets.UTF_8)
-            .build))
+          .build(buildTestMessage))
       .getMessageId
 
     val messageId2: MessageId = mailboxProbe
@@ -638,11 +675,7 @@ trait EmailQueryMethodContract {
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
         AppendCommand.builder()
           .withInternalDate(afterRequestDate)
-          .build(Message.Builder
-            .of
-            .setSubject("test")
-            .setBody("testmail", StandardCharsets.UTF_8)
-            .build))
+          .build(buildTestMessage))
       .getMessageId
 
     val messageId4: MessageId = mailboxProbe
@@ -702,11 +735,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldListMailsInAllUserMailboxes(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -764,11 +793,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldNotListMailsFromOtherUserMailboxes(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(ANDRE, "other")
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -826,11 +851,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsShouldBeSortedByDescendingOrderOfArrivalByDefault(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -1165,11 +1186,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsShouldBeIdempotent(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -1224,11 +1241,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsShouldBeSortedInAscendingOrderByDefault(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -1282,11 +1295,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsShouldBeSortedInAscendingOrder(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     val now: Instant = Instant.now()
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
@@ -1341,11 +1350,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsShouldBeSortedInDescendingOrder(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     val now: Instant = Instant.now()
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
@@ -1448,11 +1453,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldListMailsInASpecificUserMailboxes(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     val otherMailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -1823,11 +1824,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldReturnIllegalArgumentErrorForAnUnknownSpecificUserMailboxes(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     val otherMailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -2114,11 +2111,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldListMailsNotInASpecificUserMailboxes(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     val otherMailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -2166,11 +2159,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldListMailsNotInZeroMailboxes(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -2217,11 +2206,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsInAFirstMailboxAndNotSomeOtherMailboxShouldReturnMailsInFirstMailbox(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     val inbox = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     val otherMailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -2270,11 +2255,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsInAFirstMailboxAndNotInTheSameMailboxShouldReturnEmptyResult(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     val inbox = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
@@ -2323,11 +2304,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldListMailsReceivedBeforeADate(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val requestDate = ZonedDateTime.now().minusDays(1)
     val messageId1 = sendMessageToBobInbox(server, message, Date.from(requestDate.toInstant))
@@ -2375,11 +2352,7 @@ trait EmailQueryMethodContract {
   }
   @Test
   def shouldListMailsReceivedBeforeADateInclusively(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val requestDate = ZonedDateTime.now().minusDays(1)
     val messageId1 = sendMessageToBobInbox(server, message, Date.from(requestDate.toInstant))
@@ -2427,11 +2400,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldListMailsReceivedAfterADate(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
     val receivedDateMessage1 = ZonedDateTime.now().minusDays(1)
     sendMessageToBobInbox(server, message, Date.from(receivedDateMessage1.toInstant))
@@ -2480,11 +2449,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsReceivedAfterADateShouldBeExclusive(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val receivedDateMessage1 = ZonedDateTime.now().minusDays(1)
     sendMessageToBobInbox(server, message, Date.from(receivedDateMessage1.toInstant))
@@ -2532,11 +2497,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldLimitResultByTheLimitProvidedByTheClient(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
@@ -2640,11 +2601,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def theLimitshouldBeEnforcedByTheServerIfNoLimitProvidedByTheClient(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
 
     val allMessages = (0 to 300).toList.foldLeft(List[MessageId](), ZonedDateTime.now().minusYears(1))((acc, _) => {
@@ -2694,11 +2651,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def theLimitshouldBeEnforcedByTheServerIfAGreaterLimitProvidedByTheClient(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
 
     val allMessages = (0 to 300).toList.foldLeft(List[MessageId](), ZonedDateTime.now().minusYears(1))((acc, _) => {
@@ -2749,11 +2702,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def resultsShouldStartAtThePositionProvidedByTheClient(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl])
         .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder()
@@ -2814,11 +2763,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def zeroPositionQueryShouldReturnItemsFromTheStart(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl])
         .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder()
@@ -2879,11 +2824,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def resultsShouldBeEmptyWithoutErrorWhenThePositionProvidedByTheClientIsGreaterThanTheNumberOfResults(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val otherMailboxPath = MailboxPath.forUser(BOB, "other")
     val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
@@ -2942,11 +2883,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldReturnAnIllegalArgumentExceptionIfThePositionIsNegative(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
     sendMessageToBobInbox(server, message, Date.from(requestDate.toInstant))
@@ -3005,11 +2942,7 @@ trait EmailQueryMethodContract {
   @ParameterizedTest
   @MethodSource(value = Array("jmapSystemKeywords"))
   def listMailsBySystemKeywordShouldReturnOnlyMailsWithThisSystemKeyword(keywordFlag: Flags, keywordName: String, server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val messageId = server.getProbe(classOf[MailboxProbeImpl])
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(keywordFlag).build(message))
@@ -3289,11 +3222,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsByCustomKeywordShouldReturnOnlyMailsWithThisCustomKeyword(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     val messageId = server.getProbe(classOf[MailboxProbeImpl])
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("custom")).build(message))
@@ -3340,11 +3269,7 @@ trait EmailQueryMethodContract {
   @ParameterizedTest
   @MethodSource(value = Array("jmapSystemKeywords"))
   def listMailsNotBySystemKeywordShouldReturnOnlyMailsWithoutThisSystemKeyword(keywordFlag: Flags, keywordName: String, server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     server.getProbe(classOf[MailboxProbeImpl])
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(keywordFlag).build(message))
@@ -3390,11 +3315,7 @@ trait EmailQueryMethodContract {
 
   @Test
   def listMailsNotByCustomKeywordShouldReturnOnlyMailsWithoutThisCustomKeyword(server: GuiceJamesServer): Unit = {
-    val message: Message = Message.Builder
-      .of
-      .setSubject("test")
-      .setBody("testmail", StandardCharsets.UTF_8)
-      .build
+    val message: Message = buildTestMessage
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
     server.getProbe(classOf[MailboxProbeImpl])
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.builder().withFlags(new Flags("custom")).build(message))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
index e91530b..8ca075a 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
@@ -20,6 +20,7 @@
 package org.apache.james.jmap.mail
 import org.apache.james.jmap.mail.Email.Size
 import org.apache.james.jmap.mail.IsAscending.{ASCENDING, DESCENDING}
+import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.jmap.model.Limit.Limit
 import org.apache.james.jmap.model.Position.Position
 import org.apache.james.jmap.model.{AccountId, CanCalculateChanges, Keyword, LimitUnparsed, PositionUnparsed, QueryState, UTCDate}
@@ -67,7 +68,7 @@ case class EmailQueryRequest(accountId: AccountId,
                              comparator: Option[Set[Comparator]],
                              collapseThreads: Option[CollapseThreads],
                              anchor: Option[Anchor],
-                             anchorOffset: Option[AnchorOffset])
+                             anchorOffset: Option[AnchorOffset]) extends WithAccountId
 
 sealed trait SortProperty {
   def toSortClause: Either[UnsupportedSortException, SortClause]
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
index 0cf6810..4787cb3 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
@@ -37,7 +37,6 @@ import org.apache.james.mailbox.exception.MailboxNotFoundException
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
 import org.apache.james.metrics.api.MetricFactory
-import org.reactivestreams.Publisher
 import play.api.libs.json.{JsError, JsSuccess}
 import reactor.core.scala.publisher.{SFlux, SMono}
 
@@ -45,35 +44,38 @@ import scala.jdk.CollectionConverters._
 
 class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
                                   mailboxManager: MailboxManager,
-                                  metricFactory: MetricFactory,
-                                  sessionSupplier: SessionSupplier) extends Method {
+                                  val metricFactory: MetricFactory,
+                                  val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[EmailQueryRequest] {
   override val methodName: MethodName = MethodName("Email/query")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
-  override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] =
-    metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-      asEmailQueryRequest(invocation.arguments)
-        .flatMap(processRequest(mailboxSession, invocation, _, capabilities))
-        .onErrorResume {
-          case e: UnsupportedRequestParameterException => SMono.just(Invocation.error(
-            ErrorCode.InvalidArguments,
-            s"The following parameter ${e.unsupportedParam} is syntactically valid, but is not supported by the server.",
-            invocation.methodCallId))
-          case e: UnsupportedSortException => SMono.just(Invocation.error(
-            ErrorCode.UnsupportedSort,
-            s"The sort ${e.unsupportedSort} is syntactically valid, but it includes a property the server does not support sorting on or a collation method it does not recognise.",
-            invocation.methodCallId))
-          case e: UnsupportedFilterException => SMono.just(Invocation.error(
-            ErrorCode.UnsupportedFilter,
-            s"The filter ${e.unsupportedFilter} is syntactically valid, but the server cannot process it. If the filter was the result of a user’s search input, the client SHOULD suggest that the user simplify their search.",
-            invocation.methodCallId))
-          case e: IllegalArgumentException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId))
-          case e: MailboxNotFoundException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId))
-          case e: Throwable => SMono.raiseError(e)
-        }
-        .map(invocationResult => (invocationResult, processingContext)))
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, request: EmailQueryRequest): SMono[(Invocation, ProcessingContext)] = {
+    processRequest(mailboxSession, invocation, request, capabilities)
+      .onErrorResume {
+        case e: UnsupportedRequestParameterException => SMono.just(Invocation.error(
+          ErrorCode.InvalidArguments,
+          s"The following parameter ${e.unsupportedParam} is syntactically valid, but is not supported by the server.",
+          invocation.methodCallId))
+        case e: UnsupportedSortException => SMono.just(Invocation.error(
+          ErrorCode.UnsupportedSort,
+          s"The sort ${e.unsupportedSort} is syntactically valid, but it includes a property the server does not support sorting on or a collation method it does not recognise.",
+          invocation.methodCallId))
+        case e: UnsupportedFilterException => SMono.just(Invocation.error(
+          ErrorCode.UnsupportedFilter,
+          s"The filter ${e.unsupportedFilter} is syntactically valid, but the server cannot process it. If the filter was the result of a user’s search input, the client SHOULD suggest that the user simplify their search.",
+          invocation.methodCallId))
+        case e: IllegalArgumentException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId))
+        case e: MailboxNotFoundException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId))
+        case e: Throwable => SMono.raiseError(e)
+      }
+      .map(invocationResult => (invocationResult, processingContext))
+  }
+
 
-  private def processRequest(mailboxSession: MailboxSession, invocation: Invocation, request: EmailQueryRequest, capabilities: Set[CapabilityIdentifier]): SMono[Invocation] = {
+  private def processRequest(mailboxSession: MailboxSession,
+                             invocation: Invocation,
+                             request: EmailQueryRequest,
+                             capabilities: Set[CapabilityIdentifier]): SMono[Invocation] = {
     searchQueryFromRequest(request, capabilities, mailboxSession) match {
       case Left(error) => SMono.raiseError(error)
       case Right(searchQuery) =>  for {
@@ -84,6 +86,8 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
     }
   }
 
+  override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[EmailQueryRequest] = asEmailQueryRequest(invocation.arguments)
+
   private def executeQuery(mailboxSession: MailboxSession, request: EmailQueryRequest, searchQuery: MultimailboxesSearchQuery, position: Position, limitToUse: Limit): SMono[EmailQueryResponse] = {
     SFlux.fromPublisher(mailboxManager.search(searchQuery, mailboxSession, limitToUse))
       .drop(position.value)


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


[james-project] 08/08: JAMES-3387 validate accountId for vacationResponse/set

Posted by rc...@apache.org.
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 d124e2b21f76860a13686834ba5ce6864554c65d
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Thu Sep 24 15:14:14 2020 +0200

    JAMES-3387 validate accountId for vacationResponse/set
---
 .../VacationResponseSetMethodContract.scala        | 50 ++++++++++++++++++++++
 .../jmap/method/VacationResponseSetMethod.scala    | 23 ++++------
 .../james/jmap/vacation/VacationResponseSet.scala  |  3 +-
 3 files changed, 61 insertions(+), 15 deletions(-)

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/VacationResponseSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/VacationResponseSetMethodContract.scala
index d2fc11e..a2a8c21 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/VacationResponseSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/VacationResponseSetMethodContract.scala
@@ -45,6 +45,56 @@ trait VacationResponseSetMethodContract {
   }
 
   @Test
+  def vacationResponseSetShouldFailWhenWrongAccountId(server: GuiceJamesServer): Unit = {
+    val request =
+      s"""
+         |{
+         |  "using": [ "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail",
+         |    "urn:ietf:params:jmap:vacationresponse" ],
+         |  "methodCalls": [
+         |    ["VacationResponse/set", {
+         |      "accountId": "unknownAccountId",
+         |      "update": {
+         |        "singleton": {
+         |          "isEnabled": true,
+         |          "fromDate": "2014-10-30T14:12:00Z",
+         |          "toDate": "2014-11-30T14:12:00Z",
+         |          "subject": "I am in vacation",
+         |          "textBody": "I'm currently enjoying life. Please disturb me later",
+         |          "htmlBody": "I'm currently enjoying <b>life</b>. <br/>Please disturb me later"
+         |        }
+         |      }
+         |    }, "c1"]
+         |  ]
+         |}
+         |""".stripMargin
+
+    val response =  `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      """{
+        |  "sessionState": "75128aab4b1b",
+        |  "methodResponses": [
+        |    ["error", {
+        |      "type": "accountNotFound"
+        |    }, "c1"]
+        |  ]
+        |}""".stripMargin)
+  }
+
+  @Test
   @Tag(CategoryTags.BASIC_FEATURE)
   def updateShouldSucceed(): Unit = {
     val request =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
index c3febb9..66e52e0 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
@@ -30,12 +30,9 @@ import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CA
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.SetError.SetErrorDescription
 import org.apache.james.jmap.model.{Capabilities, Invocation, State}
-import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.jmap.vacation.{VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
-import org.apache.james.util.date.ZonedDateTimeProvider
-import org.reactivestreams.Publisher
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
 import reactor.core.scala.publisher.{SFlux, SMono}
 
@@ -73,22 +70,20 @@ object VacationResponseSetMethod {
   val VACATION_RESPONSE_PATCH_OBJECT_KEY = "singleton"
 }
 
-class VacationResponseSetMethod @Inject() (vacationRepository: VacationRepository,
-                                           zonedDateTimeProvider: ZonedDateTimeProvider,
-                                           metricFactory: MetricFactory) extends Method {
+class VacationResponseSetMethod @Inject()(vacationRepository: VacationRepository,
+                                          val metricFactory: MetricFactory,
+                                          val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[VacationResponseSetRequest] {
   override val methodName: MethodName = MethodName("VacationResponse/set")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY, VACATION_RESPONSE_CAPABILITY)
 
-  override def process(capabilities: Set[CapabilityIdentifier],
-                       invocation: InvocationWithContext,
-                       mailboxSession: MailboxSession): Publisher[InvocationWithContext] = {
-      metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-        asVacationResponseSetRequest(invocation.invocation.arguments)
-          .flatMap(vacationResponseSetRequest => update(mailboxSession, vacationResponseSetRequest)
-            .map(updateResult => createResponse(invocation.invocation, vacationResponseSetRequest, updateResult)))
-          .map(InvocationWithContext(_, invocation.processingContext)))
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: VacationResponseSetRequest): SMono[InvocationWithContext] = {
+    update(mailboxSession, request)
+      .map(updateResult => createResponse(invocation.invocation, request, updateResult))
+      .map(InvocationWithContext(_, invocation.processingContext))
   }
 
+  override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[VacationResponseSetRequest] = asVacationResponseSetRequest(invocation.arguments)
+
   private def update(mailboxSession: MailboxSession, vacationResponseSetRequest: VacationResponseSetRequest): SMono[VacationResponseUpdateResults] = {
     SFlux.fromIterable(vacationResponseSetRequest.parsePatch()
       .map[SMono[VacationResponseUpdateResult]]({
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseSet.scala
index b7b00ca..6a68a53 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseSet.scala
@@ -24,6 +24,7 @@ import java.time.format.DateTimeFormatter
 
 import org.apache.james.jmap.api.vacation.Vacation.ID
 import org.apache.james.jmap.api.vacation.VacationPatch
+import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.jmap.model.AccountId
 import org.apache.james.jmap.model.SetError.{SetErrorDescription, SetErrorType, invalidArgumentValue, serverFailValue}
 import org.apache.james.jmap.model.State.State
@@ -35,7 +36,7 @@ import scala.util.{Failure, Success, Try}
 case class VacationResponseSetRequest(accountId: AccountId,
                                       update: Option[Map[String, VacationResponsePatchObject]],
                                       create: Option[Map[String, JsObject]],
-                                      destroy: Option[Seq[String]]) {
+                                      destroy: Option[Seq[String]]) extends WithAccountId{
   def parsePatch(): Map[String, Either[IllegalArgumentException, VacationResponsePatchObject]] =
     update.getOrElse(Map())
     .map({


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


[james-project] 05/08: JAMES-3387 validate accountId for mailbox/get

Posted by rc...@apache.org.
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 60732e18edfb709509681ca8fcc376fd340bf425
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Thu Sep 24 14:46:20 2020 +0200

    JAMES-3387 validate accountId for mailbox/get
---
 .../contract/MailboxGetMethodContract.scala        | 41 ++++++++++++++++++++
 .../org/apache/james/jmap/mail/MailboxGet.scala    |  3 +-
 .../james/jmap/method/MailboxGetMethod.scala       | 45 ++++++++++------------
 3 files changed, 63 insertions(+), 26 deletions(-)

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/MailboxGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
index 3da44bd..843460f 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
@@ -74,6 +74,47 @@ trait MailboxGetMethodContract {
   }
 
   @Test
+  def mailboxGetShouldFailWhenWrongAccountId(): Unit = {
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail",
+         |    "urn:ietf:params:jmap:vacationresponse"],
+         |  "methodCalls": [[
+         |    "Mailbox/get",
+         |    {
+         |      "accountId": "unknownAccountId",
+         |      "ids": null
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    val response =  `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      """{
+        |  "sessionState": "75128aab4b1b",
+        |  "methodResponses": [
+        |    ["error", {
+        |      "type": "accountNotFound"
+        |    }, "c1"]
+        |  ]
+        |}""".stripMargin)
+  }
+
+  @Test
   def getMailboxesShouldIncludeRightsAndNamespaceIfSharesCapabilityIsUsed(server: GuiceJamesServer): Unit = {
     val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
       .createMailbox(MailboxPath.forUser(BOB, "custom"))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
index d36a430..b230a77 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
@@ -20,6 +20,7 @@
 package org.apache.james.jmap.mail
 
 import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
+import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.jmap.model.State.State
 import org.apache.james.jmap.model.{AccountId, Properties}
 
@@ -27,7 +28,7 @@ case class Ids(value: List[UnparsedMailboxId])
 
 case class MailboxGetRequest(accountId: AccountId,
                              ids: Option[Ids],
-                             properties: Option[Properties])
+                             properties: Option[Properties]) extends WithAccountId
 
 case class NotFound(value: Set[UnparsedMailboxId]) {
   def merge(other: NotFound): NotFound = NotFound(this.value ++ other.value)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index d747717..15c01c9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -37,7 +37,6 @@ import org.apache.james.mailbox.model.search.MailboxQuery
 import org.apache.james.mailbox.model.{MailboxId, MailboxMetaData}
 import org.apache.james.mailbox.{MailboxManager, MailboxSession, SubscriptionManager}
 import org.apache.james.metrics.api.MetricFactory
-import org.reactivestreams.Publisher
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
 import reactor.core.scala.publisher.{SFlux, SMono}
 import reactor.core.scheduler.Schedulers
@@ -50,8 +49,8 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
                                   quotaFactory : QuotaLoaderWithPreloadedDefaultFactory,
                                   mailboxIdFactory: MailboxId.Factory,
                                   mailboxFactory: MailboxFactory,
-                                  metricFactory: MetricFactory,
-                                  val sessionSupplier: SessionSupplier) extends Method {
+                                  val metricFactory: MetricFactory,
+                                  val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[MailboxGetRequest] {
   override val methodName: MethodName = MethodName("Mailbox/get")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
@@ -73,30 +72,26 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
       notFound = notFound)
   }
 
-  override def process(capabilities: Set[CapabilityIdentifier],
-                       invocation: InvocationWithContext,
-                       mailboxSession: MailboxSession): Publisher[InvocationWithContext] = {
-    metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-      asMailboxGetRequest(invocation.invocation.arguments)
-        .flatMap(mailboxGetRequest => {
-          val requestedProperties: Properties = mailboxGetRequest.properties.getOrElse(Mailbox.allProperties)
-          requestedProperties -- Mailbox.allProperties match {
-            case invalidProperties if invalidProperties.isEmpty() => getMailboxes(capabilities, mailboxGetRequest, invocation.processingContext, mailboxSession)
-              .reduce(MailboxGetResults.empty(), MailboxGetResults.merge)
-              .map(mailboxes => mailboxes.asResponse(mailboxGetRequest.accountId))
-              .map(mailboxGetResponse => Invocation(
-                methodName = methodName,
-                arguments = Arguments(serializer.serialize(mailboxGetResponse, requestedProperties, capabilities).as[JsObject]),
-                methodCallId = invocation.invocation.methodCallId))
-            case invalidProperties: Properties =>
-              SMono.just(Invocation.error(errorCode = ErrorCode.InvalidArguments,
-                description = s"The following properties [${invalidProperties.format()}] do not exist.",
-                methodCallId = invocation.invocation.methodCallId))
-          }
-        })
-        .map(InvocationWithContext(_, invocation.processingContext)))
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxGetRequest): SMono[InvocationWithContext] = {
+    val requestedProperties: Properties = request.properties.getOrElse(Mailbox.allProperties)
+    (requestedProperties -- Mailbox.allProperties match {
+      case invalidProperties if invalidProperties.isEmpty() => getMailboxes(capabilities, request, invocation.processingContext, mailboxSession)
+        .reduce(MailboxGetResults.empty(), MailboxGetResults.merge)
+        .map(mailboxes => mailboxes.asResponse(request.accountId))
+        .map(mailboxGetResponse => Invocation(
+          methodName = methodName,
+          arguments = Arguments(serializer.serialize(mailboxGetResponse, requestedProperties, capabilities).as[JsObject]),
+          methodCallId = invocation.invocation.methodCallId))
+      case invalidProperties: Properties =>
+        SMono.just(Invocation.error(errorCode = ErrorCode.InvalidArguments,
+          description = s"The following properties [${invalidProperties.format()}] do not exist.",
+          methodCallId = invocation.invocation.methodCallId))
+    }).map(InvocationWithContext(_, invocation.processingContext))
+
   }
 
+  override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[MailboxGetRequest] = asMailboxGetRequest(invocation.arguments)
+
   private def asMailboxGetRequest(arguments: Arguments): SMono[MailboxGetRequest] = {
     serializer.deserializeMailboxGetRequest(arguments.value) match {
       case JsSuccess(mailboxGetRequest, _) => SMono.just(mailboxGetRequest)


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


[james-project] 04/08: JAMES-3387 refactor extract InvocationWithContext

Posted by rc...@apache.org.
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 a667ad5f4db98e1c9fbe9c33d6c63667b6bddcec
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Thu Sep 24 13:58:47 2020 +0200

    JAMES-3387 refactor extract InvocationWithContext
---
 .../apache/james/jmap/method/CoreEchoMethod.scala  |  6 ++--
 .../apache/james/jmap/method/EmailGetMethod.scala  |  9 +++--
 .../james/jmap/method/EmailQueryMethod.scala       | 30 +++-------------
 .../james/jmap/method/MailboxGetMethod.scala       | 15 ++++----
 .../james/jmap/method/MailboxQueryMethod.scala     | 10 +++---
 .../james/jmap/method/MailboxSetMethod.scala       | 10 +++---
 .../org/apache/james/jmap/method/Method.scala      | 37 ++++++++++++++------
 .../jmap/method/VacationResponseGetMethod.scala    | 17 +++++----
 .../jmap/method/VacationResponseSetMethod.scala    | 11 +++---
 .../apache/james/jmap/routes/JMAPApiRoutes.scala   | 40 ++++++++++------------
 .../james/jmap/method/CoreEchoMethodTest.scala     | 11 +++---
 .../james/jmap/routes/JMAPApiRoutesTest.scala      |  2 +-
 12 files changed, 93 insertions(+), 105 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
index c956a81..dfe2aa5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/CoreEchoMethod.scala
@@ -20,12 +20,10 @@ package org.apache.james.jmap.method
 
 
 import eu.timepit.refined.auto._
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.CORE_CAPABILITY
 import org.apache.james.jmap.model.Invocation.MethodName
-import org.apache.james.jmap.model.{Capabilities, Invocation}
-import org.apache.james.jmap.routes.ProcessingContext
+import org.apache.james.jmap.model.{Capabilities}
 import org.apache.james.mailbox.MailboxSession
 import org.reactivestreams.Publisher
 import reactor.core.scala.publisher.SMono
@@ -33,7 +31,7 @@ import reactor.core.scala.publisher.SMono
 class CoreEchoMethod extends Method {
   override val methodName = MethodName("Core/echo")
 
-  override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] = SMono.just((invocation, processingContext))
+  override def process(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): Publisher[InvocationWithContext] = SMono.just(invocation)
 
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY)
 }
\ No newline at end of file
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
index 0dbae7e..36d4e93 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
@@ -33,7 +33,6 @@ import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CA
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.State.INSTANCE
 import org.apache.james.jmap.model.{AccountId, Capabilities, ErrorCode, Invocation, Properties}
-import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.model.MessageId
 import org.apache.james.metrics.api.MetricFactory
@@ -87,11 +86,11 @@ class EmailGetMethod @Inject() (readerFactory: EmailViewReaderFactory,
   override val methodName = MethodName("Email/get")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
-  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, request: EmailGetRequest): SMono[(Invocation, ProcessingContext)] = {
-    computeResponseInvocation(request, invocation, mailboxSession).onErrorResume({
-      case e: IllegalArgumentException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId))
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: EmailGetRequest): SMono[InvocationWithContext] = {
+    computeResponseInvocation(request, invocation.invocation, mailboxSession).onErrorResume({
+      case e: IllegalArgumentException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.invocation.methodCallId))
       case e: Throwable => SMono.raiseError(e)
-    }).map(invocationResult => (invocationResult, processingContext))
+    }).map(invocationResult => InvocationWithContext(invocationResult, invocation.processingContext))
   }
 
   override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[EmailGetRequest] = asEmailGetRequest(invocation.arguments)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
index 4787cb3..5099c18 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
@@ -23,17 +23,15 @@ import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{EmailQuerySerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, EmailQueryResponse, UnsupportedFilterException, UnsupportedRequestParameterException, UnsupportedSortException}
+import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, EmailQueryResponse, UnsupportedRequestParameterException}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY}
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.Limit.Limit
 import org.apache.james.jmap.model.Position.Position
-import org.apache.james.jmap.model.{CanCalculateChanges, Capabilities, ErrorCode, Invocation, Limit, Position, QueryState}
-import org.apache.james.jmap.routes.ProcessingContext
+import org.apache.james.jmap.model.{CanCalculateChanges, Capabilities, Invocation, Limit, Position, QueryState}
 import org.apache.james.jmap.utils.search.MailboxFilter
 import org.apache.james.jmap.utils.search.MailboxFilter.QueryFilter
-import org.apache.james.mailbox.exception.MailboxNotFoundException
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
 import org.apache.james.metrics.api.MetricFactory
@@ -49,29 +47,11 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
   override val methodName: MethodName = MethodName("Email/query")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
-  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, request: EmailQueryRequest): SMono[(Invocation, ProcessingContext)] = {
-    processRequest(mailboxSession, invocation, request, capabilities)
-      .onErrorResume {
-        case e: UnsupportedRequestParameterException => SMono.just(Invocation.error(
-          ErrorCode.InvalidArguments,
-          s"The following parameter ${e.unsupportedParam} is syntactically valid, but is not supported by the server.",
-          invocation.methodCallId))
-        case e: UnsupportedSortException => SMono.just(Invocation.error(
-          ErrorCode.UnsupportedSort,
-          s"The sort ${e.unsupportedSort} is syntactically valid, but it includes a property the server does not support sorting on or a collation method it does not recognise.",
-          invocation.methodCallId))
-        case e: UnsupportedFilterException => SMono.just(Invocation.error(
-          ErrorCode.UnsupportedFilter,
-          s"The filter ${e.unsupportedFilter} is syntactically valid, but the server cannot process it. If the filter was the result of a user’s search input, the client SHOULD suggest that the user simplify their search.",
-          invocation.methodCallId))
-        case e: IllegalArgumentException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId))
-        case e: MailboxNotFoundException => SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId))
-        case e: Throwable => SMono.raiseError(e)
-      }
-      .map(invocationResult => (invocationResult, processingContext))
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: EmailQueryRequest): SMono[InvocationWithContext] = {
+    processRequest(mailboxSession, invocation.invocation, request, capabilities)
+      .map(invocationResult => InvocationWithContext(invocationResult, invocation.processingContext))
   }
 
-
   private def processRequest(mailboxSession: MailboxSession,
                              invocation: Invocation,
                              request: EmailQueryRequest,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index 6e660c8..d747717 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -74,28 +74,27 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
   }
 
   override def process(capabilities: Set[CapabilityIdentifier],
-                       invocation: Invocation,
-                       mailboxSession: MailboxSession,
-                       processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] = {
+                       invocation: InvocationWithContext,
+                       mailboxSession: MailboxSession): Publisher[InvocationWithContext] = {
     metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-      asMailboxGetRequest(invocation.arguments)
+      asMailboxGetRequest(invocation.invocation.arguments)
         .flatMap(mailboxGetRequest => {
           val requestedProperties: Properties = mailboxGetRequest.properties.getOrElse(Mailbox.allProperties)
           requestedProperties -- Mailbox.allProperties match {
-            case invalidProperties if invalidProperties.isEmpty() => getMailboxes(capabilities, mailboxGetRequest, processingContext, mailboxSession)
+            case invalidProperties if invalidProperties.isEmpty() => getMailboxes(capabilities, mailboxGetRequest, invocation.processingContext, mailboxSession)
               .reduce(MailboxGetResults.empty(), MailboxGetResults.merge)
               .map(mailboxes => mailboxes.asResponse(mailboxGetRequest.accountId))
               .map(mailboxGetResponse => Invocation(
                 methodName = methodName,
                 arguments = Arguments(serializer.serialize(mailboxGetResponse, requestedProperties, capabilities).as[JsObject]),
-                methodCallId = invocation.methodCallId))
+                methodCallId = invocation.invocation.methodCallId))
             case invalidProperties: Properties =>
               SMono.just(Invocation.error(errorCode = ErrorCode.InvalidArguments,
                 description = s"The following properties [${invalidProperties.format()}] do not exist.",
-                methodCallId = invocation.methodCallId))
+                methodCallId = invocation.invocation.methodCallId))
           }
         })
-        .map((_, processingContext)))
+        .map(InvocationWithContext(_, invocation.processingContext)))
   }
 
   private def asMailboxGetRequest(arguments: Arguments): SMono[MailboxGetRequest] = {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
index 268dc1d..ea0200b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
@@ -41,19 +41,19 @@ class MailboxQueryMethod @Inject()(systemMailboxesProvider: SystemMailboxesProvi
   override val methodName = MethodName("Mailbox/query")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
-  override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] =
+  override def process(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): Publisher[InvocationWithContext] =
     metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-      asMailboxQueryRequest(invocation.arguments)
-        .flatMap(processRequest(mailboxSession, invocation, _))
+      asMailboxQueryRequest(invocation.invocation.arguments)
+        .flatMap(processRequest(mailboxSession, invocation.invocation, _))
         .onErrorResume {
           case e: IllegalArgumentException => SMono.just(
             Invocation.error(
               errorCode = ErrorCode.InvalidArguments,
               description = e.getMessage,
-              methodCallId = invocation.methodCallId))
+              methodCallId = invocation.invocation.methodCallId))
           case e: Throwable => SMono.raiseError(e)
         }
-        .map(invocationResult => (invocationResult, processingContext)))
+        .map(invocationResult => InvocationWithContext(invocationResult, invocation.processingContext)))
 
   private def processRequest(mailboxSession: MailboxSession, invocation: Invocation, request: MailboxQueryRequest): SMono[Invocation] = {
     SFlux.fromPublisher(systemMailboxesProvider.getMailboxByRole(request.filter.role, mailboxSession.getUser))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
index dc772c5..df6b77c 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
@@ -153,11 +153,11 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
   override val methodName: MethodName = MethodName("Mailbox/set")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
-  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, request: MailboxSetRequest): SMono[(Invocation, ProcessingContext)] = for {
-    creationResultsWithUpdatedProcessingContext <- createMailboxes(mailboxSession, request, processingContext)
-    deletionResults <- deleteMailboxes(mailboxSession, request, processingContext)
-    updateResults <- updateMailboxes(mailboxSession, request, processingContext, capabilities)
-  } yield (createResponse(capabilities, invocation, request, creationResultsWithUpdatedProcessingContext._1, deletionResults, updateResults), creationResultsWithUpdatedProcessingContext._2)
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxSetRequest): SMono[InvocationWithContext] = for {
+    creationResultsWithUpdatedProcessingContext <- createMailboxes(mailboxSession, request, invocation.processingContext)
+    deletionResults <- deleteMailboxes(mailboxSession, request, invocation.processingContext)
+    updateResults <- updateMailboxes(mailboxSession, request, invocation.processingContext, capabilities)
+  } yield InvocationWithContext(createResponse(capabilities, invocation.invocation, request, creationResultsWithUpdatedProcessingContext._1, deletionResults, updateResults), creationResultsWithUpdatedProcessingContext._2)
 
   override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[MailboxSetRequest] = asMailboxSetRequest(invocation.arguments)
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
index f4a40a5..5ee14cb 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/Method.scala
@@ -20,6 +20,7 @@ package org.apache.james.jmap.method
 
 
 import org.apache.james.jmap.http.SessionSupplier
+import org.apache.james.jmap.mail.{UnsupportedFilterException, UnsupportedRequestParameterException, UnsupportedSortException}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.Invocation.MethodName
 import org.apache.james.jmap.model.{AccountId, Capabilities, ErrorCode, Invocation, Session}
@@ -30,6 +31,8 @@ import org.apache.james.metrics.api.MetricFactory
 import org.reactivestreams.Publisher
 import reactor.core.scala.publisher.SMono
 
+case class InvocationWithContext(invocation: Invocation, processingContext: ProcessingContext)
+
 trait Method {
   val JMAP_RFC8621_PREFIX: String = "JMAP-RFC8621-"
 
@@ -37,7 +40,7 @@ trait Method {
 
   val requiredCapabilities: Capabilities
 
-  def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)]
+  def process(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): Publisher[InvocationWithContext]
 }
 
 trait WithAccountId {
@@ -47,18 +50,32 @@ trait MethodRequiringAccountId[REQUEST <: WithAccountId] extends Method {
   def metricFactory: MetricFactory
   def sessionSupplier: SessionSupplier
 
-  override def process(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] = {
+  override def process(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): Publisher[InvocationWithContext] = {
     val result = (for {
-      request <- getRequest(mailboxSession, invocation)
-      response <- validateAccountId(request.accountId, mailboxSession, sessionSupplier, invocation).flatMap{
-        case Right(_) =>  doProcess(capabilities, invocation, mailboxSession, processingContext, request)
-        case Left(invocation) => SMono.just((invocation, processingContext))
+      request <- getRequest(mailboxSession, invocation.invocation)
+      response <- validateAccountId(request.accountId, mailboxSession, sessionSupplier, invocation.invocation).flatMap{
+        case Right(_) => doProcess(capabilities, invocation, mailboxSession, request)
+        case Left(errorInvocation) => SMono.just(InvocationWithContext(errorInvocation, invocation.processingContext))
       }
     } yield response)
       .onErrorResume {
-      case e: IllegalArgumentException => SMono.just((Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.methodCallId), processingContext))
-      case e: Throwable => SMono.raiseError(e)
-    }
+        case e: UnsupportedRequestParameterException => SMono.just(InvocationWithContext(Invocation.error(
+          ErrorCode.InvalidArguments,
+          s"The following parameter ${e.unsupportedParam} is syntactically valid, but is not supported by the server.",
+          invocation.invocation.methodCallId), invocation.processingContext))
+        case e: UnsupportedSortException => SMono.just(InvocationWithContext(Invocation.error(
+          ErrorCode.UnsupportedSort,
+          s"The sort ${e.unsupportedSort} is syntactically valid, but it includes a property the server does not support sorting on or a collation method it does not recognise.",
+          invocation.invocation.methodCallId), invocation.processingContext))
+        case e: UnsupportedFilterException => SMono.just(InvocationWithContext(Invocation.error(
+          ErrorCode.UnsupportedFilter,
+          s"The filter ${e.unsupportedFilter} is syntactically valid, but the server cannot process it. If the filter was the result of a user’s search input, the client SHOULD suggest that the user simplify their search.",
+          invocation.invocation.methodCallId), invocation.processingContext))
+        case e: IllegalArgumentException => SMono.just(InvocationWithContext(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.invocation.methodCallId), invocation.processingContext))
+        case e: MailboxNotFoundException => SMono.just(InvocationWithContext(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, invocation.invocation.methodCallId), invocation.processingContext))
+        case e: Throwable => SMono.raiseError(e)
+      }
+
 
     metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value, result)
   }
@@ -70,7 +87,7 @@ trait MethodRequiringAccountId[REQUEST <: WithAccountId] extends Method {
       .switchIfEmpty(SMono.just(Left[Invocation, Session](Invocation.error(ErrorCode.AccountNotFound, invocation.methodCallId))))
   }
 
-  def doProcess(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext, request: REQUEST): SMono[(Invocation, ProcessingContext)]
+  def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: REQUEST): SMono[InvocationWithContext]
 
   def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[REQUEST]
 }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
index a3c3e8b..a9a3a7f 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
@@ -64,29 +64,28 @@ class VacationResponseGetMethod @Inject() (vacationRepository: VacationRepositor
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY, VACATION_RESPONSE_CAPABILITY)
 
   override def process(capabilities: Set[CapabilityIdentifier],
-                       invocation: Invocation,
-                       mailboxSession: MailboxSession,
-                       processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] = {
+                       invocation: InvocationWithContext,
+                       mailboxSession: MailboxSession): Publisher[InvocationWithContext] = {
     metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-      asVacationResponseGetRequest(invocation.arguments)
-        .fold(e => handleRequestValidationErrors(e, invocation.methodCallId),
+      asVacationResponseGetRequest(invocation.invocation.arguments)
+        .fold(e => handleRequestValidationErrors(e, invocation.invocation.methodCallId),
           vacationResponseGetRequest => {
             val requestedProperties: Properties = vacationResponseGetRequest.properties.getOrElse(VacationResponse.allProperties)
             requestedProperties -- VacationResponse.allProperties match {
-              case invalidProperties if invalidProperties.isEmpty() => getVacationResponse(vacationResponseGetRequest, processingContext, mailboxSession)
+              case invalidProperties if invalidProperties.isEmpty() => getVacationResponse(vacationResponseGetRequest, invocation.processingContext, mailboxSession)
                 .reduce(VacationResponseGetResult.empty, VacationResponseGetResult.merge)
                 .map(vacationResult => vacationResult.asResponse(vacationResponseGetRequest.accountId))
                 .map(vacationResponseGetResponse => Invocation(
                   methodName = methodName,
                   arguments = Arguments(VacationSerializer.serialize(vacationResponseGetResponse, requestedProperties).as[JsObject]),
-                  methodCallId = invocation.methodCallId))
+                  methodCallId = invocation.invocation.methodCallId))
               case invalidProperties: Properties =>
                 SMono.just(Invocation.error(errorCode = ErrorCode.InvalidArguments,
                   description = s"The following properties [${invalidProperties.format}] do not exist.",
-                  methodCallId = invocation.methodCallId))
+                  methodCallId = invocation.invocation.methodCallId))
             }
           })
-          .map((_, processingContext)))
+          .map(InvocationWithContext(_, invocation.processingContext)))
   }
 
   private def asVacationResponseGetRequest(arguments: Arguments): Either[IllegalArgumentException, VacationResponseGetRequest] =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
index 4b24625..c3febb9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseSetMethod.scala
@@ -80,14 +80,13 @@ class VacationResponseSetMethod @Inject() (vacationRepository: VacationRepositor
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY, VACATION_RESPONSE_CAPABILITY)
 
   override def process(capabilities: Set[CapabilityIdentifier],
-                       invocation: Invocation,
-                       mailboxSession: MailboxSession,
-                       processingContext: ProcessingContext): Publisher[(Invocation, ProcessingContext)] = {
+                       invocation: InvocationWithContext,
+                       mailboxSession: MailboxSession): Publisher[InvocationWithContext] = {
       metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-        asVacationResponseSetRequest(invocation.arguments)
+        asVacationResponseSetRequest(invocation.invocation.arguments)
           .flatMap(vacationResponseSetRequest => update(mailboxSession, vacationResponseSetRequest)
-            .map(updateResult => createResponse(invocation, vacationResponseSetRequest, updateResult)))
-          .map((_, processingContext)))
+            .map(updateResult => createResponse(invocation.invocation, vacationResponseSetRequest, updateResult)))
+          .map(InvocationWithContext(_, invocation.processingContext)))
   }
 
   private def update(mailboxSession: MailboxSession, vacationResponseSetRequest: VacationResponseSetRequest): SMono[VacationResponseUpdateResults] = {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
index ab870e1..3ddf463 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/JMAPApiRoutes.scala
@@ -35,7 +35,7 @@ import org.apache.james.jmap.exceptions.UnauthorizedException
 import org.apache.james.jmap.http.rfc8621.InjectionKeys
 import org.apache.james.jmap.http.{Authenticator, MailboxesProvisioner, SessionSupplier, UserProvisioning}
 import org.apache.james.jmap.json.ResponseSerializer
-import org.apache.james.jmap.method.Method
+import org.apache.james.jmap.method.{InvocationWithContext, Method}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.Invocation.MethodName
 import org.apache.james.jmap.model.ProblemDetails.{notJSONProblem, notRequestProblem, unknownCapabilityProblem}
@@ -120,25 +120,25 @@ class JMAPApiRoutes (val authenticator: Authenticator,
       SMono.raiseError(UnsupportedCapabilitiesException(unsupportedCapabilities))
     } else {
       processSequentiallyAndUpdateContext(requestObject, mailboxSession, processingContext, capabilities)
-        .flatMap((invocations : Seq[(Invocation, ProcessingContext)]) =>
+        .flatMap((invocations : Seq[InvocationWithContext]) =>
           SMono.fromPublisher(httpServerResponse.status(OK)
             .header(CONTENT_TYPE, JSON_CONTENT_TYPE)
             .sendString(
               SMono.fromCallable(() =>
-                ResponseSerializer.serialize(ResponseObject(ResponseObject.SESSION_STATE, invocations.map(_._1))).toString),
+                ResponseSerializer.serialize(ResponseObject(ResponseObject.SESSION_STATE, invocations.map(_.invocation))).toString),
               StandardCharsets.UTF_8)
             .`then`())
         )
     }
   }
 
-  private def processSequentiallyAndUpdateContext(requestObject: RequestObject, mailboxSession: MailboxSession, processingContext: ProcessingContext, capabilities: Set[CapabilityIdentifier]): SMono[Seq[(Invocation, ProcessingContext)]] = {
+  private def processSequentiallyAndUpdateContext(requestObject: RequestObject, mailboxSession: MailboxSession, processingContext: ProcessingContext, capabilities: Set[CapabilityIdentifier]): SMono[Seq[(InvocationWithContext)]] = {
     SFlux.fromIterable(requestObject.methodCalls)
-      .foldLeft(List[SMono[(Invocation, ProcessingContext)]]())((acc, elem) => {
+      .foldLeft(List[SMono[InvocationWithContext]]())((acc, elem) => {
         val lastProcessingContext: SMono[ProcessingContext] = acc.headOption
-          .map(last => last.map(_._2))
+          .map(last => last.map(_.processingContext))
           .getOrElse(SMono.just(processingContext))
-        val invocation: SMono[(Invocation, ProcessingContext)] = lastProcessingContext.flatMap(context => process(capabilities, mailboxSession, context, elem))
+        val invocation: SMono[InvocationWithContext] = lastProcessingContext.flatMap(context => process(capabilities, mailboxSession, InvocationWithContext(elem, context)))
         invocation.cache() :: acc
       })
       .map(_.reverse)
@@ -147,29 +147,27 @@ class JMAPApiRoutes (val authenticator: Authenticator,
         .collectSeq())
   }
 
-  private def process(capabilities: Set[CapabilityIdentifier], mailboxSession: MailboxSession, processingContext: ProcessingContext, invocation: Invocation) : SMono[(Invocation, ProcessingContext)] = {
+  private def process(capabilities: Set[CapabilityIdentifier], mailboxSession: MailboxSession, invocation: InvocationWithContext) : SMono[InvocationWithContext] = {
     SMono.defer(() => {
-      processingContext.resolveBackReferences(invocation) match {
-        case Left(e) => SMono.just((Invocation.error(
+      invocation.processingContext.resolveBackReferences(invocation.invocation) match {
+        case Left(e) => SMono.just(InvocationWithContext(Invocation.error(
           errorCode = ErrorCode.InvalidResultReference,
           description = s"Failed resolving back-reference: ${e.message}",
-          methodCallId = invocation.methodCallId), processingContext))
-        case Right(resolvedInvocation) => processMethodWithMatchName(capabilities, resolvedInvocation, mailboxSession, processingContext)
-          .map {
-            case (invocation: Invocation, updatedProcessingContext: ProcessingContext) => (invocation, updatedProcessingContext.recordInvocation(invocation))
-          }
+          methodCallId = invocation.invocation.methodCallId), invocation.processingContext))
+        case Right(resolvedInvocation) => processMethodWithMatchName(capabilities, InvocationWithContext(resolvedInvocation, invocation.processingContext), mailboxSession)
+          .map((invocationWithContext: InvocationWithContext) => InvocationWithContext(invocationWithContext.invocation, invocationWithContext.processingContext.recordInvocation(invocationWithContext.invocation)))
       }
     })
   }
 
-  private def processMethodWithMatchName(capabilities: Set[CapabilityIdentifier], invocation: Invocation, mailboxSession: MailboxSession, processingContext: ProcessingContext): SMono[(Invocation, ProcessingContext)] =
-    SMono.justOrEmpty(methodsByName.get(invocation.methodName))
+  private def processMethodWithMatchName(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): SMono[InvocationWithContext] =
+    SMono.justOrEmpty(methodsByName.get(invocation.invocation.methodName))
       .flatMap(method => validateCapabilities(capabilities, method.requiredCapabilities)
-      .fold(e => SMono.just((Invocation.error(ErrorCode.UnknownMethod, e.description, invocation.methodCallId), processingContext)),
-        _ => SMono.fromPublisher(method.process(capabilities, invocation, mailboxSession, processingContext))
+      .fold(e => SMono.just(InvocationWithContext(Invocation.error(ErrorCode.UnknownMethod, e.description, invocation.invocation.methodCallId), invocation.processingContext)),
+        _ => SMono.fromPublisher(method.process(capabilities, invocation, mailboxSession))
       ))
-      .onErrorResume(throwable => SMono.just((Invocation.error(ErrorCode.ServerFail, throwable.getMessage, invocation.methodCallId), processingContext)))
-      .switchIfEmpty(SMono.just((Invocation.error(ErrorCode.UnknownMethod, invocation.methodCallId), processingContext)))
+      .onErrorResume(throwable => SMono.just(InvocationWithContext(Invocation.error(ErrorCode.ServerFail, throwable.getMessage, invocation.invocation.methodCallId), invocation.processingContext)))
+      .switchIfEmpty(SMono.just(InvocationWithContext(Invocation.error(ErrorCode.UnknownMethod, invocation.invocation.methodCallId), invocation.processingContext)))
 
   private def validateCapabilities(capabilities: Set[CapabilityIdentifier], requiredCapabilities: Capabilities): Either[MissingCapabilityException, Unit] = {
     val missingCapabilities = requiredCapabilities.ids -- capabilities
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
index 77da9e6..18a2624 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/method/CoreEchoMethodTest.scala
@@ -18,10 +18,9 @@
  * ***************************************************************/
 package org.apache.james.jmap.method
 
-import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.Fixture.{invocation1, invocation2}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.{CapabilityIdentifier, Invocation, JmapRfc8621Configuration}
+import org.apache.james.jmap.model.{CapabilityIdentifier, Invocation}
 import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.MailboxSession
 import org.mockito.Mockito.mock
@@ -37,15 +36,15 @@ class CoreEchoMethodTest extends AnyWordSpec with Matchers {
   "CoreEcho" should {
     "Process" should {
       "success and return the same with parameters as the invocation request" in {
-        val expectedResponse: (Invocation, ProcessingContext) = (invocation1, ProcessingContext(Map.empty, Map.empty))
-        val dataResponse = SMono.fromPublisher(echoMethod.process(capabilities, invocation1, mockedSession, ProcessingContext(Map.empty, Map.empty))).block()
+        val expectedResponse: InvocationWithContext = InvocationWithContext(invocation1, ProcessingContext(Map.empty, Map.empty))
+        val dataResponse = SMono.fromPublisher(echoMethod.process(capabilities, InvocationWithContext(invocation1,ProcessingContext(Map.empty, Map.empty)), mockedSession)).block()
 
         dataResponse shouldBe expectedResponse
       }
 
       "success and not return anything else different than the original invocation" in {
-        val wrongExpected: (Invocation, ProcessingContext) = (invocation2, ProcessingContext(Map.empty, Map.empty))
-        val dataResponse = SMono.fromPublisher(echoMethod.process(capabilities, invocation1, mockedSession, ProcessingContext(Map.empty, Map.empty))).block()
+        val wrongExpected: InvocationWithContext = InvocationWithContext(invocation2, ProcessingContext(Map.empty, Map.empty))
+        val dataResponse = SMono.fromPublisher(echoMethod.process(capabilities, InvocationWithContext(invocation1, ProcessingContext(Map.empty, Map.empty)), mockedSession)).block()
         
         dataResponse should not be(wrongExpected)
       }
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 7e5298f..b9f23bc 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
@@ -437,7 +437,7 @@ class JMAPApiRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
     doThrow(new RuntimeException("Unexpected Exception occur, the others method may proceed normally"))
       .doCallRealMethod()
       .when(mockCoreEchoMethod)
-      .process(any[Set[CapabilityIdentifier]], any(), any(), any())
+      .process(any[Set[CapabilityIdentifier]], any(), any())
 
     when(mockCoreEchoMethod.methodName).thenReturn(MethodName("Core/echo"))
     when(mockCoreEchoMethod.requiredCapabilities).thenReturn(Capabilities(CORE_CAPABILITY))


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


[james-project] 07/08: JAMES-3387 validate accountId for vacationResponse/get

Posted by rc...@apache.org.
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 c532fadb010178d14ad69de846575c571e29a8fc
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Thu Sep 24 15:01:30 2020 +0200

    JAMES-3387 validate accountId for vacationResponse/get
---
 .../VacationResponseGetMethodContract.scala        | 41 ++++++++++++++++
 .../james/jmap/mail/VacationResponseGet.scala      |  3 +-
 .../jmap/method/VacationResponseGetMethod.scala    | 57 ++++++++++------------
 3 files changed, 70 insertions(+), 31 deletions(-)

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/VacationResponseGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/VacationResponseGetMethodContract.scala
index 268b840..1ca2caf 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/VacationResponseGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/VacationResponseGetMethodContract.scala
@@ -62,6 +62,47 @@ trait VacationResponseGetMethodContract {
   }
 
   @Test
+  def vacationResponseGetShouldFailWhenWrongAccountId(server: GuiceJamesServer): Unit = {
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail",
+         |    "urn:ietf:params:jmap:vacationresponse"],
+         |  "methodCalls": [[
+         |    "VacationResponse/get",
+         |    {
+         |      "accountId": "unknownAccountId",
+         |      "ids": null
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    val response =  `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      """{
+        |  "sessionState": "75128aab4b1b",
+        |  "methodResponses": [
+        |    ["error", {
+        |      "type": "accountNotFound"
+        |    }, "c1"]
+        |  ]
+        |}""".stripMargin)
+  }
+
+  @Test
   @Tag(CategoryTags.BASIC_FEATURE)
   def vacationResponseShouldBeDisabledByDefault(): Unit = {
     val response = `given`
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponseGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponseGet.scala
index 4329c84..5509e18 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponseGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponseGet.scala
@@ -20,6 +20,7 @@
 package org.apache.james.jmap.mail
 
 import org.apache.james.jmap.mail.VacationResponse.UnparsedVacationResponseId
+import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.jmap.model.State.State
 import org.apache.james.jmap.model.{AccountId, Properties}
 
@@ -27,7 +28,7 @@ case class VacationResponseIds(value: List[UnparsedVacationResponseId])
 
 case class VacationResponseGetRequest(accountId: AccountId,
                                       ids: Option[VacationResponseIds],
-                                      properties: Option[Properties])
+                                      properties: Option[Properties]) extends WithAccountId
 
 case class VacationResponseNotFound(value: Set[UnparsedVacationResponseId]) {
   def merge(other: VacationResponseNotFound): VacationResponseNotFound = VacationResponseNotFound(this.value ++ other.value)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
index a9a3a7f..2978afd 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
@@ -34,7 +34,6 @@ import org.apache.james.jmap.model.{AccountId, Capabilities, ErrorCode, Invocati
 import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
-import org.reactivestreams.Publisher
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
 import reactor.core.scala.publisher.{SFlux, SMono}
 
@@ -58,40 +57,38 @@ case class VacationResponseGetResult(vacationResponses: Set[VacationResponse], n
       notFound = notFound)
 }
 
-class VacationResponseGetMethod @Inject() (vacationRepository: VacationRepository,
-                                           metricFactory: MetricFactory) extends Method {
+class VacationResponseGetMethod @Inject()(vacationRepository: VacationRepository,
+                                          val metricFactory: MetricFactory,
+                                          val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[VacationResponseGetRequest] {
   override val methodName: MethodName = MethodName("VacationResponse/get")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY, VACATION_RESPONSE_CAPABILITY)
 
-  override def process(capabilities: Set[CapabilityIdentifier],
-                       invocation: InvocationWithContext,
-                       mailboxSession: MailboxSession): Publisher[InvocationWithContext] = {
-    metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-      asVacationResponseGetRequest(invocation.invocation.arguments)
-        .fold(e => handleRequestValidationErrors(e, invocation.invocation.methodCallId),
-          vacationResponseGetRequest => {
-            val requestedProperties: Properties = vacationResponseGetRequest.properties.getOrElse(VacationResponse.allProperties)
-            requestedProperties -- VacationResponse.allProperties match {
-              case invalidProperties if invalidProperties.isEmpty() => getVacationResponse(vacationResponseGetRequest, invocation.processingContext, mailboxSession)
-                .reduce(VacationResponseGetResult.empty, VacationResponseGetResult.merge)
-                .map(vacationResult => vacationResult.asResponse(vacationResponseGetRequest.accountId))
-                .map(vacationResponseGetResponse => Invocation(
-                  methodName = methodName,
-                  arguments = Arguments(VacationSerializer.serialize(vacationResponseGetResponse, requestedProperties).as[JsObject]),
-                  methodCallId = invocation.invocation.methodCallId))
-              case invalidProperties: Properties =>
-                SMono.just(Invocation.error(errorCode = ErrorCode.InvalidArguments,
-                  description = s"The following properties [${invalidProperties.format}] do not exist.",
-                  methodCallId = invocation.invocation.methodCallId))
-            }
-          })
-          .map(InvocationWithContext(_, invocation.processingContext)))
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: VacationResponseGetRequest): SMono[InvocationWithContext] = {
+    {
+      val requestedProperties: Properties = request.properties.getOrElse(VacationResponse.allProperties)
+      (requestedProperties -- VacationResponse.allProperties match {
+        case invalidProperties if invalidProperties.isEmpty() => getVacationResponse(request, invocation.processingContext, mailboxSession)
+          .reduce(VacationResponseGetResult.empty, VacationResponseGetResult.merge)
+          .map(vacationResult => vacationResult.asResponse(request.accountId))
+          .map(vacationResponseGetResponse => Invocation(
+            methodName = methodName,
+            arguments = Arguments(VacationSerializer.serialize(vacationResponseGetResponse, requestedProperties).as[JsObject]),
+            methodCallId = invocation.invocation.methodCallId))
+        case invalidProperties: Properties =>
+          SMono.just(Invocation.error(errorCode = ErrorCode.InvalidArguments,
+            description = s"The following properties [${invalidProperties.format}] do not exist.",
+            methodCallId = invocation.invocation.methodCallId))
+      }).map(InvocationWithContext(_, invocation.processingContext))
+        .onErrorResume{ case e: Exception => handleRequestValidationErrors(e, invocation.invocation.methodCallId)
+          .map(errorInvocation => InvocationWithContext(errorInvocation,  invocation.processingContext))}
+    }
   }
 
-  private def asVacationResponseGetRequest(arguments: Arguments): Either[IllegalArgumentException, VacationResponseGetRequest] =
-    VacationSerializer.deserializeVacationResponseGetRequest(arguments.value) match {
-      case JsSuccess(vacationResponseGetRequest, _) => Right(vacationResponseGetRequest)
-      case errors: JsError => Left(new IllegalArgumentException(ResponseSerializer.serialize(errors).toString))
+  override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[VacationResponseGetRequest] = asVacationResponseGetRequest(invocation.arguments)
+
+  private def asVacationResponseGetRequest(arguments: Arguments): SMono[VacationResponseGetRequest] = VacationSerializer.deserializeVacationResponseGetRequest(arguments.value) match {
+      case JsSuccess(vacationResponseGetRequest, _) => SMono.just(vacationResponseGetRequest)
+      case errors: JsError => SMono.raiseError(new IllegalArgumentException(ResponseSerializer.serialize(errors).toString))
     }
 
   private def handleRequestValidationErrors(exception: Exception, methodCallId: MethodCallId): SMono[Invocation] = exception match {


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


[james-project] 06/08: JAMES-3387 validate accountId form mailbox/query

Posted by rc...@apache.org.
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 506563908bd4c62b4468065455278efd7b13ca0f
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Thu Sep 24 14:50:45 2020 +0200

    JAMES-3387 validate accountId form mailbox/query
---
 .../contract/MailboxQueryMethodContract.scala      | 40 ++++++++++++++++++++++
 .../org/apache/james/jmap/mail/MailboxQuery.scala  |  3 +-
 .../james/jmap/method/MailboxQueryMethod.scala     | 33 +++++++++---------
 3 files changed, 58 insertions(+), 18 deletions(-)

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/MailboxQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
index fe714be..76a6ec2 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxQueryMethodContract.scala
@@ -48,6 +48,46 @@ trait MailboxQueryMethodContract {
   }
 
   @Test
+  def mailboxQueryShouldFailWhenWrongAccountId(server: GuiceJamesServer): Unit = {
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Mailbox/query",
+         |    {
+         |      "accountId": "unknownAccountId",
+         |      "filter": {"role":"Inbox"}
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    val response =  `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      """{
+        |  "sessionState": "75128aab4b1b",
+        |  "methodResponses": [
+        |    ["error", {
+        |      "type": "accountNotFound"
+        |    }, "c1"]
+        |  ]
+        |}""".stripMargin)
+  }
+
+  @Test
   def roleShouldAllowToRetrieveTheInbox(server: GuiceJamesServer): Unit = {
     val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl])
       .createMailbox(MailboxPath.forUser(BOB, "INBOX"))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxQuery.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxQuery.scala
index 6d74b49..00b44e9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxQuery.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxQuery.scala
@@ -19,13 +19,14 @@
 
 package org.apache.james.jmap.mail
 
+import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.jmap.model.Limit.Limit
 import org.apache.james.jmap.model.Position.Position
 import org.apache.james.jmap.model.{AccountId, CanCalculateChanges, Position, QueryState}
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.model.MailboxId
 
-case class MailboxQueryRequest(accountId: AccountId, filter: MailboxFilter)
+case class MailboxQueryRequest(accountId: AccountId, filter: MailboxFilter) extends WithAccountId
 
 case class MailboxFilter(role: Role)
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
index ea0200b..8070ab7 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxQueryMethod.scala
@@ -27,33 +27,32 @@ import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY}
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model._
-import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.{MailboxSession, SystemMailboxesProvider}
 import org.apache.james.metrics.api.MetricFactory
-import org.reactivestreams.Publisher
 import play.api.libs.json.{JsError, JsSuccess}
 import reactor.core.scala.publisher.{SFlux, SMono}
 import reactor.core.scheduler.Schedulers
 
 class MailboxQueryMethod @Inject()(systemMailboxesProvider: SystemMailboxesProvider,
-                                   metricFactory: MetricFactory,
-                                   val sessionSupplier: SessionSupplier) extends Method {
+                                   val metricFactory: MetricFactory,
+                                   val sessionSupplier: SessionSupplier) extends MethodRequiringAccountId[MailboxQueryRequest] {
   override val methodName = MethodName("Mailbox/query")
   override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY)
 
-  override def process(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): Publisher[InvocationWithContext] =
-    metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
-      asMailboxQueryRequest(invocation.invocation.arguments)
-        .flatMap(processRequest(mailboxSession, invocation.invocation, _))
-        .onErrorResume {
-          case e: IllegalArgumentException => SMono.just(
-            Invocation.error(
-              errorCode = ErrorCode.InvalidArguments,
-              description = e.getMessage,
-              methodCallId = invocation.invocation.methodCallId))
-          case e: Throwable => SMono.raiseError(e)
-        }
-        .map(invocationResult => InvocationWithContext(invocationResult, invocation.processingContext)))
+  override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxQueryRequest): SMono[InvocationWithContext] = {
+    processRequest(mailboxSession, invocation.invocation, request)
+      .onErrorResume {
+      case e: IllegalArgumentException => SMono.just(
+        Invocation.error(
+          errorCode = ErrorCode.InvalidArguments,
+          description = e.getMessage,
+          methodCallId = invocation.invocation.methodCallId))
+      case e: Throwable => SMono.raiseError(e)
+    }
+      .map(invocationResult => InvocationWithContext(invocationResult, invocation.processingContext))
+  }
+
+  override def getRequest(mailboxSession: MailboxSession, invocation: Invocation): SMono[MailboxQueryRequest] = asMailboxQueryRequest(invocation.arguments)
 
   private def processRequest(mailboxSession: MailboxSession, invocation: Invocation, request: MailboxQueryRequest): SMono[Invocation] = {
     SFlux.fromPublisher(systemMailboxesProvider.getMailboxByRole(request.filter.role, mailboxSession.getUser))


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