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 d014a4bc3259505e3c4cd6661b80d96e59278247
Author: Florent Azavant <fazav...@linagora.com>
AuthorDate: Thu Nov 7 14:28:26 2024 +0100

    [ISSUE-5314] only the `p` right can be granted to `anyone`
---
 .../contract/MailboxSetMethodContract.scala        | 118 +++++++++++++++++++++
 .../org/apache/james/jmap/mail/MailboxSet.scala    |  39 +++++--
 2 files changed, 147 insertions(+), 10 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 d93c732d31..52b6fb8b3d 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
@@ -6009,6 +6009,64 @@ trait MailboxSetMethodContract {
       .containsKey(MailboxACL.ANYONE_KEY)
   }
 
+  @Test
+  def partialRightsUpdateShouldNotGrantAnyoneRightOtherThanPost(server: 
GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(BOB, "mailbox")
+    val mailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+
+    val request =
+      s"""
+         |{
+         |   "using": [ "urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "update": {
+         |                    "${mailboxId.serialize}": {
+         |                      "sharedWith/anyone": ["r", "l"]
+         |                    }
+         |                }
+         |           },
+         |    "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)
+      .whenIgnoringPaths("methodResponses[0][1].newState", 
"methodResponses[0][1].oldState",
+        "methodResponses[1][1].state")
+      .isEqualTo(
+        s"""{
+           |  "sessionState": "${SESSION_STATE.value}",
+           |  "methodResponses": [
+           |    ["Mailbox/set", {
+           |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |      "notUpdated": {
+           |        "${mailboxId.serialize}": {
+           |          "type": "invalidPatch",
+           |          "description": "only the `Post` right can be granted to 
the identifier `anyone`"
+           |        }
+           |      }
+           |    }, "c1"]
+           |  ]
+           |}""".stripMargin)
+  }
+
   @Test
   def resetRightsUpdateShouldCorrectlyHandleAnyoneKeyword(server: 
GuiceJamesServer): Unit = {
     val path = MailboxPath.forUser(BOB, "mailbox")
@@ -6088,6 +6146,66 @@ trait MailboxSetMethodContract {
       .containsKey(MailboxACL.ANYONE_KEY)
   }
 
+  @Test
+  def resetRightsUpdateShouldNotGrantAnyoneRightOtherThanPost(server: 
GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(BOB, "mailbox")
+    val mailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+
+    val request =
+      s"""
+         |{
+         |   "using": [ "urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "update": {
+         |                    "${mailboxId.serialize}": {
+         |                      "sharedWith": {
+         |                        "anyone":["r", "l"]
+         |                      }
+         |                    }
+         |                }
+         |           },
+         |    "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)
+      .whenIgnoringPaths("methodResponses[0][1].newState", 
"methodResponses[0][1].oldState",
+        "methodResponses[1][1].state")
+      .isEqualTo(
+        s"""{
+           |  "sessionState": "${SESSION_STATE.value}",
+           |  "methodResponses": [
+           |    ["Mailbox/set", {
+           |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |      "notUpdated": {
+           |        "${mailboxId.serialize}": {
+           |          "type": "invalidPatch",
+           |          "description": "only the `Post` right can be granted to 
the identifier `anyone`"
+           |        }
+           |      }
+           |    }, "c1"]
+           |  ]
+           |}""".stripMargin)
+  }
+
   @Test
   def partialRightsUpdateShouldFailWhenInvalidRights(server: 
GuiceJamesServer): Unit = {
     val path = MailboxPath.forUser(BOB, "mailbox")
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 1071271491..ac05d4d91d 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
@@ -33,11 +33,13 @@ 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.method.{MailboxCreationParseException, 
SetRequest, WithAccountId}
-import org.apache.james.mailbox.model.MailboxACL.EntryKey
+import org.apache.james.mailbox.model.MailboxACL.{ANYONE_KEY, 
ANYONE_NEGATIVE_KEY, EntryKey}
 import org.apache.james.mailbox.model.{MailboxId, MailboxACL => JavaMailboxACL}
 import org.apache.james.mailbox.{MailboxSession, Role}
 import play.api.libs.json.{JsBoolean, JsError, JsNull, JsObject, JsString, 
JsSuccess, JsValue}
 
+import scala.collection.convert.ImplicitConversions.`collection asJava`
+
 case class MailboxSetRequest(accountId: AccountId,
                              ifInState: Option[UuidState],
                              create: Option[Map[MailboxCreationId, JsObject]],
@@ -265,11 +267,29 @@ object NameUpdate {
   }
 }
 
+object IsSubscribedUpdate {
+  def parse(newValue: JsValue): Either[PatchUpdateValidationException, Update] 
= newValue match {
+    case JsBoolean(value) => 
scala.Right(IsSubscribedUpdate(Some(IsSubscribed(value))))
+    case JsNull => scala.Right(IsSubscribedUpdate(None))
+    case _ => Left(InvalidUpdateException("isSubscribed", "Expecting a JSON 
boolean as an argument"))
+  }
+}
+
+object SharedWithAnyoneValidator {
+  def isValidPatch(entryKey: EntryKey, rights: Seq[Right]): Boolean = {
+    !((entryKey == ANYONE_KEY || entryKey == ANYONE_NEGATIVE_KEY) && 
rights.exists(_ != Right.Post))}
+
+  def areValidPatches(rightsMap: Map[EntryKey, Seq[Right]]): Boolean =
+    rightsMap.forall { case (entryKey, rightsSeq) => isValidPatch(entryKey, 
rightsSeq)}
+}
+
 object SharedWithResetUpdate {
   def parse(serializer: MailboxSerializer, capabilities: 
Set[CapabilityIdentifier])
            (newValue: JsValue): Either[PatchUpdateValidationException, Update] 
=
     if (capabilities.contains(CapabilityIdentifier.JAMES_SHARES)) {
       serializer.deserializeRights(input = newValue) match {
+        case JsSuccess(value, _) if 
!SharedWithAnyoneValidator.areValidPatches(value.rights) =>
+          Left(InvalidPatchException("only the `Post` right can be granted to 
the identifier `anyone`"))
         case JsSuccess(value, _) => scala.Right(SharedWithResetUpdate(value))
         case JsError(errors) => Left(InvalidUpdateException("sharedWith", 
s"Specified value do not match the expected JSON format: $errors"))
       }
@@ -278,25 +298,24 @@ object SharedWithResetUpdate {
     }
 }
 
-object IsSubscribedUpdate {
-  def parse(newValue: JsValue): Either[PatchUpdateValidationException, Update] 
= newValue match {
-    case JsBoolean(value) => 
scala.Right(IsSubscribedUpdate(Some(IsSubscribed(value))))
-    case JsNull => scala.Right(IsSubscribedUpdate(None))
-    case _ => Left(InvalidUpdateException("isSubscribed", "Expecting a JSON 
boolean as an argument"))
-  }
-}
-
 object SharedWithPartialUpdate {
   def parse(serializer: MailboxSerializer, capabilities: 
Set[CapabilityIdentifier])
            ( property: String, newValue: JsValue): 
Either[PatchUpdateValidationException, Update] =
     if (capabilities.contains(CapabilityIdentifier.JAMES_SHARES)) {
       parseEntryKey(property)
         .flatMap(entryKey => parseRights(newValue, property, serializer)
-          .map(rights => SharedWithPartialUpdate(entryKey, rights)))
+        .flatMap(rights => createUpdateIfValidPatch(entryKey, rights)))
     } else {
       MailboxPatchObject.notFound(property)
     }
 
+  private def createUpdateIfValidPatch(entryKey: EntryKey, rights: 
Rfc4314Rights): Either[PatchUpdateValidationException, Update] =
+    if (SharedWithAnyoneValidator.isValidPatch(entryKey, rights.toRights)) {
+      scala.Right(SharedWithPartialUpdate(entryKey, rights))
+    } else {
+      scala.Left(InvalidPatchException("only the `Post` right can be granted 
to the identifier `anyone`"))
+    }
+
   def parseEntryKey(property: String): Either[PatchUpdateValidationException, 
EntryKey] = try {
     
scala.Right(EntryKey.deserialize(property.substring(MailboxPatchObject.sharedWithPrefix.length)))
   } catch {


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

Reply via email to