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

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

commit 6e58b8789ce700e6db70988202e6f851c298160a
Author: Benoit Tellier <[email protected]>
AuthorDate: Tue Aug 25 14:27:44 2020 +0700

    JAMES-3357 Mailbox/set create should reject unknown or server-set properties
---
 .../contract/MailboxSetMethodContract.scala        | 62 +++++++++++++++++++++-
 .../org/apache/james/jmap/mail/MailboxSet.scala    | 27 ++++++++++
 .../james/jmap/method/MailboxSetMethod.scala       |  9 ++--
 3 files changed, 92 insertions(+), 6 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 50434e1..8805072 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
@@ -657,7 +657,6 @@ trait MailboxSetMethodContract {
   }
 
   @Test
-  @Disabled("should we support that? Anyway seems hard with Play-JSON")
   def mailboxSetShouldReturnNotCreatedWhenUnknownParameter(): Unit = {
     val request =
       """
@@ -706,7 +705,66 @@ trait MailboxSetMethodContract {
          |      "notCreated": {
          |        "C42": {
          |          "type": "invalidArguments",
-         |          "description": "Unknown 'unknown' property in mailbox 
object"
+         |          "description": "Some unknown properties were specified",
+         |          "properties": ["unknown"]
+         |        }
+         |      }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin)
+  }
+
+  @Test
+  def mailboxSetShouldReturnNotCreatedWhenServerSetParameter(): Unit = {
+    val request =
+      """
+        |{
+        |   "using": [ "urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail" ],
+        |   "methodCalls": [
+        |       [
+        |           "Mailbox/set",
+        |           {
+        |                "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |                "create": {
+        |                    "C42": {
+        |                      "name": "plop",
+        |                      "id": "what?"
+        |                    }
+        |                }
+        |           },
+        |    "c1"
+        |       ]
+        |   ]
+        |}
+        |""".stripMargin
+
+    val response: String =
+      `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(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [[
+         |    "Mailbox/set",
+         |    {
+         |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "newState": "000001",
+         |      "notCreated": {
+         |        "C42": {
+         |          "type": "invalidArguments",
+         |          "description": "Some server-set properties were specified",
+         |          "properties": ["id"]
          |        }
          |      }
          |    },
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 73fc849..d1620aa 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
@@ -32,6 +32,7 @@ import org.apache.james.jmap.json.Serializer
 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}
+import org.apache.james.jmap.method.MailboxCreationParseException
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.State.State
 import org.apache.james.jmap.model.{AccountId, CapabilityIdentifier}
@@ -59,6 +60,32 @@ object MailboxSetRequest {
 }
 
 case class RemoveEmailsOnDestroy(value: Boolean) extends AnyVal
+
+object MailboxCreationRequest {
+  private val serverSetProperty = Set("id", "sortOrder", "role", 
"totalEmails", "totalThreads", "unreadEmails", "unreadThreads", "myRights")
+  private val assignableProperties = Set("name", "parentId", "isSubscribed", 
"rights")
+  private val knownProperties = assignableProperties ++ serverSetProperty
+
+  def validateProperties(jsObject: JsObject): 
Either[MailboxCreationParseException, JsObject] =
+    (jsObject.keys.intersect(serverSetProperty), 
jsObject.keys.diff(knownProperties)) match {
+      case (_, unknownProperties) if unknownProperties.nonEmpty =>
+        Left(MailboxCreationParseException(MailboxSetError.invalidArgument(
+          Some(SetErrorDescription("Some unknown properties were specified")),
+          Some(toProperties(unknownProperties.toList)))))
+      case (specifiedServerSetProperties, _) if 
specifiedServerSetProperties.nonEmpty =>
+        Left(MailboxCreationParseException(MailboxSetError.invalidArgument(
+          Some(SetErrorDescription("Some server-set properties were 
specified")),
+          Some(toProperties(specifiedServerSetProperties.toList)))))
+      case _ => scala.Right(jsObject)
+    }
+
+  private def toProperties(strings: List[String]): Properties = 
Properties(strings
+    .flatMap(string => {
+      val refinedValue: Either[String, NonEmptyString] = 
refineV[NonEmpty](string)
+      refinedValue.fold(_ => None,  Some(_))
+    }))
+}
+
 case class MailboxCreationRequest(name: MailboxName,
                                   parentId: Option[UnparsedMailboxId],
                                   isSubscribed: Option[IsSubscribed],
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 a412f8a..3f532d9 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
@@ -369,10 +369,11 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
   }
 
   private def parseCreate(jsObject: JsObject): 
Either[MailboxCreationParseException, MailboxCreationRequest] =
-    Json.fromJson(jsObject)(serializer.mailboxCreationRequest) match {
-      case JsSuccess(creationRequest, _) => Right(creationRequest)
-      case JsError(errors) => 
Left(MailboxCreationParseException(mailboxSetError(errors)))
-    }
+    MailboxCreationRequest.validateProperties(jsObject)
+      .flatMap(validJsObject => 
Json.fromJson(validJsObject)(serializer.mailboxCreationRequest) match {
+        case JsSuccess(creationRequest, _) => Right(creationRequest)
+        case JsError(errors) => 
Left(MailboxCreationParseException(mailboxSetError(errors)))
+      })
 
   private def mailboxSetError(errors: collection.Seq[(JsPath, 
collection.Seq[JsonValidationError])]): MailboxSetError =
     errors.head match {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to