http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/pom.xml ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/pom.xml b/server/protocols/webadmin/webadmin-core/pom.xml new file mode 100644 index 0000000..151bbca --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/pom.xml @@ -0,0 +1,281 @@ +<?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-core</artifactId> + <packaging>jar</packaging> + + <name>Apache James :: Server :: Web Admin :: Core</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-lifecycle-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>james-server-jwt</artifactId> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>metrics-api</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.github.fge</groupId> + <artifactId>throwing-lambdas</artifactId> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + <dependency> + <groupId>com.sparkjava</groupId> + <artifactId>spark-core</artifactId> + <version>2.5.5</version> + </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.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>nl.jqno.equalsverifier</groupId> + <artifactId>equalsverifier</artifactId> + <version>1.7.6</version> + <scope>test</scope> + </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-core/src/main/java/org/apache/james/webadmin/CORSFilter.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/CORSFilter.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/CORSFilter.java new file mode 100644 index 0000000..c4cfb29 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/CORSFilter.java @@ -0,0 +1,40 @@ +/**************************************************************** + * 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; + +import spark.Filter; +import spark.Request; +import spark.Response; + + +public class CORSFilter implements Filter { + private final String urlCORSOrigin; + + public CORSFilter(String urlCORSOrigin) { + this.urlCORSOrigin = urlCORSOrigin; + } + + @Override + public void handle(Request request, Response response) throws Exception { + response.header("Access-Control-Allow-Origin", urlCORSOrigin); + response.header("Access-Control-Request-Method", "DELETE, GET, POST, PUT"); + response.header("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept"); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Constants.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Constants.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Constants.java new file mode 100644 index 0000000..1031a86 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Constants.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; + +public interface Constants { + + String SEPARATOR = "/"; + String EMPTY_BODY = ""; + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/FixedPort.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/FixedPort.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/FixedPort.java new file mode 100644 index 0000000..7ada8a1 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/FixedPort.java @@ -0,0 +1,54 @@ +/**************************************************************** + * 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; + +import java.util.Objects; + +import com.google.common.base.Preconditions; + +public class FixedPort implements Port { + + private final int port; + + public FixedPort(int port) { + Preconditions.checkArgument(port > 0 && port < 65536, "Port should be strictly contained between 0 and 65536"); + this.port = port; + } + + @Override + public int toInt() { + return port; + } + + @Override + public final boolean equals(Object o) { + if (o instanceof FixedPort) { + FixedPort that = (FixedPort) o; + + return Objects.equals(this.port, that.port); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hash(port); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Port.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Port.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Port.java new file mode 100644 index 0000000..ff7a50b --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Port.java @@ -0,0 +1,26 @@ +/**************************************************************** + * 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; + +public interface Port { + + int toInt(); + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/RandomPort.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/RandomPort.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/RandomPort.java new file mode 100644 index 0000000..2b3ab5d --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/RandomPort.java @@ -0,0 +1,50 @@ +/**************************************************************** + * 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; + +import java.io.IOException; +import java.net.ServerSocket; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.base.Throwables; + +public class RandomPort implements Port { + + public static int findFreePort() { + try (ServerSocket socket = new ServerSocket(0)) { + return socket.getLocalPort(); + } catch (IOException e) { + throw Throwables.propagate(e); + } + } + + private final Supplier<Integer> portSupplier; + + public RandomPort() { + portSupplier = Suppliers.memoize(RandomPort::findFreePort); + } + + @Override + public int toInt() { + return portSupplier.get(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Routes.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Routes.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Routes.java new file mode 100644 index 0000000..bc8554a --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/Routes.java @@ -0,0 +1,28 @@ +/**************************************************************** + * 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; + +import spark.Service; + +public interface Routes { + + void define(Service service); + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/TlsConfiguration.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/TlsConfiguration.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/TlsConfiguration.java new file mode 100644 index 0000000..d549ea3 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/TlsConfiguration.java @@ -0,0 +1,124 @@ +/**************************************************************** + * 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; + +import java.util.Objects; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; + +public class TlsConfiguration { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String keystoreFilePath; + private String keystorePassword; + private String truststoreFilePath; + private String truststorePassword; + + public Builder raw(String keystoreFilePath, + String keystorePassword, + String truststoreFilePath, + String truststorePassword){ + Preconditions.checkNotNull(keystoreFilePath); + Preconditions.checkNotNull(keystorePassword); + + this.keystoreFilePath = keystoreFilePath; + this.keystorePassword = keystorePassword; + this.truststoreFilePath = truststoreFilePath; + this.truststorePassword = truststorePassword; + return this; + } + + public Builder selfSigned(String keystoreFilePath, String keystorePassword){ + Preconditions.checkNotNull(keystoreFilePath); + Preconditions.checkNotNull(keystorePassword); + + this.keystoreFilePath = keystoreFilePath; + this.keystorePassword = keystorePassword; + return this; + } + + public TlsConfiguration build() { + Preconditions.checkState(hasKeystoreInformation(), "If enabled, you need to provide keystore information"); + Preconditions.checkState(optionalHasTrustStoreInformation(), "You need to provide both information about trustStore"); + return new TlsConfiguration(keystoreFilePath, keystorePassword, truststoreFilePath, truststorePassword); + } + + private boolean optionalHasTrustStoreInformation() { + return (truststoreFilePath == null) == (truststorePassword == null); + } + + private boolean hasKeystoreInformation() { + return keystorePassword != null && keystoreFilePath != null; + } + + } + + private final String keystoreFilePath; + private final String keystorePassword; + private final String truststoreFilePath; + private final String truststorePassword; + + @VisibleForTesting + TlsConfiguration(String keystoreFilePath, String keystorePassword, String truststoreFilePath, String truststorePassword) { + this.keystoreFilePath = keystoreFilePath; + this.keystorePassword = keystorePassword; + this.truststoreFilePath = truststoreFilePath; + this.truststorePassword = truststorePassword; + } + + public String getKeystoreFilePath() { + return keystoreFilePath; + } + + public String getKeystorePassword() { + return keystorePassword; + } + + public String getTruststoreFilePath() { + return truststoreFilePath; + } + + public String getTruststorePassword() { + return truststorePassword; + } + + @Override + public final boolean equals(Object o) { + if (o instanceof TlsConfiguration) { + TlsConfiguration that = (TlsConfiguration) o; + + return Objects.equals(this.keystoreFilePath, that.keystoreFilePath) + && Objects.equals(this.keystorePassword, that.keystorePassword) + && Objects.equals(this.truststoreFilePath, that.truststoreFilePath) + && Objects.equals(this.truststorePassword, that.truststorePassword); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hash(keystoreFilePath, keystorePassword, truststoreFilePath, truststorePassword); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminConfiguration.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminConfiguration.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminConfiguration.java new file mode 100644 index 0000000..716645c --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminConfiguration.java @@ -0,0 +1,169 @@ +/**************************************************************** + * 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; + +import java.util.Objects; +import java.util.Optional; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; + +public class WebAdminConfiguration { + + public static final boolean DEFAULT_CORS_DISABLED = false; + public static final String CORS_ALL_ORIGINS = "*"; + + public static WebAdminConfiguration testingConfiguration() { + return WebAdminConfiguration.builder() + .enabled() + .port(new RandomPort()) + .build(); + } + + public static final WebAdminConfiguration DISABLED_CONFIGURATION = WebAdminConfiguration.builder() + .disabled() + .build(); + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Optional<Boolean> enabled = Optional.empty(); + private Optional<Port> port = Optional.empty(); + private Optional<Boolean> enableCORS = Optional.empty(); + private Optional<TlsConfiguration> tlsConfiguration = Optional.empty(); + private Optional<String> urlCORSOrigin = Optional.empty(); + + public Builder tls(TlsConfiguration tlsConfiguration) { + this.tlsConfiguration = Optional.of(tlsConfiguration); + return this; + } + + public Builder tls(Optional<TlsConfiguration> tlsConfiguration) { + this.tlsConfiguration = tlsConfiguration; + return this; + } + + public Builder port(Port port) { + this.port = Optional.of(port); + return this; + } + + public Builder enable(boolean isEnabled) { + this.enabled = Optional.of(isEnabled); + return this; + } + public Builder enabled() { + return enable(true); + } + + public Builder disabled() { + return enable(false); + } + + public Builder urlCORSOrigin(String origin) { + this.urlCORSOrigin = Optional.ofNullable(origin); + return this; + } + + public Builder enableCORS(boolean isEnabled) { + this.enableCORS = Optional.of(isEnabled); + return this; + } + + public Builder CORSenabled() { + return enableCORS(true); + } + + public Builder CORSdisabled() { + return enableCORS(false); + } + + public WebAdminConfiguration build() { + Preconditions.checkState(enabled.isPresent(), "You need to explicitly enable or disable WebAdmin server"); + Preconditions.checkState(!enabled.get() || port.isPresent(), "You need to specify a port for WebAdminConfiguration"); + return new WebAdminConfiguration(enabled.get(), + port, + tlsConfiguration, + enableCORS.orElse(DEFAULT_CORS_DISABLED), + urlCORSOrigin.orElse(CORS_ALL_ORIGINS)); + } + } + + private final boolean enabled; + private final Optional<Port> port; + private final Optional<TlsConfiguration> tlsConfiguration; + private final boolean enableCORS; + private final String urlCORSOrigin; + + @VisibleForTesting + WebAdminConfiguration(boolean enabled, Optional<Port> port, Optional<TlsConfiguration> tlsConfiguration, boolean enableCORS, String urlCORSOrigin) { + this.enabled = enabled; + this.port = port; + this.tlsConfiguration = tlsConfiguration; + this.enableCORS = enableCORS; + this.urlCORSOrigin = urlCORSOrigin; + } + + public boolean isEnabled() { + return enabled; + } + + public String getUrlCORSOrigin() { + return urlCORSOrigin; + } + + public Port getPort() { + return port.orElseThrow(() -> new IllegalStateException("No port was specified")); + } + + public TlsConfiguration getTlsConfiguration() { + return tlsConfiguration.orElseThrow(() -> new IllegalStateException("No tls configuration")); + } + + public boolean isEnableCORS() { + return enableCORS; + } + + public boolean isTlsEnabled() { + return tlsConfiguration.isPresent(); + } + + @Override + public final boolean equals(Object o) { + if (o instanceof WebAdminConfiguration) { + WebAdminConfiguration that = (WebAdminConfiguration) o; + + return Objects.equals(this.enabled, that.enabled) + && Objects.equals(this.port, that.port) + && Objects.equals(this.tlsConfiguration, that.tlsConfiguration) + && Objects.equals(this.enableCORS, that.enableCORS) + && Objects.equals(this.urlCORSOrigin, that.urlCORSOrigin); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hash(enabled, port, tlsConfiguration, enableCORS, urlCORSOrigin); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminServer.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminServer.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminServer.java new file mode 100644 index 0000000..abce524 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/WebAdminServer.java @@ -0,0 +1,129 @@ +/**************************************************************** + * 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; + +import java.io.IOException; +import java.util.Set; + +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.HierarchicalConfiguration; +import org.apache.james.lifecycle.api.Configurable; +import org.apache.james.metrics.api.MetricFactory; +import org.apache.james.webadmin.authentication.AuthenticationFilter; +import org.apache.james.webadmin.authentication.NoAuthenticationFilter; +import org.apache.james.webadmin.metric.MetricPostFilter; +import org.apache.james.webadmin.metric.MetricPreFilter; +import org.apache.james.webadmin.routes.CORSRoute; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableSet; + +import spark.Service; + +public class WebAdminServer implements Configurable { + + private static final Logger LOGGER = LoggerFactory.getLogger(WebAdminServer.class); + public static final HierarchicalConfiguration NO_CONFIGURATION = null; + public static final int DEFAULT_PORT = 8080; + + private final WebAdminConfiguration configuration; + private final Set<Routes> routesList; + private final Service service; + private final AuthenticationFilter authenticationFilter; + private final MetricFactory metricFactory; + + // Spark do not allow to retrieve allocated port when using a random port. Thus we generate the port. + @Inject + private WebAdminServer(WebAdminConfiguration configuration, Set<Routes> routesList, AuthenticationFilter authenticationFilter, + MetricFactory metricFactory) { + this.configuration = configuration; + this.routesList = routesList; + this.authenticationFilter = authenticationFilter; + this.metricFactory = metricFactory; + this.service = Service.ignite(); + } + + @VisibleForTesting + public WebAdminServer(MetricFactory metricFactory, Routes... routes) throws IOException { + this(WebAdminConfiguration.testingConfiguration(), + ImmutableSet.copyOf(routes), + new NoAuthenticationFilter(), + metricFactory); + } + + @Override + public void configure(HierarchicalConfiguration config) throws ConfigurationException { + if (configuration.isEnabled()) { + service.port(configuration.getPort().toInt()); + configureHTTPS(); + configureCORS(); + configureMetrics(); + service.before(authenticationFilter); + routesList.forEach(routes -> routes.define(service)); + service.awaitInitialization(); + LOGGER.info("Web admin server started"); + } + } + + private void configureMetrics() { + service.before(new MetricPreFilter(metricFactory)); + service.after(new MetricPostFilter()); + } + + private void configureHTTPS() { + if (configuration.isTlsEnabled()) { + TlsConfiguration tlsConfiguration = configuration.getTlsConfiguration(); + service.secure(tlsConfiguration.getKeystoreFilePath(), + tlsConfiguration.getKeystorePassword(), + tlsConfiguration.getTruststoreFilePath(), + tlsConfiguration.getTruststorePassword()); + LOGGER.info("Web admin set up to use HTTPS"); + } + } + + private void configureCORS() { + if (configuration.isEnabled()) { + service.before(new CORSFilter(configuration.getUrlCORSOrigin())); + new CORSRoute().define(service); + LOGGER.info("Web admin set up to enable CORS from " + configuration.getUrlCORSOrigin()); + } + } + + @PreDestroy + public void destroy() { + if (configuration.isEnabled()) { + service.stop(); + LOGGER.info("Web admin server stopped"); + } + } + + public void await() { + service.awaitInitialization(); + } + + public Port getPort() { + return configuration.getPort(); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/AuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/AuthenticationFilter.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/AuthenticationFilter.java new file mode 100644 index 0000000..f73b818 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/AuthenticationFilter.java @@ -0,0 +1,25 @@ +/**************************************************************** + * 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.authentication; + +import spark.Filter; + +public interface AuthenticationFilter extends Filter { +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java new file mode 100644 index 0000000..4832d6f --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java @@ -0,0 +1,76 @@ +/**************************************************************** + * 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.authentication; + +import static spark.Spark.halt; + +import java.util.Optional; + +import javax.inject.Inject; + +import org.apache.james.jwt.JwtTokenVerifier; + +import spark.Request; +import spark.Response; + +public class JwtFilter implements AuthenticationFilter { + public static final String AUTHORIZATION_HEADER_PREFIX = "Bearer "; + public static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + public static final String OPTIONS = "OPTIONS"; + + private final JwtTokenVerifier jwtTokenVerifier; + + @Inject + public JwtFilter(JwtTokenVerifier jwtTokenVerifier) { + this.jwtTokenVerifier = jwtTokenVerifier; + } + + @Override + public void handle(Request request, Response response) throws Exception { + if (request.requestMethod() != OPTIONS) { + Optional<String> bearer = Optional.ofNullable(request.headers(AUTHORIZATION_HEADER_NAME)) + .filter(value -> value.startsWith(AUTHORIZATION_HEADER_PREFIX)) + .map(value -> value.substring(AUTHORIZATION_HEADER_PREFIX.length())); + + checkHeaderPresent(bearer); + checkValidSignature(bearer); + checkIsAdmin(bearer); + } + } + + private void checkHeaderPresent(Optional<String> bearer) { + if (!bearer.isPresent()) { + halt(401, "No Bearer header."); + } + } + + private void checkValidSignature(Optional<String> bearer) { + if (!jwtTokenVerifier.verify(bearer.get())) { + halt(401, "Invalid Bearer header."); + } + } + + private void checkIsAdmin(Optional<String> bearer) { + if (!jwtTokenVerifier.hasAttribute("admin", true, bearer.get())) { + halt(401, "Non authorized user."); + } + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/NoAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/NoAuthenticationFilter.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/NoAuthenticationFilter.java new file mode 100644 index 0000000..45a2f5b --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/NoAuthenticationFilter.java @@ -0,0 +1,31 @@ +/**************************************************************** + * 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.authentication; + +import spark.Request; +import spark.Response; + +public class NoAuthenticationFilter implements AuthenticationFilter { + + @Override + public void handle(Request request, Response response) throws Exception { + + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/metric/MetricPostFilter.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/metric/MetricPostFilter.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/metric/MetricPostFilter.java new file mode 100644 index 0000000..4d9b8b7 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/metric/MetricPostFilter.java @@ -0,0 +1,37 @@ +/**************************************************************** + * 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.metric; + +import org.apache.james.metrics.api.TimeMetric; + +import spark.Filter; +import spark.Request; +import spark.Response; + +public class MetricPostFilter implements Filter { + + @Override + public void handle(Request request, Response response) throws Exception { + if (request.attribute(MetricPreFilter.METRICS) instanceof TimeMetric) { + TimeMetric timeMetric = request.attribute(MetricPreFilter.METRICS); + timeMetric.stopAndPublish(); + } + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/metric/MetricPreFilter.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/metric/MetricPreFilter.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/metric/MetricPreFilter.java new file mode 100644 index 0000000..1ed9d76 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/metric/MetricPreFilter.java @@ -0,0 +1,41 @@ +/**************************************************************** + * 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.metric; + +import org.apache.james.metrics.api.MetricFactory; + +import spark.Filter; +import spark.Request; +import spark.Response; + +public class MetricPreFilter implements Filter { + public static final String METRICS = "metrics"; + private final MetricFactory metricFactory; + + public MetricPreFilter(MetricFactory metricFactory) { + this.metricFactory = metricFactory; + } + + @Override + public void handle(Request request, Response response) throws Exception { + request.attribute(METRICS, metricFactory.timer("webAdmin")); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/CORSRoute.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/CORSRoute.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/CORSRoute.java new file mode 100644 index 0000000..39e9bd7 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/CORSRoute.java @@ -0,0 +1,42 @@ +/**************************************************************** + * 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 org.apache.james.webadmin.Routes; + +import spark.Service; + +public class CORSRoute implements Routes { + + @Override + public void define(Service service) { + service.options("/*", (request, response) -> { + String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers"); + if (accessControlRequestHeaders != null) { + response.header("Access-Control-Allow-Headers", accessControlRequestHeaders); + } + String accessControlRequestMethod = request.headers("Access-Control-Request-Method"); + if (accessControlRequestMethod != null) { + response.header("Access-Control-Allow-Methods", accessControlRequestMethod); + } + return ""; + }); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractException.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractException.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractException.java new file mode 100644 index 0000000..dc6b578 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractException.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 JsonExtractException extends Exception { + + public JsonExtractException(Throwable throwable) { + super(throwable); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java new file mode 100644 index 0000000..f4eb420 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java @@ -0,0 +1,44 @@ +/**************************************************************** + * 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 java.io.IOException; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonExtractor<Request> { + + private final ObjectMapper objectMapper; + private final Class<Request> type; + + public JsonExtractor(Class<Request> type) { + this.objectMapper = new ObjectMapper(); + this.type = type; + } + + public Request parse(String text) throws JsonExtractException { + try { + return objectMapper.readValue(text, type); + } catch (IOException e) { + throw new JsonExtractException(e); + } + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonTransformer.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonTransformer.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonTransformer.java new file mode 100644 index 0000000..3c32f0c --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonTransformer.java @@ -0,0 +1,41 @@ +/**************************************************************** + * 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 com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +import spark.ResponseTransformer; + +public class JsonTransformer implements ResponseTransformer { + + private final ObjectMapper objectMapper; + + public JsonTransformer() { + objectMapper = new ObjectMapper(); + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + } + + @Override + public String render(Object o) throws JsonProcessingException { + return objectMapper.writeValueAsString(o); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/FixedPortTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/FixedPortTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/FixedPortTest.java new file mode 100644 index 0000000..2712daa --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/FixedPortTest.java @@ -0,0 +1,57 @@ +/**************************************************************** + * 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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; + +public class FixedPortTest { + + @Test + public void toIntShouldThrowOnNegativePort() { + assertThatThrownBy(() -> new FixedPort(-1)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void toIntShouldThrowOnNullPort() { + assertThatThrownBy(() -> new FixedPort(0)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void toIntShouldThrowOnTooBigNumbers() { + assertThatThrownBy(() -> new FixedPort(65536)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void toIntShouldReturnedDesiredPort() { + int expectedPort = 452; + assertThat(new FixedPort(expectedPort).toInt()).isEqualTo(expectedPort); + } + + @Test + public void shouldMatchBeanContract() { + EqualsVerifier.forClass(FixedPort.class).verify(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/RandomPortTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/RandomPortTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/RandomPortTest.java new file mode 100644 index 0000000..07d1996 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/RandomPortTest.java @@ -0,0 +1,34 @@ +/**************************************************************** + * 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; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +public class RandomPortTest { + + @Test + public void toIntShouldReturnTwoTimeTheSameResult() { + RandomPort testee = new RandomPort(); + assertThat(testee.toInt()).isEqualTo(testee.toInt()); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/TlsConfigurationTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/TlsConfigurationTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/TlsConfigurationTest.java new file mode 100644 index 0000000..2d93282 --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/TlsConfigurationTest.java @@ -0,0 +1,88 @@ +/**************************************************************** + * 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; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import nl.jqno.equalsverifier.EqualsVerifier; + +public class TlsConfigurationTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void buildShouldThrowWhenNotEnabled() { + expectedException.expect(IllegalStateException.class); + + TlsConfiguration.builder().build(); + } + + @Test + public void buildShouldThrowWhenEnableWithoutKeystore() { + expectedException.expect(IllegalStateException.class); + + TlsConfiguration.builder().build(); + } + + @Test + public void selfSignedShouldThrowOnNullKeyStorePath() { + expectedException.expect(NullPointerException.class); + + TlsConfiguration.builder() + .selfSigned(null, "abc"); + } + + @Test + public void selfSignedShouldThrowOnNullKeyStorePassword() { + expectedException.expect(NullPointerException.class); + + TlsConfiguration.builder() + .selfSigned("abc", null); + } + + @Test + public void buildShouldWorkOnSelfSignedHttps() { + assertThat( + TlsConfiguration.builder() + .selfSigned("abcd", "efgh") + .build()) + .isEqualTo(new TlsConfiguration("abcd", "efgh", null, null)); + } + + @Test + public void buildShouldWorkOnTrustedHttps() { + assertThat( + TlsConfiguration.builder() + .raw("a", "b", "c", "d") + .build()) + .isEqualTo(new TlsConfiguration("a", "b", "c", "d")); + } + + @Test + public void shouldRespectBeanContract() { + EqualsVerifier.forClass(TlsConfiguration.class).verify(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/WebAdminConfigurationTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/WebAdminConfigurationTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/WebAdminConfigurationTest.java new file mode 100644 index 0000000..1030dcf --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/WebAdminConfigurationTest.java @@ -0,0 +1,181 @@ +/**************************************************************** + * 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; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import nl.jqno.equalsverifier.EqualsVerifier; + +public class WebAdminConfigurationTest { + + public static final FixedPort PORT = new FixedPort(80); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void buildShouldThrowWhenNoPortButEnabled() { + expectedException.expect(IllegalStateException.class); + + WebAdminConfiguration.builder().enabled().build(); + } + + @Test + public void buildShouldWorkWithoutPortWhenDisabled() { + assertThat(WebAdminConfiguration.builder() + .disabled() + .build()) + .extracting(WebAdminConfiguration::isEnabled) + .containsExactly(false); + } + + @Test + public void buildShouldFailOnNoEnable() { + expectedException.expect(IllegalStateException.class); + + WebAdminConfiguration.builder().port(PORT).build(); + } + + @Test + public void builderShouldBuildWithRightPort() { + assertThat( + WebAdminConfiguration.builder() + .enabled() + .port(PORT) + .build()) + .extracting(WebAdminConfiguration::getPort) + .containsExactly(PORT); + } + + + @Test + public void builderShouldBuildWithEnable() { + assertThat( + WebAdminConfiguration.builder() + .enabled() + .port(PORT) + .build()) + .extracting(WebAdminConfiguration::isEnabled) + .containsExactly(true); + } + + @Test + public void builderShouldAcceptHttps() { + TlsConfiguration tlsConfiguration = TlsConfiguration.builder() + .selfSigned("abcd", "efgh") + .build(); + + assertThat( + WebAdminConfiguration.builder() + .enabled() + .tls(tlsConfiguration) + .port(PORT) + .build()) + .extracting(WebAdminConfiguration::getTlsConfiguration) + .containsExactly(tlsConfiguration); + } + + @Test + public void builderShouldReturnTlsEnableWhenTlsConfiguration() { + TlsConfiguration tlsConfiguration = TlsConfiguration.builder() + .selfSigned("abcd", "efgh") + .build(); + + assertThat( + WebAdminConfiguration.builder() + .enabled() + .tls(tlsConfiguration) + .port(PORT) + .build()) + .extracting(WebAdminConfiguration::getTlsConfiguration) + .containsExactly(tlsConfiguration); + } + + @Test + public void builderShouldReturnTlsDisableWhenNoTlsConfiguration() { + assertThat( + WebAdminConfiguration.builder() + .enabled() + .port(PORT) + .build()) + .extracting(WebAdminConfiguration::isTlsEnabled) + .containsExactly(false); + } + + @Test + public void builderShouldCORSEnabled() { + assertThat( + WebAdminConfiguration.builder() + .enabled() + .port(PORT) + .CORSenabled() + .build()) + .extracting(WebAdminConfiguration::isEnableCORS) + .containsExactly(true); + } + + @Test + public void builderShouldAcceptAllOriginsByDefault() { + assertThat( + WebAdminConfiguration.builder() + .enabled() + .port(PORT) + .CORSenabled() + .build()) + .extracting(WebAdminConfiguration::getUrlCORSOrigin) + .containsExactly("*"); + } + + @Test + public void builderShouldCORSDisabled() { + assertThat( + WebAdminConfiguration.builder() + .enabled() + .port(PORT) + .CORSdisabled() + .build()) + .extracting(WebAdminConfiguration::isEnableCORS) + .containsExactly(false); + } + + @Test + public void builderShouldCORSWithOrigin() { + String origin = "linagora.com"; + assertThat( + WebAdminConfiguration.builder() + .enabled() + .port(PORT) + .CORSenabled() + .urlCORSOrigin(origin) + .build()) + .extracting(WebAdminConfiguration::getUrlCORSOrigin) + .containsExactly(origin); + } + + @Test + public void shouldMatchBeanContract() { + EqualsVerifier.forClass(WebAdminConfiguration.class).verify(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/cca0f398/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/authentication/JwtFilterTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/authentication/JwtFilterTest.java b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/authentication/JwtFilterTest.java new file mode 100644 index 0000000..6512ddb --- /dev/null +++ b/server/protocols/webadmin/webadmin-core/src/test/java/org/apache/james/webadmin/authentication/JwtFilterTest.java @@ -0,0 +1,124 @@ +/**************************************************************** + * 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.authentication; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.james.jwt.JwtTokenVerifier; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.google.common.collect.ImmutableSet; + +import spark.HaltException; +import spark.Request; +import spark.Response; + +public class JwtFilterTest { + + public static final Matcher<HaltException> STATUS_CODE_MATCHER_401 = new BaseMatcher<HaltException>() { + @Override + public boolean matches(Object o) { + if (o instanceof HaltException) { + HaltException haltException = (HaltException) o; + return haltException.statusCode() == 401; + } + return false; + } + + @Override + public void describeTo(Description description) {} + }; + + private JwtTokenVerifier jwtTokenVerifier; + private JwtFilter jwtFilter; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp() { + jwtTokenVerifier = mock(JwtTokenVerifier.class); + jwtFilter = new JwtFilter(jwtTokenVerifier); + } + + @Test + public void handleShouldRejectRequestWithHeaders() throws Exception { + Request request = mock(Request.class); + when(request.headers()).thenReturn(ImmutableSet.of()); + + expectedException.expect(HaltException.class); + expectedException.expect(STATUS_CODE_MATCHER_401); + + jwtFilter.handle(request, mock(Response.class)); + } + + @Test + public void handleShouldRejectRequestWithBearersHeaders() throws Exception { + Request request = mock(Request.class); + when(request.headers(JwtFilter.AUTHORIZATION_HEADER_NAME)).thenReturn("Invalid value"); + + expectedException.expect(HaltException.class); + expectedException.expect(STATUS_CODE_MATCHER_401); + + jwtFilter.handle(request, mock(Response.class)); + } + + @Test + public void handleShouldRejectRequestWithInvalidBearerHeaders() throws Exception { + Request request = mock(Request.class); + when(request.headers(JwtFilter.AUTHORIZATION_HEADER_NAME)).thenReturn("Bearer value"); + when(jwtTokenVerifier.verify("value")).thenReturn(false); + + expectedException.expect(HaltException.class); + expectedException.expect(STATUS_CODE_MATCHER_401); + + jwtFilter.handle(request, mock(Response.class)); + } + + @Test + public void handleShouldRejectRequestWithoutAdminClaim() throws Exception { + Request request = mock(Request.class); + when(request.headers(JwtFilter.AUTHORIZATION_HEADER_NAME)).thenReturn("Bearer value"); + when(jwtTokenVerifier.verify("value")).thenReturn(true); + when(jwtTokenVerifier.hasAttribute("admin", true, "value")).thenReturn(false); + + expectedException.expect(HaltException.class); + expectedException.expect(STATUS_CODE_MATCHER_401); + + jwtFilter.handle(request, mock(Response.class)); + } + + @Test + public void handleShouldAcceptValidJwt() throws Exception { + Request request = mock(Request.class); + when(request.headers(JwtFilter.AUTHORIZATION_HEADER_NAME)).thenReturn("Bearer value"); + when(jwtTokenVerifier.verify("value")).thenReturn(true); + when(jwtTokenVerifier.hasAttribute("admin", true, "value")).thenReturn(true); + + jwtFilter.handle(request, mock(Response.class)); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org