Author: matthieu
Date: Fri Dec 11 10:07:38 2015
New Revision: 1719318

URL: http://svn.apache.org/viewvc?rev=1719318&view=rev
Log:
JAMES-1644 use AccessTokenRepository

Added:
    
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/InvalidAccessToken.java
    
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/api/AccessTokenManager.java
    
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/AccessTokenManagerImpl.java
    
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/AccessTokenManagerImplTest.java
Modified:
    
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/main/java/org/apache/james/jmap/api/access/AccessTokenRepository.java
    
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/AccessTokenAlreadyStored.java
    
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepository.java
    
james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepositoryTest.java
    
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/AccessTokenResponse.java
    
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java

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=1719318&r1=1719317&r2=1719318&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
 Fri Dec 11 10:07:38 2015
@@ -20,21 +20,26 @@
 package org.apache.james.jmap.api.access;
 
 import java.util.Objects;
+import java.util.UUID;
 
 public class AccessToken {
 
     public static AccessToken fromString(String tokenString) {
-        return new AccessToken(tokenString);
+        return new AccessToken(UUID.fromString(tokenString));
     }
 
-    private final String token;
+    private final UUID token;
 
-    private AccessToken(String token) {
+    private AccessToken(UUID token) {
         this.token = token;
     }
+    
+    public static AccessToken generate() {
+        return new AccessToken(UUID.randomUUID());
+    }
 
     public String serialize() {
-        return token;
+        return token.toString();
     }
 
     @Override

Modified: 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/AccessTokenRepository.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/AccessTokenRepository.java?rev=1719318&r1=1719317&r2=1719318&view=diff
==============================================================================
--- 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/AccessTokenRepository.java
 (original)
+++ 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/AccessTokenRepository.java
 Fri Dec 11 10:07:38 2015
@@ -20,13 +20,14 @@
 package org.apache.james.jmap.api.access;
 
 import org.apache.james.jmap.api.access.exceptions.AccessTokenAlreadyStored;
+import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
 
 public interface AccessTokenRepository {
 
-    void addToken(AccessToken accessToken) throws AccessTokenAlreadyStored;
+    void addToken(String username, AccessToken accessToken) throws 
AccessTokenAlreadyStored;
 
     void removeToken(AccessToken accessToken);
 
-    boolean verifyToken(AccessToken accessToken);
+    String getUsernameFromToken(AccessToken accessToken) throws 
InvalidAccessToken;
 
 }

Modified: 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/AccessTokenAlreadyStored.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/AccessTokenAlreadyStored.java?rev=1719318&r1=1719317&r2=1719318&view=diff
==============================================================================
--- 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/AccessTokenAlreadyStored.java
 (original)
+++ 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/AccessTokenAlreadyStored.java
 Fri Dec 11 10:07:38 2015
@@ -21,7 +21,7 @@ package org.apache.james.jmap.api.access
 
 import org.apache.james.jmap.api.access.AccessToken;
 
-public class AccessTokenAlreadyStored extends Exception {
+public class AccessTokenAlreadyStored extends RuntimeException {
 
     public AccessTokenAlreadyStored(AccessToken token) {
         super(token.serialize() + " is already stored");

Added: 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/InvalidAccessToken.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/InvalidAccessToken.java?rev=1719318&view=auto
==============================================================================
--- 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/InvalidAccessToken.java
 (added)
+++ 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/access/exceptions/InvalidAccessToken.java
 Fri Dec 11 10:07:38 2015
@@ -0,0 +1,29 @@
+/****************************************************************
+ * 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;
+
+import org.apache.james.jmap.api.access.AccessToken;
+
+public class InvalidAccessToken extends RuntimeException {
+
+    public InvalidAccessToken(AccessToken token) {
+        super(token.serialize() + " is missing");
+    }
+}

Modified: 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepository.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepository.java?rev=1719318&r1=1719317&r2=1719318&view=diff
==============================================================================
--- 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepository.java
 (original)
+++ 
james/project/trunk/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepository.java
 Fri Dec 11 10:07:38 2015
@@ -19,19 +19,23 @@
 
 package org.apache.james.jmap.memory.access;
 
-import com.google.common.base.Preconditions;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 import org.apache.commons.collections4.map.PassiveExpiringMap;
 import org.apache.james.jmap.api.access.AccessToken;
 import org.apache.james.jmap.api.access.AccessTokenRepository;
 import org.apache.james.jmap.api.access.exceptions.AccessTokenAlreadyStored;
+import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import com.google.common.base.Preconditions;
 
 @Singleton
 public class MemoryAccessTokenRepository implements AccessTokenRepository {
 
-    private final PassiveExpiringMap<AccessToken, Boolean> 
tokensExpirationDates;
+    private final PassiveExpiringMap<AccessToken, String> 
tokensExpirationDates;
 
     @Inject
     public MemoryAccessTokenRepository(long durationInMilliseconds) {
@@ -39,10 +43,12 @@ public class MemoryAccessTokenRepository
     }
 
     @Override
-    public void addToken(AccessToken accessToken) throws 
AccessTokenAlreadyStored{
+    public void addToken(String username, AccessToken accessToken) throws 
AccessTokenAlreadyStored{
+        Preconditions.checkNotNull(username);
+        Preconditions.checkArgument(! username.isEmpty(), "Username should not 
be empty");
         Preconditions.checkNotNull(accessToken);
         synchronized (tokensExpirationDates) {
-            if (tokensExpirationDates.putIfAbsent(accessToken, true) != null) {
+            if (tokensExpirationDates.putIfAbsent(accessToken, username) != 
null) {
                 throw new AccessTokenAlreadyStored(accessToken);
             }
         }
@@ -57,10 +63,12 @@ public class MemoryAccessTokenRepository
     }
 
     @Override
-    public boolean verifyToken(AccessToken accessToken) {
+    public String getUsernameFromToken(AccessToken accessToken) throws 
InvalidAccessToken {
         Preconditions.checkNotNull(accessToken);
         synchronized (tokensExpirationDates) {
-            return tokensExpirationDates.containsKey(accessToken);
+            return Optional
+                    .ofNullable(tokensExpirationDates.get(accessToken))
+                    .orElseThrow(() -> new InvalidAccessToken(accessToken));
         }
     }
 

Modified: 
james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepositoryTest.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepositoryTest.java?rev=1719318&r1=1719317&r2=1719318&view=diff
==============================================================================
--- 
james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepositoryTest.java
 (original)
+++ 
james/project/trunk/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/access/MemoryAccessTokenRepositoryTest.java
 Fri Dec 11 10:07:38 2015
@@ -24,12 +24,14 @@ import static org.assertj.core.api.Asser
 
 import org.apache.james.jmap.api.access.AccessToken;
 import org.apache.james.jmap.api.access.exceptions.AccessTokenAlreadyStored;
+import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
 import org.junit.Before;
 import org.junit.Test;
 
 public class MemoryAccessTokenRepositoryTest {
 
-    private static final AccessToken TOKEN = AccessToken.fromString("TOKEN");
+    private static final AccessToken TOKEN = AccessToken.generate();
+    private static final String USERNAME = "username";
     private static final int TTL_IN_MS = 100;
 
     private MemoryAccessTokenRepository accessTokenRepository;
@@ -41,42 +43,52 @@ public class MemoryAccessTokenRepository
 
     @Test
     public void validTokenMustWork() throws Exception {
-        accessTokenRepository.addToken(TOKEN);
-        assertThat(accessTokenRepository.verifyToken(TOKEN)).isTrue();
+        accessTokenRepository.addToken(USERNAME, TOKEN);
+        
assertThat(accessTokenRepository.getUsernameFromToken(TOKEN)).isEqualTo(USERNAME);
     }
 
-    @Test
+    @Test(expected=InvalidAccessToken.class)
     public void nonStoredTokensMustBeInvalid() throws Exception {
-        assertThat(accessTokenRepository.verifyToken(TOKEN)).isFalse();
+        accessTokenRepository.getUsernameFromToken(TOKEN);
     }
 
-    @Test
+    @Test(expected=InvalidAccessToken.class)
     public void removedTokensMustBeInvalid() throws Exception {
-        accessTokenRepository.addToken(TOKEN);
+        accessTokenRepository.addToken(USERNAME, TOKEN);
         accessTokenRepository.removeToken(TOKEN);
-        assertThat(accessTokenRepository.verifyToken(TOKEN)).isFalse();
+        accessTokenRepository.getUsernameFromToken(TOKEN);
     }
 
     @Test(expected = AccessTokenAlreadyStored.class)
     public void addTokenMustThrowWhenTokenIsAlreadyStored() throws Exception {
         try {
-            accessTokenRepository.addToken(TOKEN);
+            accessTokenRepository.addToken(USERNAME, TOKEN);
         } catch(Exception e) {
             fail("Exception caught", e);
         }
-        accessTokenRepository.addToken(TOKEN);
+        accessTokenRepository.addToken(USERNAME, TOKEN);
     }
 
-    @Test
+    @Test(expected=InvalidAccessToken.class)
     public void outDatedTokenMustBeInvalid() throws Exception {
-        accessTokenRepository.addToken(TOKEN);
+        accessTokenRepository.addToken(USERNAME, TOKEN);
         Thread.sleep(200);
-        assertThat(accessTokenRepository.verifyToken(TOKEN)).isFalse();
+        accessTokenRepository.getUsernameFromToken(TOKEN);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void addTokenMustThrowWhenUsernameIsNull() throws Exception {
+        accessTokenRepository.addToken(null, TOKEN);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void addTokenMustThrowWhenUsernameIsEmpty() throws Exception {
+        accessTokenRepository.addToken("", TOKEN);
     }
 
     @Test(expected = NullPointerException.class)
     public void addTokenMustThrowWhenTokenIsNull() throws Exception {
-        accessTokenRepository.addToken(null);
+        accessTokenRepository.addToken(USERNAME, null);
     }
 
     @Test(expected = NullPointerException.class)
@@ -85,8 +97,8 @@ public class MemoryAccessTokenRepository
     }
 
     @Test(expected = NullPointerException.class)
-    public void verifyTokenTokenMustThrowWhenTokenIsNull() throws Exception {
-        accessTokenRepository.verifyToken(null);
+    public void getUsernameFromTokenMustThrowWhenTokenIsNull() throws 
Exception {
+        accessTokenRepository.getUsernameFromToken(null);
     }
 
 }

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=1719318&r1=1719317&r2=1719318&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:07:38 2015
@@ -26,6 +26,7 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.james.jmap.api.AccessTokenManager;
 import org.apache.james.jmap.api.ContinuationTokenManager;
 import org.apache.james.jmap.exceptions.BadRequestException;
 import org.apache.james.jmap.exceptions.InternalErrorException;
@@ -52,18 +53,19 @@ public class AuthenticationServlet exten
     private final ObjectMapper mapper;
     private final UsersRepository usersRepository;
     private final ContinuationTokenManager continuationTokenManager;
-
+    private final AccessTokenManager accessTokenManager;
+    
     @Inject
-    @VisibleForTesting AuthenticationServlet(UsersRepository usersRepository, 
ContinuationTokenManager continuationTokenManager) {
+    @VisibleForTesting AuthenticationServlet(UsersRepository usersRepository, 
ContinuationTokenManager continuationTokenManager, AccessTokenManager 
accessTokenManager) {
         this.usersRepository = usersRepository;
         this.continuationTokenManager = continuationTokenManager;
+        this.accessTokenManager = accessTokenManager;
         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 {
@@ -119,7 +121,7 @@ public class AuthenticationServlet exten
                 .build();
             mapper.writeValue(resp.getOutputStream(), 
continuationTokenResponse);
         } catch (Exception e) {
-            throw new InternalErrorException("Error while responding to 
continuation token");
+            throw new InternalErrorException("Error while responding to 
continuation token", e);
         }
     }
 
@@ -139,7 +141,7 @@ public class AuthenticationServlet exten
     private void manageAuthenticationResponse(AccessTokenRequest request, 
HttpServletResponse resp) throws IOException {
         String username = request.getToken().getUsername();
         if (authenticate(request, username)) {
-            returnAccessTokenResponse(resp);
+            returnAccessTokenResponse(resp, username);
         } else {
             LOG.info("Authentication failure for " + username);
             returnUnauthorizedResponse(resp);
@@ -156,15 +158,14 @@ public class AuthenticationServlet exten
         return authenticated;
     }
 
-    private void returnAccessTokenResponse(HttpServletResponse resp) throws 
IOException {
+    private void returnAccessTokenResponse(HttpServletResponse resp, String 
username) throws IOException {
         resp.setContentType(JSON_CONTENT_TYPE_UTF8);
         resp.setStatus(HttpServletResponse.SC_CREATED);
         AccessTokenResponse response = AccessTokenResponse
-                .builder()
-                // TODO Answer a real token
-                .accessToken("token")
-                // TODO Send API endpoints
-                .build();
+            .builder()
+            .accessToken(accessTokenManager.grantAccessToken(username))
+            // TODO Send API endpoints
+            .build();
         mapper.writeValue(resp.getOutputStream(), response);
     }
 

Added: 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/api/AccessTokenManager.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/api/AccessTokenManager.java?rev=1719318&view=auto
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/api/AccessTokenManager.java
 (added)
+++ 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/api/AccessTokenManager.java
 Fri Dec 11 10:07:38 2015
@@ -0,0 +1,31 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.api;
+
+import org.apache.james.jmap.api.access.AccessToken;
+import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
+
+public interface AccessTokenManager {
+
+    AccessToken grantAccessToken(String username);
+
+    String getUsernameFromToken(AccessToken token) throws InvalidAccessToken;
+
+}

Added: 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/AccessTokenManagerImpl.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/AccessTokenManagerImpl.java?rev=1719318&view=auto
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/AccessTokenManagerImpl.java
 (added)
+++ 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/AccessTokenManagerImpl.java
 Fri Dec 11 10:07:38 2015
@@ -0,0 +1,50 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.crypto;
+
+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.InvalidAccessToken;
+
+import com.google.common.base.Preconditions;
+
+public class AccessTokenManagerImpl implements AccessTokenManager {
+
+    private final AccessTokenRepository accessTokenRepository;
+
+    public AccessTokenManagerImpl(AccessTokenRepository accessTokenRepository) 
{
+        this.accessTokenRepository = accessTokenRepository;
+    }
+
+    @Override
+    public AccessToken grantAccessToken(String username) {
+        Preconditions.checkNotNull(username);
+        AccessToken accessToken = AccessToken.generate();
+        accessTokenRepository.addToken(username, accessToken);
+        return accessToken;
+    }
+
+    @Override
+    public String getUsernameFromToken(AccessToken token) throws 
InvalidAccessToken {
+        return accessTokenRepository.getUsernameFromToken(token);
+    }
+
+}

Modified: 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenResponse.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenResponse.java?rev=1719318&r1=1719317&r2=1719318&view=diff
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenResponse.java
 (original)
+++ 
james/project/trunk/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AccessTokenResponse.java
 Fri Dec 11 10:07:38 2015
@@ -18,6 +18,8 @@
  ****************************************************************/
 package org.apache.james.jmap.model;
 
+import org.apache.james.jmap.api.access.AccessToken;
+
 public class AccessTokenResponse {
 
     public static Builder builder() {
@@ -25,7 +27,7 @@ public class AccessTokenResponse {
     }
 
     public static class Builder {
-        private String accessToken;
+        private AccessToken accessToken;
         private String api;
         private String eventSource;
         private String upload;
@@ -33,7 +35,7 @@ public class AccessTokenResponse {
 
         private Builder() {}
 
-        public Builder accessToken(String accessToken) {
+        public Builder accessToken(AccessToken accessToken) {
             this.accessToken = accessToken;
             return this;
         }
@@ -63,13 +65,13 @@ public class AccessTokenResponse {
         }
     }
 
-    private final String accessToken;
+    private final AccessToken accessToken;
     private final String api;
     private final String eventSource;
     private final String upload;
     private final String download;
 
-    private AccessTokenResponse(String accessToken, String api, String 
eventSource, String upload, String download) {
+    private AccessTokenResponse(AccessToken accessToken, String api, String 
eventSource, String upload, String download) {
         this.accessToken = accessToken;
         this.api = api;
         this.eventSource = eventSource;
@@ -78,7 +80,7 @@ public class AccessTokenResponse {
     }
 
     public String getAccessToken() {
-        return accessToken;
+        return accessToken.serialize();
     }
 
     public String getApi() {

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=1719318&r1=1719317&r2=1719318&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:07:38 2015
@@ -22,18 +22,22 @@ 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 java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
 import org.apache.james.http.jetty.Configuration;
 import org.apache.james.http.jetty.JettyHttpServer;
+import org.apache.james.jmap.api.AccessTokenManager;
 import org.apache.james.jmap.api.ContinuationTokenManager;
-import org.apache.james.jmap.model.ContinuationToken;
+import org.apache.james.jmap.crypto.AccessTokenManagerImpl;
+import org.apache.james.jmap.crypto.JamesSignatureHandlerProvider;
+import org.apache.james.jmap.crypto.SignedContinuationTokenManager;
+import org.apache.james.jmap.memory.access.MemoryAccessTokenRepository;
 import org.apache.james.jmap.utils.ZonedDateTimeProvider;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
@@ -45,26 +49,27 @@ import com.google.common.base.Charsets;
 import com.jayway.restassured.RestAssured;
 import com.jayway.restassured.http.ContentType;
 
-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 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 static final ZonedDateTime afterExpirationDate = 
ZonedDateTime.parse("2011-12-03T10:30:31+01:00", 
DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+
     private JettyHttpServer server;
+
     private UsersRepository mockedUsersRepository;
-    private ContinuationTokenManager mockedContinuationTokenManager;
+    private ContinuationTokenManager continuationTokenManager;
     private ZonedDateTimeProvider mockedZonedDateTimeProvider;
-    
+    private AccessTokenManager accessTokenManager;
+
     @Before
     public void setup() throws Exception {
         mockedUsersRepository = mock(UsersRepository.class);
-        mockedContinuationTokenManager = mock(ContinuationTokenManager.class);
         mockedZonedDateTimeProvider = mock(ZonedDateTimeProvider.class);
+        accessTokenManager = new AccessTokenManagerImpl(new 
MemoryAccessTokenRepository(100));
+        continuationTokenManager = new SignedContinuationTokenManager(new 
JamesSignatureHandlerProvider().provide(), mockedZonedDateTimeProvider);
         
-        AuthenticationServlet authenticationServlet = new 
AuthenticationServlet(mockedUsersRepository, mockedContinuationTokenManager);
+        AuthenticationServlet authenticationServlet = new 
AuthenticationServlet(mockedUsersRepository, continuationTokenManager, 
accessTokenManager);
         
         server = JettyHttpServer.create(
                 Configuration.builder()
@@ -82,17 +87,7 @@ public class JMAPAuthenticationTest {
     public void teardown() throws Exception {
         server.stop();
     }
-
-    @Test
-    public void shouldReturnMalformedRequestWhenXMLContentType() {
-        given()
-            .contentType(ContentType.XML)
-        .when()
-            .post("/authentication")
-        .then()
-            .statusCode(400);
-    }
-
+    
     @Test
     public void mustReturnMalformedRequestWhenContentTypeIsMissing() {
         given()
@@ -158,41 +153,37 @@ public class JMAPAuthenticationTest {
     }
 
     @Test
-    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);
-
+    public void mustReturnMalformedRequestWhenBodyIsNotAcceptable() {
         given()
             .contentType(ContentType.JSON)
             .accept(ContentType.JSON)
-            .body("{\"username\": \"[email protected]\", \"clientName\": 
\"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe 
Blogg’s iPhone\"}")
+            .body("{\"badAttributeName\": \"value\"}")
         .when()
             .post("/authentication")
         .then()
-            .statusCode(200)
-            .contentType(ContentType.JSON);
+            .statusCode(400);
     }
-    
-
 
     @Test
-    public void mustReturnMalformedRequestWhenBodyIsNotAcceptable() {
+    public void mustReturnJsonResponse() throws Exception {
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(oldDate);
+
         given()
             .contentType(ContentType.JSON)
             .accept(ContentType.JSON)
-            .body("{\"badAttributeName\": \"value\"}")
+            .body("{\"username\": \"[email protected]\", \"clientName\": 
\"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe 
Blogg’s iPhone\"}")
         .when()
             .post("/authentication")
         .then()
-            .statusCode(400);
+            .statusCode(200)
+            .contentType(ContentType.JSON);
     }
 
     @Test
     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"));
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(oldDate);
 
         given()
             .contentType(ContentType.JSON)
@@ -207,9 +198,8 @@ public class JMAPAuthenticationTest {
 
     @Test
     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"));
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(oldDate);
 
         given()
             .contentType(ContentType.JSON)
@@ -224,25 +214,53 @@ public class JMAPAuthenticationTest {
 
     @Test
     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);
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(oldDate);
 
         String continuationToken =
-        with()
+                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("{\"username\": \"[email protected]\", \"clientName\": 
\"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe 
Blogg’s iPhone\"}")
-        .post("/authentication")
-            .body()
-            .path("continuationToken")
-            .toString();
+            .body("{\"token\": \"" + continuationToken + "\", \"method\": 
\"password\", \"password\": \"badpassword\"}")
+        .when()
+            .post("/authentication")
+        .then()
+            .statusCode(401);
+    }
+
+    @Test
+    public void 
mustReturnAuthenticationFailedWhenContinuationTokenIsRejectedByTheContinuationTokenManager()
 throws Exception {
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(oldDate);
+
+        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("[email protected]", "password"))
+            .thenReturn(true);
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(afterExpirationDate);
+
         given()
             .contentType(ContentType.JSON)
             .accept(ContentType.JSON)
-            .body("{\"token\": \"" + continuationToken + "\", \"method\": 
\"password\", \"password\": \"badpassword\"}")
+            .body("{\"token\": \"" + continuationToken + "\", \"method\": 
\"password\", \"password\": \"password\"}")
         .when()
             .post("/authentication")
         .then()
@@ -251,24 +269,22 @@ public class JMAPAuthenticationTest {
 
     @Test
     public void mustReturnAuthenticationFailedWhenUsersRepositoryException() 
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);
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(oldDate);
 
         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"))
+                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("[email protected]", "password"))
             .thenThrow(new UsersRepositoryException("test"));
-    
+
         given()
             .contentType(ContentType.JSON)
             .accept(ContentType.JSON)
@@ -281,24 +297,24 @@ public class JMAPAuthenticationTest {
 
     @Test
     public void mustReturnCreatedWhenGoodPassword() throws Exception {
-        
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);
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(oldDate);
 
         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"))
+                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("[email protected]", "password"))
             .thenReturn(true);
-        
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(newDate);
+
         given()
             .contentType(ContentType.JSON)
             .accept(ContentType.JSON)
@@ -311,51 +327,32 @@ 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);
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(oldDate);
 
         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")
-                .toString();
+                    .toString();
+
+        when(mockedUsersRepository.test("[email protected]", "password"))
+            .thenReturn(true);
+        when(mockedZonedDateTimeProvider.provide())
+            .thenReturn(newDate);
 
         given()
             .contentType(ContentType.JSON)
             .accept(ContentType.JSON)
-            .body("{\"token\": \"" + continuationToken + "\", \"method\": 
\"password\", \"password\": \"badpassword\"}")
+            .body("{\"token\": \"" + continuationToken + "\", \"method\": 
\"password\", \"password\": \"password\"}")
         .when()
             .post("/authentication")
         .then()
-            .statusCode(401);
+            .body("accessToken", isA(String.class));
     }
 
 }

Added: 
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/AccessTokenManagerImplTest.java
URL: 
http://svn.apache.org/viewvc/james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/AccessTokenManagerImplTest.java?rev=1719318&view=auto
==============================================================================
--- 
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/AccessTokenManagerImplTest.java
 (added)
+++ 
james/project/trunk/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/AccessTokenManagerImplTest.java
 Fri Dec 11 10:07:38 2015
@@ -0,0 +1,75 @@
+/****************************************************************
+ * 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 static org.assertj.core.api.Assertions.assertThat;
+
+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.InvalidAccessToken;
+import org.apache.james.jmap.memory.access.MemoryAccessTokenRepository;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AccessTokenManagerImplTest {
+    
+    private AccessTokenManager accessTokenManager;
+    private AccessTokenRepository accessTokenRepository;
+    
+    @Before
+    public void setUp() throws Exception {
+        accessTokenRepository = new MemoryAccessTokenRepository(100);
+        accessTokenManager = new AccessTokenManagerImpl(accessTokenRepository);
+    }
+    
+    @Test(expected=NullPointerException.class)
+    public void grantShouldThrowOnNullUsername() throws Exception {
+        accessTokenManager.grantAccessToken(null);
+    }
+    
+    @Test
+    public void grantShouldGenerateATokenOnUsername() throws Exception {
+        
assertThat(accessTokenManager.grantAccessToken("username")).isNotNull();
+    }
+
+    @Test
+    public void grantShouldStoreATokenOnUsername() throws Exception {
+        AccessToken token = accessTokenManager.grantAccessToken("username");
+        
assertThat(accessTokenRepository.getUsernameFromToken(token)).isEqualTo("username");
+    }
+    
+    @Test(expected=InvalidAccessToken.class)
+    public void getUsernameShouldThrowWhenUnknownToken() throws Exception {
+        accessTokenManager.getUsernameFromToken(AccessToken.generate());
+    }
+
+    @Test(expected=InvalidAccessToken.class)
+    public void getUsernameShouldThrowWhenOtherToken() throws Exception {
+        accessTokenManager.grantAccessToken("username");
+        accessTokenManager.getUsernameFromToken(AccessToken.generate());
+    }
+
+    @Test
+    public void getUsernameShouldReturnUsernameWhenExistingUsername() throws 
Exception {
+        AccessToken token = accessTokenManager.grantAccessToken("username");
+        
assertThat(accessTokenManager.getUsernameFromToken(token)).isEqualTo("username");
+    }
+
+}




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

Reply via email to