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 d1912b97dd66c5e381d2d4160f439de14deb08c6 Author: Benoit Tellier <btell...@linagora.com> AuthorDate: Thu Oct 29 12:42:05 2020 +0700 JAMES-3432 Advertise max upload size in the session Note that methods only need to specify capability identifier (refactoring) --- .../rfc8621/contract/SessionRoutesContract.scala | 4 ++-- .../apache/james/jmap/http/SessionSupplier.scala | 8 ++++--- .../apache/james/jmap/method/CoreEchoMethod.scala | 6 ++--- .../apache/james/jmap/method/EmailGetMethod.scala | 16 +++++-------- .../james/jmap/method/EmailQueryMethod.scala | 7 +++--- .../apache/james/jmap/method/EmailSetMethod.scala | 7 +++--- .../james/jmap/method/MailboxGetMethod.scala | 7 +++--- .../james/jmap/method/MailboxQueryMethod.scala | 5 ++--- .../james/jmap/method/MailboxSetMethod.scala | 7 +++--- .../org/apache/james/jmap/method/Method.scala | 4 ++-- .../jmap/method/VacationResponseGetMethod.scala | 7 +++--- .../jmap/method/VacationResponseSetMethod.scala | 7 +++--- .../org/apache/james/jmap/model/Capabilities.scala | 14 ++++++------ .../org/apache/james/jmap/model/Capability.scala | 13 ++++++++++- .../jmap/model/JmapRfc8621Configuration.scala | 9 +++++--- .../apache/james/jmap/routes/JMAPApiRoutes.scala | 6 ++--- .../apache/james/jmap/routes/UploadRoutes.scala | 26 +++++++++++----------- .../apache/james/jmap/http/SessionRoutesTest.scala | 4 ++-- .../james/jmap/routes/JMAPApiRoutesTest.scala | 7 +++--- 19 files changed, 83 insertions(+), 81 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/SessionRoutesContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/SessionRoutesContract.scala index 7232f15..1355d78 100644 --- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/SessionRoutesContract.scala +++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/SessionRoutesContract.scala @@ -42,7 +42,7 @@ object SessionRoutesContract { private val expected_session_object: String = """{ | "capabilities" : { | "urn:ietf:params:jmap:core" : { - | "maxSizeUpload" : 10000000, + | "maxSizeUpload" : 31457280, | "maxConcurrentUpload" : 4, | "maxSizeRequest" : 10000000, | "maxConcurrentRequests" : 4, @@ -70,7 +70,7 @@ object SessionRoutesContract { | "isReadOnly" : false, | "accountCapabilities" : { | "urn:ietf:params:jmap:core" : { - | "maxSizeUpload" : 10000000, + | "maxSizeUpload" : 31457280, | "maxConcurrentUpload" : 4, | "maxSizeRequest" : 10000000, | "maxConcurrentRequests" : 4, diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala index 3e625b5..0aa6bbc 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala @@ -26,10 +26,12 @@ import org.apache.james.jmap.model._ import reactor.core.scala.publisher.SMono class SessionSupplier @Inject() (val configuration: JmapRfc8621Configuration){ + private val maxSizeUpload = configuration.maxUploadSize + def generate(username: Username): SMono[Session] = { accounts(username) .map(account => Session( - DefaultCapabilities.SUPPORTED, + DefaultCapabilities.supported(maxSizeUpload), List(account), primaryAccounts(account.accountId), username, @@ -40,13 +42,13 @@ class SessionSupplier @Inject() (val configuration: JmapRfc8621Configuration){ } private def accounts(username: Username): SMono[Account] = SMono.defer(() => - Account.from(username, IsPersonal(true), IsReadOnly(false), DefaultCapabilities.SUPPORTED.toSet) match { + Account.from(username, IsPersonal(true), IsReadOnly(false), DefaultCapabilities.supported(maxSizeUpload).toSet) match { case Left(ex: IllegalArgumentException) => SMono.raiseError(ex) case Right(account: Account) => SMono.just(account) }) private def primaryAccounts(accountId: AccountId): Map[CapabilityIdentifier, AccountId] = - DefaultCapabilities.SUPPORTED.toSet + DefaultCapabilities.supported(maxSizeUpload).toSet .map(capability => (capability.identifier(), accountId)) .toMap } 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 7bba42e..c93e05c 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,10 +20,8 @@ package org.apache.james.jmap.method import eu.timepit.refined.auto._ -import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier -import org.apache.james.jmap.model.DefaultCapabilities.CORE_CAPABILITY +import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE} import org.apache.james.jmap.model.Invocation.MethodName -import org.apache.james.jmap.model.{Capabilities} import org.apache.james.mailbox.MailboxSession import org.reactivestreams.Publisher import reactor.core.scala.publisher.SMono @@ -33,5 +31,5 @@ class CoreEchoMethod extends Method { override def process(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): Publisher[InvocationWithContext] = SMono.just(invocation) - override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY) + override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE) } \ 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 296b836..5201c5a 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 @@ -23,16 +23,14 @@ import java.time.ZoneId 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} -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.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL} 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.model.{AccountId, ErrorCode, Invocation, Properties} import org.apache.james.mailbox.MailboxSession import org.apache.james.mailbox.model.MessageId import org.apache.james.metrics.api.MetricFactory @@ -79,12 +77,10 @@ class SystemZoneIdProvider extends ZoneIdProvider { class EmailGetMethod @Inject() (readerFactory: EmailViewReaderFactory, messageIdFactory: MessageId.Factory, - zoneIdProvider: ZoneIdProvider, - previewFactory: Preview.Factory, 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 val methodName: MethodName = MethodName("Email/get") + override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL) override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: EmailGetRequest): SMono[InvocationWithContext] = { computeResponseInvocation(request, invocation.invocation, mailboxSession).onErrorResume({ @@ -100,7 +96,7 @@ class EmailGetMethod @Inject() (readerFactory: EmailViewReaderFactory, .flatMap(properties => validateBodyProperties(request).map((properties, _))) .fold( e => SMono.raiseError(e), { - case (properties, bodyProperties) => getEmails(request, properties, mailboxSession) + case (properties, bodyProperties) => getEmails(request, mailboxSession) .map(response => Invocation( methodName = methodName, arguments = Arguments(EmailGetSerializer.serialize(response, properties, bodyProperties).as[JsObject]), @@ -143,7 +139,7 @@ class EmailGetMethod @Inject() (readerFactory: EmailViewReaderFactory, case errors: JsError => SMono.raiseError(new IllegalArgumentException(ResponseSerializer.serialize(errors).toString)) } - private def getEmails(request: EmailGetRequest, properties: Properties, mailboxSession: MailboxSession): SMono[EmailGetResponse] = + private def getEmails(request: EmailGetRequest, mailboxSession: MailboxSession): SMono[EmailGetResponse] = request.ids match { case None => SMono.raiseError(new IllegalArgumentException("ids can not be ommited for email/get")) case Some(ids) => getEmails(ids, mailboxSession, request) 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 86005c1..06fa529 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 @@ -24,12 +24,11 @@ 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, 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.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL} 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, Invocation, Limit, Position, QueryState} +import org.apache.james.jmap.model.{CanCalculateChanges, 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.model.MultimailboxesSearchQuery @@ -45,7 +44,7 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer, 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 val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL) override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: EmailQueryRequest): SMono[InvocationWithContext] = { processRequest(mailboxSession, invocation.invocation, request, capabilities) diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala index 5cf9af6..8686751 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala @@ -30,12 +30,11 @@ import org.apache.james.jmap.http.SessionSupplier import org.apache.james.jmap.json.{EmailSetSerializer, ResponseSerializer} import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId} import org.apache.james.jmap.mail.{DestroyIds, EmailCreationRequest, EmailCreationResponse, EmailSet, EmailSetRequest, EmailSetResponse, MailboxIds, ValidatedEmailSetUpdate} -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.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL} import org.apache.james.jmap.model.Invocation.{Arguments, MethodName} import org.apache.james.jmap.model.KeywordsFactory.LENIENT_KEYWORDS_FACTORY import org.apache.james.jmap.model.SetError.SetErrorDescription -import org.apache.james.jmap.model.{Capabilities, ClientId, Id, Invocation, ServerId, SetError, State, UTCDate} +import org.apache.james.jmap.model.{ClientId, Id, Invocation, ServerId, SetError, State, UTCDate} import org.apache.james.mailbox.MessageManager.{AppendCommand, FlagsUpdateMode} import org.apache.james.mailbox.exception.MailboxNotFoundException import org.apache.james.mailbox.model.{ComposedMessageIdWithMetaData, DeleteResult, MailboxId, MessageId, MessageRange} @@ -149,7 +148,7 @@ class EmailSetMethod @Inject()(serializer: EmailSetSerializer, } override val methodName: MethodName = MethodName("Email/set") - override val requiredCapabilities: Capabilities = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY) + override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL) override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: EmailSetRequest): SMono[InvocationWithContext] = { for { 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 154da66..0b41593 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 @@ -25,11 +25,10 @@ import org.apache.james.jmap.http.SessionSupplier import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer} import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId import org.apache.james.jmap.mail._ -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.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL} 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, CapabilityIdentifier, ErrorCode, Invocation, MailboxFactory, Properties, Subscriptions} +import org.apache.james.jmap.model.{AccountId, CapabilityIdentifier, ErrorCode, Invocation, MailboxFactory, Properties, Subscriptions} import org.apache.james.jmap.utils.quotas.{QuotaLoader, QuotaLoaderWithPreloadedDefaultFactory} import org.apache.james.mailbox.exception.MailboxNotFoundException import org.apache.james.mailbox.model.search.MailboxQuery @@ -52,7 +51,7 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer, 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) + override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL) object MailboxGetResults { def merge(result1: MailboxGetResults, result2: MailboxGetResults): MailboxGetResults = result1.merge(result2) 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 8070ab7..d390d0f 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 @@ -23,8 +23,7 @@ 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 -import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY} +import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL} import org.apache.james.jmap.model.Invocation.{Arguments, MethodName} import org.apache.james.jmap.model._ import org.apache.james.mailbox.{MailboxSession, SystemMailboxesProvider} @@ -37,7 +36,7 @@ class MailboxQueryMethod @Inject()(systemMailboxesProvider: SystemMailboxesProvi 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 val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL) override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxQueryRequest): SMono[InvocationWithContext] = { processRequest(mailboxSession, invocation.invocation, request) 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 6a40581..ff59c84 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 @@ -26,11 +26,10 @@ import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer} import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxGet, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, RemoveEmailsOnDestroy, ServerSetPropertyException, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, UnsupportedPropertyUpdatedException, ValidatedMai [...] -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.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL} import org.apache.james.jmap.model.Invocation.{Arguments, MethodName} import org.apache.james.jmap.model.SetError.SetErrorDescription -import org.apache.james.jmap.model.{Capabilities, ClientId, Id, Invocation, Properties, ServerId, SetError, State} +import org.apache.james.jmap.model.{ClientId, Id, Invocation, Properties, ServerId, SetError, State} import org.apache.james.jmap.routes.ProcessingContext import org.apache.james.jmap.utils.quotas.QuotaLoaderWithPreloadedDefaultFactory import org.apache.james.mailbox.MailboxManager.RenameOption @@ -153,7 +152,7 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer, 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 val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL) override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxSetRequest): SMono[InvocationWithContext] = for { creationResultsWithUpdatedProcessingContext <- createMailboxes(mailboxSession, request, invocation.processingContext) 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 824e70b..c2e52d9 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 @@ -23,7 +23,7 @@ import org.apache.james.jmap.http.SessionSupplier import org.apache.james.jmap.mail.{UnsupportedFilterException, UnsupportedNestingException, 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} +import org.apache.james.jmap.model.{AccountId, ErrorCode, Invocation, Session} import org.apache.james.jmap.routes.ProcessingContext import org.apache.james.mailbox.MailboxSession import org.apache.james.mailbox.exception.MailboxNotFoundException @@ -38,7 +38,7 @@ trait Method { val methodName: MethodName - val requiredCapabilities: Capabilities + val requiredCapabilities: Set[CapabilityIdentifier] def process(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): Publisher[InvocationWithContext] } 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 ebbc939..05cbfdb 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 @@ -26,11 +26,10 @@ import org.apache.james.jmap.http.SessionSupplier import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer} import org.apache.james.jmap.mail.VacationResponse.{UNPARSED_SINGLETON, UnparsedVacationResponseId} import org.apache.james.jmap.mail.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseNotFound} -import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier -import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY, VACATION_RESPONSE_CAPABILITY} +import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE} import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName} import org.apache.james.jmap.model.State.INSTANCE -import org.apache.james.jmap.model.{AccountId, Capabilities, ErrorCode, Invocation, MissingCapabilityException, Properties} +import org.apache.james.jmap.model.{AccountId, ErrorCode, Invocation, MissingCapabilityException, Properties} import org.apache.james.mailbox.MailboxSession import org.apache.james.metrics.api.MetricFactory import play.api.libs.json.{JsError, JsObject, JsSuccess} @@ -60,7 +59,7 @@ 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 val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE) override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: VacationResponseGetRequest): SMono[InvocationWithContext] = { { 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 66e52e0..c43978b 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 @@ -25,11 +25,10 @@ import org.apache.james.jmap.api.vacation.{AccountId, VacationPatch, VacationRep 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 -import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY, VACATION_RESPONSE_CAPABILITY} +import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE} 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.model.{Invocation, State} import org.apache.james.jmap.vacation.{VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse} import org.apache.james.mailbox.MailboxSession import org.apache.james.metrics.api.MetricFactory @@ -74,7 +73,7 @@ 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 val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE) override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: VacationResponseSetRequest): SMono[InvocationWithContext] = { update(mailboxSession, request) diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capabilities.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capabilities.scala index 48f9954..fc96632 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capabilities.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capabilities.scala @@ -19,12 +19,12 @@ package org.apache.james.jmap.model import eu.timepit.refined.auto._ -import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier +import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE} object DefaultCapabilities { - val CORE_CAPABILITY = CoreCapability( + def coreCapability(maxUploadSize: MaxSizeUpload) = CoreCapability( properties = CoreCapabilityProperties( - MaxSizeUpload(10_000_000L), + maxUploadSize, MaxConcurrentUpload(4L), MaxSizeRequest(10_000_000L), MaxConcurrentRequests(4L), @@ -40,9 +40,7 @@ object DefaultCapabilities { MaxSizeMailboxName(200L), MaxSizeAttachmentsPerEmail(20_000_000L), emailQuerySortOptions = List("receivedAt", "sentAt"), - MayCreateTopLevelMailbox(true) - ) - ) + MayCreateTopLevelMailbox(true))) val QUOTA_CAPABILITY = QuotaCapability() @@ -50,7 +48,9 @@ object DefaultCapabilities { val VACATION_RESPONSE_CAPABILITY = VacationResponseCapability() - val SUPPORTED = Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY, QUOTA_CAPABILITY, SHARES_CAPABILITY, VACATION_RESPONSE_CAPABILITY) + val SUPPORTED_CAPABILITY_IDENTIFIERS = Set(JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE, JAMES_SHARES, JAMES_QUOTA) + + def supported(maxUploadSize: MaxSizeUpload) = Capabilities(coreCapability(maxUploadSize), MAIL_CAPABILITY, QUOTA_CAPABILITY, SHARES_CAPABILITY, VACATION_RESPONSE_CAPABILITY) } case class Capabilities(capabilities: Capability*) { diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala index fbabaf9..4f1202d 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala @@ -19,6 +19,7 @@ package org.apache.james.jmap.model +import eu.timepit.refined import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.collection.NonEmpty @@ -26,7 +27,10 @@ import eu.timepit.refined.string.Uri import org.apache.james.jmap.model.CapabilityIdentifier.{CapabilityIdentifier, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE} import org.apache.james.jmap.model.CoreCapabilityProperties.CollationAlgorithm import org.apache.james.jmap.model.MailCapability.EmailQuerySortOption -import org.apache.james.jmap.model.UnsignedInt.UnsignedInt +import org.apache.james.jmap.model.UnsignedInt.{UnsignedInt, UnsignedIntConstraint} +import org.apache.james.util.Size + +import scala.util.{Failure, Success, Try} sealed trait CapabilityValidationException extends IllegalArgumentException case class MissingCapabilityException(description: String) extends CapabilityValidationException @@ -50,6 +54,13 @@ trait Capability { final case class CoreCapability(properties: CoreCapabilityProperties, identifier: CapabilityIdentifier = JMAP_CORE) extends Capability +object MaxSizeUpload { + def of(size: Size): Try[MaxSizeUpload] = refined.refineV[UnsignedIntConstraint](size.asBytes()) match { + case Right(value) => Success(MaxSizeUpload(value)) + case Left(error) => Failure(new NumberFormatException(error)) + } +} + case class MaxSizeUpload(value: UnsignedInt) case class MaxConcurrentUpload(value: UnsignedInt) case class MaxSizeRequest(value: UnsignedInt) diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/JmapRfc8621Configuration.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/JmapRfc8621Configuration.scala index f54636b..994507e 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/JmapRfc8621Configuration.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/JmapRfc8621Configuration.scala @@ -27,7 +27,7 @@ import org.apache.james.util.Size object JmapRfc8621Configuration { val LOCALHOST_URL_PREFIX: String = "http://localhost" - val UPLOAD_LIMIT_30_MB: Size = Size.of(30, Size.Unit.M) + val UPLOAD_LIMIT_30_MB: MaxSizeUpload = MaxSizeUpload.of(Size.of(30L, Size.Unit.M)).get val LOCALHOST_CONFIGURATION: JmapRfc8621Configuration = JmapRfc8621Configuration(LOCALHOST_URL_PREFIX, UPLOAD_LIMIT_30_MB) val URL_PREFIX_PROPERTIES: String = "url.prefix" val UPLOAD_LIMIT_PROPERTIES: String = "upload.max.size" @@ -35,11 +35,14 @@ object JmapRfc8621Configuration { def from(configuration: Configuration): JmapRfc8621Configuration = { JmapRfc8621Configuration( urlPrefixString = Option(configuration.getString(URL_PREFIX_PROPERTIES)).getOrElse(LOCALHOST_URL_PREFIX), - maxUploadSize = Option(configuration.getString(UPLOAD_LIMIT_PROPERTIES, null)).map(Size.parse).getOrElse(UPLOAD_LIMIT_30_MB)) + maxUploadSize = Option(configuration.getString(UPLOAD_LIMIT_PROPERTIES, null)) + .map(Size.parse) + .map(MaxSizeUpload.of(_).get) + .getOrElse(UPLOAD_LIMIT_30_MB)) } } -case class JmapRfc8621Configuration(urlPrefixString: String, maxUploadSize: Size = UPLOAD_LIMIT_30_MB) { +case class JmapRfc8621Configuration(urlPrefixString: String, maxUploadSize: MaxSizeUpload = UPLOAD_LIMIT_30_MB) { val urlPrefix: URL = new URL(urlPrefixString) val apiUrl: URL = new URL(s"$urlPrefixString/jmap") val downloadUrl: URL = new URL(urlPrefixString + "/download/$accountId/$blobId/?type=$type&name=$name") 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 790cecb..d58912b 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 @@ -113,7 +113,7 @@ class JMAPApiRoutes (val authenticator: Authenticator, httpServerResponse: HttpServerResponse, mailboxSession: MailboxSession): SMono[Void] = { val processingContext: ProcessingContext = ProcessingContext(Map.empty, Map.empty) - val unsupportedCapabilities = requestObject.using.toSet -- DefaultCapabilities.SUPPORTED.ids + val unsupportedCapabilities = requestObject.using.toSet -- DefaultCapabilities.SUPPORTED_CAPABILITY_IDENTIFIERS val capabilities: Set[CapabilityIdentifier] = requestObject.using.toSet if (unsupportedCapabilities.nonEmpty) { @@ -168,8 +168,8 @@ class JMAPApiRoutes (val authenticator: Authenticator, .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 + private def validateCapabilities(capabilities: Set[CapabilityIdentifier], requiredCapabilities: Set[CapabilityIdentifier]): Either[MissingCapabilityException, Unit] = { + val missingCapabilities = requiredCapabilities -- capabilities if (missingCapabilities.nonEmpty) { Left(MissingCapabilityException(s"Missing capability(ies): ${missingCapabilities.mkString(", ")}")) } else { diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/UploadRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/UploadRoutes.scala index 452543c..d8aa79c 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/UploadRoutes.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/UploadRoutes.scala @@ -24,32 +24,32 @@ import java.util.stream import java.util.stream.Stream import eu.timepit.refined.api.Refined +import eu.timepit.refined.auto._ +import eu.timepit.refined.numeric.NonNegative +import eu.timepit.refined.refineV import io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE -import io.netty.handler.codec.http.HttpResponseStatus.{BAD_REQUEST, CREATED} import io.netty.handler.codec.http.HttpMethod +import io.netty.handler.codec.http.HttpResponseStatus.{BAD_REQUEST, CREATED} import javax.inject.{Inject, Named} -import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes} +import org.apache.commons.fileupload.util.LimitedInputStream +import org.apache.james.jmap.exceptions.UnauthorizedException import org.apache.james.jmap.http.Authenticator import org.apache.james.jmap.http.rfc8621.InjectionKeys +import org.apache.james.jmap.json.UploadSerializer +import org.apache.james.jmap.mail.BlobId import org.apache.james.jmap.mail.Email.Size +import org.apache.james.jmap.model.Id.Id +import org.apache.james.jmap.model.{AccountId, Id, JmapRfc8621Configuration} import org.apache.james.jmap.routes.UploadRoutes.{LOGGER, sanitizeSize} -import org.apache.james.mailbox.{AttachmentManager, MailboxSession} +import org.apache.james.jmap.{Endpoint, JMAPRoute, JMAPRoutes} import org.apache.james.mailbox.model.{AttachmentMetadata, ContentType} +import org.apache.james.mailbox.{AttachmentManager, MailboxSession} import org.apache.james.util.ReactorUtils import org.slf4j.{Logger, LoggerFactory} import reactor.core.publisher.Mono import reactor.core.scala.publisher.SMono import reactor.core.scheduler.Schedulers import reactor.netty.http.server.{HttpServerRequest, HttpServerResponse} -import eu.timepit.refined.auto._ -import eu.timepit.refined.numeric.NonNegative -import eu.timepit.refined.refineV -import org.apache.commons.fileupload.util.LimitedInputStream -import org.apache.james.jmap.exceptions.UnauthorizedException -import org.apache.james.jmap.json.UploadSerializer -import org.apache.james.jmap.mail.BlobId -import org.apache.james.jmap.model.{AccountId, Id, JmapRfc8621Configuration} -import org.apache.james.jmap.model.Id.Id object UploadRoutes { val LOGGER: Logger = LoggerFactory.getLogger(classOf[DownloadRoutes]) @@ -131,7 +131,7 @@ class UploadRoutes @Inject()(@Named(InjectionKeys.RFC_8621) val authenticator: A } def handle(accountId: AccountId, contentType: ContentType, content: InputStream, mailboxSession: MailboxSession, response: HttpServerResponse): SMono[Void] = { - val maxSize: Long = configuration.maxUploadSize.asBytes() + val maxSize: Long = configuration.maxUploadSize.value.value SMono.fromCallable(() => new LimitedInputStream(content, maxSize) { override def raiseError(max: Long, count: Long): Unit = if (count > max) { diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala index a1a9e15..46c1dca 100644 --- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala +++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala @@ -118,7 +118,7 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers { val expectedJson = s"""{ | "capabilities" : { | "urn:ietf:params:jmap:core" : { - | "maxSizeUpload" : 10000000, + | "maxSizeUpload" : 31457280, | "maxConcurrentUpload" : 4, | "maxSizeRequest" : 10000000, | "maxConcurrentRequests" : 4, @@ -146,7 +146,7 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers { | "isReadOnly" : false, | "accountCapabilities" : { | "urn:ietf:params:jmap:core" : { - | "maxSizeUpload" : 10000000, + | "maxSizeUpload" : 31457280, | "maxConcurrentUpload" : 4, | "maxSizeRequest" : 10000000, | "maxConcurrentRequests" : 4, 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 b9f23bc..379a71f 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 @@ -38,10 +38,9 @@ import org.apache.james.jmap.JMAPUrls.JMAP import org.apache.james.jmap._ 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.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE} import org.apache.james.jmap.model.Invocation.MethodName -import org.apache.james.jmap.model.{Capabilities, JmapRfc8621Configuration, RequestLevelErrorType} +import org.apache.james.jmap.model.{JmapRfc8621Configuration, RequestLevelErrorType} import org.apache.james.jmap.routes.JMAPApiRoutesTest._ import org.apache.james.mailbox.extension.PreDeletionHook import org.apache.james.mailbox.inmemory.{InMemoryMailboxManager, MemoryMailboxManagerProvider} @@ -440,7 +439,7 @@ class JMAPApiRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers { .process(any[Set[CapabilityIdentifier]], any(), any()) when(mockCoreEchoMethod.methodName).thenReturn(MethodName("Core/echo")) - when(mockCoreEchoMethod.requiredCapabilities).thenReturn(Capabilities(CORE_CAPABILITY)) + when(mockCoreEchoMethod.requiredCapabilities).thenReturn(Set(JMAP_CORE)) val methods: Set[Method] = Set(mockCoreEchoMethod) val apiRoute: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, userProvisionner, mailboxesProvisioner, methods, sessionSupplier) --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org