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

Reply via email to