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

[4/4] james-project git commit: JAMES-1784 Add integration tests when downloading attachment with access token

JAMES-1784 Add integration tests when downloading attachment with access token


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/85aeb1e4
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/85aeb1e4
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/85aeb1e4

Branch: refs/heads/master
Commit: 85aeb1e45b5d5cbffbe40b7c71f7ed264e57b5a6
Parents: b6f6ac9
Author: Antoine Duprat <ad...@linagora.com>
Authored: Fri Jul 1 14:56:30 2016 +0200
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Fri Jul 8 09:54:07 2016 +0200

----------------------------------------------------------------------
 .../integration/cucumber/DownloadStepdefs.java  | 170 ++++++++++++++++---
 .../resources/cucumber/DownloadEndpoint.feature |  51 ++++--
 .../test/resources/cucumber/DownloadGet.feature |   5 +-
 .../java/org/apache/james/jmap/JMAPServer.java  |   2 +-
 .../james/jmap/crypto/SignedTokenFactory.java   |   4 +-
 .../james/jmap/model/AttachmentAccessToken.java |  20 ++-
 .../jmap/crypto/SignedTokenFactoryTest.java     |   3 +-
 .../jmap/crypto/SignedTokenManagerTest.java     |  10 ++
 8 files changed, 220 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/85aeb1e4/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
index b414de9..53fe11d 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.methods.integration.cucumber;
 
 import static com.jayway.restassured.RestAssured.with;
-import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.isIn;
 import static org.hamcrest.Matchers.notNullValue;
 
 import java.util.Date;
@@ -31,10 +31,14 @@ import javax.inject.Inject;
 import javax.mail.Flags;
 
 import org.apache.james.jmap.api.access.AccessToken;
+import org.apache.james.jmap.model.AttachmentAccessToken;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
 
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
 import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Multimap;
 import com.jayway.restassured.http.ContentType;
 import com.jayway.restassured.response.Response;
@@ -49,12 +53,21 @@ import cucumber.runtime.java.guice.ScenarioScoped;
 @ScenarioScoped
 public class DownloadStepdefs {
 
+    private static final String ONE_ATTACHMENT_EML_ATTACHEMENT_BLOB_ID = "4000c5145f633410b80be368c44e1c394bff9437";
+    private static final String EXPIRED_ATTACHMENT_TOKEN = "usera@domain.tld_"
+            + "2016-06-29T13:41:22.124Z_"
+            + "DiZa0O14MjLWrAA8P6MG35Gt5CBp7mt5U1EH/M++rIoZK7nlGJ4dPW0dvZD7h4m3o5b/Yd8DXU5x2x4+s0HOOKzD7X0RMlsU7JHJMNLvTvRGWF/C+MUyC8Zce7DtnRVPEQX2uAZhL2PBABV07Vpa8kH+NxoS9CL955Bc1Obr4G+KN2JorADlocFQA6ElXryF5YS/HPZSvq1MTC6aJIP0ku8WRpRnbwgwJnn26YpcHXcJjbkCBtd9/BhlMV6xNd2hTBkfZmYdoNo+UKBaXWzLxAlbLuxjpxwvDNJfOEyWFPgHDoRvzP+G7KzhVWjanHAHrhF0GilEa/MKpOI1qHBSwA==";
+    private static final String INVALID_ATTACHMENT_TOKEN = "usera@domain.tld_"
+            + "2015-06-29T13:41:22.124Z_"
+            + "DiZa0O14MjLWrAA8P6MG35Gt5CBp7mt5U1EH/M++rIoZK7nlGJ4dPW0dvZD7h4m3o5b/Yd8DXU5x2x4+s0HOOKzD7X0RMlsU7JHJMNLvTvRGWF/C+MUyC8Zce7DtnRVPEQX2uAZhL2PBABV07Vpa8kH+NxoS9CL955Bc1Obr4G+KN2JorADlocFQA6ElXryF5YS/HPZSvq1MTC6aJIP0ku8WRpRnbwgwJnn26YpcHXcJjbkCBtd9/BhlMV6xNd2hTBkfZmYdoNo+UKBaXWzLxAlbLuxjpxwvDNJfOEyWFPgHDoRvzP+G7KzhVWjanHAHrhF0GilEa/MKpOI1qHBSwA==";
+
     private final UserStepdefs userStepdefs;
     private final MainStepdefs mainStepdefs;
     private Response response;
     private Multimap<String, String> attachmentsByMessageId;
     private Map<String, String> blobIdByAttachmentId;
     private ValidatableResponse validatableResponse;
+    private Map<AttachmentAccessTokenKey, AttachmentAccessToken> attachmentAccessTokens;
 
     @Inject
     private DownloadStepdefs(MainStepdefs mainStepdefs, UserStepdefs userStepdefs) {
@@ -62,6 +75,7 @@ public class DownloadStepdefs {
         this.userStepdefs = userStepdefs;
         this.attachmentsByMessageId = ArrayListMultimap.create();
         this.blobIdByAttachmentId = new HashMap<>();
+        this.attachmentAccessTokens = new HashMap<>();
     }
 
     @Given("^\"([^\"]*)\" mailbox \"([^\"]*)\" contains a message \"([^\"]*)\" with an attachment \"([^\"]*)\"$")
@@ -95,38 +109,144 @@ public class DownloadStepdefs {
             with.header("Authorization", accessToken.serialize());
         }
 
-        response = with.options("/download/myBlob");
+        response = with
+                .options("/download/" + ONE_ATTACHMENT_EML_ATTACHEMENT_BLOB_ID);
     }
 
     @When("^\"([^\"]*)\" downloads \"([^\"]*)\"$")
     public void downloads(String username, String attachmentId) throws Throwable {
         String blobId = blobIdByAttachmentId.get(attachmentId);
+        response = authenticatedDownloadRequest(blobId, username)
+                .get("/download/" + blobId);
+    }
+
+    private RequestSpecification authenticatedDownloadRequest(String blobId, String username) {
         AccessToken accessToken = userStepdefs.tokenByUser.get(username);
-        RequestSpecification with = with();
+        AttachmentAccessTokenKey key = new AttachmentAccessTokenKey(username, blobId);
+        if (attachmentAccessTokens.containsKey(key)) {
+            return with().param("access_token", attachmentAccessTokens.get(key).serialize());
+        }
         if (accessToken != null) {
-            with.header("Authorization", accessToken.serialize());
+            return with().header("Authorization", accessToken.serialize());
         }
-        response = with.get("/download/" + blobId);
+        return with();
     }
-    
 
-    @When("^\"([^\"]*)\" asks for an attachment without blobId parameter$")
-    public void getDownloadWithoutBlobId(String username) throws Throwable {
-        AccessToken accessToken = userStepdefs.tokenByUser.get(username);
+    @When("^\"([^\"]*)\" is trusted for attachment \"([^\"]*)\"$")
+    public void attachmentAccessTokenFor(String username, String attachmentId) throws Throwable {
+        userStepdefs.connectUser(username);
+        trustForBlobId(blobIdByAttachmentId.get(attachmentId), username);
+    }
+
+    private static class AttachmentAccessTokenKey {
+
+        private String username;
+        private String blobId;
+
+        public AttachmentAccessTokenKey(String username, String blobId) {
+            this.username = username;
+            this.blobId = blobId;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof AttachmentAccessTokenKey) {
+                AttachmentAccessTokenKey other = (AttachmentAccessTokenKey) obj;
+                return Objects.equal(username, other.username)
+                    && Objects.equal(blobId, other.blobId);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(username, blobId);
+        }
+
+        @Override
+        public String toString() {
+            return MoreObjects
+                    .toStringHelper(this)
+                    .add("username", username)
+                    .add("blobId", blobId)
+                    .toString();
+        }
+    }
+
+    private void trustForBlobId(String blobId, String username) {
+        String serializedAttachmentAccessToken = 
+            with()
+                .header("Authorization", userStepdefs.tokenByUser.get(username).serialize())
+            .post("/download/" + blobId)
+            .then()
+                .extract()
+                .asString();
+        attachmentAccessTokens.put(
+                new AttachmentAccessTokenKey(username, blobId),
+                AttachmentAccessToken.from(
+                    serializedAttachmentAccessToken,
+                    blobId));
+    }
+
+    @When("^\"([^\"]*)\" downloads \"([^\"]*)\" with a valid authentication token$")
+    public void downloadsWithValidToken(String username, String attachmentId) throws Throwable {
+        String blobId = blobIdByAttachmentId.get(attachmentId);
+
+        response = requestWithQueryParameterAuthentication(username, blobId)
+                .get("/download/" + attachmentId);
+    }
+
+    @When("^\"([^\"]*)\" downloads \"([^\"]*)\" without any authentication token$")
+    public void getDownloadWithoutToken(String username, String attachmentId) {
+        String blobId = blobIdByAttachmentId.get(attachmentId);
+        response = with().get("/download/" + blobId);
+    }
+
+    @When("^\"([^\"]*)\" downloads \"([^\"]*)\" with an empty authentication token$")
+    public void getDownloadWithEmptyToken(String username, String attachmentId) {
+        String blobId = blobIdByAttachmentId.get(attachmentId);
         response = with()
-            .header("Authorization", accessToken.serialize())
-            .get("/download/");
+                .param("access_token", "")
+                .get("/download/" + blobId);
     }
-    
 
-    @When("^\"([^\"]*)\" asks for an attachment with wrong blobId$")
-    public void getDownloadWithWrongBlobId(String username) throws Throwable {
-        AccessToken accessToken = userStepdefs.tokenByUser.get(username);
+    @When("^\"([^\"]*)\" downloads \"([^\"]*)\" with a bad authentication token$")
+    public void getDownloadWithBadToken(String username, String attachmentId) {
+        String blobId = blobIdByAttachmentId.get(attachmentId);
         response = with()
-                .header("Authorization", accessToken.serialize())
+                .param("access_token", "bad")
+                .get("/download/" + blobId);
+    }
+
+    @When("^\"([^\"]*)\" downloads \"([^\"]*)\" with an invalid authentication token$")
+    public void getDownloadWithUnknownToken(String username, String attachmentId) {
+        String blobId = blobIdByAttachmentId.get(attachmentId);
+        response = with()
+                .param("access_token", INVALID_ATTACHMENT_TOKEN)
+                .get("/download/" + blobId);
+    }
+
+    @When("^\"([^\"]*)\" downloads \"([^\"]*)\" without blobId parameter$")
+    public void getDownloadWithoutBlobId(String username, String attachmentId) throws Throwable {
+        String blobId = blobIdByAttachmentId.get(attachmentId);
+
+        response = requestWithQueryParameterAuthentication(username, blobId)
+            .get("/download/");
+    }
+
+    @When("^\"([^\"]*)\" downloads \"([^\"]*)\" with wrong blobId$")
+    public void getDownloadWithWrongBlobId(String username, String attachmentId) throws Throwable {
+        String blobId = blobIdByAttachmentId.get(attachmentId);
+
+        response = requestWithQueryParameterAuthentication(username, blobId)
                 .get("/download/badbadbadbadbadbadbadbadbadbadbadbadbadb");
     }
 
+    private RequestSpecification requestWithQueryParameterAuthentication(String username, String blobId) {
+        trustForBlobId(blobId, username);
+        return authenticatedDownloadRequest(blobId, username);
+    }
+
     @When("^\"([^\"]*)\" asks for a token for attachment \"([^\"]*)\"$")
     public void postDownload(String username, String attachmentId) throws Throwable {
         String blobId = blobIdByAttachmentId.get(attachmentId);
@@ -142,18 +262,22 @@ public class DownloadStepdefs {
     @When("^\"([^\"]*)\" downloads \"([^\"]*)\" with \"([^\"]*)\" name$")
     public void downloadsWithName(String username, String attachmentId, String name) {
         String blobId = blobIdByAttachmentId.get(attachmentId);
-        AccessToken accessToken = userStepdefs.tokenByUser.get(username);
-        RequestSpecification with = with();
-        if (accessToken != null) {
-            with.header("Authorization", accessToken.serialize());
-        }
-        response = with.get("/download/" + blobId + "/" + name);
+        response = authenticatedDownloadRequest(blobId, username)
+                .get("/download/" + blobId + "/" + name);
+    }
+
+    @When("^\"([^\"]*)\" downloads \"([^\"]*)\" with an expired token$")
+    public void getDownloadWithExpiredToken(String username, String attachmentId) {
+        String blobId = blobIdByAttachmentId.get(attachmentId);
+        response = with()
+                .param("access_token", EXPIRED_ATTACHMENT_TOKEN)
+                .get("/download/" + blobId);
     }
 
     @Then("^the user should be authorized$")
     public void httpStatusDifferentFromUnauthorized() {
         response.then()
-            .statusCode(not(401));
+            .statusCode(isIn(ImmutableList.of(200, 404)));
     }
 
     @Then("^the user should not be authorized$")

http://git-wip-us.apache.org/repos/asf/james-project/blob/85aeb1e4/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadEndpoint.feature
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadEndpoint.feature b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadEndpoint.feature
index d059572..e46a4fe 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadEndpoint.feature
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadEndpoint.feature
@@ -17,35 +17,58 @@ Feature: Download endpoint
     When "usera@domain.tld" checks for the availability of the attachment endpoint
     Then the user should be authorized
 
-  Scenario: An authenticated user should have access to the download endpoint
-    Given "usera@domain.tld" is connected
-    When "usera@domain.tld" downloads "a1"
-    Then the user should be authorized
-
-  @Ignore
   Scenario: An unauthenticated user should not have access to the download endpoint
     When "usera@domain.tld" downloads "a1"
     Then the user should not be authorized
 
-  Scenario: A authenticated user should not have access to the download endpoint without a blobId
-    Given "usera@domain.tld" is connected
-    When "usera@domain.tld" asks for an attachment without blobId parameter
-    Then the user should receive a bad request response
+  Scenario: A user should not have access to the download endpoint without the authentication token
+    When "usera@domain.tld" downloads "a1" without any authentication token
+    Then the user should not be authorized
+
+  Scenario: A user should not have access to the download endpoint with an empty authentication token
+    When "usera@domain.tld" downloads "a1" with an empty authentication token
+    Then the user should not be authorized
+
+  Scenario: A user should not have access to the download endpoint with a bad authentication token
+    When "usera@domain.tld" downloads "a1" with a bad authentication token
+    Then the user should not be authorized
+
+  Scenario: A user should not have access to the download endpoint with an unknown authentication token
+    When "usera@domain.tld" downloads "a1" with an invalid authentication token
+    Then the user should not be authorized
+
+  Scenario: A user should not have access to the download endpoint when an authentication token has expired
+    When "usera@domain.tld" downloads "a1" with an expired token
+    Then the user should not be authorized
 
+  Scenario: A user should not have access to the download endpoint without a blobId
+    Given "usera@domain.tld" is trusted for attachment "a1"
+    When "usera@domain.tld" downloads "a1" without blobId parameter
+    Then the user should not be authorized
 
   Scenario: A user should not retrieve anything when using wrong blobId
+    Given "usera@domain.tld" is trusted for attachment "a1"
+    When "usera@domain.tld" downloads "a1" with wrong blobId
+    Then the user should not be authorized
+
+  Scenario: A user should have access to the download endpoint when an authentication token is valid
+    Given "usera@domain.tld" is trusted for attachment "a1"
+    When "usera@domain.tld" downloads "a1"
+    Then the user should be authorized
+
+  Scenario: An authenticated user should have access to the download endpoint
     Given "usera@domain.tld" is connected
-    When "usera@domain.tld" asks for an attachment with wrong blobId
-    Then the user should receive a not found response
+    When "usera@domain.tld" downloads "a1"
+    Then the user should be authorized
 
   @Ignore
-  Scenario: A user should not have access to someone else attachment
+  Scenario: An authenticated user should not have access to someone else attachment
     Given "userb@domain.tld" is connected
     When "userb@domain.tld" downloads "a1"
     Then the user should receive a not found response
 
   @Ignore
-  Scenario: A user should have access to a shared attachment
+  Scenario: An authenticated user should have access to a shared attachment
     Given "usera@domain.tld" shares its mailbox "INBOX" with "userb@domain.tld"
     And "userb@domain.tld" is connected
     When "userb@domain.tld" downloads "a1"

http://git-wip-us.apache.org/repos/asf/james-project/blob/85aeb1e4/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature
index bf3d21a..ccd7eef 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadGet.feature
@@ -13,8 +13,9 @@ Feature: Download GET
     Then the user should receive that attachment
 
   Scenario: Getting an attachment with an unknown blobId
-    When "username@domain.tld" downloads "123"
-    Then the user should receive a not found response
+    Given "username@domain.tld" mailbox "inbox" contains a message "1" with an attachment "2"
+    When "username@domain.tld" downloads "2" with a valid authentication token
+    Then the user should not be authorized
 
   Scenario: Getting an attachment previously stored with a desired name
     Given "username@domain.tld" mailbox "inbox" contains a message "1" with an attachment "2"

http://git-wip-us.apache.org/repos/asf/james-project/blob/85aeb1e4/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
index 7d235a4..e1c3ba5 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
@@ -58,7 +58,7 @@ public class JMAPServer implements Configurable {
                         .serveAsOneLevelTemplate(JMAPUrls.DOWNLOAD)
                             .with(downloadServlet)
                         .filterAsOneLevelTemplate(JMAPUrls.DOWNLOAD)
-                            .with(new AllowAllCrossOriginRequests(bypass(authenticationFilter).on("GET").and("OPTIONS").only()))
+                            .with(new AllowAllCrossOriginRequests(bypass(authenticationFilter).on("OPTIONS").only()))
                             .only()
                         .serve(JMAPUrls.UPLOAD)
                             .with(uploadServlet)

http://git-wip-us.apache.org/repos/asf/james-project/blob/85aeb1e4/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/SignedTokenFactory.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/SignedTokenFactory.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/SignedTokenFactory.java
index 7154a32..594bff3 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/SignedTokenFactory.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/SignedTokenFactory.java
@@ -65,8 +65,8 @@ public class SignedTokenFactory implements SimpleTokenFactory {
                 .blobId(blobId)
                 .expirationDate(expirationTime)
                 .signature(signatureHandler.sign(Joiner.on(AttachmentAccessToken.SEPARATOR)
-                                                    .join(username, 
-                                                            blobId,
+                                                    .join(blobId,
+                                                            username, 
                                                             DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(expirationTime))))
                 .build();
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/85aeb1e4/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AttachmentAccessToken.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AttachmentAccessToken.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AttachmentAccessToken.java
index 4d13c75..2188b1e 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AttachmentAccessToken.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/AttachmentAccessToken.java
@@ -21,11 +21,15 @@ package org.apache.james.jmap.model;
 
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.List;
 import java.util.Objects;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
 
 public class AttachmentAccessToken implements SignedExpiringToken {
 
@@ -34,7 +38,21 @@ public class AttachmentAccessToken implements SignedExpiringToken {
     public static Builder builder() {
         return new Builder();
     }
-    
+
+    public static AttachmentAccessToken from(String serializedAttachmentAccessToken, String blobId) {
+        Preconditions.checkArgument(!Strings.isNullOrEmpty(serializedAttachmentAccessToken), "'AttachmentAccessToken' is mandatory");
+        List<String> split = Splitter.on(SEPARATOR).splitToList(serializedAttachmentAccessToken);
+        Preconditions.checkArgument(split.size() == 3, "Wrong 'AttachmentAccessToken'");
+
+        String defaultValue = null;
+        return builder()
+                .blobId(blobId)
+                .username(Iterables.get(split, 0, defaultValue))
+                .expirationDate(ZonedDateTime.parse(Iterables.get(split, 1, defaultValue)))
+                .signature(Iterables.get(split, 2, defaultValue))
+                .build();
+    }
+
     public static class Builder {
         private String username;
         private String blobId;

http://git-wip-us.apache.org/repos/asf/james-project/blob/85aeb1e4/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenFactoryTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenFactoryTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenFactoryTest.java
index 371083f..dfe8c18 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenFactoryTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenFactoryTest.java
@@ -73,7 +73,6 @@ public class SignedTokenFactoryTest {
     public void generateAttachmentAccessTokenShouldHaveTheRightOutPut() throws Exception {
         zonedDateTimeProvider.setFixedDateTime(DATE);
         assertThat(toKenFactory.generateAttachmentAccessToken("user", "blobId").serialize())
-            .isEqualTo("user_2011-12-03T10:20:30+01:00_UAmjTzvmmIwvE1Yw54tE7jC1Q2nCJ1l3XX1703kYmLIeOZe7fNSLM6V8CzPFEvZ+Y4H+UD4UTkNHbmgcPbxesITnby+UfT/tIiTppJhXJvtTxSoTy9vuAJrW9/kJh6CruqtSM+BUEkLKuuzJySmvDkaHSaXwot4egGXaJ9yHgjEh2PT3uA0O0JjRNB2x8oa370fFSZsT2QgXrqeqHWWO1j6IrAf4UcyhvjNkJBK9TVNubfqGKuCZ4dz2Rm/CUvp13CpzUoVqBS1nJ1VaIw94L2rX8RkAMTlV7AXKB3kPiBX7MdGp2NBiAUlYlOLjflYl8plnv/QrRCmfGxnsvv4WVQ==");
+            .isEqualTo("user_2011-12-03T10:20:30+01:00_eSg1mVxMpqw5/u6wsTAatP7hoHDoI7blEW0hxGPrRMMj3hECT+YhbUCdhz9Lb4U+jsYPgNLDuAHwxin79xXfLoq0nVsogEE32svRYVvbaDpro+EOtkAHhYnYxWnAGxB/70u7Zyw0oYGmWOwkCkLDFsWKglMp9IUpOJQP50zbzbdW+4dKlAi/8VmN8jFyZx40envRbgEn4Q2QQbnUH/7F9+vdLIl+bAfcj6QlevqFRsUkmTZelkv1rtGUAvnPSBQL4TeBx5Qk/eEiw8IbB2lbCIAoIFZC6Vl8QOO5Y6LFzmqHL9i0BjvuoiZ8FKQS0pGd5CU6pwc7sv0xD82Vx1eFiw==");
     }
-
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/85aeb1e4/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenManagerTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenManagerTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenManagerTest.java
index c371f0b..b31f0df 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenManagerTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/SignedTokenManagerTest.java
@@ -27,6 +27,7 @@ import java.time.format.DateTimeFormatter;
 
 import org.apache.james.jmap.FixedDateZonedDateTimeProvider;
 import org.apache.james.jmap.api.SimpleTokenManager.TokenStatus;
+import org.apache.james.jmap.model.AttachmentAccessToken;
 import org.apache.james.jmap.model.ContinuationToken;
 import org.junit.Before;
 import org.junit.Test;
@@ -168,4 +169,13 @@ public class SignedTokenManagerTest {
         ContinuationToken pirateContinuationToken = new ContinuationToken("user", DATE.plusMinutes(15), "fake");
         assertThat(tokenManager.getValidity(pirateContinuationToken)).isEqualTo(TokenStatus.INVALID);
     }
+
+    @Test
+    public void signedAttachmentAccessTokenShouldBeValidated() {
+        String blobId = "blobId";
+        zonedDateTimeProvider.setFixedDateTime(DATE);
+        String serializedToken = tokenFactory.generateAttachmentAccessToken("user", blobId).serialize();
+
+        assertThat(tokenManager.isValid(AttachmentAccessToken.from(serializedToken, blobId))).isTrue();
+    }
 }


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