This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push: new fcc8181eb4 [JMAP] - Fix - The `Mailbox/set` method for shared mailboxes must require share capability (#2647) fcc8181eb4 is described below commit fcc8181eb4f4d441f7f798cefc46c373aedf3a8c Author: vttran <vtt...@linagora.com> AuthorDate: Mon Feb 24 20:04:54 2025 +0700 [JMAP] - Fix - The `Mailbox/set` method for shared mailboxes must require share capability (#2647) --- .../contract/MailboxSetMethodContract.scala | 18 ++-- .../jmap/method/MailboxSetCreatePerformer.scala | 36 ++++---- .../jmap/method/MailboxSetDeletePerformer.scala | 17 ++-- .../james/jmap/method/MailboxSetMethod.scala | 15 +++- .../jmap/method/MailboxSetUpdatePerformer.scala | 95 ++++++++++++---------- 5 files changed, 104 insertions(+), 77 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 2e92c93fd1..735e509507 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 @@ -2122,7 +2122,7 @@ trait MailboxSetMethodContract { val request = s""" |{ - | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ], | "methodCalls": [ | [ | "Mailbox/set", @@ -2185,7 +2185,7 @@ trait MailboxSetMethodContract { val request = s""" |{ - | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ], | "methodCalls": [ | [ | "Mailbox/set", @@ -2643,7 +2643,7 @@ trait MailboxSetMethodContract { val request = s""" |{ - | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ], | "methodCalls": [ | [ | "Mailbox/set", @@ -2691,7 +2691,7 @@ trait MailboxSetMethodContract { } @Test - def deleteShouldSuccessWhenHasRight(server: GuiceJamesServer): Unit = { + def deleteSharedMailboxShouldSuccessWhenHasRight(server: GuiceJamesServer): Unit = { val path = MailboxPath.forUser(ANDRE, "mailbox") val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path) server.getProbe(classOf[ACLProbeImpl]) @@ -2700,7 +2700,7 @@ trait MailboxSetMethodContract { val request = s""" |{ - | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ], | "methodCalls": [ | [ | "Mailbox/set", @@ -3791,7 +3791,7 @@ trait MailboxSetMethodContract { val request = s""" |{ - | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ], | "methodCalls": [ | ["Mailbox/set", | { @@ -8121,7 +8121,7 @@ trait MailboxSetMethodContract { val request = s""" |{ - | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ], | "methodCalls": [ | [ | "Mailbox/set", @@ -8184,7 +8184,7 @@ trait MailboxSetMethodContract { val request = s""" |{ - | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ], | "methodCalls": [ | [ | "Mailbox/set", @@ -8247,7 +8247,7 @@ trait MailboxSetMethodContract { val request = s""" |{ - | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ], | "methodCalls": [ | [ | "Mailbox/set", diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala index a44db74a71..b12554d2a5 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala @@ -93,30 +93,29 @@ class MailboxSetCreatePerformer @Inject()(serializer: MailboxSerializer, val metricFactory: MetricFactory, val sessionSupplier: SessionSupplier) { - - def createMailboxes(mailboxSession: MailboxSession, - mailboxSetRequest: MailboxSetRequest, - processingContext: ProcessingContext): SMono[(MailboxCreationResults, ProcessingContext)] = { + mailboxSetRequest: MailboxSetRequest, + processingContext: ProcessingContext, + supportSharedMailbox: Boolean): SMono[(MailboxCreationResults, ProcessingContext)] = SFlux.fromIterable(mailboxSetRequest.create - .getOrElse(Map.empty) - .view) - .fold((MailboxCreationResults(Nil), processingContext)){ - (acc : (MailboxCreationResults, ProcessingContext), elem: (MailboxCreationId, JsObject)) => { + .getOrElse(Map.empty) + .view) + .fold((MailboxCreationResults(Nil), processingContext)) { + (acc: (MailboxCreationResults, ProcessingContext), elem: (MailboxCreationId, JsObject)) => { val (mailboxCreationId, jsObject) = elem - val (creationResult, updatedProcessingContext) = createMailbox(mailboxSession, mailboxCreationId, jsObject, acc._2) + val (creationResult, updatedProcessingContext) = createMailbox(mailboxSession, mailboxCreationId, jsObject, acc._2, supportSharedMailbox) (MailboxCreationResults(acc._1.created :+ creationResult), updatedProcessingContext) } } .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER) - } private def createMailbox(mailboxSession: MailboxSession, mailboxCreationId: MailboxCreationId, jsObject: JsObject, - processingContext: ProcessingContext): (MailboxCreationResult, ProcessingContext) = { + processingContext: ProcessingContext, + supportSharedMailbox: Boolean): (MailboxCreationResult, ProcessingContext) = { parseCreate(jsObject) - .flatMap(mailboxCreationRequest => resolvePath(mailboxSession, mailboxCreationRequest) + .flatMap(mailboxCreationRequest => resolvePath(mailboxSession, mailboxCreationRequest, supportSharedMailbox) .flatMap(path => createMailbox(mailboxSession = mailboxSession, path = path, mailboxCreationRequest = mailboxCreationRequest))) @@ -136,7 +135,8 @@ class MailboxSetCreatePerformer @Inject()(serializer: MailboxSerializer, }) private def resolvePath(mailboxSession: MailboxSession, - mailboxCreationRequest: MailboxCreationRequest): Either[Exception, MailboxPath] = { + mailboxCreationRequest: MailboxCreationRequest, + supportSharedMailbox: Boolean): Either[Exception, MailboxPath] = { if (mailboxCreationRequest.name.value.contains(mailboxSession.getPathDelimiter)) { return Left(new MailboxNameException(s"The mailbox '${mailboxCreationRequest.name.value}' contains an illegal character: '${mailboxSession.getPathDelimiter}'")) } @@ -146,7 +146,8 @@ class MailboxSetCreatePerformer @Inject()(serializer: MailboxSerializer, .toEither .left .map(e => new IllegalArgumentException(e.getMessage, e)) - parentPath <- retrievePath(parentId, mailboxSession) + parentPathPreAssert <- retrievePath(parentId, mailboxSession) + parentPath <- validateCapabilityIfSharedMailbox(mailboxSession, parentId, supportSharedMailbox).apply(parentPathPreAssert) } yield { parentPath.child(mailboxCreationRequest.name, mailboxSession.getPathDelimiter) }) @@ -159,6 +160,13 @@ class MailboxSetCreatePerformer @Inject()(serializer: MailboxSerializer, case e: Exception => Left(e) } + private def validateCapabilityIfSharedMailbox(mailboxSession: MailboxSession, id: MailboxId, supportSharedMailbox: Boolean): MailboxPath => Either[Exception, MailboxPath] = + mailboxPath => if (!mailboxPath.belongsTo(mailboxSession) && !supportSharedMailbox) { + Left(new MailboxNotFoundException(id)) + } else { + Right(mailboxPath) + } + private def recordCreationIdInProcessingContext(mailboxCreationId: MailboxCreationId, processingContext: ProcessingContext, mailboxId: MailboxId): Either[IllegalArgumentException, ProcessingContext] = diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala index acf75a7b90..692fae435f 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala @@ -26,6 +26,7 @@ import org.apache.james.jmap.core.SetError import org.apache.james.jmap.core.SetError.SetErrorDescription import org.apache.james.jmap.mail.{MailboxGet, MailboxSetError, MailboxSetRequest, RemoveEmailsOnDestroy, UnparsedMailboxId} import org.apache.james.jmap.method.MailboxSetDeletePerformer.{MailboxDeletionFailure, MailboxDeletionResult, MailboxDeletionResults, MailboxDeletionSuccess} +import org.apache.james.jmap.method.MailboxSetMethod.assertCapabilityIfSharedMailbox import org.apache.james.mailbox.exception.{InsufficientRightsException, MailboxNotFoundException} import org.apache.james.mailbox.model.{FetchGroup, MailboxId, MessageRange} import org.apache.james.mailbox.{MailboxManager, MailboxSession, MessageManager, Role, SubscriptionManager} @@ -85,24 +86,25 @@ class MailboxSetDeletePerformer @Inject()(mailboxManager: MailboxManager, subscriptionManager: SubscriptionManager, mailboxIdFactory: MailboxId.Factory) { - def deleteMailboxes(mailboxSession: MailboxSession, mailboxSetRequest: MailboxSetRequest): SMono[MailboxDeletionResults] = + def deleteMailboxes(mailboxSession: MailboxSession, mailboxSetRequest: MailboxSetRequest, supportSharedMailbox: Boolean): SMono[MailboxDeletionResults] = SFlux.fromIterable(mailboxSetRequest.destroy.getOrElse(Seq()).toSet) - .flatMap(id => delete(mailboxSession, id, mailboxSetRequest.onDestroyRemoveEmails.getOrElse(RemoveEmailsOnDestroy(false))) + .flatMap(id => delete(mailboxSession, id, mailboxSetRequest.onDestroyRemoveEmails.getOrElse(RemoveEmailsOnDestroy(false)), supportSharedMailbox) .onErrorRecover(e => MailboxDeletionFailure(id, e)), maxConcurrency = 5) .collectSeq() .map(MailboxDeletionResults) .doOnSuccess(auditTrail(mailboxSession, _)) - private def delete(mailboxSession: MailboxSession, id: UnparsedMailboxId, onDestroy: RemoveEmailsOnDestroy): SMono[MailboxDeletionResult] = + private def delete(mailboxSession: MailboxSession, id: UnparsedMailboxId, onDestroy: RemoveEmailsOnDestroy, supportSharedMailbox: Boolean): SMono[MailboxDeletionResult] = MailboxGet.parse(mailboxIdFactory)(id) .fold(e => SMono.error(e), - id => doDelete(mailboxSession, id, onDestroy) + id => doDelete(mailboxSession, id, onDestroy, supportSharedMailbox) .`then`(SMono.just[MailboxDeletionResult](MailboxDeletionSuccess(id)))) - private def doDelete(mailboxSession: MailboxSession, id: MailboxId, onDestroy: RemoveEmailsOnDestroy): SMono[Unit] = + private def doDelete(mailboxSession: MailboxSession, id: MailboxId, onDestroy: RemoveEmailsOnDestroy, supportSharedMailbox: Boolean): SMono[Unit] = SMono(mailboxManager.getMailboxReactive(id, mailboxSession)) - .flatMap((mailbox: MessageManager) => + .filterWhen(assertCapabilityIfSharedMailbox(mailboxSession, id, supportSharedMailbox)) + .flatMap((mailbox: MessageManager) => { SMono(mailboxManager.hasChildrenReactive(mailbox.getMailboxPath, mailboxSession)) .flatMap(hasChildren => { if (isASystemMailbox(mailbox)) { @@ -128,7 +130,8 @@ class MailboxSetDeletePerformer @Inject()(mailboxManager: MailboxManager, .flatMap(deletedMailbox => SMono(subscriptionManager.unsubscribeReactive(deletedMailbox.generateAssociatedPath(), mailboxSession))) .`then`()) } - })) + }) + }) private def auditTrail(mailboxSession: MailboxSession, mailboxDeletionResults: MailboxDeletionResults): Unit = { if (mailboxDeletionResults.destroyed.nonEmpty) { 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 cb5d1884c7..fea82b2674 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 @@ -32,8 +32,9 @@ import org.apache.james.jmap.method.MailboxSetCreatePerformer.MailboxCreationRes import org.apache.james.jmap.method.MailboxSetDeletePerformer.MailboxDeletionResults import org.apache.james.jmap.method.MailboxSetUpdatePerformer.MailboxUpdateResults import org.apache.james.jmap.routes.SessionSupplier -import org.apache.james.mailbox.MailboxSession +import org.apache.james.mailbox.exception.MailboxNotFoundException import org.apache.james.mailbox.model.MailboxId +import org.apache.james.mailbox.{MailboxSession, MessageManager} import org.apache.james.metrics.api.MetricFactory import play.api.libs.json.JsObject import reactor.core.scala.publisher.SMono @@ -44,6 +45,13 @@ case class LoopInMailboxGraphException(mailboxId: MailboxId) extends Exception case class MailboxHasChildException(mailboxId: MailboxId) extends Exception case class MailboxCreationParseException(setError: SetError) extends Exception +object MailboxSetMethod { + def assertCapabilityIfSharedMailbox(mailboxSession: MailboxSession, id: MailboxId, supportSharedMailbox: Boolean): MessageManager => SMono[Boolean] = + mailbox => if (!mailbox.getMailboxPath.belongsTo(mailboxSession) && !supportSharedMailbox) + SMono.error(new MailboxNotFoundException(id)) + else SMono.just(true) +} + class MailboxSetMethod @Inject()(serializer: MailboxSerializer, createPerformer: MailboxSetCreatePerformer, deletePerformer: MailboxSetDeletePerformer, @@ -58,9 +66,10 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer, override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxSetRequest): SMono[InvocationWithContext] = for { oldState <- retrieveState(capabilities, mailboxSession) - creationResults <- createPerformer.createMailboxes(mailboxSession, request, invocation.processingContext) + supportSharedMailbox = capabilities.contains(JAMES_SHARES) + creationResults <- createPerformer.createMailboxes(mailboxSession, request, invocation.processingContext, supportSharedMailbox) updateResults <- updatePerformer.updateMailboxes(mailboxSession, request, capabilities) - deletionResults <- deletePerformer.deleteMailboxes(mailboxSession, request) + deletionResults <- deletePerformer.deleteMailboxes(mailboxSession, request, supportSharedMailbox) newState <- retrieveState(capabilities, mailboxSession) response = createResponse(capabilities, invocation.invocation, request, creationResults._1, deletionResults, updateResults, oldState, newState) } yield InvocationWithContext(response, creationResults._2) diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala index ad9844c45e..a757ca9e62 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala @@ -22,7 +22,7 @@ package org.apache.james.jmap.method import com.google.common.collect.ImmutableMap import eu.timepit.refined.auto._ import jakarta.inject.Inject -import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier +import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JAMES_SHARES} import org.apache.james.jmap.core.SetError.SetErrorDescription import org.apache.james.jmap.core.{Properties, SetError} import org.apache.james.jmap.json.MailboxSerializer @@ -114,8 +114,8 @@ class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer, mailboxIdFactory: MailboxId.Factory) { def updateMailboxes(mailboxSession: MailboxSession, - mailboxSetRequest: MailboxSetRequest, - capabilities: Set[CapabilityIdentifier]): SMono[MailboxUpdateResults] = { + mailboxSetRequest: MailboxSetRequest, + capabilities: Set[CapabilityIdentifier]): SMono[MailboxUpdateResults] = SFlux.fromIterable(mailboxSetRequest.update.getOrElse(Seq())) .flatMap({ case (unparsedMailboxId: UnparsedMailboxId, patch: MailboxPatchObject) => @@ -127,7 +127,6 @@ class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer, }, maxConcurrency = 5) .collectSeq() .map(MailboxUpdateResults) - } private def updateMailbox(mailboxSession: MailboxSession, mailboxId: MailboxId, @@ -135,23 +134,25 @@ class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer, patch: MailboxPatchObject, capabilities: Set[CapabilityIdentifier]): SMono[MailboxUpdateResult] = { patch.validate(mailboxIdFactory, serializer, capabilities, mailboxSession) - .fold(e => SMono.error(e), validatedPatch => - updateMailboxRights(mailboxId, validatedPatch, mailboxSession) - .`then`(updateSubscription(mailboxId, validatedPatch, mailboxSession)) - .`then`(updateMailboxPath(mailboxId, unparsedMailboxId, validatedPatch, mailboxSession))) + .fold(e => SMono.error(e), validatedPatch => { + val supportSharedMailbox: Boolean = capabilities.contains(JAMES_SHARES) + getMessageManager(supportSharedMailbox, mailboxId, mailboxSession) + .flatMap(messageManager => updateMailboxRights(mailboxId, validatedPatch, mailboxSession) + .`then`(updateSubscription(mailboxId, validatedPatch, mailboxSession, messageManager)) + .`then`(updateMailboxPath(mailboxId, unparsedMailboxId, validatedPatch, mailboxSession, messageManager, supportSharedMailbox))) + }) } - private def updateSubscription(mailboxId: MailboxId, validatedPatch: ValidatedMailboxPatchObject, mailboxSession: MailboxSession): SMono[MailboxUpdateResult] = { + private def updateSubscription(mailboxId: MailboxId, validatedPatch: ValidatedMailboxPatchObject, mailboxSession: MailboxSession, messageManager: MessageManager): SMono[MailboxUpdateResult] = { validatedPatch.isSubscribedUpdate.map(isSubscribedUpdate => { SMono.fromCallable(() => { - val mailbox = mailboxManager.getMailbox(mailboxId, mailboxSession) - val isOwner = mailbox.getMailboxPath.belongsTo(mailboxSession) + val isOwner = messageManager.getMailboxPath.belongsTo(mailboxSession) val shouldSubscribe = isSubscribedUpdate.isSubscribed.map(_.value).getOrElse(isOwner) if (shouldSubscribe) { - subscriptionManager.subscribe(mailboxSession, mailbox.getMailboxPath) + subscriptionManager.subscribe(mailboxSession, messageManager.getMailboxPath) } else { - subscriptionManager.unsubscribe(mailboxSession, mailbox.getMailboxPath) + subscriptionManager.unsubscribe(mailboxSession, messageManager.getMailboxPath) } }).`then`(SMono.just[MailboxUpdateResult](MailboxUpdateSuccess(mailboxId))) .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER) @@ -162,44 +163,44 @@ class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer, private def updateMailboxPath(mailboxId: MailboxId, unparsedMailboxId: UnparsedMailboxId, validatedPatch: ValidatedMailboxPatchObject, - mailboxSession: MailboxSession): SMono[MailboxUpdateResult] = { + mailboxSession: MailboxSession, + mailbox: MessageManager, + supportSharedMailbox: Boolean): SMono[MailboxUpdateResult] = { if (validatedPatch.shouldUpdateMailboxPath) { - SMono(mailboxManager.getMailboxReactive(mailboxId, mailboxSession)) - .flatMap(mailbox => - SMono.fromCallable[MailboxUpdateResult](() => { - try { - if (isASystemMailbox(mailbox) && !DefaultMailboxes.INBOX.equalsIgnoreCase(mailbox.getMailboxPath.getName)) { - throw SystemMailboxChangeException(mailboxId) - } - if (validatedPatch.parentIdUpdate.flatMap(_.newId).contains(mailboxId)) { - throw LoopInMailboxGraphException(mailboxId) - } - val oldPath = mailbox.getMailboxPath - val newPath = applyParentIdUpdate(mailboxId, validatedPatch.parentIdUpdate, mailboxSession) - .andThen(applyNameUpdate(validatedPatch.nameUpdate, mailboxSession)) - .apply(oldPath) - if (!oldPath.equals(newPath)) { - mailboxManager.renameMailbox(mailboxId, - newPath, - RenameOption.RENAME_SUBSCRIPTIONS, - mailboxSession) - } - MailboxUpdateSuccess(mailboxId) - } catch { - case e: Exception => MailboxUpdateFailure(unparsedMailboxId, e, Some(validatedPatch)) + SMono.fromCallable[MailboxUpdateResult](() => { + try { + if (isASystemMailbox(mailbox) && !DefaultMailboxes.INBOX.equalsIgnoreCase(mailbox.getMailboxPath.getName)) { + throw SystemMailboxChangeException(mailboxId) + } + if (validatedPatch.parentIdUpdate.flatMap(_.newId).contains(mailboxId)) { + throw LoopInMailboxGraphException(mailboxId) + } + val oldPath = mailbox.getMailboxPath + val newPath = applyParentIdUpdate(mailboxId, validatedPatch.parentIdUpdate, mailboxSession, supportSharedMailbox) + .andThen(applyNameUpdate(validatedPatch.nameUpdate, mailboxSession)) + .apply(oldPath) + if (!oldPath.equals(newPath)) { + mailboxManager.renameMailbox(mailboxId, + newPath, + RenameOption.RENAME_SUBSCRIPTIONS, + mailboxSession) } - }).subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER) - .flatMap(updateResult => createInboxIfNeeded(mailbox.getMailboxPath, mailboxSession) - .`then`(SMono.just(updateResult)))) + MailboxUpdateSuccess(mailboxId) + } catch { + case e: Exception => MailboxUpdateFailure(unparsedMailboxId, e, Some(validatedPatch)) + } + }).subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER) + .flatMap(updateResult => createInboxIfNeeded(mailbox.getMailboxPath, mailboxSession) + .`then`(SMono.just(updateResult))) } else { SMono.just[MailboxUpdateResult](MailboxUpdateSuccess(mailboxId)) } } - private def applyParentIdUpdate(mailboxId: MailboxId, maybeParentIdUpdate: Option[ParentIdUpdate], mailboxSession: MailboxSession): MailboxPath => MailboxPath = { - maybeParentIdUpdate.map(parentIdUpdate => applyParentIdUpdate(mailboxId, parentIdUpdate, mailboxSession)) + private def applyParentIdUpdate(mailboxId: MailboxId, maybeParentIdUpdate: Option[ParentIdUpdate], + mailboxSession: MailboxSession, supportSharedMailbox: Boolean): MailboxPath => MailboxPath = + maybeParentIdUpdate.map(parentIdUpdate => applyParentIdUpdate(mailboxId, parentIdUpdate, mailboxSession, supportSharedMailbox)) .getOrElse(x => x) - } private def applyNameUpdate(maybeNameUpdate: Option[NameUpdate], mailboxSession: MailboxSession): MailboxPath => MailboxPath = { originalPath => maybeNameUpdate.map(nameUpdate => { @@ -213,7 +214,8 @@ class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer, }).getOrElse(originalPath) } - private def applyParentIdUpdate(mailboxId: MailboxId, parentIdUpdate: ParentIdUpdate, mailboxSession: MailboxSession): MailboxPath => MailboxPath = { + private def applyParentIdUpdate(mailboxId: MailboxId, parentIdUpdate: ParentIdUpdate, + mailboxSession: MailboxSession, supportSharedMailbox: Boolean): MailboxPath => MailboxPath = { originalPath => { val currentName = originalPath.getName(mailboxSession.getPathDelimiter) parentIdUpdate.newId @@ -233,12 +235,17 @@ class MailboxSetUpdatePerformer @Inject()(serializer: MailboxSerializer, throw LoopInMailboxGraphException(mailboxId) } val parentPath = mailboxManager.getMailbox(id, mailboxSession).getMailboxPath + if (!parentPath.belongsTo(mailboxSession) && !supportSharedMailbox) throw new MailboxNotFoundException(id) parentPath.child(currentName, mailboxSession.getPathDelimiter) }) .getOrElse(MailboxPath.forUser(originalPath.getUser, currentName)) } } + private def getMessageManager(supportSharedMailbox: Boolean, mailboxId: MailboxId, mailboxSession: MailboxSession): SMono[MessageManager] = + SMono(mailboxManager.getMailboxReactive(mailboxId, mailboxSession)) + .filterWhen(MailboxSetMethod.assertCapabilityIfSharedMailbox(mailboxSession, mailboxId, supportSharedMailbox)) + private def updateMailboxRights(mailboxId: MailboxId, validatedPatch: ValidatedMailboxPatchObject, mailboxSession: MailboxSession): SMono[MailboxUpdateResult] = { --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org