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 <[email protected]> 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: [email protected] For additional commands, e-mail: [email protected]
