You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/07/30 04:15:18 UTC
[james-project] 05/12: JAMES-3093 User and mailboxes provisioning
for JMAP-RFC-8621
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 d2c314e5096058f04fadfbffee00732519d7949c
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Wed Jul 22 17:17:00 2020 +0700
JAMES-3093 User and mailboxes provisioning for JMAP-RFC-8621
---
.../distributed/DistributedProvisioningTest.java | 53 ++++++++++++++
.../james/jmap/rfc8621/contract/Fixture.scala | 14 ++++
.../contract/MailboxGetMethodContract.scala | 14 ----
.../rfc8621/contract/ProvisioningContract.scala | 83 ++++++++++++++++++++++
.../rfc8621/memory/MemoryProvisioningTest.java | 38 ++++++++++
.../apache/james/jmap/routes/JMAPApiRoutes.scala | 16 +++--
.../james/jmap/routes/JMAPApiRoutesTest.scala | 14 ++--
7 files changed, 209 insertions(+), 23 deletions(-)
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedProvisioningTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedProvisioningTest.java
new file mode 100644
index 0000000..f86bad6
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedProvisioningTest.java
@@ -0,0 +1,53 @@
+/****************************************************************
+ * 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.distributed;
+
+import org.apache.james.CassandraExtension;
+import org.apache.james.CassandraRabbitMQJamesConfiguration;
+import org.apache.james.CassandraRabbitMQJamesServerMain;
+import org.apache.james.DockerElasticSearchExtension;
+import org.apache.james.JamesServerBuilder;
+import org.apache.james.JamesServerExtension;
+import org.apache.james.jmap.rfc8621.contract.ProvisioningContract;
+import org.apache.james.modules.AwsS3BlobStoreExtension;
+import org.apache.james.modules.RabbitMQExtension;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.apache.james.modules.blobstore.BlobStoreConfiguration;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class DistributedProvisioningTest implements ProvisioningContract {
+ @RegisterExtension
+ static JamesServerExtension testExtension = new JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir ->
+ CassandraRabbitMQJamesConfiguration.builder()
+ .workingDirectory(tmpDir)
+ .configurationFromClasspath()
+ .blobStore(BlobStoreConfiguration.builder()
+ .objectStorage()
+ .disableCache()
+ .deduplication())
+ .build())
+ .extension(new DockerElasticSearchExtension())
+ .extension(new CassandraExtension())
+ .extension(new RabbitMQExtension())
+ .extension(new AwsS3BlobStoreExtension())
+ .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
+ .overrideWith(new TestJMAPServerModule()))
+ .build();
+}
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/Fixture.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala
index 799f8f9..7022a76 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala
@@ -134,4 +134,18 @@ object Fixture {
"-yDYktd4WT8MYhqY7MgS-wR0vO9jZFv8ZCgd_MkKCvCO0HmMjP5iQPZ0kqGkgWUH7X123tfR38MfbCVAdPDba-K3MfkogV1xvDhlkPScFr_6MxE" +
"xtedOK2JnQZn7t9sUzSrcyjWverm7gZkPptkIVoS8TsEeMMME5vFXe_nqkEG69q3kuBUm_33tbR5oNS0ZGZKlG9r41lHBjyf9J1xN4UYV8n866d" +
"a7RPPCzshIWUtO0q9T2umWTnp-6OnOdBCkndrZmRR6pPxsD5YL0_77Wq8KT_5__fGA"
+
+ val GET_ALL_MAILBOXES_REQUEST: String =
+ """{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Mailbox/get",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "ids": null
+ | },
+ | "c1"]]
+ |}""".stripMargin
}
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/MailboxGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
index 6a255a2..3d1029f 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
@@ -52,20 +52,6 @@ object MailboxGetMethodContract {
private val READ: String = Right.Read.asCharacter.toString
private val ADMINISTER: String = Right.Administer.asCharacter.toString
- private val GET_ALL_MAILBOXES_REQUEST: String =
- """{
- | "using": [
- | "urn:ietf:params:jmap:core",
- | "urn:ietf:params:jmap:mail"],
- | "methodCalls": [[
- | "Mailbox/get",
- | {
- | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
- | "ids": null
- | },
- | "c1"]]
- |}""".stripMargin
-
private val GET_ALL_MAILBOXES_REQUEST_NULL_PROPERTIES: String =
"""{
| "using": [
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/ProvisioningContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/ProvisioningContract.scala
new file mode 100644
index 0000000..37a4091
--- /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/ProvisioningContract.scala
@@ -0,0 +1,83 @@
+/****************************************************************
+ * 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`, _}
+import io.restassured.authentication.NoAuthScheme
+import io.restassured.http.Header
+import org.apache.http.HttpStatus.SC_OK
+import org.apache.james.GuiceJamesServer
+import org.apache.james.jmap.rfc8621.contract.Fixture._
+import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags
+import org.apache.james.mailbox.DefaultMailboxes
+import org.apache.james.utils.DataProbeImpl
+import org.assertj.core.api.Assertions.assertThat
+import org.hamcrest.Matchers.{hasItems, hasSize}
+import org.junit.jupiter.api.{BeforeEach, Tag, Test}
+
+object ProvisioningContract {
+ private val ARGUMENTS: String = "methodResponses[0][1]"
+}
+
+trait ProvisioningContract {
+ import ProvisioningContract._
+
+ @BeforeEach
+ def setup(server: GuiceJamesServer): Unit = {
+ server.getProbe(classOf[DataProbeImpl])
+ .fluent
+ .addDomain(DOMAIN.asString)
+ .addUser(BOB.asString, BOB_PASSWORD)
+
+ requestSpecification = baseRequestSpecBuilder(server)
+ .setAuth(new NoAuthScheme())
+ .build
+ }
+
+ @Tag(CategoryTags.BASIC_FEATURE)
+ @Test
+ def provisionUserShouldAddMissingValidUser(server: GuiceJamesServer): Unit = {
+ `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .header(new Header(AUTHORIZATION_HEADER, s"Bearer $USER_TOKEN"))
+ .body(GET_ALL_MAILBOXES_REQUEST)
+ .when
+ .post
+
+ assertThat(server.getProbe(classOf[DataProbeImpl]).listUsers())
+ .contains(USER.asString())
+ }
+
+ @Tag(CategoryTags.BASIC_FEATURE)
+ @Test
+ def provisionMailboxesShouldCreateMissingMailboxes(): Unit = {
+ `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .header(BOB_BASIC_AUTH_HEADER)
+ .body(GET_ALL_MAILBOXES_REQUEST)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .body(s"$ARGUMENTS.list", hasSize(6))
+ .body(s"$ARGUMENTS.list.name", hasItems(DefaultMailboxes.DEFAULT_MAILBOXES.toArray:_*))
+ }
+}
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/MemoryProvisioningTest.java b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryProvisioningTest.java
new file mode 100644
index 0000000..42382b4
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryProvisioningTest.java
@@ -0,0 +1,38 @@
+/****************************************************************
+ * 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.memory;
+
+import static org.apache.james.MemoryJamesServerMain.IN_MEMORY_SERVER_AGGREGATE_MODULE;
+
+import org.apache.james.GuiceJamesServer;
+import org.apache.james.JamesServerBuilder;
+import org.apache.james.JamesServerExtension;
+import org.apache.james.jmap.rfc8621.contract.ProvisioningContract;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class MemoryProvisioningTest implements ProvisioningContract {
+ @RegisterExtension
+ static JamesServerExtension testExtension = new JamesServerBuilder<>(JamesServerBuilder.defaultConfigurationProvider())
+ .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
+ .combineWith(IN_MEMORY_SERVER_AGGREGATE_MODULE)
+ .overrideWith(new TestJMAPServerModule()))
+ .build();
+}
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 320c540..e39d96d 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
@@ -33,8 +33,8 @@ import org.apache.http.HttpStatus.SC_BAD_REQUEST
import org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE
import org.apache.james.jmap.JMAPUrls.JMAP
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.http.{Authenticator, MailboxesProvisioner, UserProvisioning}
import org.apache.james.jmap.json.Serializer
import org.apache.james.jmap.method.Method
import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
@@ -57,6 +57,8 @@ object JMAPApiRoutes {
class JMAPApiRoutes (val authenticator: Authenticator,
serializer: Serializer,
+ userProvisioner: UserProvisioning,
+ mailboxesProvisioner: MailboxesProvisioner,
methods: Set[Method]) extends JMAPRoutes {
private val methodsByName: Map[MethodName, Method] = methods.map(method => method.methodName -> method).toMap
@@ -64,8 +66,10 @@ class JMAPApiRoutes (val authenticator: Authenticator,
@Inject
def this(@Named(InjectionKeys.RFC_8621) authenticator: Authenticator,
serializer: Serializer,
+ userProvisioner: UserProvisioning,
+ mailboxesProvisioner: MailboxesProvisioner,
javaMethods: java.util.Set[Method]) {
- this(authenticator, serializer, javaMethods.asScala.toSet)
+ this(authenticator, serializer, userProvisioner, mailboxesProvisioner, javaMethods.asScala.toSet)
}
override def routes(): stream.Stream[JMAPRoute] = Stream.of(
@@ -80,8 +84,12 @@ class JMAPApiRoutes (val authenticator: Authenticator,
private def post(httpServerRequest: HttpServerRequest, httpServerResponse: HttpServerResponse): Mono[Void] =
SMono(authenticator.authenticate(httpServerRequest))
- .flatMap((mailboxSession: MailboxSession) => this.requestAsJsonStream(httpServerRequest)
- .flatMap(requestObject => this.process(requestObject, httpServerResponse, mailboxSession)))
+ .flatMap((mailboxSession: MailboxSession) => SFlux.merge(Seq(
+ userProvisioner.provisionUser(mailboxSession),
+ mailboxesProvisioner.createMailboxesIfNeeded(mailboxSession)))
+ .`then`
+ .`then`(this.requestAsJsonStream(httpServerRequest)
+ .flatMap(requestObject => this.process(requestObject, httpServerResponse, mailboxSession))))
.onErrorResume(throwable => handleError(throwable, httpServerResponse))
.subscribeOn(Schedulers.elastic)
.asJava()
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 05e4029..ccec3b8 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
@@ -35,15 +35,15 @@ import org.apache.james.dnsservice.api.DNSService
import org.apache.james.domainlist.memory.MemoryDomainList
import org.apache.james.jmap.JMAPUrls.JMAP
import org.apache.james.jmap._
-import org.apache.james.jmap.http.{Authenticator, BasicAuthenticationStrategy}
+import org.apache.james.jmap.http.{Authenticator, BasicAuthenticationStrategy, MailboxesProvisioner, UserProvisioning}
import org.apache.james.jmap.json.Serializer
import org.apache.james.jmap.method.{CoreEchoMethod, Method}
import org.apache.james.jmap.model.RequestLevelErrorType
import org.apache.james.jmap.routes.JMAPApiRoutesTest._
-import org.apache.james.mailbox.MailboxManager
import org.apache.james.mailbox.extension.PreDeletionHook
-import org.apache.james.mailbox.inmemory.MemoryMailboxManagerProvider
+import org.apache.james.mailbox.inmemory.{InMemoryMailboxManager, MemoryMailboxManagerProvider}
import org.apache.james.mailbox.model.TestId
+import org.apache.james.mailbox.store.StoreSubscriptionManager
import org.apache.james.metrics.tests.RecordingMetricFactory
import org.apache.james.user.memory.MemoryUsersRepository
import org.hamcrest.Matchers.equalTo
@@ -67,13 +67,17 @@ object JMAPApiRoutesTest {
private val usersRepository = MemoryUsersRepository.withoutVirtualHosting(domainList)
usersRepository.addUser(Username.of("user1"), "password")
- private val mailboxManager: MailboxManager = MemoryMailboxManagerProvider.provideMailboxManager(empty_set)
+ private val mailboxManager: InMemoryMailboxManager = MemoryMailboxManagerProvider.provideMailboxManager(empty_set)
private val authenticationStrategy: BasicAuthenticationStrategy = new BasicAuthenticationStrategy(usersRepository, mailboxManager)
private val AUTHENTICATOR: Authenticator = Authenticator.of(new RecordingMetricFactory, authenticationStrategy)
+ private val userProvisionner: UserProvisioning = new UserProvisioning(usersRepository, new RecordingMetricFactory)
+ private val subscriptionManager: StoreSubscriptionManager = new StoreSubscriptionManager(mailboxManager.getMapperFactory)
+ private val mailboxesProvisioner: MailboxesProvisioner = new MailboxesProvisioner(mailboxManager, subscriptionManager, new RecordingMetricFactory)
+
private val JMAP_METHODS: Set[Method] = Set(new CoreEchoMethod)
- private val JMAP_API_ROUTE: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, SERIALIZER, JMAP_METHODS)
+ private val JMAP_API_ROUTE: JMAPApiRoutes = new JMAPApiRoutes(AUTHENTICATOR, SERIALIZER, userProvisionner, mailboxesProvisioner, JMAP_METHODS)
private val ROUTES_HANDLER: ImmutableSet[JMAPRoutesHandler] = ImmutableSet.of(new JMAPRoutesHandler(Version.RFC8621, JMAP_API_ROUTE))
private val userBase64String: String = Base64.getEncoder.encodeToString("user1:password".getBytes(StandardCharsets.UTF_8))
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org