This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 36767788a683fb937ccbfe4a547d907dd34d26d9 Author: Rene Cordier <rcord...@linagora.com> AuthorDate: Wed Mar 11 13:46:38 2020 +0700 JAMES-3078 UserProvisioner and tests --- .../apache/james/jmap/http/UserProvisioner.java | 84 +++++++++++++++++++ .../james/jmap/http/UserProvisionerTest.java | 94 ++++++++++++++++++++++ .../james/jmap/http/UserProvisionerThreadTest.java | 56 +++++++++++++ 3 files changed, 234 insertions(+) diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UserProvisioner.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UserProvisioner.java new file mode 100644 index 0000000..1ac9aac --- /dev/null +++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UserProvisioner.java @@ -0,0 +1,84 @@ +/**************************************************************** + * 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 static org.apache.james.metrics.api.TimeMetric.ExecutionResult.DEFAULT_100_MS_THRESHOLD; + +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; +import org.apache.james.user.api.AlreadyExistInUsersRepositoryException; +import org.apache.james.user.api.UsersRepository; +import org.apache.james.user.api.UsersRepositoryException; + +import com.google.common.annotations.VisibleForTesting; + +import reactor.core.publisher.Mono; + +public class UserProvisioner { + private final UsersRepository usersRepository; + private final MetricFactory metricFactory; + + @Inject + @VisibleForTesting + UserProvisioner(UsersRepository usersRepository, MetricFactory metricFactory) { + this.usersRepository = usersRepository; + this.metricFactory = metricFactory; + } + + public Mono<Void> provisionUser(MailboxSession session) { + if (session != null && !usersRepository.isReadOnly()) { + return Mono.fromRunnable(() -> createAccountIfNeeded(session)); + } + return Mono.empty(); + } + + private void createAccountIfNeeded(MailboxSession session) { + TimeMetric timeMetric = metricFactory.timer("JMAP-user-provisioning"); + try { + Username username = session.getUser(); + if (needsAccountCreation(username)) { + createAccount(username); + } + } catch (AlreadyExistInUsersRepositoryException e) { + // Ignore + } catch (UsersRepositoryException e) { + throw new RuntimeException(e); + } finally { + timeMetric.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD); + } + } + + private void createAccount(Username username) throws UsersRepositoryException { + usersRepository.addUser(username, generatePassword()); + } + + private boolean needsAccountCreation(Username username) throws UsersRepositoryException { + return !usersRepository.contains(username); + } + + private String generatePassword() { + return UUID.randomUUID().toString(); + } +} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerTest.java new file mode 100644 index 0000000..c043160 --- /dev/null +++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerTest.java @@ -0,0 +1,94 @@ +/**************************************************************** + * 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 static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import org.apache.james.core.Username; +import org.apache.james.domainlist.api.DomainList; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MailboxSessionUtil; +import org.apache.james.metrics.tests.RecordingMetricFactory; +import org.apache.james.user.api.UsersRepository; +import org.apache.james.user.api.UsersRepositoryException; +import org.apache.james.user.memory.MemoryUsersRepository; +import org.junit.Before; +import org.junit.Test; + +public class UserProvisionerTest { + private static final Username USERNAME = Username.of("username"); + private static final Username USERNAME_WITH_DOMAIN = Username.of("usern...@james.org"); + private static final DomainList NO_DOMAIN_LIST = null; + + private UserProvisioner testee; + private MemoryUsersRepository usersRepository; + + @Before + public void setup() throws Exception { + usersRepository = MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST); + testee = new UserProvisioner(usersRepository, new RecordingMetricFactory()); + } + + @Test + public void filterShouldDoNothingOnNullSession() throws UsersRepositoryException { + testee.provisionUser(null).block(); + + assertThat(usersRepository.list()).toIterable() + .isEmpty(); + } + + @Test + public void filterShouldAddUsernameWhenNoVirtualHostingAndMailboxSessionContainsUsername() throws UsersRepositoryException { + usersRepository.setEnableVirtualHosting(false); + MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME); + + testee.provisionUser(mailboxSession).block(); + + assertThat(usersRepository.list()).toIterable() + .contains(USERNAME); + } + + @Test + public void filterShouldFailOnInvalidVirtualHosting() { + usersRepository.setEnableVirtualHosting(false); + MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME_WITH_DOMAIN); + + assertThatThrownBy(() -> testee.provisionUser(mailboxSession).block()) + .hasCauseInstanceOf(UsersRepositoryException.class); + } + + @Test + public void filterShouldNotTryToAddUserWhenReadOnlyUsersRepository() { + UsersRepository usersRepository = mock(UsersRepository.class); + when(usersRepository.isReadOnly()).thenReturn(true); + testee = new UserProvisioner(usersRepository, new RecordingMetricFactory()); + + MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME_WITH_DOMAIN); + + testee.provisionUser(mailboxSession).block(); + + verify(usersRepository).isReadOnly(); + verifyNoMoreInteractions(usersRepository); + } +} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerThreadTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerThreadTest.java new file mode 100644 index 0000000..621af5b --- /dev/null +++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/UserProvisionerThreadTest.java @@ -0,0 +1,56 @@ +/**************************************************************** + * 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 java.util.concurrent.ExecutionException; + +import org.apache.james.core.Username; +import org.apache.james.domainlist.api.DomainList; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MailboxSessionUtil; +import org.apache.james.metrics.tests.RecordingMetricFactory; +import org.apache.james.user.memory.MemoryUsersRepository; +import org.apache.james.util.concurrency.ConcurrentTestRunner; +import org.junit.Before; +import org.junit.Test; + +public class UserProvisionerThreadTest { + private static final DomainList NO_DOMAIN_LIST = null; + + private UserProvisioner testee; + private MemoryUsersRepository usersRepository; + private MailboxSession session; + + @Before + public void before() { + usersRepository = MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST); + session = MailboxSessionUtil.create(Username.of("username")); + testee = new UserProvisioner(usersRepository, new RecordingMetricFactory()); + } + + @Test + public void testConcurrentAccessToFilterShouldNotThrow() throws ExecutionException, InterruptedException { + ConcurrentTestRunner + .builder() + .operation((threadNumber, step) -> testee.provisionUser(session)) + .threadCount(2) + .runSuccessfullyWithin(Duration.ofMinutes(1)); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org