JAMES-1759 CRUD operations on user mailboxes
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/abdd0a98 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/abdd0a98 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/abdd0a98 Branch: refs/heads/master Commit: abdd0a98db423a6149ee4d302d27e05c7ee5e81b Parents: 2e672ff Author: Benoit Tellier <[email protected]> Authored: Thu Jun 23 10:14:49 2016 +0700 Committer: Benoit Tellier <[email protected]> Committed: Thu Jun 23 16:31:27 2016 +0700 ---------------------------------------------------------------------- .../modules/server/WebAdminServerModule.java | 2 + server/protocols/webadmin/README.adoc | 93 ++- server/protocols/webadmin/pom.xml | 13 + .../james/webadmin/model/MailboxResponse.java | 33 ++ .../webadmin/routes/UserMailboxesRoutes.java | 118 ++++ .../webadmin/service/UserMailboxesService.java | 156 +++++ .../utils/MailboxHaveChildrenException.java | 27 + .../routes/UserMailboxesRoutesTest.java | 587 +++++++++++++++++++ 8 files changed, 1028 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/abdd0a98/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java ---------------------------------------------------------------------- diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java index 4e88950..cf78598 100644 --- a/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java +++ b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/WebAdminServerModule.java @@ -36,6 +36,7 @@ import org.apache.james.webadmin.Port; import org.apache.james.webadmin.Routes; import org.apache.james.webadmin.WebAdminServer; import org.apache.james.webadmin.routes.DomainRoutes; +import org.apache.james.webadmin.routes.UserMailboxesRoutes; import org.apache.james.webadmin.routes.UserRoutes; import org.apache.james.webadmin.utils.JsonTransformer; @@ -59,6 +60,7 @@ public class WebAdminServerModule extends AbstractModule { Multibinder<Routes> routesMultibinder = Multibinder.newSetBinder(binder(), Routes.class); routesMultibinder.addBinding().to(DomainRoutes.class); routesMultibinder.addBinding().to(UserRoutes.class); + routesMultibinder.addBinding().to(UserMailboxesRoutes.class); Multibinder.newSetBinder(binder(), ConfigurationPerformer.class).addBinding().to(WebAdminServerModuleConfigurationPerformer.class); } http://git-wip-us.apache.org/repos/asf/james-project/blob/abdd0a98/server/protocols/webadmin/README.adoc ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/README.adoc b/server/protocols/webadmin/README.adoc index 7f09778..adc3aa5 100644 --- a/server/protocols/webadmin/README.adoc +++ b/server/protocols/webadmin/README.adoc @@ -1,6 +1,6 @@ = Web administration for JAMES -The web administration supports for now the CRUD operations on the domains and the users, as described in the following sections. +The web administration supports for now the CRUD operations on the domains, the users and the users mailboxes, as described in the following sections. **WARNING** : This API do not allow authentication for now. It means that an administrator should ensure an attacker can not use this API. @@ -121,3 +121,94 @@ The answer looks like : Response codes : - 200 : The user name list was successfully retrieved - 500 : Internal error while retrieving the users + +== Administrating user mailboxes + +=== Creating a mailbox + +.bash +==== +curl -XPUT http://ip:port/users/usernameToBeUsed/mailboxes/mailboxNameToBeCreated +==== + +Resource name usernameToBeUsed should be an existing user +Resource name mailboxNameToBeCreated should not be empty + +Response codes : + - 204 : The mailbox now exists on the server + - 400 : The user name does not exist + - 500 : Internal error + + To create nested mailboxes, for instance a work mailbox inside the INBOX mailbox, people should use the . separator. The sample query is : + + .bash + ==== + curl -XDELETE http://ip:port/users/usernameToBeUsed/mailboxes/INBOX.work + ==== + +=== Deleting a mailbox + +.bash +==== +curl -XDELETE http://ip:port/users/usernameToBeUsed/mailboxes/mailboxNameToBeCreated +==== + +Resource name usernameToBeUsed should be an existing user +Resource name mailboxNameToBeCreated should not be empty + +Response codes : + - 204 : The mailbox now does not exist on the server + - 400 : The user name does not exist + - 409 : The mailbox can not be deleted due to its children mailboxes. Delete children mailboxes first. + - 500 : Internal error + +=== Testing existence of a mailbox + +.bash +==== +curl -XGET http://ip:port/users/usernameToBeUsed/mailboxes/mailboxNameToBeCreated +==== + +Resource name usernameToBeUsed should be an existing user +Resource name mailboxNameToBeCreated should not be empty + +Response codes : + - 204 : The mailbox exists + - 400 : The user name does not exist + - 404 : The mailbox does not exist + - 500 : Internal error + +=== Listing user mailboxes + +.bash +==== +curl -XGET http://ip:port/users/usernameToBeUsed/mailboxes +==== + +The answer looks like : + +.json +==== +[{"mailboxName":"INBOX"},{"mailboxName":"outbox"}] +==== + +Resource name usernameToBeUsed should be an existing user + +Response codes : + - 200 : The mailboxes list was successfully retrieved + - 400 : The user name does not exist + - 500 : Internal error + +=== Deleting user mailboxes + +.bash +==== +curl -XDELETE http://ip:port/users/usernameToBeUsed/mailboxes +==== + +Resource name usernameToBeUsed should be an existing user + +Response codes : + - 204 : The user do not have mailboxes anymore + - 400 : The user name does not exist + - 500 : Internal error \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/abdd0a98/server/protocols/webadmin/pom.xml ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/pom.xml b/server/protocols/webadmin/pom.xml index a23ee8e..725d192 100644 --- a/server/protocols/webadmin/pom.xml +++ b/server/protocols/webadmin/pom.xml @@ -150,6 +150,15 @@ <dependencies> <dependency> <groupId>org.apache.james</groupId> + <artifactId>apache-james-mailbox-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>apache-james-mailbox-memory</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> <artifactId>james-server-util-java8</artifactId> </dependency> <dependency> @@ -170,6 +179,10 @@ <artifactId>jackson-databind</artifactId> </dependency> <dependency> + <groupId>com.github.fge</groupId> + <artifactId>throwing-lambdas</artifactId> + </dependency> + <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/james-project/blob/abdd0a98/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/MailboxResponse.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/MailboxResponse.java b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/MailboxResponse.java new file mode 100644 index 0000000..593bfd1 --- /dev/null +++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/model/MailboxResponse.java @@ -0,0 +1,33 @@ +/**************************************************************** + * 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.webadmin.model; + +public class MailboxResponse { + + private final String mailboxName; + + public MailboxResponse(String mailboxName) { + this.mailboxName = mailboxName; + } + + public String getMailboxName() { + return mailboxName; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/abdd0a98/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/UserMailboxesRoutes.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/UserMailboxesRoutes.java b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/UserMailboxesRoutes.java new file mode 100644 index 0000000..127fc1b --- /dev/null +++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/routes/UserMailboxesRoutes.java @@ -0,0 +1,118 @@ +/**************************************************************** + * 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.webadmin.routes; + +import javax.inject.Inject; + +import org.apache.james.webadmin.Constants; +import org.apache.james.webadmin.Routes; +import org.apache.james.webadmin.service.UserMailboxesService; +import org.apache.james.webadmin.utils.JsonTransformer; +import org.apache.james.webadmin.utils.MailboxHaveChildrenException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import spark.Service; + +public class UserMailboxesRoutes implements Routes { + + private static final Logger LOGGER = LoggerFactory.getLogger(UserMailboxesRoutes.class); + + public static final String MAILBOX_NAME = ":mailboxName"; + public static final String MAILBOXES = "mailboxes"; + private static final String USER_NAME = ":userName"; + public static final String USER_MAILBOXES_BASE = UserRoutes.USERS + Constants.SEPARATOR + USER_NAME + Constants.SEPARATOR + MAILBOXES; + public static final String SPECIFIC_MAILBOX = USER_MAILBOXES_BASE + Constants.SEPARATOR + MAILBOX_NAME; + + private final UserMailboxesService userMailboxesService; + private final JsonTransformer jsonTransformer; + + @Inject + public UserMailboxesRoutes(UserMailboxesService userMailboxesService, JsonTransformer jsonTransformer) { + this.userMailboxesService = userMailboxesService; + this.jsonTransformer = jsonTransformer; + } + + @Override + public void define(Service service) { + + service.put(SPECIFIC_MAILBOX, (request, response) -> { + try { + userMailboxesService.createMailbox(request.params(USER_NAME), request.params(MAILBOX_NAME)); + response.status(204); + } catch (IllegalStateException e) { + LOGGER.info("Invalid put on user mailbox", e); + response.status(400); + } + return Constants.EMPTY_BODY; + }); + + service.delete(SPECIFIC_MAILBOX, (request, response) -> { + try { + userMailboxesService.deleteMailbox(request.params(USER_NAME), request.params(MAILBOX_NAME)); + response.status(204); + } catch (IllegalStateException e) { + LOGGER.info("Invalid delete on user mailbox", e); + response.status(400); + } catch (MailboxHaveChildrenException e) { + LOGGER.info("Attempt to delete a mailbox with children"); + response.status(409); + } + return Constants.EMPTY_BODY; + }); + + service.delete(USER_MAILBOXES_BASE, (request, response) -> { + try { + userMailboxesService.deleteMailboxes(request.params(USER_NAME)); + response.status(204); + } catch (IllegalStateException e) { + LOGGER.info("Invalid delete on user mailboxes", e); + response.status(400); + } + return Constants.EMPTY_BODY; + }); + + service.get(SPECIFIC_MAILBOX, (request, response) -> { + try { + if (userMailboxesService.testMailboxExists(request.params(USER_NAME), request.params(MAILBOX_NAME))) { + response.status(204); + } else { + response.status(404); + } + } catch (IllegalStateException e) { + LOGGER.info("Invalid get on user mailbox", e); + response.status(400); + } + return Constants.EMPTY_BODY; + }); + + service.get(USER_MAILBOXES_BASE, (request, response) -> { + response.status(200); + try { + return userMailboxesService.listMailboxes(request.params(USER_NAME)); + } catch (IllegalStateException e) { + LOGGER.info("Invalid get on user mailboxes", e); + response.status(400); + return Constants.EMPTY_BODY; + } + }, jsonTransformer); + + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/abdd0a98/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/service/UserMailboxesService.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/service/UserMailboxesService.java b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/service/UserMailboxesService.java new file mode 100644 index 0000000..7043d25 --- /dev/null +++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/service/UserMailboxesService.java @@ -0,0 +1,156 @@ +/**************************************************************** + * 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.webadmin.service; + +import java.util.List; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.exception.MailboxExistsException; +import org.apache.james.mailbox.exception.MailboxNotFoundException; +import org.apache.james.mailbox.model.MailboxMetaData; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.model.MailboxQuery; +import org.apache.james.user.api.UsersRepository; +import org.apache.james.user.api.UsersRepositoryException; +import org.apache.james.util.streams.ImmutableCollectors; +import org.apache.james.webadmin.model.MailboxResponse; +import org.apache.james.webadmin.utils.MailboxHaveChildrenException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.fge.lambdas.Throwing; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; + + +public class UserMailboxesService { + + private static final Logger LOGGER = LoggerFactory.getLogger(UserMailboxesService.class); + private static final String USER_NAME = "webAdmin"; + + private final MailboxManager mailboxManager; + private final UsersRepository usersRepository; + + @Inject + public UserMailboxesService(MailboxManager mailboxManager, UsersRepository usersRepository) { + this.mailboxManager = mailboxManager; + this.usersRepository = usersRepository; + } + + public void createMailbox(String username, String mailboxName) throws MailboxException, UsersRepositoryException { + usernamePreconditions(username); + Preconditions.checkArgument(!Strings.isNullOrEmpty(mailboxName)); + MailboxSession mailboxSession = mailboxManager.createSystemSession(USER_NAME, LOGGER); + try { + mailboxManager.createMailbox( + convertToMailboxPath(username, mailboxName, mailboxSession), + mailboxSession); + } catch (MailboxExistsException e) { + LOGGER.info("Attempt to create mailbox {} for user {} that already exists", mailboxName, username); + } + } + + public void deleteMailboxes(String username) throws MailboxException, UsersRepositoryException { + usernamePreconditions(username); + MailboxSession mailboxSession = mailboxManager.createSystemSession(USER_NAME, LOGGER); + listUserMailboxes(username, mailboxSession) + .map(MailboxMetaData::getPath) + .forEach(Throwing.consumer(mailboxPath -> deleteMailbox(mailboxSession, mailboxPath))); + } + + public List<MailboxResponse> listMailboxes(String username) throws MailboxException, UsersRepositoryException { + usernamePreconditions(username); + MailboxSession mailboxSession = mailboxManager.createSystemSession(USER_NAME, LOGGER); + return listUserMailboxes(username, mailboxSession) + .map(mailboxMetaData -> new MailboxResponse(mailboxMetaData.getPath().getName())) + .collect(ImmutableCollectors.toImmutableList()); + } + + public boolean testMailboxExists(String username, String mailboxName) throws MailboxException, UsersRepositoryException { + usernamePreconditions(username); + Preconditions.checkArgument(!Strings.isNullOrEmpty(mailboxName)); + MailboxSession mailboxSession = mailboxManager.createSystemSession(USER_NAME, LOGGER); + return mailboxManager.mailboxExists( + convertToMailboxPath(username, mailboxName, mailboxSession), + mailboxSession); + } + + public void deleteMailbox(String username, String mailboxName) throws MailboxException, UsersRepositoryException, MailboxHaveChildrenException { + usernamePreconditions(username); + Preconditions.checkArgument(!Strings.isNullOrEmpty(mailboxName)); + MailboxSession mailboxSession = mailboxManager.createSystemSession(USER_NAME, LOGGER); + MailboxPath mailboxPath = convertToMailboxPath(username, mailboxName, mailboxSession); + try { + if (!haveChildren(mailboxPath, mailboxSession)) { + deleteMailbox(mailboxSession, mailboxPath); + } else { + throw new MailboxHaveChildrenException(mailboxName); + } + } catch (MailboxNotFoundException e) { + LOGGER.info("Attempt to delete mailbox {} for user {} that does not exists", mailboxPath.getName(), mailboxPath.getUser()); + } + } + + private boolean haveChildren(MailboxPath mailboxPath, MailboxSession mailboxSession) throws MailboxException { + return mailboxManager.search( + MailboxQuery.builder() + .base(mailboxPath) + .build(), mailboxSession) + .stream() + .findAny() + .map(mailboxMetaData -> mailboxMetaData.inferiors() == MailboxMetaData.Children.HAS_CHILDREN) + .orElseThrow(() -> new MailboxNotFoundException(mailboxPath)); + } + + private void deleteMailbox(MailboxSession mailboxSession, MailboxPath mailboxPath) throws MailboxException { + try { + mailboxManager.deleteMailbox(mailboxPath, mailboxSession); + } catch (MailboxNotFoundException e) { + LOGGER.info("Attempt to delete mailbox {} for user {} that does not exists", mailboxPath.getName(), mailboxPath.getUser()); + } + } + + private void usernamePreconditions(String username) throws UsersRepositoryException { + Preconditions.checkArgument(!Strings.isNullOrEmpty(username)); + Preconditions.checkState(usersRepository.contains(username)); + } + + private MailboxPath convertToMailboxPath(String username, String mailboxName, MailboxSession mailboxSession) { + return new MailboxPath(mailboxSession.getPersonalSpace(), username, mailboxName); + } + + private Stream<MailboxMetaData> listUserMailboxes(String username, MailboxSession mailboxSession) throws MailboxException { + return mailboxManager.search(createUserMailboxesQuery(username), mailboxSession) + .stream(); + } + + private MailboxQuery createUserMailboxesQuery(String username) { + return MailboxQuery.builder() + .username(username) + .privateUserMailboxes() + .build(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/abdd0a98/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/MailboxHaveChildrenException.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/MailboxHaveChildrenException.java b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/MailboxHaveChildrenException.java new file mode 100644 index 0000000..ae8213e --- /dev/null +++ b/server/protocols/webadmin/src/main/java/org/apache/james/webadmin/utils/MailboxHaveChildrenException.java @@ -0,0 +1,27 @@ +/**************************************************************** + * 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.webadmin.utils; + +public class MailboxHaveChildrenException extends Exception { + + public MailboxHaveChildrenException(String mailboxName) { + super(mailboxName + "have children"); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/abdd0a98/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java new file mode 100644 index 0000000..a1782ab --- /dev/null +++ b/server/protocols/webadmin/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java @@ -0,0 +1,587 @@ +/**************************************************************** + * 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.webadmin.routes; + +import static com.jayway.restassured.RestAssured.when; +import static com.jayway.restassured.RestAssured.with; +import static com.jayway.restassured.config.EncoderConfig.encoderConfig; +import static com.jayway.restassured.config.RestAssuredConfig.newConfig; +import static org.apache.james.webadmin.Constants.SEPARATOR; +import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.acl.UnionMailboxACLResolver; +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.exception.MailboxExistsException; +import org.apache.james.mailbox.exception.MailboxNotFoundException; +import org.apache.james.mailbox.inmemory.InMemoryMailboxManager; +import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.store.JVMMailboxPathLocker; +import org.apache.james.mailbox.store.SimpleMailboxMetaData; +import org.apache.james.mailbox.store.mail.model.impl.MessageParser; +import org.apache.james.user.api.UsersRepository; +import org.apache.james.webadmin.WebAdminServer; +import org.apache.james.webadmin.service.UserMailboxesService; +import org.apache.james.webadmin.utils.JsonTransformer; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; +import com.jayway.restassured.RestAssured; +import com.jayway.restassured.parsing.Parser; + +import de.bechte.junit.runners.context.HierarchicalContextRunner; + +@RunWith(HierarchicalContextRunner.class) +public class UserMailboxesRoutesTest { + + public static final String USERNAME = "username"; + public static final String MAILBOX_NAME = "myMailboxName"; + private WebAdminServer webAdminServer; + private UsersRepository usersRepository; + + private void createServer(MailboxManager mailboxManager) throws Exception { + usersRepository = mock(UsersRepository.class); + when(usersRepository.contains(USERNAME)).thenReturn(true); + + webAdminServer = new WebAdminServer(new UserMailboxesRoutes(new UserMailboxesService(mailboxManager, usersRepository), new JsonTransformer())); + webAdminServer.configure(NO_CONFIGURATION); + webAdminServer.await(); + + RestAssured.port = webAdminServer.getPort().toInt(); + RestAssured.config = newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8)); + RestAssured.defaultParser = Parser.JSON; + RestAssured.basePath = UserRoutes.USERS + SEPARATOR + USERNAME + SEPARATOR + UserMailboxesRoutes.MAILBOXES; + } + + @After + public void tearDown() { + webAdminServer.destroy(); + } + + public class NormalBehaviour { + + @Before + public void setUp() throws Exception { + InMemoryMailboxManager mailboxManager = new InMemoryMailboxManager(new InMemoryMailboxSessionMapperFactory(), + (userid, passwd) -> true, + new JVMMailboxPathLocker(), + new UnionMailboxACLResolver(), + new SimpleGroupMembershipResolver(), + new MessageParser()); + mailboxManager.init(); + + createServer(mailboxManager); + } + + @Test + public void getMailboxesShouldUserErrorFoundWithNonExistingUser() throws Exception { + when(usersRepository.contains(USERNAME)).thenReturn(false); + + when() + .get() + .then() + .statusCode(400); + } + + @Test + public void getShouldReturnUserErrorWithNonExistingUser() throws Exception { + when(usersRepository.contains(USERNAME)).thenReturn(false); + + when() + .get(MAILBOX_NAME) + .then() + .statusCode(400); + } + + @Test + public void putShouldReturnUserErrorWithNonExistingUser() throws Exception { + when(usersRepository.contains(USERNAME)).thenReturn(false); + + when() + .put(MAILBOX_NAME) + .then() + .statusCode(400); + } + + @Test + public void deleteShouldReturnUserErrorWithNonExistingUser() throws Exception { + when(usersRepository.contains(USERNAME)).thenReturn(false); + + when() + .put(MAILBOX_NAME) + .then() + .statusCode(400); + } + + @Test + public void deleteMailboxesShouldReturnUserErrorWithNonExistingUser() throws Exception { + when(usersRepository.contains(USERNAME)).thenReturn(false); + + when() + .delete() + .then() + .statusCode(400); + } + + @Test + public void getMailboxesShouldReturnEmptyListByDefault() { + when() + .get() + .then() + .statusCode(200) + .body(is("[]")); + } + + @Test + public void putShouldReturnNotFoundWhenNoMailboxName() { + when() + .put() + .then() + .statusCode(404); + } + + @Test + public void putShouldReturnNotFoundWhenJustSeparator() { + when() + .put(SEPARATOR) + .then() + .statusCode(404); + } + + @Test + public void putShouldReturnOk() { + when() + .put(MAILBOX_NAME) + .then() + .statusCode(204); + } + + @Test + public void putShouldReturnOkWhenIssuedTwoTimes() { + with() + .put(MAILBOX_NAME); + + when() + .put(MAILBOX_NAME) + .then() + .statusCode(204); + } + + @Test + public void putShouldAddAMailbox() { + with() + .put(MAILBOX_NAME); + + when() + .get() + .then() + .statusCode(200) + .body(is("[{\"mailboxName\":\"myMailboxName\"}]")); + } + + @Test + public void getShouldReturnNotFoundWhenMailboxDoesNotExist() { + when() + .get(MAILBOX_NAME) + .then() + .statusCode(404); + } + + @Test + public void getShouldReturnOkWhenMailboxExists() { + with() + .put(MAILBOX_NAME); + + when() + .get(MAILBOX_NAME) + .then() + .statusCode(204); + } + + @Test + public void deleteShouldReturnOkWhenMailboxDoesNotExist() { + when() + .delete(MAILBOX_NAME) + .then() + .statusCode(204); + } + + @Test + public void deleteShouldReturnOkWhenMailboxExists() { + with() + .put(MAILBOX_NAME); + + when() + .delete(MAILBOX_NAME) + .then() + .statusCode(204); + } + + @Test + public void deleteShouldRemoveMailbox() { + with() + .put(MAILBOX_NAME); + + with() + .delete(MAILBOX_NAME); + + when() + .get(MAILBOX_NAME) + .then() + .statusCode(404); + } + + @Test + public void deleteMailboxesShouldReturnOkWhenNoMailboxes() { + when() + .delete() + .then() + .statusCode(204); + } + + @Test + public void deleteMailboxesShouldReturnOkWhenMailboxes() { + with() + .put(MAILBOX_NAME); + + when() + .delete() + .then() + .statusCode(204); + } + + @Test + public void deleteMailboxesShouldRemoveAllUserMailboxes() { + with() + .put(MAILBOX_NAME); + + with() + .put("otherMailbox"); + + with() + .delete(); + + when() + .get() + .then() + .statusCode(200) + .body(is("[]")); + } + + @Test + public void deleteShouldReturnAConflictWhenMailboxHasChildren() { + with() + .put(MAILBOX_NAME); + + with() + .put(MAILBOX_NAME + ".child"); + + when() + .delete(MAILBOX_NAME) + .then() + .statusCode(409); + } + + @Test + public void deleteShouldReturnOkWhenDeletingChildMailboxes() { + with() + .put(MAILBOX_NAME); + + with() + .put(MAILBOX_NAME + ".child"); + + when() + .delete(MAILBOX_NAME + ".child") + .then() + .statusCode(204); + } + + @Test + public void deleteShouldBeAbleToRemoveChildMailboxes() { + with() + .put(MAILBOX_NAME); + + with() + .put(MAILBOX_NAME + ".child"); + + with() + .delete(MAILBOX_NAME + ".child"); + + when() + .get() + .then() + .statusCode(200) + .body(is("[{\"mailboxName\":\"myMailboxName\"}]")); + } + } + + public class ExceptionHandling { + + private MailboxManager mailboxManager; + + @Before + public void setUp() throws Exception { + mailboxManager = mock(MailboxManager.class); + when(mailboxManager.createSystemSession(any(), any())).thenReturn(mock(MailboxSession.class)); + + createServer(mailboxManager); + } + + @Test + public void putShouldGenerateInternalErrorOnUnknownException() throws Exception { + doThrow(new RuntimeException()).when(mailboxManager).createMailbox(any(), any()); + + when() + .put(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void putShouldGenerateInternalErrorOnUnknownMailboxException() throws Exception { + doThrow(new MailboxException()).when(mailboxManager).createMailbox(any(), any()); + + when() + .put(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void putShouldReturnOkOnMailboxExists() throws Exception { + doThrow(new MailboxExistsException(MAILBOX_NAME)).when(mailboxManager).createMailbox(any(), any()); + + when() + .put(MAILBOX_NAME) + .then() + .statusCode(204); + } + + @Test + public void deleteShouldGenerateInternalErrorOnUnknownExceptionOnDelete() throws Exception { + when(mailboxManager.search(any(), any())).thenReturn(ImmutableList.of(new SimpleMailboxMetaData(new MailboxPath("#private", USERNAME, "any"), '.'))); + doThrow(new RuntimeException()).when(mailboxManager).deleteMailbox(any(), any()); + + when() + .delete(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void deleteShouldGenerateInternalErrorOnUnknownExceptionOnSearch() throws Exception { + when(mailboxManager.search(any(), any())).thenThrow(new RuntimeException()); + + when() + .delete(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void deleteShouldGenerateInternalErrorOnUnknownMailboxExceptionOnDelete() throws Exception { + when(mailboxManager.search(any(), any())).thenReturn(ImmutableList.of(new SimpleMailboxMetaData(new MailboxPath("#private", USERNAME, "any"), '.'))); + doThrow(new MailboxException()).when(mailboxManager).deleteMailbox(any(), any()); + + when() + .delete(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void deleteShouldGenerateInternalErrorOnUnknownMailboxExceptionOnSearch() throws Exception { + when(mailboxManager.search(any(), any())).thenThrow(new MailboxException()); + + when() + .delete(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void deleteShouldReturnOkOnMailboxDoesNotExists() throws Exception { + doThrow(new MailboxNotFoundException(MAILBOX_NAME)).when(mailboxManager).deleteMailbox(any(), any()); + + when() + .delete(MAILBOX_NAME) + .then() + .statusCode(204); + } + + @Test + public void deleteShouldGenerateInternalErrorOnUnknownExceptionWhenListingMailboxes() throws Exception { + doThrow(new RuntimeException()).when(mailboxManager).search(any(), any()); + + when() + .delete() + .then() + .statusCode(500); + } + + @Test + public void deleteShouldGenerateInternalErrorOnMailboxExceptionWhenListingMailboxes() throws Exception { + doThrow(new MailboxException()).when(mailboxManager).search(any(), any()); + + when() + .delete() + .then() + .statusCode(500); + } + + + @Test + public void deleteShouldGenerateInternalErrorOnUnknownExceptionWhenRemovingMailboxes() throws Exception { + when(mailboxManager.search(any(), any())).thenReturn(ImmutableList.of(new SimpleMailboxMetaData(new MailboxPath("#private", USERNAME, "any"), '.'))); + doThrow(new RuntimeException()).when(mailboxManager).deleteMailbox(any(), any()); + + when() + .delete() + .then() + .statusCode(500); + } + + @Test + public void deleteShouldReturnOkOnMailboxNotFoundExceptionWhenRemovingMailboxes() throws Exception { + when(mailboxManager.search(any(), any())).thenReturn(ImmutableList.of(new SimpleMailboxMetaData(new MailboxPath("#private", USERNAME, "any"), '.'))); + doThrow(new MailboxNotFoundException("any")).when(mailboxManager).deleteMailbox(any(), any()); + + when() + .delete() + .then() + .statusCode(204); + } + + @Test + public void deleteShouldReturnInternalErrorOnMailboxExceptionWhenRemovingMailboxes() throws Exception { + when(mailboxManager.search(any(), any())).thenReturn(ImmutableList.of(new SimpleMailboxMetaData(new MailboxPath("#private", USERNAME, "any"), '.'))); + doThrow(new MailboxException()).when(mailboxManager).deleteMailbox(any(), any()); + + when() + .delete() + .then() + .statusCode(500); + } + + @Test + public void getShouldGenerateInternalErrorOnUnknownException() throws Exception { + doThrow(new RuntimeException()).when(mailboxManager).mailboxExists(any(), any()); + + when() + .get(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void getShouldGenerateInternalErrorOnUnknownMailboxException() throws Exception { + doThrow(new MailboxException()).when(mailboxManager).mailboxExists(any(), any()); + + when() + .get(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void getMailboxesShouldGenerateInternalErrorOnUnknownException() throws Exception { + doThrow(new RuntimeException()).when(mailboxManager).search(any(), any()); + + when() + .get() + .then() + .statusCode(500); + } + + @Test + public void getMailboxesShouldGenerateInternalErrorOnUnknownMailboxException() throws Exception { + doThrow(new MailboxException()).when(mailboxManager).search(any(), any()); + + when() + .get() + .then() + .statusCode(500); + } + + @Test + public void getMailboxesShouldGenerateInternalErrorOnRepositoryException() throws Exception { + doThrow(new RuntimeException()).when(usersRepository).contains(USERNAME); + + when() + .get() + .then() + .statusCode(500); + } + + @Test + public void getShouldGenerateInternalErrorOnRepositoryException() throws Exception { + doThrow(new RuntimeException()).when(usersRepository).contains(USERNAME); + + when() + .get(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void putShouldGenerateInternalErrorOnRepositoryException() throws Exception { + doThrow(new RuntimeException()).when(usersRepository).contains(USERNAME); + + when() + .put(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void deleteShouldGenerateInternalErrorOnRepositoryException() throws Exception { + doThrow(new RuntimeException()).when(usersRepository).contains(USERNAME); + + when() + .delete(MAILBOX_NAME) + .then() + .statusCode(500); + } + + @Test + public void deleteMailboxesShouldGenerateInternalErrorOnRepositoryException() throws Exception { + doThrow(new RuntimeException()).when(usersRepository).contains(USERNAME); + + when() + .delete() + .then() + .statusCode(500); + } + + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
