blob: 6ef8a03b111b4698bcc4d857d6cf46c541a5791d [file] [log] [blame]
/****************************************************************
* 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.methods.integration.cucumber;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.mail.Flags;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.apache.http.client.utils.URIBuilder;
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.Charsets;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
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 HttpResponse response;
private Multimap<String, String> attachmentsByMessageId;
private Map<String, String> blobIdByAttachmentId;
private Map<AttachmentAccessTokenKey, AttachmentAccessToken> attachmentAccessTokens;
@Inject
private DownloadStepdefs(MainStepdefs mainStepdefs, UserStepdefs userStepdefs) {
this.mainStepdefs = mainStepdefs;
this.userStepdefs = userStepdefs;
this.attachmentsByMessageId = ArrayListMultimap.create();
this.blobIdByAttachmentId = new HashMap<>();
this.attachmentAccessTokens = new HashMap<>();
}
@Given("^\"([^\"]*)\" mailbox \"([^\"]*)\" contains a message \"([^\"]*)\" with an attachment \"([^\"]*)\"$")
public void appendMessageWithAttachmentToMailbox(String user, String mailbox, String messageId, String attachmentId) throws Throwable {
MailboxPath mailboxPath = new MailboxPath(MailboxConstants.USER_NAMESPACE, user, mailbox);
mainStepdefs.jmapServer.serverProbe().appendMessage(user, mailboxPath,
ClassLoader.getSystemResourceAsStream("eml/oneAttachment.eml"), new Date(), false, new Flags());
attachmentsByMessageId.put(messageId, attachmentId);
blobIdByAttachmentId.put(attachmentId, "4000c5145f633410b80be368c44e1c394bff9437");
}
@Given("^\"([^\"]*)\" mailbox \"([^\"]*)\" contains a message \"([^\"]*)\" with an inlined attachment \"([^\"]*)\"$")
public void appendMessageWithInlinedAttachmentToMailbox(String user, String mailbox, String messageId, String attachmentId) throws Throwable {
MailboxPath mailboxPath = new MailboxPath(MailboxConstants.USER_NAMESPACE, user, mailbox);
mainStepdefs.jmapServer.serverProbe().appendMessage(user, mailboxPath,
ClassLoader.getSystemResourceAsStream("eml/oneInlinedImage.eml"), new Date(), false, new Flags());
attachmentsByMessageId.put(messageId, attachmentId);
// TODO
//blobIdByAttachmentId.put(attachmentId, "<correctComputedBlobId>");
}
@When("^\"([^\"]*)\" checks for the availability of the attachment endpoint$")
public void optionDownload(String username) throws Throwable {
AccessToken accessToken = userStepdefs.tokenByUser.get(username);
URI target = mainStepdefs.baseUri().setPath("/download/" + ONE_ATTACHMENT_EML_ATTACHEMENT_BLOB_ID).build();
Request request = Request.Options(target);
if (accessToken != null) {
request.addHeader("Authorization", accessToken.serialize());
}
response = request.execute().returnResponse();
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\"$")
public void downloads(String username, String attachmentId) throws Throwable {
String blobId = blobIdByAttachmentId.get(attachmentId);
URIBuilder uriBuilder = mainStepdefs.baseUri().setPath("/download/" + blobId);
response = authenticatedDownloadRequest(uriBuilder, blobId, username).execute().returnResponse();
}
private Request authenticatedDownloadRequest(URIBuilder uriBuilder, String blobId, String username) throws URISyntaxException {
AccessToken accessToken = userStepdefs.tokenByUser.get(username);
AttachmentAccessTokenKey key = new AttachmentAccessTokenKey(username, blobId);
if (attachmentAccessTokens.containsKey(key)) {
uriBuilder.addParameter("access_token", attachmentAccessTokens.get(key).serialize());
}
Request request = Request.Get(uriBuilder.build());
if (accessToken != null) {
request.addHeader("Authorization", accessToken.serialize());
}
return request;
}
@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) throws Exception {
Response tokenGenerationResponse = Request.Post(mainStepdefs.baseUri().setPath("/download/" + blobId).build())
.addHeader("Authorization", userStepdefs.tokenByUser.get(username).serialize())
.execute();
String serializedAttachmentAccessToken = tokenGenerationResponse.returnContent().asString();
attachmentAccessTokens.put(
new AttachmentAccessTokenKey(username, blobId),
AttachmentAccessToken.from(
serializedAttachmentAccessToken,
blobId));
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\" with a valid authentication token but a bad blobId$")
public void downloadsWithValidToken(String username, String attachmentId) throws Throwable {
URIBuilder uriBuilder = mainStepdefs.baseUri().setPath("/download/badblobId");
response = Request.Get(uriBuilder.build())
.addHeader("Authorization", userStepdefs.tokenByUser.get(username).serialize())
.execute()
.returnResponse();
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\" without any authentication token$")
public void getDownloadWithoutToken(String username, String attachmentId) throws Exception {
String blobId = blobIdByAttachmentId.get(attachmentId);
response = Request.Get(mainStepdefs.baseUri().setPath("/download/" + blobId).build())
.execute()
.returnResponse();
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\" with an empty authentication token$")
public void getDownloadWithEmptyToken(String username, String attachmentId) throws Exception {
String blobId = blobIdByAttachmentId.get(attachmentId);
response = Request.Get(
mainStepdefs.baseUri()
.setPath("/download/" + blobId)
.addParameter("access_token", "")
.build())
.execute()
.returnResponse();
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\" with a bad authentication token$")
public void getDownloadWithBadToken(String username, String attachmentId) throws Exception {
String blobId = blobIdByAttachmentId.get(attachmentId);
response = Request.Get(
mainStepdefs.baseUri()
.setPath("/download/" + blobId)
.addParameter("access_token", "bad")
.build())
.execute()
.returnResponse();
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\" with an invalid authentication token$")
public void getDownloadWithUnknownToken(String username, String attachmentId) throws Exception {
String blobId = blobIdByAttachmentId.get(attachmentId);
response = Request.Get(
mainStepdefs.baseUri()
.setPath("/download/" + blobId)
.addParameter("access_token", INVALID_ATTACHMENT_TOKEN)
.build())
.execute()
.returnResponse();
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\" without blobId parameter$")
public void getDownloadWithoutBlobId(String username, String attachmentId) throws Throwable {
String blobId = blobIdByAttachmentId.get(attachmentId);
URIBuilder uriBuilder = mainStepdefs.baseUri().setPath("/download/");
trustForBlobId(blobId, username);
AttachmentAccessTokenKey key = new AttachmentAccessTokenKey(username, blobId);
uriBuilder.addParameter("access_token", attachmentAccessTokens.get(key).serialize());
response = Request.Get(uriBuilder.build())
.execute()
.returnResponse();
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\" with wrong blobId$")
public void getDownloadWithWrongBlobId(String username, String attachmentId) throws Throwable {
String blobId = blobIdByAttachmentId.get(attachmentId);
URIBuilder uriBuilder = mainStepdefs.baseUri().setPath("/download/badbadbadbadbadbadbadbadbadbadbadbadbadb");
trustForBlobId(blobId, username);
AttachmentAccessTokenKey key = new AttachmentAccessTokenKey(username, blobId);
uriBuilder.addParameter("access_token", attachmentAccessTokens.get(key).serialize());
response = Request.Get(uriBuilder.build())
.execute()
.returnResponse();
}
@When("^\"([^\"]*)\" asks for a token for attachment \"([^\"]*)\"$")
public void postDownload(String username, String attachmentId) throws Throwable {
String blobId = blobIdByAttachmentId.get(attachmentId);
AccessToken accessToken = userStepdefs.tokenByUser.get(username);
response = Request.Post(mainStepdefs.baseUri().setPath("/download/" + blobId).build())
.addHeader("Authorization", accessToken.serialize())
.execute()
.returnResponse();
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\" with \"([^\"]*)\" name$")
public void downloadsWithName(String username, String attachmentId, String name) throws Exception {
String blobId = blobIdByAttachmentId.get(attachmentId);
URIBuilder uriBuilder = mainStepdefs.baseUri().setPath("/download/" + blobId + "/" + name);
response = authenticatedDownloadRequest(uriBuilder, blobId, username)
.execute()
.returnResponse();
}
@When("^\"([^\"]*)\" downloads \"([^\"]*)\" with an expired token$")
public void getDownloadWithExpiredToken(String username, String attachmentId) throws Exception {
String blobId = blobIdByAttachmentId.get(attachmentId);
response = Request.Get(mainStepdefs.baseUri().setPath("/download/" + blobId)
.addParameter("access_token", EXPIRED_ATTACHMENT_TOKEN)
.build())
.execute()
.returnResponse();
}
@Then("^the user should be authorized$")
public void httpStatusDifferentFromUnauthorized() throws IOException {
assertThat(response.getStatusLine().getStatusCode()).isIn(200, 404);
}
@Then("^the user should not be authorized$")
public void httpUnauthorizedStatus() throws IOException {
assertThat(response.getStatusLine().getStatusCode()).isEqualTo(401);
}
@Then("^the user should receive a bad request response$")
public void httpBadRequestStatus() throws IOException {
assertThat(response.getStatusLine().getStatusCode()).isEqualTo(400);
}
@Then("^the user should receive that attachment$")
public void httpOkStatusAndExpectedContent() throws IOException {
assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
assertThat(IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8)).isNotEmpty();
}
@Then("^the user should receive a not found response$")
public void httpNotFoundStatus() throws IOException {
assertThat(response.getStatusLine().getStatusCode()).isEqualTo(404);
}
@Then("^the user should receive an attachment access token$")
public void accessTokenResponse() throws Throwable {
assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
assertThat(response.getHeaders("Content-Type")).extracting(Header::toString).containsExactly("Content-Type: text/plain");
assertThat(IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8)).isNotEmpty();
}
@Then("^the attachment is named \"([^\"]*)\"$")
public void assertContentDisposition(String name) throws IOException {
assertThat(response.getHeaders("Content-Disposition")).extracting(Header::toString).containsExactly("Content-Disposition: attachment; filename=\"" + name + "\"");
}
}