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:16 UTC
[james-project] 03/12: JAMES-3093 Port UserProvisioner from draft
to 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 e1ae434403a20d977a4f1e46968975c6323fde14
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Tue Jul 21 16:56:03 2020 +0700
JAMES-3093 Port UserProvisioner from draft to jmap-rfc-8621
---
.../apache/james/jmap/http/UserProvisioning.scala | 64 +++++++++++++
.../james/jmap/http/UserProvisioningTest.scala | 103 +++++++++++++++++++++
2 files changed, 167 insertions(+)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/UserProvisioning.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/UserProvisioning.scala
new file mode 100644
index 0000000..95895e5
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/UserProvisioning.scala
@@ -0,0 +1,64 @@
+/****************************************************************
+ * 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.http
+
+import java.util.UUID
+
+import javax.inject.Inject
+import org.apache.james.core.Username
+import org.apache.james.mailbox.MailboxSession
+import org.apache.james.metrics.api.MetricFactory
+import org.apache.james.metrics.api.TimeMetric.ExecutionResult.DEFAULT_100_MS_THRESHOLD
+import org.apache.james.user.api.{AlreadyExistInUsersRepositoryException, UsersRepository, UsersRepositoryException}
+import reactor.core.scala.publisher.SMono
+
+class UserProvisioning @Inject() (usersRepository: UsersRepository, metricFactory: MetricFactory) {
+
+ def provisionUser(session: MailboxSession): SMono[Unit] =
+ if (session != null && !usersRepository.isReadOnly) {
+ SMono.fromCallable(() => createAccountIfNeeded(session))
+ .`then`
+ } else {
+ SMono.empty
+ }
+
+ private def createAccountIfNeeded(session: MailboxSession): Unit = {
+ val timeMetric = metricFactory.timer("JMAP-RFC-8621-user-provisioning")
+ try {
+ val username = session.getUser
+ if (needsAccountCreation(username)) {
+ createAccount(username)
+ }
+ } catch {
+ case exception: AlreadyExistInUsersRepositoryException => // Ignore
+ case exception: UsersRepositoryException => throw new RuntimeException(exception)
+ } finally {
+ timeMetric.stopAndPublish.logWhenExceedP99(DEFAULT_100_MS_THRESHOLD)
+ }
+ }
+
+ @throws[UsersRepositoryException]
+ private def createAccount(username: Username): Unit = usersRepository.addUser(username, generatePassword)
+
+ @throws[UsersRepositoryException]
+ private def needsAccountCreation(username: Username): Boolean = !usersRepository.contains(username)
+
+ private def generatePassword: String = UUID.randomUUID.toString
+}
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/UserProvisioningTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/UserProvisioningTest.scala
new file mode 100644
index 0000000..ff799a0
--- /dev/null
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/UserProvisioningTest.scala
@@ -0,0 +1,103 @@
+/****************************************************************
+ * 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.http
+
+import java.time.Duration
+
+import org.apache.james.core.Username
+import org.apache.james.domainlist.api.DomainList
+import org.apache.james.mailbox.{MailboxSession, MailboxSessionUtil}
+import org.apache.james.metrics.tests.RecordingMetricFactory
+import org.apache.james.user.api.{UsersRepository, UsersRepositoryException}
+import org.apache.james.user.memory.MemoryUsersRepository
+import org.apache.james.util.concurrency.ConcurrentTestRunner
+import org.assertj.core.api.Assertions.{assertThat, assertThatThrownBy}
+import org.junit.jupiter.api.{BeforeEach, Test}
+import org.mockito.Mockito.{mock, verify, verifyNoMoreInteractions, when}
+
+object UserProvisioningTest {
+ private val USERNAME: Username = Username.of("username")
+ private val USERNAME_WITH_DOMAIN: Username = Username.of("username@james.org")
+ private val NO_DOMAIN_LIST: DomainList = null
+}
+
+class UserProvisioningTest {
+ import UserProvisioningTest._
+
+ var testee: UserProvisioning = _
+ var usersRepository: MemoryUsersRepository = _
+
+ @BeforeEach
+ def setup(): Unit = {
+ usersRepository = MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST)
+ testee = new UserProvisioning(usersRepository, new RecordingMetricFactory)
+ }
+
+ @Test
+ def filterShouldDoNothingOnNullSession(): Unit = {
+ testee.provisionUser(null).block()
+
+ assertThat(usersRepository.list).toIterable
+ .isEmpty
+ }
+
+ @Test
+ def filterShouldAddUsernameWhenNoVirtualHostingAndMailboxSessionContainsUsername(): Unit = {
+ usersRepository.setEnableVirtualHosting(false)
+ val mailboxSession: MailboxSession = MailboxSessionUtil.create(USERNAME)
+
+ testee.provisionUser(mailboxSession).block()
+
+ assertThat(usersRepository.list).toIterable
+ .contains(USERNAME)
+ }
+
+ @Test
+ def filterShouldFailOnInvalidVirtualHosting(): Unit = {
+ usersRepository.setEnableVirtualHosting(false)
+ val mailboxSession: MailboxSession = MailboxSessionUtil.create(USERNAME_WITH_DOMAIN)
+
+ assertThatThrownBy(() => testee.provisionUser(mailboxSession).block())
+ .hasCauseInstanceOf(classOf[UsersRepositoryException])
+ }
+
+ @Test
+ def filterShouldNotTryToAddUserWhenReadOnlyUsersRepository(): Unit = {
+ val usersRepository: UsersRepository = mock(classOf[UsersRepository])
+ when(usersRepository.isReadOnly).thenReturn(true)
+ testee = new UserProvisioning(usersRepository, new RecordingMetricFactory)
+
+ val mailboxSession: MailboxSession = MailboxSessionUtil.create(USERNAME_WITH_DOMAIN)
+ testee.provisionUser(mailboxSession).block()
+
+ verify(usersRepository).isReadOnly
+ verifyNoMoreInteractions(usersRepository)
+ }
+
+ @Test
+ def testConcurrentAccessToFilterShouldNotThrow(): Unit = {
+ val session: MailboxSession = MailboxSessionUtil.create(USERNAME)
+
+ ConcurrentTestRunner.builder
+ .operation((threadNumber: Int, step: Int) => testee.provisionUser(session))
+ .threadCount(2)
+ .runSuccessfullyWithin(Duration.ofMinutes(1))
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org