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]
