diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/commands/callables/ParseMetadataFromHeaders.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/commands/callables/ParseMetadataFromHeaders.java index 988f919e75..701ceb9790 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/commands/callables/ParseMetadataFromHeaders.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/commands/callables/ParseMetadataFromHeaders.java @@ -27,6 +27,7 @@ import java.util.Map.Entry; import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object.Metadata; +import org.jclouds.aws.s3.domain.acl.CannedAccessPolicy; import org.jclouds.aws.s3.reference.S3Headers; import org.jclouds.aws.s3.util.S3Utils; import org.jclouds.aws.util.DateService; @@ -63,6 +64,7 @@ public class ParseMetadataFromHeaders extends HttpFutureCommand.ResponseCallable addUserMetadataTo(metadata); addMd5To(metadata); + addCannedAccessPolicyTo(metadata); parseLastModifiedOrThrowException(metadata); setContentTypeOrThrowException(metadata); @@ -122,6 +124,13 @@ public class ParseMetadataFromHeaders extends HttpFutureCommand.ResponseCallable } } + private void addCannedAccessPolicyTo(S3Object.Metadata metadata) { + String aclHeader = getResponse().getFirstHeaderOrNull(S3Headers.CANNED_ACL); + if (aclHeader != null) { + metadata.setCannedAccessPolicy(CannedAccessPolicy.fromHeader(aclHeader)); + } + } + public void setKey(String key) { this.key = key; } diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/domain/S3Object.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/domain/S3Object.java index b044b18036..6dde0991b7 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/domain/S3Object.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/domain/S3Object.java @@ -27,6 +27,7 @@ import static com.google.common.base.Preconditions.*; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import org.jclouds.aws.s3.commands.options.GetObjectOptions; +import org.jclouds.aws.s3.domain.acl.CannedAccessPolicy; import org.jclouds.aws.s3.util.S3Utils; import org.jclouds.aws.s3.util.S3Utils.Md5InputStreamResult; import org.jclouds.http.ContentTypes; @@ -96,6 +97,8 @@ public class S3Object { private String cacheControl; private String dataDisposition; private String dataEncoding; + private CannedAccessPolicy cannedAccessPolicy; + private AccessControlList accessControlList; // only parsed on list private CanonicalUser owner = null; @@ -319,6 +322,22 @@ public class S3Object { public void setAllHeaders(Multimap allHeaders) { this.allHeaders = allHeaders; } + + public void setCannedAccessPolicy(CannedAccessPolicy policy) { + this.cannedAccessPolicy = policy; + } + + public CannedAccessPolicy getCannedAccessPolicy() { + return this.cannedAccessPolicy; + } + + public void setAccessControlList(AccessControlList acl) { + this.accessControlList = acl; + } + + public AccessControlList getAccessControlList() { + return this.accessControlList; + } /** * @return all http response headers associated with this S3Object diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/domain/acl/CannedAccessPolicy.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/domain/acl/CannedAccessPolicy.java index de447bb966..edc26bbf85 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/domain/acl/CannedAccessPolicy.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/domain/acl/CannedAccessPolicy.java @@ -77,4 +77,28 @@ public enum CannedAccessPolicy { public String toString() { return policyName; } + + /** + * @param capHeader + * The value of the x-amz-acl HTTP Header returned by S3 when an + * object has a canned access policy. + * + * @return + * the canned access policy object corresponding to the header value, + * or null if the given header value does not represent a valid canned + * policy. + */ + public static CannedAccessPolicy fromHeader(String capHeader) { + if ("private".equals(capHeader)) { + return CannedAccessPolicy.PRIVATE; + } else if ("public-read".equals(capHeader)) { + return CannedAccessPolicy.PUBLIC_READ; + } else if ("public-read-write".equals(capHeader)) { + return CannedAccessPolicy.PUBLIC_READ_WRITE; + } else if ("authenticated-read".equals(capHeader)) { + return CannedAccessPolicy.AUTHENTICATED_READ; + } else { + return null; + } + } } diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/commands/callables/ParseObjectFromHeadersAndHttpContentTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/commands/callables/ParseObjectFromHeadersAndHttpContentTest.java index dd6e2a0f96..8abcae1289 100644 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/commands/callables/ParseObjectFromHeadersAndHttpContentTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/commands/callables/ParseObjectFromHeadersAndHttpContentTest.java @@ -25,17 +25,25 @@ package org.jclouds.aws.s3.commands.callables; import static org.easymock.EasyMock.expect; import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.createNiceMock; import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.reset; import static org.testng.Assert.assertEquals; import org.apache.commons.io.IOUtils; import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object.Metadata; +import org.jclouds.aws.s3.domain.acl.CannedAccessPolicy; +import org.jclouds.aws.s3.reference.S3Headers; +import org.jclouds.aws.util.DateService; import org.jclouds.http.HttpException; import org.jclouds.http.HttpHeaders; import org.jclouds.http.HttpResponse; import org.testng.annotations.Test; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + /** * @author Adrian Cole */ @@ -86,5 +94,60 @@ public class ParseObjectFromHeadersAndHttpContentTest { assertEquals(object.getContentRange(), "0-10485759/20232760"); } + + @Test + public void testParseObjectFromHeadersWithCannedAccessPolicy() throws HttpException { + DateService dateService = new DateService(); + ParseMetadataFromHeaders metadataParser = new ParseMetadataFromHeaders(dateService); + metadataParser.setKey("object-key"); + ParseObjectFromHeadersAndHttpContent callable = new ParseObjectFromHeadersAndHttpContent( + metadataParser); + + HttpResponse response = createNiceMock(HttpResponse.class); + callable.setResponse(response); + + S3Object object; + + // Test with no policy + buildDefaultResponseMock(response); + expect(response.getFirstHeaderOrNull(S3Headers.CANNED_ACL)).andReturn(null); + replay(response); + object = callable.call(); + assertEquals(object.getMetadata().getCannedAccessPolicy(), null); + + // Test setting the "private" policy + buildDefaultResponseMock(response); + expect(response.getFirstHeaderOrNull(S3Headers.CANNED_ACL)).andReturn( + CannedAccessPolicy.PRIVATE.toString()); + replay(response); + object = callable.call(); + assertEquals(object.getMetadata().getCannedAccessPolicy(), CannedAccessPolicy.PRIVATE); + + // Test setting the "authenticated read" policy + buildDefaultResponseMock(response); + expect(response.getFirstHeaderOrNull(S3Headers.CANNED_ACL)).andReturn( + CannedAccessPolicy.AUTHENTICATED_READ.toString()); + replay(response); + object = callable.call(); + assertEquals(object.getMetadata().getCannedAccessPolicy(), + CannedAccessPolicy.AUTHENTICATED_READ); + } + + private void buildDefaultResponseMock(HttpResponse response) { + reset(response); + + // Headers required for the parse classes to work, but we don't care about this data. + String data = "test data"; + expect(response.getStatusCode()).andReturn(200).atLeastOnce(); + Multimap emptyHeaders = HashMultimap.create(); + expect(response.getHeaders()).andReturn(emptyHeaders).atLeastOnce(); + expect(response.getContent()).andReturn(IOUtils.toInputStream(data)).atLeastOnce(); + expect(response.getFirstHeaderOrNull(HttpHeaders.LAST_MODIFIED)) + .andReturn(new DateService().rfc822DateFormat()).atLeastOnce(); + expect(response.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE)) + .andReturn("text/plain").atLeastOnce(); + expect(response.getFirstHeaderOrNull(HttpHeaders.CONTENT_LENGTH)) + .andReturn("" + data.length()).atLeastOnce(); + } }