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 989365f58ac6eaa72a98b35d76350ef247070da7 Author: duc91 <[email protected]> AuthorDate: Tue Jul 28 17:36:38 2020 +0700 JAMES-3316 Configure session hardcoded prefixes --- .../james/jmap/rfc8621/RFC8621MethodsModule.java | 23 +++++++++ .../src/test/resources/jmap.properties | 2 + .../rfc8621/contract/SessionRoutesContract.scala | 30 ++++++++--- .../src/test/resources/jmap.properties | 2 + .../org/apache/james/jmap/http/SessionRoutes.scala | 2 +- .../apache/james/jmap/http/SessionSupplier.scala | 18 +++---- .../jmap/model/JmapRfc8621Configuration.scala | 43 ++++++++++++++++ .../apache/james/jmap/http/SessionRoutesTest.scala | 16 +++--- .../james/jmap/http/SessionSupplierTest.scala | 5 +- .../scala/org/apache/james/jmap/json/Fixture.scala | 2 +- .../jmap/model/JmapRfc8621ConfigurationTest.scala | 59 ++++++++++++++++++++++ 11 files changed, 172 insertions(+), 30 deletions(-) diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java index 4c856da..3012858 100644 --- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java +++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java @@ -20,6 +20,12 @@ package org.apache.james.jmap.rfc8621; +import static org.apache.james.jmap.model.JmapRfc8621Configuration.LOCALHOST_CONFIGURATION; + +import java.io.FileNotFoundException; + +import org.apache.commons.configuration2.Configuration; +import org.apache.commons.configuration2.ex.ConfigurationException; import org.apache.james.jmap.JMAPRoutesHandler; import org.apache.james.jmap.Version; import org.apache.james.jmap.http.Authenticator; @@ -31,8 +37,12 @@ import org.apache.james.jmap.jwt.JWTAuthenticationStrategy; import org.apache.james.jmap.method.CoreEchoMethod; import org.apache.james.jmap.method.MailboxGetMethod; import org.apache.james.jmap.method.Method; +import org.apache.james.jmap.model.JmapRfc8621Configuration; import org.apache.james.jmap.routes.JMAPApiRoutes; import org.apache.james.metrics.api.MetricFactory; +import org.apache.james.utils.PropertiesProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.inject.AbstractModule; import com.google.inject.Provides; @@ -43,6 +53,7 @@ import com.google.inject.multibindings.ProvidesIntoSet; import com.google.inject.name.Named; public class RFC8621MethodsModule extends AbstractModule { + private static final Logger LOGGER = LoggerFactory.getLogger(RFC8621MethodsModule.class); @Override protected void configure() { @@ -69,4 +80,16 @@ public class RFC8621MethodsModule extends AbstractModule { basicAuthenticationStrategy, jwtAuthenticationStrategy); } + + @Provides + @Singleton + JmapRfc8621Configuration provideConfiguration(PropertiesProvider propertiesProvider) throws ConfigurationException { + try { + Configuration configuration = propertiesProvider.getConfiguration("jmap"); + return JmapRfc8621Configuration.from(configuration); + } catch (FileNotFoundException e) { + LOGGER.warn("Could not find JMAP configuration file [jmap.properties]. JMAP server will be enabled with default value."); + return LOCALHOST_CONFIGURATION(); + } + } } diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/resources/jmap.properties b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/resources/jmap.properties new file mode 100644 index 0000000..a0da434 --- /dev/null +++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/resources/jmap.properties @@ -0,0 +1,2 @@ +# Configuration urlPrefix for JMAP routes. +url.prefix=http://domain.com \ No newline at end of file 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 274d660..02415ef 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 @@ -18,21 +18,28 @@ * ***************************************************************/ package org.apache.james.jmap.rfc8621.contract +import java.nio.charset.StandardCharsets + import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT import io.restassured.RestAssured._ +import io.restassured.builder.RequestSpecBuilder +import io.restassured.config.EncoderConfig.encoderConfig +import io.restassured.config.RestAssuredConfig.newConfig +import io.restassured.http.ContentType 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.draft.JmapGuiceProbe import org.apache.james.jmap.http.UserCredential import org.apache.james.jmap.rfc8621.contract.Fixture._ -import org.apache.james.jmap.rfc8621.contract.SessionRoutesContract.expected_session_object +import org.apache.james.jmap.rfc8621.contract.SessionRoutesContract.{EXPECTED_BASE_PATH, expected_session_object} import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags import org.apache.james.utils.DataProbeImpl import org.junit.jupiter.api.{BeforeEach, Tag, Test} object SessionRoutesContract { - private val expected_session_object = """{ + private val expected_session_object: String = """{ | "capabilities" : { | "urn:ietf:params:jmap:core" : { | "maxSizeUpload" : 10000000, @@ -91,12 +98,13 @@ object SessionRoutesContract { | "urn:apache:james:params:jmap:mail:shares": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6" | }, | "username" : "[email protected]", - | "apiUrl" : "http://this-url-is-hardcoded.org/jmap", - | "downloadUrl" : "http://this-url-is-hardcoded.org/download", - | "uploadUrl" : "http://this-url-is-hardcoded.org/upload", - | "eventSourceUrl" : "http://this-url-is-hardcoded.org/eventSource", + | "apiUrl" : "http://domain.com/jmap", + | "downloadUrl" : "http://domain.com/download", + | "uploadUrl" : "http://domain.com/upload", + | "eventSourceUrl" : "http://domain.com/eventSource", | "state" : "000001" |}""".stripMargin + private val EXPECTED_BASE_PATH: String = "/jmap" } trait SessionRoutesContract { @@ -109,7 +117,15 @@ trait SessionRoutesContract { .addUser(BOB.asString, BOB_PASSWORD) .addUser(ANDRE.asString, ANDRE_PASSWORD) - requestSpecification = baseRequestSpecBuilder(server) + val jmapGuiceProbe: JmapGuiceProbe = server.getProbe(classOf[JmapGuiceProbe]) + requestSpecification = new RequestSpecBuilder() + .setContentType(ContentType.JSON) + .setAccept(ContentType.JSON) + .setConfig(newConfig.encoderConfig(encoderConfig.defaultContentCharset(StandardCharsets.UTF_8))) + .setPort(jmapGuiceProbe + .getJmapPort + .getValue) + .setBasePath(EXPECTED_BASE_PATH) .setAuth(authScheme(UserCredential(BOB, BOB_PASSWORD))) .build } diff --git a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/resources/jmap.properties b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/resources/jmap.properties new file mode 100644 index 0000000..a0da434 --- /dev/null +++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/resources/jmap.properties @@ -0,0 +1,2 @@ +# Configuration urlPrefix for JMAP routes. +url.prefix=http://domain.com \ No newline at end of file diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala index 047e8da..557282f 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala @@ -48,7 +48,7 @@ object SessionRoutes { class SessionRoutes @Inject() (@Named(InjectionKeys.RFC_8621) val authenticator: Authenticator, val serializer: Serializer, - val sessionSupplier: SessionSupplier = new SessionSupplier()) extends JMAPRoutes { + val sessionSupplier: SessionSupplier) extends JMAPRoutes { private val generateSession: JMAPRoute.Action = (request, response) => SMono.fromPublisher(authenticator.authenticate(request)) 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 191480a..40fc252 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 @@ -19,19 +19,13 @@ package org.apache.james.jmap.http -import java.net.URL - +import javax.inject.Inject import org.apache.james.core.Username -import org.apache.james.jmap.http.SessionSupplier.HARD_CODED_URL_PREFIX import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier import org.apache.james.jmap.model._ import reactor.core.scala.publisher.SMono -object SessionSupplier { - private val HARD_CODED_URL_PREFIX = "http://this-url-is-hardcoded.org" -} - -class SessionSupplier { +class SessionSupplier @Inject() (val configuration: JmapRfc8621Configuration){ def generate(username: Username): SMono[Session] = { accounts(username) .map(account => Session( @@ -39,10 +33,10 @@ class SessionSupplier { List(account), primaryAccounts(account.accountId), username, - apiUrl = new URL(s"$HARD_CODED_URL_PREFIX/jmap"), - downloadUrl = new URL(s"$HARD_CODED_URL_PREFIX/download"), - uploadUrl = new URL(s"$HARD_CODED_URL_PREFIX/upload"), - eventSourceUrl = new URL(s"$HARD_CODED_URL_PREFIX/eventSource"))) + apiUrl = configuration.apiUrl, + downloadUrl = configuration.downloadUrl, + uploadUrl = configuration.uploadUrl, + eventSourceUrl = configuration.eventSourceUrl)) } private def accounts(username: Username): SMono[Account] = SMono.defer(() => 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 new file mode 100644 index 0000000..dbd5654 --- /dev/null +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/JmapRfc8621Configuration.scala @@ -0,0 +1,43 @@ +/**************************************************************** + * 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.model + +import java.net.URL + +import eu.timepit.refined.api.Refined +import org.apache.commons.configuration2.Configuration + +object JmapRfc8621Configuration { + val LOCALHOST_URL_PREFIX: String = "http://localhost" + private def from(urlPrefixString: String): JmapRfc8621Configuration = JmapRfc8621Configuration(urlPrefixString) + var LOCALHOST_CONFIGURATION: JmapRfc8621Configuration = from(LOCALHOST_URL_PREFIX) + val URL_PREFIX_PROPERTIES: String = "url.prefix" + + def from(configuration: Configuration): JmapRfc8621Configuration = + JmapRfc8621Configuration(Option(configuration.getString(URL_PREFIX_PROPERTIES)).getOrElse(LOCALHOST_URL_PREFIX)) +} + +case class JmapRfc8621Configuration(urlPrefixString: String) { + val urlPrefix: URL = new URL(urlPrefixString) + val apiUrl: URL = new URL(s"$urlPrefixString/jmap") + val downloadUrl: URL = new URL(s"$urlPrefixString/download") + val uploadUrl: URL = new URL(s"$urlPrefixString/upload") + val eventSourceUrl: URL = new URL(s"$urlPrefixString/eventSource") +} 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 d1f4a77..f2fbb07 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 @@ -27,11 +27,13 @@ import io.restassured.builder.RequestSpecBuilder import io.restassured.config.EncoderConfig.encoderConfig import io.restassured.config.RestAssuredConfig.newConfig import io.restassured.http.ContentType +import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson import org.apache.http.HttpStatus import org.apache.james.core.Username import org.apache.james.jmap._ import org.apache.james.jmap.http.SessionRoutesTest.{BOB, TEST_CONFIGURATION} import org.apache.james.jmap.json.Serializer +import org.apache.james.jmap.model.JmapRfc8621Configuration import org.apache.james.mailbox.MailboxSession import org.apache.james.mailbox.model.TestId import org.mockito.ArgumentMatchers.any @@ -65,7 +67,7 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers { val sessionRoutes = new SessionRoutes( serializer = new Serializer(new TestId.Factory), - sessionSupplier = new SessionSupplier(), + sessionSupplier = new SessionSupplier(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION), authenticator = mockedAuthFilter) jmapServer = new JMAPServer( TEST_CONFIGURATION, @@ -100,7 +102,7 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers { .thenReturn .getBody .asString() - val expectedJson = """{ + val expectedJson = s"""{ | "capabilities" : { | "urn:ietf:params:jmap:core" : { | "maxSizeUpload" : 10000000, @@ -159,13 +161,13 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers { | "urn:apache:james:params:jmap:mail:shares": "0fe275bf13ff761407c17f64b1dfae2f4b3186feea223d7267b79f873a105401" | }, | "username" : "[email protected]", - | "apiUrl" : "http://this-url-is-hardcoded.org/jmap", - | "downloadUrl" : "http://this-url-is-hardcoded.org/download", - | "uploadUrl" : "http://this-url-is-hardcoded.org/upload", - | "eventSourceUrl" : "http://this-url-is-hardcoded.org/eventSource", + | "apiUrl" : "${JmapRfc8621Configuration.LOCALHOST_URL_PREFIX}/jmap", + | "downloadUrl" : "${JmapRfc8621Configuration.LOCALHOST_URL_PREFIX}/download", + | "uploadUrl" : "${JmapRfc8621Configuration.LOCALHOST_URL_PREFIX}/upload", + | "eventSourceUrl" : "${JmapRfc8621Configuration.LOCALHOST_URL_PREFIX}/eventSource", | "state" : "000001" |}""".stripMargin - Json.parse(sessionJson) should equal(Json.parse(expectedJson)) + assertThatJson(sessionJson).isEqualTo(expectedJson) } } diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala index 11bd415..71f00d0 100644 --- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala +++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala @@ -21,6 +21,7 @@ package org.apache.james.jmap.http import org.apache.james.core.Username import org.apache.james.jmap.http.SessionSupplierTest.USERNAME +import org.apache.james.jmap.model.JmapRfc8621Configuration import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec @@ -32,11 +33,11 @@ class SessionSupplierTest extends AnyWordSpec with Matchers { "generate" should { "return correct username" in { - new SessionSupplier().generate(USERNAME).block().username should equal(USERNAME) + new SessionSupplier(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION).generate(USERNAME).block().username should equal(USERNAME) } "return correct account" which { - val accounts = new SessionSupplier().generate(USERNAME).block().accounts + val accounts = new SessionSupplier(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION).generate(USERNAME).block().accounts "has size" in { accounts should have size 1 diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala index c1c1d79..e249535 100644 --- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala +++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/Fixture.scala @@ -24,7 +24,7 @@ import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier import org.apache.james.jmap.model.Id.Id import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName} import org.apache.james.jmap.model.{ClientId, CreatedIds, Invocation, ResponseObject, ServerId} -import play.api.libs.json.{JsValue, Json} +import play.api.libs.json.Json object Fixture { val id: Id = "aHR0cHM6Ly93d3cuYmFzZTY0ZW5jb2RlLm9yZy8" diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/JmapRfc8621ConfigurationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/JmapRfc8621ConfigurationTest.scala new file mode 100644 index 0000000..16af554 --- /dev/null +++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/model/JmapRfc8621ConfigurationTest.scala @@ -0,0 +1,59 @@ +/**************************************************************** + * 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.model + +import java.net.URL + +import org.apache.commons.configuration2.{Configuration, PropertiesConfiguration} +import org.apache.james.jmap.model.JmapRfc8621Configuration.URL_PREFIX_PROPERTIES +import org.apache.james.jmap.model.JmapRfc8621ConfigurationTest.{emptyConfiguration, providedConfiguration} +import org.scalatest.matchers.must.Matchers +import org.scalatest.wordspec.AnyWordSpec + +object JmapRfc8621ConfigurationTest { + val emptyConfiguration: Configuration = new PropertiesConfiguration() + def providedConfiguration(): Configuration = { + val configuration: Configuration = new PropertiesConfiguration() + configuration.addProperty(URL_PREFIX_PROPERTIES, "http://random-domain.com") + configuration + } +} + +class JmapRfc8621ConfigurationTest extends AnyWordSpec with Matchers { + "JmapRfc8621ConfigurationTest" should { + "succeed to configuration urlPrefix when provided" in { + val jmapRfc8621Configuration: JmapRfc8621Configuration = JmapRfc8621Configuration.from(providedConfiguration()) + + jmapRfc8621Configuration.apiUrl must be(new URL("http://random-domain.com/jmap")) + jmapRfc8621Configuration.downloadUrl must be(new URL("http://random-domain.com/download")) + jmapRfc8621Configuration.uploadUrl must be(new URL("http://random-domain.com/upload")) + jmapRfc8621Configuration.eventSourceUrl must be(new URL("http://random-domain.com/eventSource")) + } + + "load default config for urlPrefix when no configuration provided" in { + val jmapRfc8621Configuration: JmapRfc8621Configuration = JmapRfc8621Configuration.from(emptyConfiguration) + + jmapRfc8621Configuration.apiUrl must be(new URL("http://localhost/jmap")) + jmapRfc8621Configuration.downloadUrl must be(new URL("http://localhost/download")) + jmapRfc8621Configuration.uploadUrl must be(new URL("http://localhost/upload")) + jmapRfc8621Configuration.eventSourceUrl must be(new URL("http://localhost/eventSource")) + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
