http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/utils/JsonExtractorTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/utils/JsonExtractorTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/utils/JsonExtractorTest.java new file mode 100644 index 0000000..ff5f2cb --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/utils/JsonExtractorTest.java @@ -0,0 +1,114 @@ +/**************************************************************** + * 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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.Before; +import org.junit.Test; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Preconditions; + +public class JsonExtractorTest { + + private JsonExtractor<Request> jsonExtractor; + + @Before + public void setUp() { + jsonExtractor = new JsonExtractor<>(Request.class); + } + + @Test + public void parseShouldThrowOnNullInput() throws Exception { + assertThatThrownBy(() -> jsonExtractor.parse(null)).isInstanceOf(NullPointerException.class); + } + + @Test + public void parseShouldThrowOnEmptyInput() throws Exception { + assertThatThrownBy(() -> jsonExtractor.parse("")).isInstanceOf(JsonExtractException.class); + } + + @Test + public void parseShouldThrowOnBrokenJson() throws Exception { + assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":\"broken")).isInstanceOf(JsonExtractException.class); + } + + @Test + public void parseShouldThrowOnEmptyJson() throws Exception { + assertThatThrownBy(() -> jsonExtractor.parse("{}")).isInstanceOf(JsonExtractException.class); + } + + @Test + public void parseShouldThrowOnMissingMandatoryField() throws Exception { + assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":\"any\"}")).isInstanceOf(JsonExtractException.class); + } + + @Test + public void parseShouldThrowOnValidationProblemIllegalArgumentException() throws Exception { + assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":\"\",\"field2\":\"any\"}")).isInstanceOf(JsonExtractException.class); + } + + @Test + public void parseShouldThrowOnValidationProblemNPE() throws Exception { + assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":null,\"field2\":\"any\"}")).isInstanceOf(JsonExtractException.class); + } + + @Test + public void parseShouldThrowOnExtraFiled() throws Exception { + assertThatThrownBy(() -> jsonExtractor.parse("{\"field1\":\"value\",\"field2\":\"any\",\"extra\":\"extra\"}")).isInstanceOf(JsonExtractException.class); + } + + @Test + public void parseShouldInstantiateDestinationClass() throws Exception { + String field1 = "value1"; + String field2 = "value2"; + Request request = jsonExtractor.parse("{\"field1\":\"" + field1 + "\",\"field2\":\"" + field2 + "\"}"); + + assertThat(request.getField1()).isEqualTo(field1); + assertThat(request.getField2()).isEqualTo(field2); + } + + static class Request { + private final String field1; + private final String field2; + + @JsonCreator + public Request(@JsonProperty("field1") String field1, + @JsonProperty("field2") String field2) { + Preconditions.checkNotNull(field1); + Preconditions.checkNotNull(field2); + Preconditions.checkArgument(!field1.isEmpty()); + this.field1 = field1; + this.field2 = field2; + } + + public String getField1() { + return field1; + } + + public String getField2() { + return field2; + } + } + +}
http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-data/pom.xml ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-data/pom.xml b/server/protocols/webadmin/webadmin-data/pom.xml new file mode 100644 index 0000000..781baff --- /dev/null +++ b/server/protocols/webadmin/webadmin-data/pom.xml @@ -0,0 +1,280 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>james-server</artifactId> + <groupId>org.apache.james</groupId> + <version>3.0.0-beta6-SNAPSHOT</version> + <relativePath>../../../pom.xml</relativePath> + </parent> + + <artifactId>james-server-webadmin-data</artifactId> + <packaging>jar</packaging> + + <name>Apache James :: Server :: Web Admin :: data</name> + + <profiles> + <profile> + <id>noTest</id> + <activation> + <os> + <family>windows</family> + </os> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <skipTests>true</skipTests> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>disable-build-for-older-jdk</id> + <activation> + <jdk>(,1.8)</jdk> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>default-jar</id> + <phase>none</phase> + </execution> + <execution> + <id>jar</id> + <phase>none</phase> + </execution> + <execution> + <id>test-jar</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>default-compile</id> + <phase>none</phase> + </execution> + <execution> + <id>default-testCompile</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <executions> + <execution> + <id>default-test</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-source-plugin</artifactId> + <executions> + <execution> + <id>attach-sources</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-install-plugin</artifactId> + <executions> + <execution> + <id>default-install</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <executions> + <execution> + <id>default-resources</id> + <phase>none</phase> + </execution> + <execution> + <id>default-testResources</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-site-plugin</artifactId> + <executions> + <execution> + <id>attach-descriptor</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>build-for-jdk-8</id> + <activation> + <jdk>[1.8,)</jdk> + </activation> + <dependencies> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>james-server-util-java8</artifactId> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>james-server-data-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>james-server-data-memory</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>james-server-webadmin-core</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>metrics-logger</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + <dependency> + <groupId>com.jayway.restassured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>de.bechte.junit</groupId> + <artifactId>junit-hierarchicalcontextrunner</artifactId> + <version>4.11.3</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>javax.inject</groupId> + <artifactId>javax.inject</artifactId> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>${assertj-3.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>java-hamcrest</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <archive> + <manifest> + <mainClass>fully.qualified.MainClass</mainClass> + </manifest> + </archive> + <descriptorRefs> + <descriptorRef>jar-with-dependencies</descriptorRef> + </descriptorRefs> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>animal-sniffer-java-8</id> + <activation> + <jdk>[1.8,)</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>animal-sniffer-maven-plugin</artifactId> + <configuration> + <signature> + <groupId>org.codehaus.mojo.signature</groupId> + <artifactId>java18</artifactId> + <version>1.0</version> + </signature> + </configuration> + <executions> + <execution> + <id>check_java_8</id> + <phase>test</phase> + <goals> + <goal>check</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/AddUserRequest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/AddUserRequest.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/AddUserRequest.java new file mode 100644 index 0000000..1d102c2 --- /dev/null +++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/AddUserRequest.java @@ -0,0 +1,39 @@ +/**************************************************************** + * 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.dto; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Preconditions; + +public class AddUserRequest { + + private final char[] password; + + @JsonCreator + public AddUserRequest(@JsonProperty("password") char[] password) { + Preconditions.checkNotNull(password); + this.password = password; + } + + public char[] getPassword() { + return password; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/UserResponse.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/UserResponse.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/UserResponse.java new file mode 100644 index 0000000..5fe1846 --- /dev/null +++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/UserResponse.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.dto; + +public class UserResponse { + + private final String username; + + public UserResponse(String username) { + this.username = username; + } + + public String getUsername() { + return username; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DomainRoutes.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DomainRoutes.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DomainRoutes.java new file mode 100644 index 0000000..2008278 --- /dev/null +++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DomainRoutes.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 static org.apache.james.webadmin.Constants.SEPARATOR; + +import javax.inject.Inject; + +import org.apache.james.domainlist.api.DomainList; +import org.apache.james.domainlist.api.DomainListException; +import org.apache.james.webadmin.Constants; +import org.apache.james.webadmin.Routes; +import org.apache.james.webadmin.utils.JsonTransformer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; + +import spark.Request; +import spark.Response; +import spark.Service; + +public class DomainRoutes implements Routes { + + private static final String DOMAIN_NAME = ":domainName"; + private static final Logger LOGGER = LoggerFactory.getLogger(DomainRoutes.class); + + public static final String DOMAINS = "/domains"; + public static final String SPECIFIC_DOMAIN = DOMAINS + SEPARATOR + DOMAIN_NAME; + public static final int MAXIMUM_DOMAIN_SIZE = 256; + + + private final DomainList domainList; + private final JsonTransformer jsonTransformer; + + @Inject + public DomainRoutes(DomainList domainList, JsonTransformer jsonTransformer) { + this.domainList = domainList; + this.jsonTransformer = jsonTransformer; + } + + @Override + public void define(Service service) { + service.get(DOMAINS, + (request, response) -> domainList.getDomains(), + jsonTransformer); + + service.get(SPECIFIC_DOMAIN, this::exists); + + service.put(SPECIFIC_DOMAIN, this::addDomain); + + service.delete(SPECIFIC_DOMAIN, this::removeDomain); + } + + private String removeDomain(Request request, Response response) { + try { + String domain = request.params(DOMAIN_NAME); + removeDomain(domain); + } catch (DomainListException e) { + LOGGER.info("{} did not exists", request.params(DOMAIN_NAME)); + } + response.status(204); + return Constants.EMPTY_BODY; + } + + private void removeDomain(String domain) throws DomainListException { + Preconditions.checkArgument(!Strings.isNullOrEmpty(domain)); + domainList.removeDomain(domain); + } + + private String addDomain(Request request, Response response) { + try { + addDomain(request.params(DOMAIN_NAME)); + response.status(204); + } catch (DomainListException e) { + LOGGER.info("{} already exists", request.params(DOMAIN_NAME)); + response.status(204); + } catch (IllegalArgumentException e) { + LOGGER.info("Invalid request for domain creation"); + response.status(400); + } + return Constants.EMPTY_BODY; + } + + private void addDomain(String domain) throws DomainListException { + Preconditions.checkArgument(!Strings.isNullOrEmpty(domain)); + Preconditions.checkArgument(!domain.contains("@")); + Preconditions.checkArgument(domain.length() < MAXIMUM_DOMAIN_SIZE); + domainList.addDomain(domain); + } + + private String exists(Request request, Response response) throws DomainListException { + if (!domainList.containsDomain(request.params(DOMAIN_NAME))) { + response.status(404); + } else { + response.status(204); + } + return Constants.EMPTY_BODY; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java new file mode 100644 index 0000000..0ead90a --- /dev/null +++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/UserRoutes.java @@ -0,0 +1,104 @@ +/**************************************************************** + * 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 org.apache.james.webadmin.Constants.SEPARATOR; + +import javax.inject.Inject; + +import org.apache.james.user.api.UsersRepositoryException; +import org.apache.james.webadmin.Constants; +import org.apache.james.webadmin.Routes; +import org.apache.james.webadmin.dto.AddUserRequest; +import org.apache.james.webadmin.service.UserService; +import org.apache.james.webadmin.utils.JsonExtractException; +import org.apache.james.webadmin.utils.JsonExtractor; +import org.apache.james.webadmin.utils.JsonTransformer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import spark.Request; +import spark.Response; +import spark.Service; + +public class UserRoutes implements Routes { + + private static final String USER_NAME = ":userName"; + private static final Logger LOGGER = LoggerFactory.getLogger(UserRoutes.class); + + public static final String USERS = "/users"; + + private final UserService userService; + private final JsonTransformer jsonTransformer; + private final JsonExtractor<AddUserRequest> jsonExtractor; + + @Inject + public UserRoutes(UserService userService, JsonTransformer jsonTransformer) { + this.userService = userService; + this.jsonTransformer = jsonTransformer; + this.jsonExtractor = new JsonExtractor<>(AddUserRequest.class); + } + + @Override + public void define(Service service) { + service.get(USERS, + (request, response) -> userService.getUsers(), + jsonTransformer); + + service.put(USERS + SEPARATOR + USER_NAME, this::upsertUser); + + service.delete(USERS + SEPARATOR + USER_NAME, this::removeUser); + } + + private String removeUser(Request request, Response response) { + String username = request.params(USER_NAME); + try { + userService.removeUser(username); + response.status(204); + return Constants.EMPTY_BODY; + } catch (UsersRepositoryException e) { + response.status(204); + return "The user " + username + " does not exists"; + } catch (IllegalArgumentException e) { + LOGGER.info("Invalid user path", e); + response.status(400); + return Constants.EMPTY_BODY; + } + } + + private String upsertUser(Request request, Response response) throws UsersRepositoryException { + try { + return userService.upsertUser(request.params(USER_NAME), + jsonExtractor.parse(request.body()).getPassword(), + response); + } catch (JsonExtractException e) { + LOGGER.info("Error while deserializing addUser request", e); + response.status(400); + return Constants.EMPTY_BODY; + } catch (IllegalArgumentException e) { + LOGGER.info("Invalid user path", e); + response.status(400); + return Constants.EMPTY_BODY; + } + } + + + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java new file mode 100644 index 0000000..ee0952d --- /dev/null +++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.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.webadmin.service; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import org.apache.james.user.api.UsersRepository; +import org.apache.james.user.api.UsersRepositoryException; +import org.apache.james.user.api.model.User; +import org.apache.james.util.streams.Iterators; +import org.apache.james.webadmin.dto.UserResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.steveash.guavate.Guavate; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; + +import spark.Response; + +public class UserService { + + private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class); + private static final String EMPTY_BODY = ""; + public static final int MAXIMUM_MAIL_ADDRESS_LENGTH = 255; + + private final UsersRepository usersRepository; + + @Inject + public UserService(UsersRepository usersRepository) { + this.usersRepository = usersRepository; + } + + public List<UserResponse> getUsers() throws UsersRepositoryException { + return Optional.ofNullable(usersRepository.list()) + .map(Iterators::toStream) + .orElse(Stream.of()) + .map(UserResponse::new) + .collect(Guavate.toImmutableList()); + } + + public void removeUser(String username) throws UsersRepositoryException { + usernamePreconditions(username); + usersRepository.removeUser(username); + } + + public String upsertUser(String username, char[] password, Response response) throws UsersRepositoryException { + usernamePreconditions(username); + User user = usersRepository.getUserByName(username); + try { + upsert(user, username, password); + response.status(204); + } catch (UsersRepositoryException e) { + LOGGER.info("Error creating or updating user : {}", e.getMessage()); + response.status(409); + } + return EMPTY_BODY; + } + + private void usernamePreconditions(String username) { + Preconditions.checkArgument(!Strings.isNullOrEmpty(username)); + Preconditions.checkArgument(username.length() < MAXIMUM_MAIL_ADDRESS_LENGTH); + } + + private void upsert(User user, String username, char[] password) throws UsersRepositoryException { + if (user == null) { + usersRepository.addUser(username, new String(password)); + } else { + user.setPassword(new String(password)); + usersRepository.updateUser(user); + } + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DomainRoutesTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DomainRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DomainRoutesTest.java new file mode 100644 index 0000000..1df89d2 --- /dev/null +++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DomainRoutesTest.java @@ -0,0 +1,326 @@ +/**************************************************************** + * 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.given; +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.containsString; +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 java.net.InetAddress; + +import org.apache.james.dnsservice.api.DNSService; +import org.apache.james.domainlist.api.DomainList; +import org.apache.james.domainlist.api.DomainListException; +import org.apache.james.domainlist.memory.MemoryDomainList; +import org.apache.james.metrics.logger.DefaultMetricFactory; +import org.apache.james.webadmin.WebAdminServer; +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Charsets; +import com.jayway.restassured.RestAssured; +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.http.ContentType; + +import de.bechte.junit.runners.context.HierarchicalContextRunner; + +@RunWith(HierarchicalContextRunner.class) +public class DomainRoutesTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(DomainRoutesTest.class); + public static final String DOMAIN = "domain"; + + private WebAdminServer webAdminServer; + + private void createServer(DomainList domainList) throws Exception { + webAdminServer = new WebAdminServer( + new DefaultMetricFactory(), + new DomainRoutes(domainList, new JsonTransformer())); + webAdminServer.configure(NO_CONFIGURATION); + webAdminServer.await(); + + RestAssured.requestSpecification = new RequestSpecBuilder() + .setContentType(ContentType.JSON) + .setAccept(ContentType.JSON) + .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8))) + .setPort(webAdminServer.getPort().toInt()) + .setBasePath(DomainRoutes.DOMAINS) + .build(); + + } + + @After + public void stop() { + webAdminServer.destroy(); + } + + public class NormalBehaviour { + + @Before + public void setUp() throws Exception { + DNSService dnsService = mock(DNSService.class); + when(dnsService.getHostName(any())).thenReturn("localhost"); + when(dnsService.getLocalHost()).thenReturn(InetAddress.getByName("localhost")); + + MemoryDomainList domainList = new MemoryDomainList(); + domainList.setDNSService(dnsService); + domainList.setLog(LOGGER); + domainList.setAutoDetectIP(false); + createServer(domainList); + } + + @Test + public void getDomainsShouldBeEmptyByDefault() { + given() + .get() + .then() + .statusCode(200) + .body(is("[]")); + } + + @Test + public void putShouldReturnErrorWhenUsedWithEmptyDomain() { + given() + .put(SEPARATOR) + .then() + .statusCode(404); + } + + @Test + public void deleteShouldReturnErrorWhenUsedWithEmptyDomain() { + given() + .delete(SEPARATOR) + .then() + .statusCode(404); + } + + @Test + public void putShouldBeOk() { + given() + .put(DOMAIN) + .then() + .statusCode(204); + } + + @Test + public void getDomainsShouldDisplayAddedDomains() { + with() + .put(DOMAIN); + + when() + .get() + .then() + .statusCode(200) + .body(containsString(DOMAIN)); + } + + @Test + public void putShouldReturnUserErrorWhenNameContainsAT() { + when() + .put(DOMAIN + "@" + DOMAIN) + .then() + .statusCode(400); + } + + @Test + public void putShouldReturnUserErrorWhenNameContainsUrlSeparator() { + when() + .put(DOMAIN + "/" + DOMAIN) + .then() + .statusCode(404); + } + + @Test + public void putShouldReturnUserErrorWhenNameIsTooLong() { + when() + .put(DOMAIN + "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789." + + "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789." + + "0123456789.0123456789.0123456789.") + .then() + .statusCode(400); + } + + @Test + public void putShouldWorkOnTheSecondTimeForAGivenValue() { + with() + .put(DOMAIN); + + when() + .put(DOMAIN) + .then() + .statusCode(204); + } + + @Test + public void deleteShouldRemoveTheGivenDomain() { + with() + .put(DOMAIN); + + when() + .delete(DOMAIN) + .then() + .statusCode(204); + + when() + .get() + .then() + .statusCode(200) + .body(is("[]")); + } + + @Test + public void deleteShouldBeOkWhenTheDomainIsNotPresent() { + given() + .delete(DOMAIN) + .then() + .statusCode(204); + } + + @Test + public void getDomainShouldReturnOkWhenTheDomainIsPresent() { + with() + .put(DOMAIN); + + when() + .get(DOMAIN) + .then() + .statusCode(204); + } + + @Test + public void getDomainShouldReturnNotFoundWhenTheDomainIsAbsent() { + given() + .get(DOMAIN) + .then() + .statusCode(404); + } + + } + + public class ExceptionHandling { + + private DomainList domainList; + private String domain; + + @Before + public void setUp() throws Exception { + domainList = mock(DomainList.class); + createServer(domainList); + domain = "domain"; + } + + @Test + public void deleteShouldReturnErrorOnUnknownException() throws Exception { + doThrow(new RuntimeException()).when(domainList).removeDomain(domain); + + when() + .delete(DOMAIN) + .then() + .statusCode(500); + } + + @Test + public void putShouldReturnErrorOnUnknownException() throws Exception { + doThrow(new RuntimeException()).when(domainList).addDomain(domain); + + when() + .put(DOMAIN) + .then() + .statusCode(500); + } + + @Test + public void getDomainShouldReturnErrorOnUnknownException() throws Exception { + when(domainList.containsDomain(domain)).thenThrow(new RuntimeException()); + + when() + .get(DOMAIN) + .then() + .statusCode(500); + } + + @Test + public void getDomainsShouldReturnErrorOnUnknownException() throws Exception { + when(domainList.getDomains()).thenThrow(new RuntimeException()); + + when() + .get() + .then() + .statusCode(500); + } + + @Test + public void deleteShouldReturnOkWhenDomainListException() throws Exception { + doThrow(new DomainListException("message")).when(domainList).removeDomain(domain); + + when() + .delete(DOMAIN) + .then() + .statusCode(204); + } + + @Test + public void putShouldReturnOkWhenDomainListException() throws Exception { + doThrow(new DomainListException("message")).when(domainList).addDomain(domain); + + when() + .put(DOMAIN) + .then() + .statusCode(204); + } + + @Test + public void getDomainShouldReturnErrorOnDomainListException() throws Exception { + when(domainList.containsDomain(domain)).thenThrow(new DomainListException("message")); + + when() + .get(DOMAIN) + .then() + .statusCode(500); + } + + @Test + public void getDomainsShouldReturnErrorOnDomainListException() throws Exception { + when(domainList.getDomains()).thenThrow(new DomainListException("message")); + + when() + .get() + .then() + .statusCode(500); + } + + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UsersRoutesTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UsersRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UsersRoutesTest.java new file mode 100644 index 0000000..684ea9b --- /dev/null +++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UsersRoutesTest.java @@ -0,0 +1,422 @@ +/**************************************************************** + * 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.given; +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.WebAdminServer.NO_CONFIGURATION; +import static org.hamcrest.CoreMatchers.equalTo; +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.domainlist.api.DomainList; +import org.apache.james.metrics.logger.DefaultMetricFactory; +import org.apache.james.user.api.UsersRepository; +import org.apache.james.user.api.UsersRepositoryException; +import org.apache.james.user.api.model.User; +import org.apache.james.user.memory.MemoryUsersRepository; +import org.apache.james.webadmin.WebAdminServer; +import org.apache.james.webadmin.service.UserService; +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.jayway.restassured.RestAssured; +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.http.ContentType; + +import de.bechte.junit.runners.context.HierarchicalContextRunner; + +@RunWith(HierarchicalContextRunner.class) +public class UsersRoutesTest { + + public static final String DOMAIN = "domain"; + public static final String USERNAME = "username@" + DOMAIN; + private WebAdminServer webAdminServer; + + private void createServer(UsersRepository usersRepository) throws Exception { + webAdminServer = new WebAdminServer( + new DefaultMetricFactory(), + new UserRoutes(new UserService(usersRepository), new JsonTransformer())); + webAdminServer.configure(NO_CONFIGURATION); + webAdminServer.await(); + + RestAssured.requestSpecification = new RequestSpecBuilder() + .setContentType(ContentType.JSON) + .setAccept(ContentType.JSON) + .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8))) + .setPort(webAdminServer.getPort().toInt()) + .setBasePath(UserRoutes.USERS) + .build(); + } + + @After + public void stop() { + webAdminServer.destroy(); + } + + public class NormalBehaviour { + + @Before + public void setUp() throws Exception { + DomainList domainList = mock(DomainList.class); + when(domainList.containsDomain(DOMAIN)).thenReturn(true); + + MemoryUsersRepository usersRepository = MemoryUsersRepository.withVirtualHosting(); + usersRepository.setDomainList(domainList); + + createServer(usersRepository); + } + + @Test + public void getUsersShouldBeEmptyByDefault() { + when() + .get() + .then() + .statusCode(200) + .body(is("[]")); + } + + @Test + public void putShouldReturnUserErrorWhenNoBody() { + when() + .put(USERNAME) + .then() + .statusCode(400); + } + + @Test + public void postShouldReturnUserErrorWhenEmptyJsonBody() { + given() + .body("{}") + .when() + .put(USERNAME) + .then() + .statusCode(400); + } + + @Test + public void postShouldReturnUserErrorWhenWrongJsonBody() { + given() + .body("{\"bad\":\"any\"}") + .when() + .put(USERNAME) + .then() + .statusCode(400); + } + + @Test + public void postShouldReturnOkWhenValidJsonBody() { + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME) + .then() + .statusCode(204); + } + + @Test + public void postShouldReturnRequireNonNullPassword() { + given() + .body("{\"password\":null}") + .when() + .put(USERNAME) + .then() + .statusCode(400); + } + + @Test + public void postShouldAddTheUser() { + with() + .body("{\"password\":\"password\"}") + .put(USERNAME); + + when() + .get() + .then() + .statusCode(200) + .body(equalTo("[{\"username\":\"" + USERNAME + "\"}]")); + } + + @Test + public void postingTwoTimesShouldBeAllowed() { + // Given + with() + .body("{\"password\":\"password\"}") + .put(USERNAME); + + // When + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME) + .then() + .statusCode(204); + + // Then + when() + .get() + .then() + .statusCode(200) + .body(equalTo("[{\"username\":\"" + USERNAME + "\"}]")); + } + + @Test + public void deleteShouldReturnOk() { + when() + .delete(USERNAME) + .then() + .statusCode(204); + } + + @Test + public void deleteShouldReturnBadRequestWhenEmptyUserName() { + when() + .delete("/") + .then() + .statusCode(404); + } + + @Test + public void deleteShouldReturnBadRequestWhenUsernameIsTooLong() { + when() + .delete(USERNAME + "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789." + + "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789." + + "0123456789.0123456789.0123456789.") + .then() + .statusCode(400); + } + + @Test + public void deleteShouldReturnNotFoundWhenUsernameContainsSlash() { + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME + "/" + USERNAME) + .then() + .statusCode(404); + } + + @Test + public void putShouldReturnBadRequestWhenEmptyUserName() { + given() + .body("{\"password\":\"password\"}") + .when() + .put("/") + .then() + .statusCode(404); + } + + @Test + public void putShouldReturnBadRequestWhenUsernameIsTooLong() { + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME + "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789." + + "0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789." + + "0123456789.0123456789.0123456789.") + .then() + .statusCode(400); + } + + @Test + public void putShouldReturnNotFoundWhenUsernameContainsSlash() { + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME + "/" + USERNAME) + .then() + .statusCode(404); + } + + @Test + public void deleteShouldRemoveAssociatedUser() { + // Given + with() + .body("{\"password\":\"password\"}") + .put(USERNAME); + + // When + when() + .delete(USERNAME) + .then() + .statusCode(204); + + // Then + when() + .get() + .then() + .statusCode(200) + .body(equalTo("[]")); + } + + @Test + public void deleteShouldStillBeValidWithExtraBody() { + given() + .body("{\"bad\":\"any\"}") + .when() + .delete(USERNAME) + .then() + .statusCode(204); + } + } + + public class ErrorHandling { + + private UsersRepository usersRepository; + private String username; + private String password; + + @Before + public void setUp() throws Exception { + usersRepository = mock(UsersRepository.class); + createServer(usersRepository); + username = "username@domain"; + password = "password"; + } + + @Test + public void deleteShouldStillBeOkWhenNoUser() throws Exception { + doThrow(new UsersRepositoryException("message")).when(usersRepository).removeUser(username); + + when() + .delete(USERNAME) + .then() + .statusCode(204); + } + + @Test + public void getShouldFailOnRepositoryException() throws Exception { + when(usersRepository.list()).thenThrow(new UsersRepositoryException("message")); + + when() + .get() + .then() + .statusCode(500); + } + + @Test + public void postShouldFailOnRepositoryExceptionOnGetUserByName() throws Exception { + when(usersRepository.getUserByName(username)).thenThrow(new UsersRepositoryException("message")); + + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME) + .then() + .statusCode(500); + } + + @Test + public void postShouldNotFailOnRepositoryExceptionOnAddUser() throws Exception { + when(usersRepository.getUserByName(username)).thenReturn(null); + doThrow(new UsersRepositoryException("message")).when(usersRepository).addUser(username, password); + + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME) + .then() + .statusCode(409); + } + + @Test + public void postShouldFailOnRepositoryExceptionOnUpdateUser() throws Exception { + when(usersRepository.getUserByName(username)).thenReturn(mock(User.class)); + doThrow(new UsersRepositoryException("message")).when(usersRepository).updateUser(any()); + + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME) + .then() + .statusCode(409); + } + + + @Test + public void deleteShouldFailOnUnknownException() throws Exception { + doThrow(new RuntimeException()).when(usersRepository).removeUser(username); + + when() + .delete(USERNAME) + .then() + .statusCode(500); + } + + @Test + public void getShouldFailOnUnknownException() throws Exception { + when(usersRepository.list()).thenThrow(new RuntimeException()); + + when() + .get() + .then() + .statusCode(500); + } + + @Test + public void postShouldFailOnUnknownExceptionOnGetUserByName() throws Exception { + when(usersRepository.getUserByName(username)).thenThrow(new RuntimeException()); + + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME) + .then() + .statusCode(500); + } + + @Test + public void postShouldFailOnUnknownExceptionOnAddUser() throws Exception { + when(usersRepository.getUserByName(username)).thenReturn(null); + doThrow(new RuntimeException()).when(usersRepository).addUser(username, password); + + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME) + .then() + .statusCode(500); + } + + @Test + public void postShouldFailOnUnknownExceptionOnGetUpdateUser() throws Exception { + when(usersRepository.getUserByName(username)).thenReturn(mock(User.class)); + doThrow(new RuntimeException()).when(usersRepository).updateUser(any()); + + given() + .body("{\"password\":\"password\"}") + .when() + .put(USERNAME) + .then() + .statusCode(500); + } + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-mailbox/pom.xml ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-mailbox/pom.xml b/server/protocols/webadmin/webadmin-mailbox/pom.xml new file mode 100644 index 0000000..3b17b61 --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox/pom.xml @@ -0,0 +1,292 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>james-server</artifactId> + <groupId>org.apache.james</groupId> + <version>3.0.0-beta6-SNAPSHOT</version> + <relativePath>../../../pom.xml</relativePath> + </parent> + + <artifactId>james-server-webadmin-mailbox</artifactId> + <packaging>jar</packaging> + + <name>Apache James :: Server :: Web Admin :: mailbox</name> + + <profiles> + <profile> + <id>noTest</id> + <activation> + <os> + <family>windows</family> + </os> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <skipTests>true</skipTests> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>disable-build-for-older-jdk</id> + <activation> + <jdk>(,1.8)</jdk> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>default-jar</id> + <phase>none</phase> + </execution> + <execution> + <id>jar</id> + <phase>none</phase> + </execution> + <execution> + <id>test-jar</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>default-compile</id> + <phase>none</phase> + </execution> + <execution> + <id>default-testCompile</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <executions> + <execution> + <id>default-test</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-source-plugin</artifactId> + <executions> + <execution> + <id>attach-sources</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-install-plugin</artifactId> + <executions> + <execution> + <id>default-install</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <executions> + <execution> + <id>default-resources</id> + <phase>none</phase> + </execution> + <execution> + <id>default-testResources</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-site-plugin</artifactId> + <executions> + <execution> + <id>attach-descriptor</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>build-for-jdk-8</id> + <activation> + <jdk>[1.8,)</jdk> + </activation> + <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> + <groupId>org.apache.james</groupId> + <artifactId>james-server-data-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>james-server-data-memory</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>james-server-webadmin-core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>metrics-logger</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + <dependency> + <groupId>com.jayway.restassured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>de.bechte.junit</groupId> + <artifactId>junit-hierarchicalcontextrunner</artifactId> + <version>4.11.3</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>javax.inject</groupId> + <artifactId>javax.inject</artifactId> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>${assertj-3.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>java-hamcrest</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <archive> + <manifest> + <mainClass>fully.qualified.MainClass</mainClass> + </manifest> + </archive> + <descriptorRefs> + <descriptorRef>jar-with-dependencies</descriptorRef> + </descriptorRefs> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>animal-sniffer-java-8</id> + <activation> + <jdk>[1.8,)</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>animal-sniffer-maven-plugin</artifactId> + <configuration> + <signature> + <groupId>org.codehaus.mojo.signature</groupId> + <artifactId>java18</artifactId> + <version>1.0</version> + </signature> + </configuration> + <executions> + <execution> + <id>check_java_8</id> + <phase>test</phase> + <goals> + <goal>check</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/MailboxResponse.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/MailboxResponse.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/MailboxResponse.java new file mode 100644 index 0000000..b552f45 --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/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.dto; + +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/cca0f398/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/QuotaDTO.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/QuotaDTO.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/QuotaDTO.java new file mode 100644 index 0000000..b3403ae --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/QuotaDTO.java @@ -0,0 +1,73 @@ +/**************************************************************** + * 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.dto; + +import org.apache.james.mailbox.model.Quota; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.google.common.base.Preconditions; + +@JsonDeserialize(builder = QuotaDTO.Builder.class) +public class QuotaDTO { + public static Builder builder() { + return new Builder(); + } + + @JsonPOJOBuilder(withPrefix="") + public static class Builder { + private long count; + private long size; + + public Builder count(long count) { + this.count = count; + return this; + } + + public Builder size(long size) { + this.size = size; + return this; + } + + public QuotaDTO build() { + return new QuotaDTO(count, size); + } + + } + + private final long count; + private final long size; + + private QuotaDTO(long count, long size) { + Preconditions.checkArgument(count >= Quota.UNLIMITED); + Preconditions.checkArgument(size >= Quota.UNLIMITED); + this.count = count; + this.size = size; + } + + public long getCount() { + return count; + } + + public long getSize() { + return size; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/QuotaRequest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/QuotaRequest.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/QuotaRequest.java new file mode 100644 index 0000000..7ae9e85 --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/dto/QuotaRequest.java @@ -0,0 +1,39 @@ +/**************************************************************** + * 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.dto; + +import com.google.common.base.Preconditions; + +public class QuotaRequest { + public static QuotaRequest parse(String serialized) { + return new QuotaRequest(Long.valueOf(serialized)); + } + + private final long value; + + public QuotaRequest(long value) { + Preconditions.checkArgument(value >= 0); + this.value = value; + } + + public long getValue() { + return value; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/GlobalQuotaRoutes.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/GlobalQuotaRoutes.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/GlobalQuotaRoutes.java new file mode 100644 index 0000000..eeca83a --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/GlobalQuotaRoutes.java @@ -0,0 +1,130 @@ +/**************************************************************** + * 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.mailbox.model.Quota; +import org.apache.james.mailbox.quota.MaxQuotaManager; +import org.apache.james.webadmin.Constants; +import org.apache.james.webadmin.Routes; +import org.apache.james.webadmin.dto.QuotaDTO; +import org.apache.james.webadmin.dto.QuotaRequest; +import org.apache.james.webadmin.utils.JsonExtractException; +import org.apache.james.webadmin.utils.JsonExtractor; +import org.apache.james.webadmin.utils.JsonTransformer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import spark.Service; + +public class GlobalQuotaRoutes implements Routes { + + public static final String QUOTA_ENDPOINT = "/quota"; + public static final String COUNT_ENDPOINT = QUOTA_ENDPOINT + "/count"; + public static final String SIZE_ENDPOINT = QUOTA_ENDPOINT + "/size"; + private static final Logger LOGGER = LoggerFactory.getLogger(Routes.class); + + private final MaxQuotaManager maxQuotaManager; + private final JsonTransformer jsonTransformer; + private final JsonExtractor<QuotaDTO> jsonExtractor; + + @Inject + public GlobalQuotaRoutes(MaxQuotaManager maxQuotaManager, JsonTransformer jsonTransformer) { + this.maxQuotaManager = maxQuotaManager; + this.jsonTransformer = jsonTransformer; + this.jsonExtractor = new JsonExtractor<>(QuotaDTO.class); + } + + @Override + public void define(Service service) { + service.get(COUNT_ENDPOINT, (request, response) -> { + long value = maxQuotaManager.getDefaultMaxMessage(); + response.status(200); + return value; + }, jsonTransformer); + + service.delete(COUNT_ENDPOINT, (request, response) -> { + maxQuotaManager.setDefaultMaxMessage(Quota.UNLIMITED); + response.status(204); + return Constants.EMPTY_BODY; + }); + + service.put(COUNT_ENDPOINT, (request, response) -> { + try { + QuotaRequest quotaRequest = QuotaRequest.parse(request.body()); + maxQuotaManager.setDefaultMaxMessage(quotaRequest.getValue()); + response.status(204); + } catch (IllegalArgumentException e) { + LOGGER.info("Invalid quota. Need to be an integer value greater than 0"); + response.status(400); + } + return Constants.EMPTY_BODY; + }); + + service.get(SIZE_ENDPOINT, (request, response) -> { + long value = maxQuotaManager.getDefaultMaxStorage(); + response.status(200); + return value; + }, jsonTransformer); + + service.delete(SIZE_ENDPOINT, (request, response) -> { + maxQuotaManager.setDefaultMaxStorage(Quota.UNLIMITED); + response.status(204); + return Constants.EMPTY_BODY; + }); + + service.put(SIZE_ENDPOINT, (request, response) -> { + try { + QuotaRequest quotaRequest = QuotaRequest.parse(request.body()); + maxQuotaManager.setDefaultMaxStorage(quotaRequest.getValue()); + response.status(204); + } catch (IllegalArgumentException e) { + LOGGER.info("Invalid quota. Need to be an integer value greater than 0"); + response.status(400); + } + return Constants.EMPTY_BODY; + }); + + service.get(QUOTA_ENDPOINT, (request, response) -> { + QuotaDTO quotaDTO = QuotaDTO.builder() + .count(maxQuotaManager.getDefaultMaxMessage()) + .size(maxQuotaManager.getDefaultMaxStorage()).build(); + response.status(200); + return quotaDTO; + }, jsonTransformer); + + service.put(QUOTA_ENDPOINT, ((request, response) -> { + try { + QuotaDTO quotaDTO = jsonExtractor.parse(request.body()); + maxQuotaManager.setDefaultMaxMessage(quotaDTO.getCount()); + maxQuotaManager.setDefaultMaxStorage(quotaDTO.getSize()); + response.status(204); + } catch (JsonExtractException e) { + LOGGER.info("Malformed JSON", e); + response.status(400); + } catch (IllegalArgumentException e) { + LOGGER.info("Quota should be positive or unlimited (-1)", e); + response.status(400); + } + return Constants.EMPTY_BODY; + })); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org