This is an automated email from the ASF dual-hosted git repository. chibenwa pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 0d316940c8e2416130806b95e840bfd18eec9ab7 Author: Benoit TELLIER <[email protected]> AuthorDate: Wed Sep 2 15:29:32 2026 +0200 [ENHANCEMENT] JMAP: allow disabling webpush --- docs/modules/servers/partials/configure/jmap.adoc | 6 + .../sample-configuration/jmap.properties | 4 + .../sample-configuration/jmap.properties | 4 + .../sample-configuration/jmap.properties | 4 + .../james/modules/event/JMAPEventBusModule.java | 8 +- .../java/org/apache/james/jmap/JmapGuiceProbe.java | 6 + .../modules/protocols/JmapEventBusModule.java | 27 +++- .../rfc8621/contract/WebPushDisabledContract.scala | 144 +++++++++++++++++++++ ...ushTest.java => MemoryWebPushDisabledTest.java} | 21 +-- .../jmap/rfc8621/memory/MemoryWebPushTest.java | 11 ++ .../james/jmap/core/JmapRfc8621Configuration.scala | 3 + .../jmap/method/PushSubscriptionGetMethod.scala | 13 +- .../jmap/method/PushSubscriptionSetMethod.scala | 13 +- 13 files changed, 243 insertions(+), 21 deletions(-) diff --git a/docs/modules/servers/partials/configure/jmap.adoc b/docs/modules/servers/partials/configure/jmap.adoc index 5fe4cf5bd7..ac8f6708c7 100644 --- a/docs/modules/servers/partials/configure/jmap.adoc +++ b/docs/modules/servers/partials/configure/jmap.adoc @@ -104,6 +104,12 @@ then `apiUrl, downloadUrl, uploadUrl, eventSourceUrl, webSocketUrl` in response If the HTTP request to Jmap session endpoint has the `X-JMAP-WEBSOCKET-PREFIX` header with the value `ws://new-domain/prefix`, then `capabilities."urn:ietf:params:jmap:websocket".url` in response will be "ws://new-domain/prefix/jmap/ws". +| webpush.enabled +| Optional boolean. Defaults to true. Whether WebPush is enableded. When set to false the push listener is not +registered at all - no group registration is created on the JMAP event bus - and `PushSubscription/get` and +`PushSubscription/set` answer an `unknownMethod` JMAP error. Note that this does not affect JMAP push over +WebSocket nor EventSource, which do not rely on push subscriptions. + | webpush.prevent.server.side.request.forgery | Optional boolean. Prevent server side request forgery by preventing calls to the private network ranges. Defaults to true, can be disabled for testing. diff --git a/server/apps/distributed-app/sample-configuration/jmap.properties b/server/apps/distributed-app/sample-configuration/jmap.properties index 881fa372d6..b6d1ceec5b 100644 --- a/server/apps/distributed-app/sample-configuration/jmap.properties +++ b/server/apps/distributed-app/sample-configuration/jmap.properties @@ -41,5 +41,9 @@ tls.secret=james72laBalle # oidc.token.cache.maxSize=10000 # oidc.token.cache.redis.command.timeout=3s +# Whether WebPush is supported. Defaults to true. When set to false the push listener is not registered at all and +# PushSubscription/get and PushSubscription/set answer an `unknownMethod` JMAP error. +# webpush.enabled=false + # Prevent server side request forgery by preventing calls to the private network ranges. Defaults to true, can be disabled for testing. # webpush.prevent.server.side.request.forgery=false \ No newline at end of file diff --git a/server/apps/memory-app/sample-configuration/jmap.properties b/server/apps/memory-app/sample-configuration/jmap.properties index 40e4011bd9..7b255ed1bf 100644 --- a/server/apps/memory-app/sample-configuration/jmap.properties +++ b/server/apps/memory-app/sample-configuration/jmap.properties @@ -38,5 +38,9 @@ delay.sends.enabled=true # oidc.token.cache.expiration=5m # oidc.token.cache.maxSize=10000 +# Whether WebPush is supported. Defaults to true. When set to false the push listener is not registered at all and +# PushSubscription/get and PushSubscription/set answer an `unknownMethod` JMAP error. +# webpush.enabled=false + # Prevent server side request forgery by preventing calls to the private network ranges. Defaults to true, can be disabled for testing. # webpush.prevent.server.side.request.forgery=false \ No newline at end of file diff --git a/server/apps/postgres-app/sample-configuration/jmap.properties b/server/apps/postgres-app/sample-configuration/jmap.properties index 881fa372d6..b6d1ceec5b 100644 --- a/server/apps/postgres-app/sample-configuration/jmap.properties +++ b/server/apps/postgres-app/sample-configuration/jmap.properties @@ -41,5 +41,9 @@ tls.secret=james72laBalle # oidc.token.cache.maxSize=10000 # oidc.token.cache.redis.command.timeout=3s +# Whether WebPush is supported. Defaults to true. When set to false the push listener is not registered at all and +# PushSubscription/get and PushSubscription/set answer an `unknownMethod` JMAP error. +# webpush.enabled=false + # Prevent server side request forgery by preventing calls to the private network ranges. Defaults to true, can be disabled for testing. # webpush.prevent.server.side.request.forgery=false \ No newline at end of file diff --git a/server/container/guice/distributed/src/main/java/org/apache/james/modules/event/JMAPEventBusModule.java b/server/container/guice/distributed/src/main/java/org/apache/james/modules/event/JMAPEventBusModule.java index ecb6bc46b9..d06f83b356 100644 --- a/server/container/guice/distributed/src/main/java/org/apache/james/modules/event/JMAPEventBusModule.java +++ b/server/container/guice/distributed/src/main/java/org/apache/james/modules/event/JMAPEventBusModule.java @@ -40,6 +40,7 @@ import org.apache.james.events.RoutingKeyConverter; import org.apache.james.jmap.InjectionKeys; import org.apache.james.jmap.change.Factory; import org.apache.james.jmap.change.JmapEventSerializer; +import org.apache.james.jmap.core.JmapRfc8621Configuration; import org.apache.james.jmap.pushsubscription.PushListener; import org.apache.james.utils.InitializationOperation; import org.apache.james.utils.InitilizationOperationBuilder; @@ -59,12 +60,15 @@ public class JMAPEventBusModule extends AbstractModule { } @ProvidesIntoSet - InitializationOperation workQueue(@Named(InjectionKeys.JMAP) RabbitMQEventBus instance, PushListener pushListener) { + InitializationOperation workQueue(@Named(InjectionKeys.JMAP) RabbitMQEventBus instance, PushListener pushListener, + JmapRfc8621Configuration jmapConfiguration) { return InitilizationOperationBuilder .forClass(RabbitMQEventBus.class) .init(() -> { instance.start(); - instance.register(pushListener); + if (jmapConfiguration.webPushEnabled()) { + instance.register(pushListener); + } }); } diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JmapGuiceProbe.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JmapGuiceProbe.java index 2ab5b5356e..9fdd420010 100644 --- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JmapGuiceProbe.java +++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JmapGuiceProbe.java @@ -22,12 +22,14 @@ package org.apache.james.jmap; import static org.apache.james.jmap.utils.AccountIdUtil.toVacationAccountId; import java.util.Arrays; +import java.util.Collection; import jakarta.inject.Inject; import org.apache.james.core.Username; import org.apache.james.events.EventBus; import org.apache.james.events.EventListener; +import org.apache.james.events.Group; import org.apache.james.jmap.api.change.EmailChange; import org.apache.james.jmap.api.change.EmailChangeRepository; import org.apache.james.jmap.api.change.MailboxChangeRepository; @@ -75,6 +77,10 @@ public class JmapGuiceProbe implements GuiceProbe { return jmapServer.getPort(); } + public Collection<Group> listRegisteredGroups() { + return eventBus.listRegisteredGroups(); + } + public void addEventListener(EventListener.GroupEventListener listener) { eventBus.register(listener); } diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/modules/protocols/JmapEventBusModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/modules/protocols/JmapEventBusModule.java index 71bdb0df15..cab33e090b 100644 --- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/modules/protocols/JmapEventBusModule.java +++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/modules/protocols/JmapEventBusModule.java @@ -20,18 +20,39 @@ package org.apache.james.modules.protocols; import org.apache.james.events.EventBus; -import org.apache.james.events.EventListener; import org.apache.james.jmap.InjectionKeys; +import org.apache.james.jmap.core.JmapRfc8621Configuration; import org.apache.james.jmap.pushsubscription.PushListener; +import org.apache.james.modules.mailbox.ListenersConfiguration; +import org.apache.james.modules.mailbox.MailboxListenersLoaderImpl; +import org.apache.james.utils.InitializationOperation; +import org.apache.james.utils.InitilizationOperationBuilder; import com.google.inject.AbstractModule; -import com.google.inject.multibindings.Multibinder; +import com.google.inject.multibindings.ProvidesIntoSet; import com.google.inject.name.Names; public class JmapEventBusModule extends AbstractModule { @Override protected void configure() { bind(EventBus.class).annotatedWith(Names.named(InjectionKeys.JMAP)).to(EventBus.class); - Multibinder.newSetBinder(binder(), EventListener.ReactiveGroupEventListener.class).addBinding().to(PushListener.class); + } + + /** + * Registered here rather than within the {@link org.apache.james.events.EventListener.ReactiveGroupEventListener} + * multibinder as WebPush can be turned off, in which case no registration - thus no group, and no queue for + * distributed event buses - should be created at all. + */ + @ProvidesIntoSet + InitializationOperation registerPushListener(EventBus eventBus, PushListener pushListener, + JmapRfc8621Configuration jmapConfiguration, + ListenersConfiguration listenersConfiguration) { + return InitilizationOperationBuilder + .forClass(MailboxListenersLoaderImpl.class) + .init(() -> { + if (jmapConfiguration.webPushEnabled() && listenersConfiguration.isGroupListenerConsumptionEnabled()) { + eventBus.register(pushListener); + } + }); } } 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/WebPushDisabledContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/WebPushDisabledContract.scala new file mode 100644 index 0000000000..84122210ad --- /dev/null +++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/WebPushDisabledContract.scala @@ -0,0 +1,144 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.jmap.rfc8621.contract + +import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT +import io.restassured.RestAssured.{`given`, requestSpecification} +import io.restassured.http.ContentType.JSON +import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson +import org.apache.http.HttpStatus.SC_OK +import org.apache.james.GuiceJamesServer +import org.apache.james.jmap.JmapGuiceProbe +import org.apache.james.jmap.change.MailboxChangeListenerGroup +import org.apache.james.jmap.core.JmapRfc8621Configuration +import org.apache.james.jmap.core.ResponseObject.SESSION_STATE +import org.apache.james.jmap.http.UserCredential +import org.apache.james.jmap.pushsubscription.PushListenerGroup +import org.apache.james.jmap.rfc8621.contract.Fixture._ +import org.apache.james.utils.DataProbeImpl +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.{BeforeEach, Test} + +object WebPushDisabledContract { + val configuration: JmapRfc8621Configuration = JmapRfc8621Configuration( + urlPrefixString = "http://127.0.0.1", + websocketPrefixString = "ws://127.0.0.1", + webPushEnabled = false) +} + +trait WebPushDisabledContract { + @BeforeEach + def setUp(server: GuiceJamesServer): Unit = { + server.getProbe(classOf[DataProbeImpl]) + .fluent() + .addDomain(DOMAIN.asString()) + .addUser(BOB.asString(), BOB_PASSWORD) + + requestSpecification = baseRequestSpecBuilder(server) + .setAuth(authScheme(UserCredential(BOB, BOB_PASSWORD))) + .addHeader(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER) + .build() + } + + @Test + def pushListenerShouldNotBeRegistered(server: GuiceJamesServer): Unit = { + val groups = server.getProbe(classOf[JmapGuiceProbe]).listRegisteredGroups() + + assertThat(groups).doesNotContain(PushListenerGroup()) + // Ensures the above is not vacuously true: other JMAP listeners are registered on that very event bus + assertThat(groups).contains(MailboxChangeListenerGroup()) + } + + @Test + def pushSubscriptionGetShouldBeRejected(): Unit = { + val response = `given` + .body( + """{ + | "using": ["urn:ietf:params:jmap:core"], + | "methodCalls": [[ + | "PushSubscription/get", + | { + | "ids": null + | }, + | "c1"]] + |}""".stripMargin) + .when + .post + .`then` + .statusCode(SC_OK) + .contentType(JSON) + .extract + .body + .asString + + assertThatJson(response).isEqualTo( + s"""{ + | "sessionState": "${SESSION_STATE.value}", + | "methodResponses": [[ + | "error", + | { + | "type": "unknownMethod", + | "description": "PushSubscription is disabled on this server: set `webpush.enabled` in jmap.properties to enable it" + | }, + | "c1"]] + |}""".stripMargin) + } + + @Test + def pushSubscriptionSetShouldBeRejected(): Unit = { + val response = `given` + .body( + """{ + | "using": ["urn:ietf:params:jmap:core"], + | "methodCalls": [[ + | "PushSubscription/set", + | { + | "create": { + | "4f29": { + | "deviceClientId": "a889-ffea-910", + | "url": "https://example.com/push/?device=X8980fc&client=12c6d086", + | "types": ["Mailbox"] + | } + | } + | }, + | "c1"]] + |}""".stripMargin) + .when + .post + .`then` + .statusCode(SC_OK) + .contentType(JSON) + .extract + .body + .asString + + assertThatJson(response).isEqualTo( + s"""{ + | "sessionState": "${SESSION_STATE.value}", + | "methodResponses": [[ + | "error", + | { + | "type": "unknownMethod", + | "description": "PushSubscription is disabled on this server: set `webpush.enabled` in jmap.properties to enable it" + | }, + | "c1"]] + |}""".stripMargin) + } +} diff --git a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushTest.java b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushDisabledTest.java similarity index 71% copy from server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushTest.java copy to server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushDisabledTest.java index ab6cbc0ba3..0eb3796fc5 100644 --- a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushTest.java +++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushDisabledTest.java @@ -21,34 +21,27 @@ package org.apache.james.jmap.rfc8621.memory; import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT; -import org.apache.james.ClockExtension; import org.apache.james.JamesServerBuilder; import org.apache.james.JamesServerExtension; import org.apache.james.MemoryJamesConfiguration; import org.apache.james.MemoryJamesServerMain; -import org.apache.james.jmap.pushsubscription.PushClientConfiguration; -import org.apache.james.jmap.rfc8621.contract.PushServerExtension; -import org.apache.james.jmap.rfc8621.contract.PushSubscriptionProbeModule; -import org.apache.james.jmap.rfc8621.contract.WebPushContract; +import org.apache.james.jmap.core.JmapRfc8621Configuration; +import org.apache.james.jmap.rfc8621.contract.WebPushDisabledContract; import org.apache.james.modules.TestJMAPServerModule; import org.junit.jupiter.api.extension.RegisterExtension; -public class MemoryWebPushTest implements WebPushContract { - +public class MemoryWebPushDisabledTest implements WebPushDisabledContract { @RegisterExtension static JamesServerExtension testExtension = new JamesServerBuilder<MemoryJamesConfiguration>(tmpDir -> MemoryJamesConfiguration.builder() .workingDirectory(tmpDir) .configurationFromClasspath() .usersRepository(DEFAULT) + .enableJMAP() .build()) .server(configuration -> MemoryJamesServerMain.createServer(configuration) - .overrideWith(new TestJMAPServerModule(), new PushSubscriptionProbeModule()) - .overrideWith(binder -> binder.bind(PushClientConfiguration.class).toInstance(PushClientConfiguration.UNSAFE_DEFAULT()))) - .extension(new ClockExtension()) - .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS) + .overrideWith(binder -> binder.bind(JmapRfc8621Configuration.class).toInstance(WebPushDisabledContract.configuration())) + .overrideWith(new TestJMAPServerModule())) + .lifeCycle(JamesServerExtension.Lifecycle.PER_TEST) .build(); - - @RegisterExtension - static PushServerExtension pushServerExtension = new PushServerExtension(); } diff --git a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushTest.java b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushTest.java index ab6cbc0ba3..2dd4a5d699 100644 --- a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushTest.java +++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryWebPushTest.java @@ -20,17 +20,22 @@ package org.apache.james.jmap.rfc8621.memory; import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT; +import static org.assertj.core.api.Assertions.assertThat; import org.apache.james.ClockExtension; +import org.apache.james.GuiceJamesServer; import org.apache.james.JamesServerBuilder; import org.apache.james.JamesServerExtension; import org.apache.james.MemoryJamesConfiguration; import org.apache.james.MemoryJamesServerMain; +import org.apache.james.jmap.JmapGuiceProbe; import org.apache.james.jmap.pushsubscription.PushClientConfiguration; +import org.apache.james.jmap.pushsubscription.PushListenerGroup; import org.apache.james.jmap.rfc8621.contract.PushServerExtension; import org.apache.james.jmap.rfc8621.contract.PushSubscriptionProbeModule; import org.apache.james.jmap.rfc8621.contract.WebPushContract; import org.apache.james.modules.TestJMAPServerModule; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class MemoryWebPushTest implements WebPushContract { @@ -51,4 +56,10 @@ public class MemoryWebPushTest implements WebPushContract { @RegisterExtension static PushServerExtension pushServerExtension = new PushServerExtension(); + + @Test + void pushListenerShouldBeRegisteredWhenWebPushIsEnabled(GuiceJamesServer server) { + assertThat(server.getProbe(JmapGuiceProbe.class).listRegisteredGroups()) + .contains(new PushListenerGroup()); + } } diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala index c9f5098aa7..85d02bad67 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/JmapRfc8621Configuration.scala @@ -42,6 +42,7 @@ object JmapConfigProperties { val URL_PREFIX_PROPERTY: String = "url.prefix" val WEBSOCKET_URL_PREFIX_PROPERTY: String = "websocket.url.prefix" val WEBSOCKET_PING_INTERVAL_PROPERTY: String = "websocket.ping.interval" + val WEB_PUSH_ENABLED_PROPERTY: String = "webpush.enabled" val WEB_PUSH_MAX_TIMEOUT_SECONDS_PROPERTY: String = "webpush.maxTimeoutSeconds" val WEB_PUSH_MAX_CONNECTIONS_PROPERTY: String = "webpush.maxConnections" val WEB_PUSH_PREVENT_SERVER_SIDE_REQUEST_FORGERY: String = "webpush.prevent.server.side.request.forgery" @@ -104,6 +105,7 @@ object JmapRfc8621Configuration { maxObjectsInSet = Option(configuration.getLong(JMAP_SET_MAX_SIZE_PROPERTY, null)) .map(value => MaxObjectsInSet(UnsignedInt.liftOrThrow(value))) .getOrElse(JMAP_MAX_OBJECT_IN_SET), + webPushEnabled = configuration.getBoolean(WEB_PUSH_ENABLED_PROPERTY, true), maxTimeoutSeconds = Optional.ofNullable(configuration.getInteger(WEB_PUSH_MAX_TIMEOUT_SECONDS_PROPERTY, null)).map(Integer2int).toScala, maxConnections = Optional.ofNullable(configuration.getInteger(WEB_PUSH_MAX_CONNECTIONS_PROPERTY, null)).map(Integer2int).toScala, preventServerSideRequestForgery = Optional.ofNullable(configuration.getBoolean(WEB_PUSH_PREVENT_SERVER_SIDE_REQUEST_FORGERY, null)).orElse(true), @@ -127,6 +129,7 @@ case class JmapRfc8621Configuration(urlPrefixString: String, jmapEmailGetFullMaxSize: JmapEmailGetFullMaxSize = JMAP_EMAIL_GET_FULL_MAX_SIZE_DEFAULT, maxObjectsInGet: MaxObjectsInGet = JMAP_MAX_OBJECT_IN_GET, maxObjectsInSet: MaxObjectsInSet = JMAP_MAX_OBJECT_IN_SET, + webPushEnabled: Boolean = true, maxTimeoutSeconds: Option[Int] = None, maxConnections: Option[Int] = None, authenticationStrategies: Option[java.util.List[String]] = None, diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionGetMethod.scala index 1f3bbf1021..e986cdcd89 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionGetMethod.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionGetMethod.scala @@ -28,12 +28,13 @@ import org.apache.james.jmap.api.pushsubscription.PushSubscriptionHelpers.isInTh import org.apache.james.jmap.api.pushsubscription.PushSubscriptionRepository import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE} import org.apache.james.jmap.core.Invocation.{Arguments, MethodName} -import org.apache.james.jmap.core.{Ids, Invocation, JmapRfc8621Configuration, PushSubscriptionDTO, PushSubscriptionGetRequest, PushSubscriptionGetResponse, SessionTranslator, UnparsedPushSubscriptionId} +import org.apache.james.jmap.core.{ErrorCode, Ids, Invocation, JmapRfc8621Configuration, PushSubscriptionDTO, PushSubscriptionGetRequest, PushSubscriptionGetResponse, SessionTranslator, UnparsedPushSubscriptionId} import org.apache.james.jmap.json.PushSubscriptionSerializer import org.apache.james.jmap.routes.SessionSupplier import org.apache.james.lifecycle.api.Startable import org.apache.james.mailbox.MailboxSession import org.apache.james.metrics.api.MetricFactory +import org.reactivestreams.Publisher import play.api.libs.json.JsObject import reactor.core.scala.publisher.{SFlux, SMono} @@ -58,6 +59,16 @@ class PushSubscriptionGetMethod @Inject()(pushSubscriptionSerializer: PushSubscr override val methodName: Invocation.MethodName = MethodName("PushSubscription/get") override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE) + override def process(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): Publisher[InvocationWithContext] = + if (configuration.webPushEnabled) { + super.process(capabilities, invocation, mailboxSession) + } else { + SMono.just(InvocationWithContext(Invocation.error( + errorCode = ErrorCode.UnknownMethod, + description = "PushSubscription is disabled on this server: set `webpush.enabled` in jmap.properties to enable it", + methodCallId = invocation.invocation.methodCallId), invocation.processingContext)) + } + override def getRequest(invocation: Invocation): Either[Exception, PushSubscriptionGetRequest] = pushSubscriptionSerializer.deserializePushSubscriptionGetRequest(invocation.arguments.value).asEitherRequest .flatMap(request => request.validate(configuration).map(_ => request)) diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetMethod.scala index f6263f58b9..1e24277fac 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetMethod.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/PushSubscriptionSetMethod.scala @@ -23,12 +23,13 @@ import eu.timepit.refined.auto._ import jakarta.inject.Inject import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE} import org.apache.james.jmap.core.Invocation.{Arguments, MethodName} -import org.apache.james.jmap.core.{Invocation, JmapRfc8621Configuration, PushSubscriptionSetRequest, PushSubscriptionSetResponse, SessionTranslator} +import org.apache.james.jmap.core.{ErrorCode, Invocation, JmapRfc8621Configuration, PushSubscriptionSetRequest, PushSubscriptionSetResponse, SessionTranslator} import org.apache.james.jmap.json.PushSubscriptionSerializer import org.apache.james.jmap.routes.SessionSupplier import org.apache.james.lifecycle.api.Startable import org.apache.james.mailbox.MailboxSession import org.apache.james.metrics.api.MetricFactory +import org.reactivestreams.Publisher import reactor.core.scala.publisher.SMono class PushSubscriptionSetMethod @Inject()(createPerformer: PushSubscriptionSetCreatePerformer, @@ -42,6 +43,16 @@ class PushSubscriptionSetMethod @Inject()(createPerformer: PushSubscriptionSetCr override val methodName: Invocation.MethodName = MethodName("PushSubscription/set") override val requiredCapabilities: Set[CapabilityIdentifier] = Set(JMAP_CORE) + override def process(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession): Publisher[InvocationWithContext] = + if (configuration.webPushEnabled) { + super.process(capabilities, invocation, mailboxSession) + } else { + SMono.just(InvocationWithContext(Invocation.error( + errorCode = ErrorCode.UnknownMethod, + description = "PushSubscription is disabled on this server: set `webpush.enabled` in jmap.properties to enable it", + methodCallId = invocation.invocation.methodCallId), invocation.processingContext)) + } + override def getRequest(invocation: Invocation): Either[Exception, PushSubscriptionSetRequest] = pushSubscriptionSerializer.deserializePushSubscriptionSetRequest(invocation.arguments.value).asEitherRequest .flatMap(request => request.validate(configuration).map(_ => request)) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
