You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ad...@apache.org on 2016/01/18 11:18:16 UTC

svn commit: r1725214 [1/2] - in /james/project/trunk/server: ./ container/cassandra-guice/src/main/java/org/apache/james/jmap/ container/cassandra-guice/src/main/java/org/apache/james/modules/protocols/ container/cassandra-guice/src/test/java/org/apach...

Author: aduprat
Date: Mon Jan 18 10:18:15 2016
New Revision: 1725214

URL: http://svn.apache.org/viewvc?rev=1725214&view=rev
Log:
JAMES-1655 JWT implementation. Contributed by Duprat <ad...@linagora.com>

extract interface AuthenticationStrategy from AuthenticationFilter, and add an impl. for JWT auth
handle CORS required headers (through AllowAllCrossOriginRequests)
exclude OPTIONS request from being authenticated (related to CORS transactions).
add some jmap integration test for jwt auth. Contributed by Vignon <fv...@linagora.com>

Added:
    james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/NotAnAccessTokenException.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AccessTokenAuthenticationStrategy.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AllowAllCrossOriginRequests.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationStrategy.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/BypassAuthOnRequestMethod.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/JwtTokenVerifier.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/MissingOrInvalidKeyException.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyProvider.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyReader.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxSessionCreationException.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/NoValidAuthHeaderException.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/UnauthorizedException.java
    james/project/trunk/server/protocols/jmap/src/main/resources/
    james/project/trunk/server/protocols/jmap/src/main/resources/jwt-public.der
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AccessTokenAuthenticationStrategyTest.java
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/BypassAuthOnRequestMethodTest.java
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/JWTAuthenticationStrategyTest.java
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/JwtTokenVerifierTest.java
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyProviderTest.java
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyReaderTest.java
Removed:
    james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/NotAnUUIDException.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/BypassOnPostFilter.java
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/BypassOnPostFilterTest.java
Modified:
    james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
    james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/modules/protocols/JMAPServerModule.java
    james/project/trunk/server/container/cassandra-guice/src/test/java/org/apache/james/modules/TestJMAPServerModule.java
    james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/AccessToken.java
    james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/access/AccessTokenTest.java
    james/project/trunk/server/pom.xml
    james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java
    james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMailboxesMethodTest.java
    james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
    james/project/trunk/server/protocols/jmap/pom.xml
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationFilter.java
    james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
    james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AuthenticationFilterTest.java

Modified: james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java (original)
+++ james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/jmap/JMAPCommonModule.java Mon Jan 18 10:18:15 2016
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.james.jmap;
 
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.james.jmap.api.AccessTokenManager;
@@ -31,7 +32,9 @@ import org.apache.james.jmap.memory.acce
 import org.apache.james.jmap.utils.DefaultZonedDateTimeProvider;
 import org.apache.james.jmap.utils.ZonedDateTimeProvider;
 
+import com.google.common.collect.ImmutableList;
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 import com.google.inject.name.Names;
 
 public class JMAPCommonModule extends AbstractModule {
@@ -49,4 +52,13 @@ public class JMAPCommonModule extends Ab
         bind(AccessTokenManager.class).to(AccessTokenManagerImpl.class);
     }
 
+    @Provides
+    public List<AuthenticationStrategy> authStrategies(
+            AccessTokenAuthenticationStrategy accessTokenAuthenticationStrategy,
+            JWTAuthenticationStrategy jwtAuthenticationStrategy) {
+
+        return ImmutableList.of(
+                jwtAuthenticationStrategy,
+                accessTokenAuthenticationStrategy);
+    }
 }

Modified: james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/modules/protocols/JMAPServerModule.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/modules/protocols/JMAPServerModule.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/modules/protocols/JMAPServerModule.java (original)
+++ james/project/trunk/server/container/cassandra-guice/src/main/java/org/apache/james/modules/protocols/JMAPServerModule.java Mon Jan 18 10:18:15 2016
@@ -28,6 +28,9 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.google.inject.multibindings.Multibinder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.security.Security;
 
 public class JMAPServerModule extends AbstractModule {
 
@@ -53,6 +56,11 @@ public class JMAPServerModule extends Ab
         public void initModule() throws Exception {
             signatureHandler.init();
             server.configure(null);
+            registerPEMWithSecurityProvider();
+        }
+
+        private void registerPEMWithSecurityProvider() {
+            Security.addProvider(new BouncyCastleProvider());
         }
     }
 

Modified: james/project/trunk/server/container/cassandra-guice/src/test/java/org/apache/james/modules/TestJMAPServerModule.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/container/cassandra-guice/src/test/java/org/apache/james/modules/TestJMAPServerModule.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/container/cassandra-guice/src/test/java/org/apache/james/modules/TestJMAPServerModule.java (original)
+++ james/project/trunk/server/container/cassandra-guice/src/test/java/org/apache/james/modules/TestJMAPServerModule.java Mon Jan 18 10:18:15 2016
@@ -35,6 +35,16 @@ import com.google.inject.name.Names;
 
 public class TestJMAPServerModule extends AbstractModule{
 
+    private static final String PUBLIC_PEM_KEY = "-----BEGIN PUBLIC KEY-----\n" +
+            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlChO/nlVP27MpdkG0Bh\n" +
+            "16XrMRf6M4NeyGa7j5+1UKm42IKUf3lM28oe82MqIIRyvskPc11NuzSor8HmvH8H\n" +
+            "lhDs5DyJtx2qp35AT0zCqfwlaDnlDc/QDlZv1CoRZGpQk1Inyh6SbZwYpxxwh0fi\n" +
+            "+d/4RpE3LBVo8wgOaXPylOlHxsDizfkL8QwXItyakBfMO6jWQRrj7/9WDhGf4Hi+\n" +
+            "GQur1tPGZDl9mvCoRHjFrD5M/yypIPlfMGWFVEvV5jClNMLAQ9bYFuOc7H1fEWw6\n" +
+            "U1LZUUbJW9/CH45YXz82CYqkrfbnQxqRb2iVbVjs/sHopHd1NTiCfUtwvcYJiBVj\n" +
+            "kwIDAQAB\n" +
+            "-----END PUBLIC KEY-----";
+
     private final int maximumLimit;
 
     public TestJMAPServerModule(int maximumLimit) {
@@ -53,6 +63,7 @@ public class TestJMAPServerModule extend
         return JMAPConfiguration.builder()
                 .keystore("keystore")
                 .secret("james72laBalle")
+                .jwtPublicKeyPem(Optional.of(PUBLIC_PEM_KEY))
                 .build();
     }
     

Modified: james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/AccessToken.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/AccessToken.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/AccessToken.java (original)
+++ james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/AccessToken.java Mon Jan 18 10:18:15 2016
@@ -19,18 +19,18 @@
 
 package org.apache.james.jmap.api.access;
 
+import org.apache.james.jmap.api.access.exceptions.NotAnAccessTokenException;
+
 import java.util.Objects;
 import java.util.UUID;
 
-import org.apache.james.jmap.api.access.exceptions.NotAnUUIDException;
-
 public class AccessToken {
 
-    public static AccessToken fromString(String tokenString) throws NotAnUUIDException {
+    public static AccessToken fromString(String tokenString) throws NotAnAccessTokenException {
         try {
             return new AccessToken(UUID.fromString(tokenString));
         } catch (IllegalArgumentException e) {
-            throw new NotAnUUIDException(e);
+            throw new NotAnAccessTokenException(e);
         }
     }
 

Added: james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/NotAnAccessTokenException.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/NotAnAccessTokenException.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/NotAnAccessTokenException.java (added)
+++ james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/NotAnAccessTokenException.java Mon Jan 18 10:18:15 2016
@@ -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.jmap.api.access.exceptions;
+
+public class NotAnAccessTokenException extends RuntimeException {
+
+    public NotAnAccessTokenException(Exception e) {
+        super(e);
+    }
+}

Modified: james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/access/AccessTokenTest.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/access/AccessTokenTest.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/access/AccessTokenTest.java (original)
+++ james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/access/AccessTokenTest.java Mon Jan 18 10:18:15 2016
@@ -21,18 +21,18 @@ package org.apache.james.jmap.api.access
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-import org.apache.james.jmap.api.access.exceptions.NotAnUUIDException;
+import org.apache.james.jmap.api.access.exceptions.NotAnAccessTokenException;
 import org.junit.Test;
 
 public class AccessTokenTest {
 
-    @Test(expected=NotAnUUIDException.class)
-    public void fromStringShouldThrowWhenNotAnUUID() throws NotAnUUIDException {
+    @Test(expected=NotAnAccessTokenException.class)
+    public void fromStringShouldThrowWhenNotAnUUID() throws NotAnAccessTokenException {
         AccessToken.fromString("bad");
     }
 
     @Test
-    public void fromStringShouldWork() throws NotAnUUIDException {
+    public void fromStringShouldWork() throws NotAnAccessTokenException {
         String expectedToken = "dab315ad-a59a-4107-8d00-0fef9a0745b8";
 
         AccessToken accessToken = AccessToken.fromString(expectedToken);

Modified: james/project/trunk/server/pom.xml
URL: http://svn.apache.org/viewvc/james/project/trunk/server/pom.xml?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/pom.xml (original)
+++ james/project/trunk/server/pom.xml Mon Jan 18 10:18:15 2016
@@ -193,7 +193,6 @@
         <cassandra-unit.version>2.1.9.2</cassandra-unit.version>
         <assertj-1.version>1.7.1</assertj-1.version>
         <assertj-3.version>3.2.0</assertj-3.version>
-        <throwing-lambdas.version>0.5.0</throwing-lambdas.version>
     </properties>
 
     <dependencyManagement>
@@ -1390,6 +1389,11 @@
                 <scope>test</scope>
             </dependency>
             <dependency>
+                <groupId>com.github.fge</groupId>
+                <artifactId>throwing-lambdas</artifactId>
+                <version>0.5.0</version>
+            </dependency>
+            <dependency>
                 <groupId>com.jayway.awaitility</groupId>
                 <artifactId>awaitility</artifactId>
                 <version>1.6.3</version>
@@ -1400,6 +1404,16 @@
                 <version>2.6.0</version>
             </dependency>
             <dependency>
+                <groupId>io.jsonwebtoken</groupId>
+                <artifactId>jjwt</artifactId>
+                <version>0.6.0</version>
+            </dependency>
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcprov-jdk16</artifactId>
+                <version>1.46</version>
+            </dependency>
+            <dependency>
                 <groupId>org.eclipse.jetty</groupId>
                 <artifactId>jetty-http</artifactId>
                 <version>${jetty.version}</version>
@@ -1414,11 +1428,6 @@
                 <artifactId>jetty-servlet</artifactId>
                 <version>${jetty.version}</version>
             </dependency>
-            <dependency>
-                <groupId>com.github.fge</groupId>
-                <artifactId>throwing-lambdas</artifactId>
-                <version>${throwing-lambdas.version}</version>
-            </dependency>
         </dependencies>
     </dependencyManagement>
 

Modified: james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java (original)
+++ james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java Mon Jan 18 10:18:15 2016
@@ -49,7 +49,6 @@ public abstract class JMAPAuthentication
     private static final ZonedDateTime newDate = ZonedDateTime.parse("2011-12-03T10:16:30+01:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME);
     private static final ZonedDateTime afterExpirationDate = ZonedDateTime.parse("2011-12-03T10:30:31+01:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME);
 
-    
     private TemporaryFolder temporaryFolder = new TemporaryFolder();
     private EmbeddedElasticSearch embeddedElasticSearch = new EmbeddedElasticSearch(temporaryFolder);
     private EmbeddedCassandra cassandra = EmbeddedCassandra.createStartServer();
@@ -330,6 +329,47 @@ public abstract class JMAPAuthentication
     }
 
     @Test
+    public void getMustReturnEndpointsWhenValidJwtAuthorizationHeader() throws Exception {
+        String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" +
+                "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZ" +
+                "DN_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf" +
+                "49t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2" +
+                "qNOR8Q31ydinyqzXvCSzVJOf6T60-w";
+
+        given()
+                .header("Authorization", "Bearer " + token)
+                .when()
+                .get("/authentication")
+                .then()
+                .statusCode(200);
+    }
+
+    @Test
+    public void getMustReturnBadCredentialsWhenInvalidJwtAuthorizationHeader() throws Exception {
+        String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" +
+                "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZ" +
+                "EN_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf" +
+                "49t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2" +
+                "qNOR8Q31ydinyqzXvCSzVJOf6T60-w";
+
+        given()
+                .header("Authorization", "Bearer " + token)
+                .when()
+                .get("/authentication")
+                .then()
+                .statusCode(401);
+    }
+
+    @Test
+    public void optionsRequestsShouldNeverRequireAuthentication() {
+        given()
+                .when()
+                .options("/authentication")
+                .then()
+                .statusCode(200);
+    }
+
+    @Test
     public void getMustReturnEndpointsWhenCorrectAuthentication() throws Exception {
         String continuationToken = fromGoodContinuationTokenRequest();
         zonedDateTimeProvider.setFixedDateTime(newDate);

Modified: james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMailboxesMethodTest.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMailboxesMethodTest.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMailboxesMethodTest.java (original)
+++ james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMailboxesMethodTest.java Mon Jan 18 10:18:15 2016
@@ -24,6 +24,8 @@ import static com.jayway.restassured.con
 import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.isEmptyOrNullString;
 import static org.hamcrest.Matchers.startsWith;
 
 import java.io.ByteArrayInputStream;
@@ -33,6 +35,8 @@ import java.util.Map;
 
 import javax.mail.Flags;
 
+import com.google.common.collect.ImmutableMap;
+import com.jayway.jsonpath.JsonPath;
 import org.apache.james.backends.cassandra.EmbeddedCassandra;
 import org.apache.james.jmap.JmapAuthentication;
 import org.apache.james.jmap.JmapServer;
@@ -47,8 +51,6 @@ import org.junit.rules.RuleChain;
 import org.junit.rules.TemporaryFolder;
 
 import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableMap;
-import com.jayway.jsonpath.JsonPath;
 import com.jayway.restassured.RestAssured;
 import com.jayway.restassured.http.ContentType;
 
@@ -159,6 +161,53 @@ public abstract class GetMailboxesMethod
     }
 
     @Test
+    public void getMailboxesShouldReturnEmptyListWhenNoMailboxesWithJWTAuthWorkflow() {
+
+        String authToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" +
+                "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZ" +
+                "DN_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf" +
+                "49t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2" +
+                "qNOR8Q31ydinyqzXvCSzVJOf6T60-w";
+
+        given()
+                .accept(ContentType.JSON)
+                .contentType(ContentType.JSON)
+                .header("Authorization", "Bearer " + authToken)
+                .body("[[\"getMailboxes\", {}, \"#0\"]]")
+                .when()
+                .post("/jmap")
+                .then()
+                .statusCode(200)
+                .body("[0][0]", equalTo("mailboxes"))
+                .body("[0][1].accountId", isEmptyOrNullString())
+                .body("[0][1].state", isEmptyOrNullString())
+                .body("[0][1].notFound", isEmptyOrNullString())
+                .body("[0][1].list", hasSize(0))
+                .body("[0][2]", equalTo("#0"));
+    }
+
+
+    @Test
+    public void getMailboxesShouldErrorWithBadJWTToken() {
+
+        String badAuthToken = "BADTOKENOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" +
+                "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZ" +
+                "DN_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf" +
+                "49t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2" +
+                "qNOR8Q31ydinyqzXvCSzVJOf6T60-w";
+
+        given()
+                .accept(ContentType.JSON)
+                .contentType(ContentType.JSON)
+                .header("Authorization", "Bearer " + badAuthToken)
+                .body("[[\"getMailboxes\", {}, \"#0\"]]")
+                .when()
+                .post("/jmap")
+                .then()
+                .statusCode(401);
+    }
+
+    @Test
     public void getMailboxesShouldReturnMailboxesWhenAvailable() throws Exception {
         String user = "user";
         jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, user, "name");

Modified: james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java (original)
+++ james/project/trunk/server/protocols/jmap-integration-testing/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java Mon Jan 18 10:18:15 2016
@@ -24,6 +24,7 @@ import static com.jayway.restassured.con
 import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.startsWith;
 
 import java.io.ByteArrayInputStream;
@@ -100,7 +101,7 @@ public abstract class GetMessagesMethodT
     
     @Test
     public void getMessagesShouldIgnoreUnknownArguments() throws Exception {
-        String response = given()
+        given()
             .accept(ContentType.JSON)
             .contentType(ContentType.JSON)
             .header("Authorization", accessToken.serialize())
@@ -109,13 +110,9 @@ public abstract class GetMessagesMethodT
             .post("/jmap")
         .then()
             .statusCode(200)
-            .content(startsWith("[[\"messages\","))
-            .extract()
-            .asString();
-
-        String firstResponsePath = "$.[0].[1]";
-        assertThat(JsonPath.parse(response).<Integer>read(firstResponsePath + ".notFound.length()")).isEqualTo(0);
-        assertThat(JsonPath.parse(response).<Integer>read(firstResponsePath + ".list.length()")).isEqualTo(0);
+            .body("[0][1].notFound", hasSize(0))
+            .body("[0][1].list", hasSize(0))
+            .body("[0][2]", equalTo("#0"));
     }
 
     @Test

Modified: james/project/trunk/server/protocols/jmap/pom.xml
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/pom.xml?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/pom.xml (original)
+++ james/project/trunk/server/protocols/jmap/pom.xml Mon Jan 18 10:18:15 2016
@@ -226,6 +226,10 @@
                     <artifactId>commons-codec</artifactId>
                 </dependency>
                 <dependency>
+                    <groupId>io.jsonwebtoken</groupId>
+                    <artifactId>jjwt</artifactId>
+                </dependency>
+                <dependency>
                     <groupId>javax.inject</groupId>
                     <artifactId>javax.inject</artifactId>
                 </dependency>
@@ -235,6 +239,10 @@
                     <version>3.0.1</version>
                     <scope>provided</scope>
                 </dependency>
+                <dependency>
+                    <groupId>org.bouncycastle</groupId>
+                    <artifactId>bcprov-jdk16</artifactId>
+                </dependency>
                 
                 <dependency>
                     <groupId>org.apache.james</groupId>

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AccessTokenAuthenticationStrategy.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AccessTokenAuthenticationStrategy.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AccessTokenAuthenticationStrategy.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AccessTokenAuthenticationStrategy.java Mon Jan 18 10:18:15 2016
@@ -0,0 +1,90 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.jmap;
+
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+import org.apache.james.jmap.api.AccessTokenManager;
+import org.apache.james.jmap.api.access.AccessToken;
+import org.apache.james.jmap.api.access.exceptions.NotAnAccessTokenException;
+import org.apache.james.jmap.exceptions.MailboxSessionCreationException;
+import org.apache.james.jmap.exceptions.NoValidAuthHeaderException;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+public class AccessTokenAuthenticationStrategy implements AuthenticationStrategy {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AccessTokenAuthenticationStrategy.class);
+
+    private final AccessTokenManager accessTokenManager;
+    private final MailboxManager mailboxManager;
+
+    @Inject
+    @VisibleForTesting
+    AccessTokenAuthenticationStrategy(AccessTokenManager accessTokenManager, MailboxManager mailboxManager) {
+        this.accessTokenManager = accessTokenManager;
+        this.mailboxManager = mailboxManager;
+    }
+
+    @Override
+    public MailboxSession createMailboxSession(Stream<String> authHeaders) throws MailboxSessionCreationException, NoValidAuthHeaderException {
+
+        Optional<String> username = authHeaders
+            .map(AccessToken::fromString)
+            .map(accessTokenManager::getUsernameFromToken)
+            .findFirst();
+
+        if (username.isPresent()) {
+            try {
+                return mailboxManager.createSystemSession(username.get(), LOG);
+            } catch (MailboxException e) {
+                throw new MailboxSessionCreationException(e);
+            }
+        }
+        throw new NoValidAuthHeaderException();
+    }
+
+    @Override
+    public boolean checkAuthorizationHeader(Stream<String> authHeaders) {
+        return authHeaders
+                .map(this::accessTokenFrom)
+                .anyMatch(this::isValid);
+    }
+
+    private Optional<AccessToken> accessTokenFrom(String header) {
+        try {
+            return Optional.of(AccessToken.fromString(header));
+        } catch (NotAnAccessTokenException e) {
+            return Optional.empty();
+        }
+    }
+
+    private boolean isValid(Optional<AccessToken> token) {
+        return token.map(accessTokenManager::isValid)
+            .orElse(false);
+    }
+}

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AllowAllCrossOriginRequests.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AllowAllCrossOriginRequests.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AllowAllCrossOriginRequests.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AllowAllCrossOriginRequests.java Mon Jan 18 10:18:15 2016
@@ -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.jmap;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+public class AllowAllCrossOriginRequests implements Filter {
+
+    private final Filter nestedFilter;
+
+    public AllowAllCrossOriginRequests(Filter nestedFilter) {
+        this.nestedFilter = nestedFilter;
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+
+        HttpServletResponse servletResponse = (HttpServletResponse) response;
+        servletResponse.addHeader("Access-Control-Allow-Origin", "*");
+        servletResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
+        servletResponse.addHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept");
+        nestedFilter.doFilter(request, response, chain);
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    @Override
+    public void destroy() {
+    }
+}

Modified: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationFilter.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationFilter.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationFilter.java (original)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationFilter.java Mon Jan 18 10:18:15 2016
@@ -19,7 +19,10 @@
 package org.apache.james.jmap;
 
 import java.io.IOException;
-import java.util.Optional;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.stream.Stream;
 
 import javax.inject.Inject;
 import javax.servlet.Filter;
@@ -31,30 +34,30 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.james.jmap.api.AccessTokenManager;
-import org.apache.james.jmap.api.access.AccessToken;
-import org.apache.james.jmap.api.access.exceptions.NotAnUUIDException;
-import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.jmap.exceptions.MailboxSessionCreationException;
+import org.apache.james.jmap.exceptions.NoValidAuthHeaderException;
+import org.apache.james.jmap.exceptions.UnauthorizedException;
 import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.exception.BadCredentialsException;
-import org.apache.james.mailbox.exception.MailboxException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
 
+import io.jsonwebtoken.JwtException;
+
 public class AuthenticationFilter implements Filter {
-    
-    private static final Logger LOG = LoggerFactory.getLogger(AuthenticationFilter.class);
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class);
+
     public static final String MAILBOX_SESSION = "mailboxSession";
+    private static final String AUTHORIZATION_HEADERS = "Authorization";
 
-    private final AccessTokenManager accessTokenManager;
-    private final MailboxManager mailboxManager;
+    private final List<AuthenticationStrategy> authMethods;
 
     @Inject
-    public AuthenticationFilter(AccessTokenManager accessTokenManager, MailboxManager mailboxManager) {
-        this.accessTokenManager = accessTokenManager;
-        this.mailboxManager = mailboxManager;
+    @VisibleForTesting
+    AuthenticationFilter(List<AuthenticationStrategy> authMethods) {
+        this.authMethods = authMethods;
     }
 
     @Override
@@ -66,43 +69,34 @@ public class AuthenticationFilter implem
         HttpServletRequest httpRequest = (HttpServletRequest) request;
         HttpServletResponse httpResponse = (HttpServletResponse) response;
 
-        Optional<String> authHeader = Optional.ofNullable(httpRequest.getHeader("Authorization"));
-        if (!checkAuthorizationHeader(authHeader)) {
+        try {
+            HttpServletRequest requestWithSession = authMethods.stream()
+                    .filter(auth -> auth.checkAuthorizationHeader(getAuthHeaders(httpRequest)))
+                    .findFirst()
+                    .map(auth -> addSessionToRequest(httpRequest, createSession(auth, getAuthHeaders(httpRequest))))
+                    .orElseThrow(UnauthorizedException::new);
+            chain.doFilter(requestWithSession, response);
+
+        } catch (UnauthorizedException | NoValidAuthHeaderException | MailboxSessionCreationException | JwtException e) {
+            LOGGER.error("Exception occurred during authentication process", e);
             httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
-            return;
         }
 
-        addSessionToRequest(httpRequest, httpResponse, authHeader);
-
-        chain.doFilter(httpRequest, response);
     }
 
-    private void addSessionToRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Optional<String> authHeader) throws IOException {
-        try {
-            MailboxSession mailboxSession = createMailboxSession(authHeader);
-            httpRequest.setAttribute(MAILBOX_SESSION, mailboxSession);
-        } catch (MailboxException e) {
-            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
-        }
+    private Stream<String> getAuthHeaders(HttpServletRequest httpRequest) {
+        Enumeration<String> authHeaders = httpRequest.getHeaders(AUTHORIZATION_HEADERS);
+
+        return authHeaders != null ? Collections.list(authHeaders).stream() : Stream.of();
     }
 
-    @VisibleForTesting MailboxSession createMailboxSession(Optional<String> authHeader) throws BadCredentialsException, MailboxException {
-        String username = authHeader
-            .map(AccessToken::fromString)
-            .map(accessTokenManager::getUsernameFromToken)
-            .orElseThrow(() -> new BadCredentialsException());
-        return mailboxManager.createSystemSession(username, LOG);
+    private HttpServletRequest addSessionToRequest(HttpServletRequest httpRequest, MailboxSession mailboxSession) {
+        httpRequest.setAttribute(MAILBOX_SESSION, mailboxSession);
+        return httpRequest;
     }
 
-    private boolean checkAuthorizationHeader(Optional<String> authHeader) throws IOException {
-        try {
-            return authHeader
-                    .map(AccessToken::fromString)
-                    .map(accessTokenManager::isValid)
-                    .orElse(false);
-        } catch (NotAnUUIDException e) {
-            return false;
-        }
+    private MailboxSession createSession(AuthenticationStrategy authenticationMethod, Stream<String> authorizationHeaders) {
+        return authenticationMethod.createMailboxSession(authorizationHeaders);
     }
 
     @Override

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationStrategy.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationStrategy.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationStrategy.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationStrategy.java Mon Jan 18 10:18:15 2016
@@ -0,0 +1,30 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.jmap;
+
+import java.util.stream.Stream;
+
+import org.apache.james.mailbox.MailboxSession;
+
+public interface AuthenticationStrategy {
+
+    MailboxSession createMailboxSession(Stream<String> requestHeaders);
+
+    boolean checkAuthorizationHeader(Stream<String> requestHeaders);
+}

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/BypassAuthOnRequestMethod.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/BypassAuthOnRequestMethod.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/BypassAuthOnRequestMethod.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/BypassAuthOnRequestMethod.java Mon Jan 18 10:18:15 2016
@@ -0,0 +1,118 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.function.Predicate;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+
+public class BypassAuthOnRequestMethod implements Filter {
+
+    public static Builder bypass(AuthenticationFilter authenticationFilter) {
+        return new Builder(authenticationFilter);
+    }
+
+    public static class Builder {
+        private ImmutableList.Builder<Predicate<HttpServletRequest>> reasons = new ImmutableList.Builder<>();
+        private final AuthenticationFilter authenticationFilter;
+
+        private Builder(AuthenticationFilter authenticationFilter) {
+            this.authenticationFilter = authenticationFilter;
+        }
+
+        public InitializedBuilder on(String requestMethod) {
+            Preconditions.checkArgument(!Strings.isNullOrEmpty(requestMethod), "'requestMethod' is mandatory");
+            String trimmedRequestMethod = requestMethod.trim();
+            Preconditions.checkArgument(!Strings.isNullOrEmpty(trimmedRequestMethod), "'requestMethod' is mandatory");
+            reasons.add(r -> r.getMethod().equalsIgnoreCase(trimmedRequestMethod));
+            return new InitializedBuilder(this);
+        }
+
+        public class InitializedBuilder {
+            private final Builder builder;
+
+            private InitializedBuilder(Builder builder) {
+                this.builder = builder;
+            }
+
+            public InitializedBuilder and(String requestMethod) {
+                return builder.on(requestMethod);
+            }
+
+            public BypassAuthOnRequestMethod only() {
+                return new BypassAuthOnRequestMethod(builder.authenticationFilter, builder.reasons.build());
+            }
+        }
+    }
+
+
+    private final AuthenticationFilter authenticationFilter;
+    private final List<Predicate<HttpServletRequest>> listOfReasonsToBypassAuth;
+
+    private BypassAuthOnRequestMethod(AuthenticationFilter authenticationFilter, List<Predicate<HttpServletRequest>> listOfReasonsToBypassAuth) {
+        this.authenticationFilter = authenticationFilter;
+        this.listOfReasonsToBypassAuth = listOfReasonsToBypassAuth;
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest httpRequest = (HttpServletRequest)request;
+
+        if (shouldBypassAuth(httpRequest)) {
+            bypassAuth(request, response, chain);
+        } else {
+            tryAuth(httpRequest, response, chain);
+        }
+    }
+
+    private boolean shouldBypassAuth(HttpServletRequest httpRequest) {
+        return listOfReasonsToBypassAuth.stream()
+                .anyMatch(r -> r.test(httpRequest));
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    private void bypassAuth(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        chain.doFilter(request, response);
+    }
+
+    private void tryAuth(HttpServletRequest httpRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        authenticationFilter.doFilter(httpRequest, response, chain);
+    }
+
+}

Modified: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java (original)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java Mon Jan 18 10:18:15 2016
@@ -19,6 +19,8 @@
 
 package org.apache.james.jmap;
 
+import static org.apache.james.jmap.BypassAuthOnRequestMethod.bypass;
+
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -39,17 +41,19 @@ public class JMAPServer implements Confi
     private final JettyHttpServer server;
 
     @Inject
-    private JMAPServer(PortConfiguration portConfiguration, 
-            AuthenticationServlet authenticationServlet, JMAPServlet jmapServlet,
-            AuthenticationFilter authenticationFilter) {
+    private JMAPServer(PortConfiguration portConfiguration,
+                       AuthenticationServlet authenticationServlet, JMAPServlet jmapServlet,
+                       AuthenticationFilter authenticationFilter) {
 
         server = JettyHttpServer.create(
                 configurationBuilderFor(portConfiguration)
-                .serve("/authentication").with(authenticationServlet)
-                .filter("/authentication").with(new BypassOnPostFilter(authenticationFilter))
-                .serve("/jmap").with(jmapServlet)
-                .filter("/jmap").with(authenticationFilter)
-                .build());
+                        .serve("/authentication").with(authenticationServlet)
+                        .filter("/authentication").with(new AllowAllCrossOriginRequests(
+                            bypass(authenticationFilter).on("POST").and("OPTIONS").only()))
+                        .serve("/jmap").with(jmapServlet)
+                        .filter("/jmap").with(new AllowAllCrossOriginRequests(
+                            bypass(authenticationFilter).on("OPTIONS").only()))
+                        .build());
     }
 
     private Builder configurationBuilderFor(PortConfiguration portConfiguration) {
@@ -83,5 +87,4 @@ public class JMAPServer implements Confi
     public int getPort() {
         return server.getPort();
     }
-
 }

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java Mon Jan 18 10:18:15 2016
@@ -0,0 +1,82 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.jmap;
+
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+import org.apache.james.jmap.crypto.JwtTokenVerifier;
+import org.apache.james.jmap.exceptions.MailboxSessionCreationException;
+import org.apache.james.jmap.exceptions.NoValidAuthHeaderException;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+public class JWTAuthenticationStrategy implements AuthenticationStrategy {
+
+    private static final Logger LOG = LoggerFactory.getLogger(JWTAuthenticationStrategy.class);
+    @VisibleForTesting static final String AUTHORIZATION_HEADER_PREFIX = "Bearer ";
+    private final JwtTokenVerifier tokenManager;
+    private final MailboxManager mailboxManager;
+
+    @Inject
+    @VisibleForTesting
+    JWTAuthenticationStrategy(JwtTokenVerifier tokenManager, MailboxManager mailboxManager) {
+        this.tokenManager = tokenManager;
+        this.mailboxManager = mailboxManager;
+    }
+
+    @Override
+    public MailboxSession createMailboxSession(Stream<String> authHeaders) throws MailboxSessionCreationException, NoValidAuthHeaderException {
+
+        Stream<String> userLoginStream = extractTokensFromAuthHeaders(authHeaders)
+                .filter(tokenManager::verify)
+                .map(tokenManager::extractLogin);
+
+        Stream<MailboxSession> mailboxSessionStream = userLoginStream
+                .map(l -> {
+                    try {
+                        return mailboxManager.createSystemSession(l, LOG);
+                    } catch (MailboxException e) {
+                        throw new MailboxSessionCreationException(e);
+                    }
+                });
+
+        return mailboxSessionStream
+                .findFirst()
+                .orElseThrow(() -> new NoValidAuthHeaderException());
+    }
+
+    @Override
+    public boolean checkAuthorizationHeader(Stream<String> authHeaders) {
+        return extractTokensFromAuthHeaders(authHeaders)
+                .anyMatch(tokenManager::verify);
+    }
+
+    private Stream<String> extractTokensFromAuthHeaders(Stream<String> authHeaders) {
+        return authHeaders
+                .filter(h -> h.startsWith(AUTHORIZATION_HEADER_PREFIX))
+                .map(h -> h.substring(AUTHORIZATION_HEADER_PREFIX.length()));
+    }
+}

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/JwtTokenVerifier.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/JwtTokenVerifier.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/JwtTokenVerifier.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/JwtTokenVerifier.java Mon Jan 18 10:18:15 2016
@@ -0,0 +1,62 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.jmap.crypto;
+
+import javax.inject.Inject;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.SignatureException;
+import io.jsonwebtoken.UnsupportedJwtException;
+
+public class JwtTokenVerifier {
+
+    private final PublicKeyProvider pubKeyProvider;
+
+    @Inject
+    @VisibleForTesting
+    JwtTokenVerifier(PublicKeyProvider pubKeyProvider) {
+        this.pubKeyProvider = pubKeyProvider;
+    }
+
+    public boolean verify(String token) throws JwtException {
+        parseToken(token);
+        return true;
+    }
+
+    public String extractLogin(String token) throws JwtException {
+        Jws<Claims> jws = parseToken(token);
+        return jws
+                .getBody()
+                .getSubject();
+    }
+
+    private Jws<Claims> parseToken(String token) throws JwtException {
+        return Jwts
+                .parser()
+                .setSigningKey(pubKeyProvider.get())
+                .parseClaimsJws(token);
+    }
+}

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/MissingOrInvalidKeyException.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/MissingOrInvalidKeyException.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/MissingOrInvalidKeyException.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/MissingOrInvalidKeyException.java Mon Jan 18 10:18:15 2016
@@ -0,0 +1,22 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.jmap.crypto;
+
+public class MissingOrInvalidKeyException extends RuntimeException {
+}

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyProvider.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyProvider.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyProvider.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyProvider.java Mon Jan 18 10:18:15 2016
@@ -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.jmap.crypto;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.james.jmap.JMAPConfiguration;
+
+import javax.inject.Inject;
+import java.security.PublicKey;
+
+public class PublicKeyProvider {
+
+    private final JMAPConfiguration config;
+    private final PublicKeyReader reader;
+
+    @Inject
+    @VisibleForTesting
+    PublicKeyProvider(JMAPConfiguration config, PublicKeyReader reader) {
+        this.config = config;
+        this.reader = reader;
+    }
+
+    public PublicKey get() throws MissingOrInvalidKeyException {
+        return reader.fromPEM(config.getJwtPublicKeyPem())
+                .orElseThrow(() -> new MissingOrInvalidKeyException());
+    }
+
+}

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyReader.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyReader.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyReader.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyReader.java Mon Jan 18 10:18:15 2016
@@ -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.jmap.crypto;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Optional;
+
+import org.bouncycastle.openssl.PEMReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PublicKeyReader {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(PublicKeyReader.class);
+
+    Optional<RSAPublicKey> fromPEM(Optional<String> pemKey) {
+
+        return pemKey
+                .map(k -> new PEMReader(new StringReader(k)))
+                .flatMap(this::publicKeyFrom);
+    }
+
+    private Optional<RSAPublicKey> publicKeyFrom(PEMReader reader) {
+        try {
+            Object readPEM = reader.readObject();
+            RSAPublicKey rsaPublicKey = null;
+            if (readPEM instanceof RSAPublicKey) {
+                rsaPublicKey = (RSAPublicKey) readPEM;
+            }
+            return Optional.ofNullable(rsaPublicKey);
+        } catch (IOException e) {
+            LOGGER.warn("Error when reading the PEM file", e);
+            return Optional.empty();
+        }
+    }
+}
\ No newline at end of file

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxSessionCreationException.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxSessionCreationException.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxSessionCreationException.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxSessionCreationException.java Mon Jan 18 10:18:15 2016
@@ -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.jmap.exceptions;
+
+public class MailboxSessionCreationException extends RuntimeException {
+
+    public MailboxSessionCreationException(Exception e) {
+        super(e);
+    }
+}

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/NoValidAuthHeaderException.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/NoValidAuthHeaderException.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/NoValidAuthHeaderException.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/NoValidAuthHeaderException.java Mon Jan 18 10:18:15 2016
@@ -0,0 +1,23 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.exceptions;
+
+public class NoValidAuthHeaderException extends RuntimeException {
+}

Added: james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/UnauthorizedException.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/UnauthorizedException.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/UnauthorizedException.java (added)
+++ james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/UnauthorizedException.java Mon Jan 18 10:18:15 2016
@@ -0,0 +1,23 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.exceptions;
+
+public class UnauthorizedException extends RuntimeException {
+}

Added: james/project/trunk/server/protocols/jmap/src/main/resources/jwt-public.der
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/resources/jwt-public.der?rev=1725214&view=auto
==============================================================================
    (empty)

Added: james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AccessTokenAuthenticationStrategyTest.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AccessTokenAuthenticationStrategyTest.java?rev=1725214&view=auto
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AccessTokenAuthenticationStrategyTest.java (added)
+++ james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AccessTokenAuthenticationStrategyTest.java Mon Jan 18 10:18:15 2016
@@ -0,0 +1,133 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.jmap;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import org.apache.james.jmap.api.access.AccessToken;
+import org.apache.james.jmap.api.access.exceptions.NotAnAccessTokenException;
+import org.apache.james.jmap.crypto.AccessTokenManagerImpl;
+import org.apache.james.jmap.exceptions.MailboxSessionCreationException;
+import org.apache.james.jmap.exceptions.NoValidAuthHeaderException;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+
+public class AccessTokenAuthenticationStrategyTest {
+
+    private AccessTokenManagerImpl mockedAccessTokenManager;
+    private MailboxManager mockedMailboxManager;
+    private AccessTokenAuthenticationStrategy testee;
+
+    @Before
+    public void setup() {
+        mockedAccessTokenManager = mock(AccessTokenManagerImpl.class);
+        mockedMailboxManager = mock(MailboxManager.class);
+
+        testee = new AccessTokenAuthenticationStrategy(mockedAccessTokenManager, mockedMailboxManager);
+    }
+
+    @Test
+    public void createMailboxSessionShouldThrowWhenNoAuthProvided() {
+        assertThatThrownBy(() -> testee.createMailboxSession(Stream.empty()))
+            .isExactlyInstanceOf(NoValidAuthHeaderException.class);
+    }
+
+    @Test
+    public void createMailboxSessionShouldThrowWhenAuthHeaderIsNotAnUUID() {
+        assertThatThrownBy(() -> testee.createMailboxSession(Stream.of("bad")))
+                .isExactlyInstanceOf(NotAnAccessTokenException.class);
+    }
+
+    @Test
+    public void createMailboxSessionShouldThrowWhenMailboxExceptionHasOccurred() throws Exception {
+        String username = "username";
+        when(mockedMailboxManager.createSystemSession(eq(username), any(Logger.class)))
+                .thenThrow(new MailboxException());
+
+        UUID authHeader = UUID.randomUUID();
+        when(mockedAccessTokenManager.getUsernameFromToken(AccessToken.fromString(authHeader.toString())))
+                .thenReturn(username);
+
+        assertThatThrownBy(() -> testee.createMailboxSession(Stream.of(authHeader.toString())))
+                .isExactlyInstanceOf(MailboxSessionCreationException.class);
+    }
+
+    @Test
+    public void createMailboxSessionShouldReturnWhenAuthHeadersAreValid() throws Exception {
+        String username = "123456789";
+        MailboxSession fakeMailboxSession = mock(MailboxSession.class);
+
+        when(mockedMailboxManager.createSystemSession(eq(username), any(Logger.class)))
+                .thenReturn(fakeMailboxSession);
+
+        UUID authHeader = UUID.randomUUID();
+        when(mockedAccessTokenManager.getUsernameFromToken(AccessToken.fromString(authHeader.toString())))
+                .thenReturn(username);
+
+        MailboxSession result = testee.createMailboxSession(Stream.of(authHeader.toString()));
+        assertThat(result).isEqualTo(fakeMailboxSession);
+    }
+
+    @Test
+    public void checkAuthorizationHeaderShouldReturnFalseWhenAuthHeaderIsEmpty() {
+        assertThat(testee.checkAuthorizationHeader(Stream.empty())).isFalse();
+    }
+
+    @Test
+    public void checkAuthorizationHeaderShouldReturnFalseWhenAuthHeaderIsInvalid() {
+        assertThat(testee.checkAuthorizationHeader(Stream.of("bad"))).isFalse();
+    }
+
+    @Test
+    public void checkAuthorizationHeaderShouldReturnFalseWhenAuthHeadersAreInvalid() {
+        assertThat(testee.checkAuthorizationHeader(Stream.of("bad", "alsobad"))).isFalse();
+    }
+
+    @Test
+    public void checkAuthorizationHeaderShouldReturnTrueWhenAuthHeaderIsValid() {
+
+        String validToken = UUID.randomUUID().toString();
+        when(mockedAccessTokenManager.isValid(AccessToken.fromString(validToken)))
+                .thenReturn(true);
+
+        assertThat(testee.checkAuthorizationHeader(Stream.of(validToken))).isTrue();
+    }
+
+    @Test
+    public void checkAuthorizationHeaderShouldReturnTrueWhenOneAuthHeaderIsValid() {
+
+        String validToken = UUID.randomUUID().toString();
+        when(mockedAccessTokenManager.isValid(AccessToken.fromString(validToken)))
+                .thenReturn(true);
+
+        assertThat(testee.checkAuthorizationHeader(Stream.of("bad", validToken))).isTrue();
+    }
+}
\ No newline at end of file

Modified: james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AuthenticationFilterTest.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AuthenticationFilterTest.java?rev=1725214&r1=1725213&r2=1725214&view=diff
==============================================================================
--- james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AuthenticationFilterTest.java (original)
+++ james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/AuthenticationFilterTest.java Mon Jan 18 10:18:15 2016
@@ -24,31 +24,29 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import java.util.Optional;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
 
 import javax.servlet.FilterChain;
 import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.james.jmap.api.AccessTokenManager;
 import org.apache.james.jmap.api.access.AccessToken;
 import org.apache.james.jmap.api.access.AccessTokenRepository;
-import org.apache.james.jmap.api.access.exceptions.NotAnUUIDException;
-import org.apache.james.jmap.crypto.AccessTokenManagerImpl;
 import org.apache.james.jmap.memory.access.MemoryAccessTokenRepository;
-import org.apache.james.mailbox.MailboxManager;
-import org.apache.james.mailbox.exception.BadCredentialsException;
+import org.apache.james.mailbox.MailboxSession;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableList;
+
 public class AuthenticationFilterTest {
     private static final String TOKEN = "df991d2a-1c5a-4910-a90f-808b6eda133e";
 
     private HttpServletRequest mockedRequest;
     private HttpServletResponse mockedResponse;
-    private AccessTokenManager accessTokenManager;
     private AccessTokenRepository accessTokenRepository;
     private AuthenticationFilter testee;
     private FilterChain filterChain;
@@ -59,10 +57,11 @@ public class AuthenticationFilterTest {
         mockedResponse = mock(HttpServletResponse.class);
 
         accessTokenRepository = new MemoryAccessTokenRepository(TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS));
-        accessTokenManager = new AccessTokenManagerImpl(accessTokenRepository);
-        MailboxManager mockedMailboxManager = mock(MailboxManager.class);
 
-        testee = new AuthenticationFilter(accessTokenManager, mockedMailboxManager);
+        when(mockedRequest.getMethod()).thenReturn("POST");
+        List<AuthenticationStrategy> fakeAuthenticationStrategies = ImmutableList.of( new FakeAuthenticationStrategy(false));
+
+        testee = new AuthenticationFilter(fakeAuthenticationStrategies);
         filterChain = mock(FilterChain.class);
     }
 
@@ -94,21 +93,12 @@ public class AuthenticationFilterTest {
 
         accessTokenRepository.addToken("user@domain.tld", token);
 
-        testee.doFilter(mockedRequest, mockedResponse, filterChain);
+        AuthenticationFilter sut = new AuthenticationFilter(ImmutableList.of( new FakeAuthenticationStrategy(true)));
+        sut.doFilter(mockedRequest, mockedResponse, filterChain);
 
         verify(filterChain).doFilter(any(ServletRequest.class), eq(mockedResponse));
     }
 
-    @Test(expected=BadCredentialsException.class)
-    public void createMailboxSessionShouldThrowWhenAuthHeaderIsEmpty() throws Exception {
-        testee.createMailboxSession(Optional.empty());
-    }
-
-    @Test(expected=NotAnUUIDException.class)
-    public void createMailboxSessionShouldThrowWhenAuthHeaderIsNotAnUUID() throws Exception {
-        testee.createMailboxSession(Optional.of("bad"));
-    }
-
     @Test
     public void filterShouldReturnUnauthorizedOnBadAuthorizationHeader() throws Exception {
         when(mockedRequest.getHeader("Authorization"))
@@ -118,4 +108,34 @@ public class AuthenticationFilterTest {
         
         verify(mockedResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED);
     }
+
+    @Test
+    public void filterShouldReturnUnauthorizedWhenNoStrategy() throws Exception {
+        when(mockedRequest.getHeader("Authorization"))
+                .thenReturn(TOKEN);
+
+        AuthenticationFilter sut = new AuthenticationFilter(ImmutableList.of());
+        sut.doFilter(mockedRequest, mockedResponse, filterChain);
+
+        verify(mockedResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED);
+    }
+
+    private class FakeAuthenticationStrategy implements AuthenticationStrategy {
+
+        private final boolean isAuthorized;
+
+        private FakeAuthenticationStrategy(boolean isAuthorized) {
+            this.isAuthorized = isAuthorized;
+        }
+
+        @Override
+        public MailboxSession createMailboxSession(Stream<String> requestHeaders) {
+            return null;
+        }
+
+        @Override
+        public boolean checkAuthorizationHeader(Stream<String> requestHeaders) {
+            return isAuthorized;
+        }
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org