Author: matthieu
Date: Fri Dec 11 10:06:32 2015
New Revision: 1719311
URL: http://svn.apache.org/viewvc?rev=1719311&view=rev
Log:
JAMES-1644 AuthenticationServlet should use ContinuationTokenManager
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/BadRequestException.java
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InternalErrorException.java
Removed:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/BadRequestException.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/AccessTokenRequest.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=1719311&r1=1719310&r2=1719311&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:06:32 2015
@@ -19,7 +19,6 @@
package org.apache.james.jmap;
import java.io.IOException;
-import java.time.ZonedDateTime;
import javax.inject.Inject;
import javax.servlet.ServletException;
@@ -27,10 +26,12 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.james.jmap.api.ContinuationTokenManager;
+import org.apache.james.jmap.exceptions.BadRequestException;
+import org.apache.james.jmap.exceptions.InternalErrorException;
import org.apache.james.jmap.json.MultipleObjectMapperBuilder;
import org.apache.james.jmap.model.AccessTokenRequest;
import org.apache.james.jmap.model.AccessTokenResponse;
-import org.apache.james.jmap.model.ContinuationToken;
import org.apache.james.jmap.model.ContinuationTokenRequest;
import org.apache.james.jmap.model.ContinuationTokenResponse;
import org.apache.james.user.api.UsersRepository;
@@ -50,16 +51,19 @@ public class AuthenticationServlet exten
private final ObjectMapper mapper;
private final UsersRepository usersRepository;
+ private final ContinuationTokenManager continuationTokenManager;
@Inject
- @VisibleForTesting AuthenticationServlet(UsersRepository usersRepository) {
+ @VisibleForTesting AuthenticationServlet(UsersRepository usersRepository,
ContinuationTokenManager continuationTokenManager) {
this.usersRepository = usersRepository;
+ this.continuationTokenManager = continuationTokenManager;
this.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 {
try {
@@ -76,6 +80,9 @@ public class AuthenticationServlet exten
} catch (BadRequestException e) {
LOG.warn("Invalid authentication request received.", e);
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ } catch (InternalErrorException e) {
+ LOG.error("Internal error", e);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
@@ -104,23 +111,37 @@ public class AuthenticationServlet exten
private void handleContinuationTokenRequest(ContinuationTokenRequest
request, HttpServletResponse resp) throws IOException {
resp.setContentType(JSON_CONTENT_TYPE_UTF8);
-
- ContinuationTokenResponse continuationTokenResponse =
ContinuationTokenResponse
+ try {
+ ContinuationTokenResponse continuationTokenResponse =
ContinuationTokenResponse
.builder()
- // TODO Answer a real token
- .continuationToken(new ContinuationToken("fake",
ZonedDateTime.now(), "fake"))
+
.continuationToken(continuationTokenManager.generateToken(request.getUsername()))
.methods(ContinuationTokenResponse.AuthenticationMethod.PASSWORD)
.build();
-
- mapper.writeValue(resp.getOutputStream(), continuationTokenResponse);
+ mapper.writeValue(resp.getOutputStream(),
continuationTokenResponse);
+ } catch (Exception e) {
+ throw new InternalErrorException("Error while responding to
continuation token");
+ }
}
private void handleAccessTokenRequest(AccessTokenRequest request,
HttpServletResponse resp) throws IOException {
- // TODO get username from continuationToken
- String username = "username";
+ try {
+ if (!continuationTokenManager.isValid(request.getToken())) {
+ LOG.warn("Use of an invalid ContinuationToken : " +
request.getToken().serialize());
+ returnUnauthorizedResponse(resp);
+ } else {
+ manageAuthenticationResponse(request, resp);
+ }
+ } catch(Exception e) {
+ throw new InternalErrorException("Internal error while managing
access token request", e);
+ }
+ }
+
+ private void manageAuthenticationResponse(AccessTokenRequest request,
HttpServletResponse resp) throws IOException {
+ String username = request.getToken().getUsername();
if (authenticate(request, username)) {
returnAccessTokenResponse(resp);
} else {
+ LOG.info("Authentication failure for " + username);
returnUnauthorizedResponse(resp);
}
}
@@ -151,5 +172,4 @@ public class AuthenticationServlet exten
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
-
}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/BadRequestException.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/BadRequestException.java?rev=1719311&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/BadRequestException.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/BadRequestException.java
Fri Dec 11 10:06:32 2015
@@ -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.exceptions;
+
+public class BadRequestException extends RuntimeException {
+
+ public BadRequestException(String message) {
+ super(message);
+ }
+
+ public BadRequestException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
Added:
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InternalErrorException.java
URL:
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InternalErrorException.java?rev=1719311&view=auto
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InternalErrorException.java
(added)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InternalErrorException.java
Fri Dec 11 10:06:32 2015
@@ -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.exceptions;
+
+public class InternalErrorException extends RuntimeException {
+
+ public InternalErrorException(String message) {
+ super(message);
+ }
+
+ public InternalErrorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
Modified:
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=1719311&r1=1719310&r2=1719311&view=diff
==============================================================================
---
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenRequest.java
(original)
+++
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenRequest.java
Fri Dec 11 10:06:32 2015
@@ -22,8 +22,6 @@ import com.fasterxml.jackson.databind.an
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import org.apache.james.jmap.exceptions.MalformedContinuationTokenException;
-import java.time.DateTimeException;
-
@JsonDeserialize(builder=AccessTokenRequest.Builder.class)
public class AccessTokenRequest {
@@ -42,7 +40,7 @@ public class AccessTokenRequest {
private Builder() {}
- public Builder token(String token) throws
MalformedContinuationTokenException, DateTimeException {
+ public Builder token(String token) throws
MalformedContinuationTokenException {
this.token = ContinuationToken.fromString(token);
return this;
}
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=1719311&r1=1719310&r2=1719311&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:06:32 2015
@@ -22,13 +22,19 @@ import static com.jayway.restassured.Res
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.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.isA;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.apache.james.http.jetty.Configuration;
import org.apache.james.http.jetty.JettyHttpServer;
+import org.apache.james.jmap.api.ContinuationTokenManager;
+import org.apache.james.jmap.model.ContinuationToken;
+import org.apache.james.jmap.utils.ZonedDateTimeProvider;
import org.apache.james.user.api.UsersRepository;
import org.apache.james.user.api.UsersRepositoryException;
import org.junit.After;
@@ -39,15 +45,26 @@ import com.google.common.base.Charsets;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.http.ContentType;
-public class JMAPAuthenticationTest {
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+public class JMAPAuthenticationTest {
+
+ private static final ZonedDateTime oldDate =
ZonedDateTime.parse("2011-12-03T10:15:30+01:00",
DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+ private static final ZonedDateTime newDate =
ZonedDateTime.parse("2011-12-03T10:16:30+01:00",
DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+
private JettyHttpServer server;
private UsersRepository mockedUsersRepository;
+ private ContinuationTokenManager mockedContinuationTokenManager;
+ private ZonedDateTimeProvider mockedZonedDateTimeProvider;
@Before
public void setup() throws Exception {
mockedUsersRepository = mock(UsersRepository.class);
- AuthenticationServlet authenticationServlet = new
AuthenticationServlet(mockedUsersRepository);
+ mockedContinuationTokenManager = mock(ContinuationTokenManager.class);
+ mockedZonedDateTimeProvider = mock(ZonedDateTimeProvider.class);
+
+ AuthenticationServlet authenticationServlet = new
AuthenticationServlet(mockedUsersRepository, mockedContinuationTokenManager);
server = JettyHttpServer.create(
Configuration.builder()
@@ -141,7 +158,11 @@ public class JMAPAuthenticationTest {
}
@Test
- public void mustReturnJsonResponse() {
+ public void mustReturnJsonResponse() throws Exception {
+
when(mockedContinuationTokenManager.generateToken(eq("[email protected]")))
+ .thenAnswer(invocationOnMock -> new
ContinuationToken("[email protected]", newDate, "signature"));
+
when(mockedContinuationTokenManager.isValid(any())).thenAnswer(invocationOnMock
-> true);
+
given()
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
@@ -152,6 +173,7 @@ public class JMAPAuthenticationTest {
.statusCode(200)
.contentType(ContentType.JSON);
}
+
@Test
@@ -167,7 +189,11 @@ public class JMAPAuthenticationTest {
}
@Test
- public void methodShouldContainPasswordWhenValidResquest() {
+ public void methodShouldContainPasswordWhenValidResquest() throws
Exception {
+
when(mockedZonedDateTimeProvider.provide()).thenAnswer(invocationOnMock ->
oldDate);
+
when(mockedContinuationTokenManager.generateToken(eq("[email protected]")))
+ .thenAnswer(invocationOnMock -> new
ContinuationToken("[email protected]", newDate, "signature"));
+
given()
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
@@ -180,7 +206,11 @@ public class JMAPAuthenticationTest {
}
@Test
- public void mustReturnContinuationTokenWhenValidResquest() {
+ public void mustReturnContinuationTokenWhenValidResquest() throws
Exception {
+
when(mockedZonedDateTimeProvider.provide()).thenAnswer(invocationOnMock ->
oldDate);
+
when(mockedContinuationTokenManager.generateToken(eq("[email protected]")))
+ .thenAnswer(invocationOnMock -> new
ContinuationToken("[email protected]", newDate, "signature"));
+
given()
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
@@ -193,17 +223,22 @@ public class JMAPAuthenticationTest {
}
@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();
+ public void mustReturnAuthenticationFailedWhenBadPassword() throws
Exception {
+
when(mockedZonedDateTimeProvider.provide()).thenAnswer(invocationOnMock ->
oldDate);
+
when(mockedContinuationTokenManager.generateToken(eq("[email protected]")))
+ .thenAnswer(invocationOnMock -> new
ContinuationToken("[email protected]", newDate, "signature"));
+
when(mockedContinuationTokenManager.isValid(any())).thenAnswer(invocationOnMock
-> true);
+ 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)
@@ -216,19 +251,24 @@ public class JMAPAuthenticationTest {
@Test
public void mustReturnAuthenticationFailedWhenUsersRepositoryException()
throws Exception {
- 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();
+
when(mockedZonedDateTimeProvider.provide()).thenAnswer(invocationOnMock ->
oldDate);
+
when(mockedContinuationTokenManager.generateToken(eq("[email protected]")))
+ .thenAnswer(invocationOnMock -> new
ContinuationToken("[email protected]", newDate, "signature"));
+
when(mockedContinuationTokenManager.isValid(any())).thenAnswer(invocationOnMock
-> true);
+ 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();
+
when(mockedUsersRepository.test("username", "password"))
.thenThrow(new UsersRepositoryException("test"));
-
+
given()
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
@@ -241,19 +281,24 @@ public class JMAPAuthenticationTest {
@Test
public void mustReturnCreatedWhenGoodPassword() throws Exception {
- 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();
+
when(mockedZonedDateTimeProvider.provide()).thenAnswer(invocationOnMock ->
oldDate);
+
when(mockedContinuationTokenManager.generateToken(eq("[email protected]")))
+ .thenAnswer(invocationOnMock -> new ContinuationToken("username",
newDate, "signature"));
+
when(mockedContinuationTokenManager.isValid(any())).thenAnswer(invocationOnMock
-> true);
+ 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();
+
when(mockedUsersRepository.test("username", "password"))
.thenReturn(true);
-
+
given()
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
@@ -266,27 +311,51 @@ public class JMAPAuthenticationTest {
@Test
public void mustSendJsonContainingAccessTokenWhenGoodPassword() throws
Exception {
+
when(mockedZonedDateTimeProvider.provide()).thenAnswer(invocationOnMock ->
oldDate);
+ ContinuationToken sentContinuationToken = new
ContinuationToken("[email protected]", newDate, "signature");
+
when(mockedContinuationTokenManager.generateToken(eq("[email protected]")))
+ .thenAnswer(invocationOnMock -> sentContinuationToken);
+
when(mockedContinuationTokenManager.isValid(any())).thenAnswer(invocationOnMock
-> true);
+
+ ContinuationToken receivedContinuationToken =
ContinuationToken.fromString(
+ 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());
+
+ assertThat(receivedContinuationToken).isEqualTo(sentContinuationToken);
+ }
+
+
+ @Test
+ public void
mustReturnAuthenticationFailedWhenContinuationTokenIsRejectedByTheContinuationTokenManager()
throws Exception {
+
when(mockedZonedDateTimeProvider.provide()).thenAnswer(invocationOnMock ->
oldDate);
+
when(mockedContinuationTokenManager.generateToken(eq("[email protected]")))
+ .thenAnswer(invocationOnMock -> new
ContinuationToken("[email protected]", newDate, "signature"));
+
when(mockedContinuationTokenManager.isValid(any())).thenAnswer(invocationOnMock
-> false);
+
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")
+ .post("/authentication")
.body()
- .path("continuationToken")
+ .path("continuationToken")
.toString();
- when(mockedUsersRepository.test("username", "password"))
- .thenReturn(true);
-
given()
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
- .body("{\"token\": \"" + continuationToken + "\", \"method\":
\"password\", \"password\": \"password\"}")
+ .body("{\"token\": \"" + continuationToken + "\", \"method\":
\"password\", \"password\": \"badpassword\"}")
.when()
.post("/authentication")
.then()
- .contentType(ContentType.JSON)
- .body("accessToken", isA(String.class));
+ .statusCode(401);
}
+
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]