Author: matthieu
Date: Fri Dec 11 10:05:39 2015
New Revision: 1719306

URL: http://svn.apache.org/viewvc?rev=1719306&view=rev
Log:
JAMES-1644 JMAP Authentication Servlet Handle two different requests with 
Jackson

Added:
    
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/
    
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleClassesDeserializer.java
    
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleObjectMapperBuilder.java
    
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenRequest.java
    
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/
    
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/MultipleObjectMapperTest.java
Modified:
    
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationServlet.java
    
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/ContinuationTokenRequest.java
    
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java

Modified: 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationServlet.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationServlet.java?rev=1719306&r1=1719305&r2=1719306&view=diff
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationServlet.java
 (original)
+++ 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/AuthenticationServlet.java
 Fri Dec 11 10:05:39 2015
@@ -25,6 +25,8 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.james.jmap.json.MultipleObjectMapperBuilder;
+import org.apache.james.jmap.model.AccessTokenRequest;
 import org.apache.james.jmap.model.ContinuationTokenRequest;
 import org.apache.james.jmap.model.ContinuationTokenResponse;
 import org.slf4j.Logger;
@@ -39,7 +41,10 @@ public class AuthenticationServlet exten
 
     private static final Logger LOG = 
LoggerFactory.getLogger(AuthenticationServlet.class);
 
-    private ObjectMapper mapper = new ObjectMapper();
+    private ObjectMapper mapper = new MultipleObjectMapperBuilder()
+            .registerClass(ContinuationTokenRequest.UNIQUE_JSON_PATH, 
ContinuationTokenRequest.class)
+            .registerClass(AccessTokenRequest.UNIQUE_JSON_PATH, 
AccessTokenRequest.class)
+            .build();
 
     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {
@@ -52,15 +57,33 @@ public class AuthenticationServlet exten
             return;
         }
 
-        ContinuationTokenRequest continuationTokenRequest;
+        Object request;
         try {
-            continuationTokenRequest = mapper.readValue(req.getReader(), 
ContinuationTokenRequest.class);
+            request = mapper.readValue(req.getReader(), Object.class);
         } catch (Exception e) {
             LOG.warn("Invalid authentication request received.", e);
             resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
             return;
         }
 
+        if (request instanceof ContinuationTokenRequest) {
+            handleContinuationTokenRequest((ContinuationTokenRequest)request, 
resp);
+        } else if (request instanceof AccessTokenRequest) {
+            handleAccessTokenRequest((AccessTokenRequest)request, resp);
+        }
+    }
+
+    private boolean checkJsonContentType(HttpServletRequest req) {
+        return req.getContentType().equals(JSON_CONTENT_TYPE_UTF8);
+    }
+
+    private boolean checkAcceptJsonOnly(HttpServletRequest req) {
+        String accept = req.getHeader("Accept");
+        return accept != null && accept.contains(JSON_CONTENT_TYPE);
+    }
+
+
+    private void handleContinuationTokenRequest(ContinuationTokenRequest 
request, HttpServletResponse resp) throws IOException {
         resp.setContentType(JSON_CONTENT_TYPE_UTF8);
 
         ContinuationTokenResponse continuationTokenResponse = 
ContinuationTokenResponse
@@ -71,15 +94,10 @@ public class AuthenticationServlet exten
                 .build();
 
         mapper.writeValue(resp.getOutputStream(), continuationTokenResponse);
-
     }
 
-    private boolean checkJsonContentType(HttpServletRequest req) {
-        return req.getContentType().equals(JSON_CONTENT_TYPE_UTF8);
+    private void handleAccessTokenRequest(AccessTokenRequest request, 
HttpServletResponse resp) throws IOException {
+        resp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
     }
 
-    private boolean checkAcceptJsonOnly(HttpServletRequest req) {
-        String accept = req.getHeader("Accept");
-        return accept != null && accept.contains(JSON_CONTENT_TYPE);
-    }
 }

Added: 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleClassesDeserializer.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleClassesDeserializer.java?rev=1719306&view=auto
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleClassesDeserializer.java
 (added)
+++ 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleClassesDeserializer.java
 Fri Dec 11 10:05:39 2015
@@ -0,0 +1,67 @@
+/****************************************************************
+ * 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.json;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+
+public class MultipleClassesDeserializer extends StdDeserializer<Object> {
+
+    private Map<String, Class<?>> registry =  new HashMap<String, Class<?>>();
+
+    MultipleClassesDeserializer() {
+        super(Object.class);
+    }
+
+    @Override
+    public Object deserialize(JsonParser p, DeserializationContext ctxt) 
throws IOException, JsonProcessingException {
+        ObjectMapper mapper = (ObjectMapper) p.getCodec();
+        final JsonNode root = mapper.readTree(p);
+
+        return registry.entrySet().stream()
+                .filter(req -> ! (root.at(req.getKey()).isMissingNode()))
+                .map(x -> readValue(mapper, root, x.getValue()))
+                .findFirst()
+                .orElseThrow(() -> new JsonMappingException("Can't map request 
to a known registered class"));
+    }
+
+    private Object readValue(ObjectMapper mapper, final JsonNode root, 
Class<?> clazz) {
+        try {
+            return mapper.treeToValue(root, clazz);
+        } catch (JsonProcessingException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    public void registerClass(String uniqueJsonPath, Class<?> clazz) {
+        Preconditions.checkArgument(! registry.containsKey(uniqueJsonPath), 
"Path %s has already been registered", uniqueJsonPath);
+        registry.put(uniqueJsonPath, clazz);
+    }
+}

Added: 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleObjectMapperBuilder.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleObjectMapperBuilder.java?rev=1719306&view=auto
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleObjectMapperBuilder.java
 (added)
+++ 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/MultipleObjectMapperBuilder.java
 Fri Dec 11 10:05:39 2015
@@ -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.jmap.json;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class MultipleObjectMapperBuilder {
+
+    private MultipleClassesDeserializer multipleClassesDeserializer = new 
MultipleClassesDeserializer();
+
+    public MultipleObjectMapperBuilder registerClass(String uniqueJsonPath, 
Class<?> clazz) {
+        multipleClassesDeserializer.registerClass(uniqueJsonPath, clazz);
+        return this;
+    }
+
+    public ObjectMapper build() {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule();
+        module.addDeserializer(Object.class, multipleClassesDeserializer);
+        mapper.registerModule(module);
+        return mapper;
+    }
+}

Added: 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenRequest.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenRequest.java?rev=1719306&view=auto
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenRequest.java
 (added)
+++ 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenRequest.java
 Fri Dec 11 10:05:39 2015
@@ -0,0 +1,83 @@
+/****************************************************************
+ * 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.model;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder=AccessTokenRequest.Builder.class)
+public class AccessTokenRequest {
+
+    public static final String UNIQUE_JSON_PATH = "/token";
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    @JsonPOJOBuilder(withPrefix="")
+    public static class Builder {
+
+        private String token;
+        private String method;
+        private String password;
+
+        private Builder() {}
+
+        public Builder token(String token) {
+            this.token = token;
+            return this;
+        }
+
+        public Builder method(String method) {
+            this.method = method;
+            return this;
+        }
+
+        public Builder password(String password) {
+            this.password = password;
+            return this;
+        }
+
+        public AccessTokenRequest build() {
+            return new AccessTokenRequest(token, method, password);
+        }
+    }
+
+    private final String token;
+    private final String method;
+    private final String password;
+
+    private AccessTokenRequest(String token, String method, String password) {
+        this.token = token;
+        this.method = method;
+        this.password = password;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public String getMethod() {
+        return method;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+}

Modified: 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/ContinuationTokenRequest.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/ContinuationTokenRequest.java?rev=1719306&r1=1719305&r2=1719306&view=diff
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/ContinuationTokenRequest.java
 (original)
+++ 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/ContinuationTokenRequest.java
 Fri Dec 11 10:05:39 2015
@@ -24,6 +24,8 @@ import com.fasterxml.jackson.databind.an
 @JsonDeserialize(builder=ContinuationTokenRequest.Builder.class)
 public class ContinuationTokenRequest {
 
+    public static final String UNIQUE_JSON_PATH = "/username";
+
     public static Builder builder() {
         return new Builder();
     }

Modified: 
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java?rev=1719306&r1=1719305&r2=1719306&view=diff
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java
 (original)
+++ 
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java
 Fri Dec 11 10:05:39 2015
@@ -19,6 +19,7 @@
 package org.apache.james.jmap;
 
 import static com.jayway.restassured.RestAssured.given;
+import static com.jayway.restassured.RestAssured.with;
 import static com.jayway.restassured.config.EncoderConfig.encoderConfig;
 import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
 import static org.hamcrest.Matchers.hasItem;
@@ -145,8 +146,21 @@ public class JMAPAuthenticationTest {
             .contentType(ContentType.JSON);
     }
 
+
+    @Test
+    public void mustReturnMalformedRequestWhenBodyIsNotAcceptable() {
+        given()
+            .contentType(ContentType.JSON)
+            .accept(ContentType.JSON)
+            .body("{\"badAttributeName\": \"value\"}")
+        .when()
+            .post("/authentication")
+        .then()
+            .statusCode(400);
+    }
+
     @Test
-    public void getContinuationTokenWhenValidResquest() {
+    public void methodShouldContainPasswordWhenValidResquest() {
         given()
             .contentType(ContentType.JSON)
             .accept(ContentType.JSON)
@@ -155,9 +169,43 @@ public class JMAPAuthenticationTest {
             .post("/authentication")
         .then()
             .statusCode(200)
-            .body("continuationToken", isA(String.class))
             .body("methods", hasItem("password"));
     }
 
+    @Test
+    public void mustReturnContinuationTokenWhenValidResquest() {
+        given()
+            .contentType(ContentType.JSON)
+            .accept(ContentType.JSON)
+            .body("{\"username\": \"[email protected]\", \"clientName\": 
\"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe 
Blogg’s iPhone\"}")
+        .when()
+            .post("/authentication")
+        .then()
+            .statusCode(200)
+            .body("continuationToken", isA(String.class));
+    }
+
+    @Test
+    public void mustReturnAuthenticationFailedWhenBadPassword() {
+        String continuationToken =
+                with()
+                    .contentType(ContentType.JSON)
+                    .accept(ContentType.JSON)
+                    .body("{\"username\": \"[email protected]\", \"clientName\": 
\"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe 
Blogg’s iPhone\"}")
+                .post("/authentication")
+                    .body()
+                    .path("continuationToken")
+                .toString();
+
+        given()
+            .contentType(ContentType.JSON)
+            .accept(ContentType.JSON)
+            .body("{\"token\": \"" + continuationToken + "\", \"method\": 
\"password\", \"password\": \"badpassword\"}")
+        .when()
+            .post("/authentication")
+        .then()
+            .statusCode(401);
+    }
+
 
 }

Added: 
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/MultipleObjectMapperTest.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/MultipleObjectMapperTest.java?rev=1719306&view=auto
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/MultipleObjectMapperTest.java
 (added)
+++ 
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/MultipleObjectMapperTest.java
 Fri Dec 11 10:05:39 2015
@@ -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.json;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class MultipleObjectMapperTest {
+
+    private ObjectMapper mapper;
+
+    @SuppressWarnings("unused")
+    private static class First {
+        public String first;
+        public String other;
+    }
+
+    @SuppressWarnings("unused")
+    private static class Second {
+        public String second;
+        public String other;
+    }
+
+    @Before
+    public void setup() {
+        mapper = new MultipleObjectMapperBuilder()
+                    .registerClass("/first", First.class)
+                    .registerClass("/second", Second.class)
+                    .build();
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void registeringSameUniquePathShouldThrowAnException() throws 
Exception {
+        new MultipleObjectMapperBuilder()
+            .registerClass("/other", First.class)
+            .registerClass("/other", Second.class);
+    }
+
+    @Test
+    public void registeringSameClassTwoTimesIsOK() throws Exception {
+        ObjectMapper uselessMapper = new MultipleObjectMapperBuilder()
+                .registerClass("/first", First.class)
+                .registerClass("/other", First.class)
+                .build();
+        String json = "{\"first\": \"value\", \"other\": \"other\"}";
+        Object o = uselessMapper.readValue(json, Object.class);
+        assertThat(o).isInstanceOf(First.class);
+    }
+
+    @Test(expected=JsonMappingException.class)
+    public void badJsonShouldThrowException() throws Exception {
+        String json = "{\"bad\": \"value\"}";
+        mapper.readValue(json, Object.class);
+    }
+
+    @Test
+    public void firstJsonShouldReturnFirstClass() throws Exception {
+        String json = "{\"first\": \"value\", \"other\": \"other\"}";
+        Object o = mapper.readValue(json, Object.class);
+        assertThat(o).isInstanceOf(First.class);
+    }
+
+    @Test
+    public void secondJsonShouldReturnSecondClass() throws Exception {
+        String json = "{\"second\": \"value\", \"other\": \"other\"}";
+        Object o = mapper.readValue(json, Object.class);
+        assertThat(o).isInstanceOf(Second.class);
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to