Initial partial implementation of Access Control List support (Issue 47). This commit adds support for retrieving ACL settings for S3 buckets and objects, but not for modifying these settings.

git-svn-id: http://jclouds.googlecode.com/svn/trunk@885 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
jamurty 2009-05-31 21:32:42 +00:00
parent fd338a0c70
commit bb2ea532b2
12 changed files with 938 additions and 7 deletions

View File

@ -24,6 +24,7 @@
package org.jclouds.aws.s3;
import org.jclouds.aws.s3.commands.options.*;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
@ -211,4 +212,18 @@ public interface S3Connection {
* @see org.jclouds.aws.s3.commands.ListOwnedBuckets
*/
Future<List<S3Bucket.Metadata>> listOwnedBuckets();
/**
* @return access permissions of the bucket
*
* @see org.jclouds.aws.s3.commands.GetAccessControlList
*/
Future<AccessControlList> getBucketACL(String bucket);
/**
* @return access permissions of the object
*
* @see org.jclouds.aws.s3.commands.GetAccessControlList
*/
Future<AccessControlList> getObjectACL(String bucket, String objectKey);
}

View File

@ -0,0 +1,99 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.aws.s3.commands;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.http.HttpResponseException;
import org.jclouds.http.commands.callables.xml.ParseSax;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.name.Named;
/**
* A GET request operation directed at an object or bucket URI with the "acl" parameter retrieves
* the Access Control List (ACL) settings for that S3 item.
* <p />
* To list a bucket or object's ACL, you must have READ_ACP access to the item.
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
* @author James Murty
*/
public class GetAccessControlList extends S3FutureCommand<AccessControlList> {
@Inject
public GetAccessControlList(@Named("jclouds.http.address") String amazonHost,
ParseSax<AccessControlList> accessControlListParser,
@Assisted("bucketName") String bucket)
{
super("GET", "/?acl", accessControlListParser, amazonHost, bucket);
}
@Inject
public GetAccessControlList(@Named("jclouds.http.address") String amazonHost,
ParseSax<AccessControlList> accessControlListParser,
@Assisted("bucketName") String bucket,
@Assisted("objectKey") String objectKey)
{
super("GET", "/" + objectKey + "?acl", accessControlListParser, amazonHost, bucket);
}
@Override
public AccessControlList get() throws InterruptedException, ExecutionException {
try {
return super.get();
} catch (ExecutionException e) {
return attemptNotFound(e);
}
}
@VisibleForTesting
AccessControlList attemptNotFound(ExecutionException e) throws ExecutionException {
if (e.getCause() != null && e.getCause() instanceof HttpResponseException) {
AWSResponseException responseException = (AWSResponseException) e.getCause();
if ("NoSuchBucket".equals(responseException.getError().getCode())
|| "NoSuchObject".equals(responseException.getError().getCode()))
{
return AccessControlList.NOT_FOUND;
}
}
throw e;
}
@Override
public AccessControlList get(long l, TimeUnit timeUnit) throws InterruptedException, ExecutionException,
TimeoutException {
try {
return super.get(l, timeUnit);
} catch (ExecutionException e) {
return attemptNotFound(e);
}
}
}

View File

@ -140,4 +140,14 @@ public class S3CommandFactory {
sourceObject, destinationBucket, destinationObject, options);
}
public GetAccessControlList createGetBucketACL(String bucket) {
return new GetAccessControlList(
amazonHost, parserFactory.createAccessControlListParser(), bucket);
}
public GetAccessControlList createGetObjectACL(String bucket, String objectKey) {
return new GetAccessControlList(
amazonHost, parserFactory.createAccessControlListParser(), bucket, objectKey);
}
}

View File

@ -0,0 +1,390 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.aws.s3.domain;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.jclouds.aws.s3.domain.acl.CannedAccessPolicy;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
/**
* An Access Control List (ACL) describes the access control settings for a bucket or object in S3.
*
* ACL settings comprise a set of {@link Grant}s, each of which specifies a {@link Permission}
* that has been granted to a specific {@link Grantee}. If an entity tries to access or modify an
* item in S3, the operation will be denied unless the item has ACL settings that explicitly
* permit that entity to perform that action.
*
*
* @author James Murty
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
public class AccessControlList {
public static final AccessControlList NOT_FOUND = new AccessControlList();
private CanonicalUser owner;
private final SortedSet<Grant> grants = new TreeSet<Grant>();
public AccessControlList() {
}
public void setOwner(CanonicalUser owner) {
this.owner = owner;
}
public CanonicalUser getOwner() {
return owner;
}
/**
* @return an unmodifiable set of grants represented by this ACL.
*/
public Set<Grant> getGrants() {
return Collections.unmodifiableSet(grants);
}
/**
* @return an unmodifiable set of grantees who have been assigned permissions in this ACL.
*/
public Set<Grantee> getGrantees() {
SortedSet<Grantee> grantees = new TreeSet<Grantee>();
for (Grant grant : getGrants()) {
grantees.add(grant.getGrantee());
}
return Collections.unmodifiableSet(grantees);
}
/**
* Add a permission for the given grantee.
*
* @param grantee
* @param permission
* @return
*/
public AccessControlList addPermission(Grantee grantee, Permission permission) {
Grant grant = new Grant(grantee, permission);
grants.add(grant);
return this;
}
/**
* Add a permission for the given group grantee.
*
* @param groupGranteeURI
* @param permission
* @return
*/
public AccessControlList addPermission(GroupGranteeURI groupGranteeURI, Permission permission) {
return addPermission(new GroupGrantee(groupGranteeURI), permission);
}
/**
* Revoke a permission for the given grantee, if this specific permission was granted.
*
* Note that you must be very explicit about the permissions you revoke, you cannot revoke
* partial permissions and expect this class to determine the implied remaining permissions.
* For example, if you revoke the {@link Permission#READ} permission from a grantee with
* {@link Permission#FULL_CONTROL} access, <strong>the revocation will do nothing</strong>
* and the grantee will retain full access. To change the access settings for this grantee,
* you must first remove the {@link Permission#FULL_CONTROL} permission the add back the
* {@link Permission#READ} permission.
*
* @param grantee
* @param permission
* @return
*/
public AccessControlList revokePermission(Grantee grantee, Permission permission) {
Collection<Grant> grantsForGrantee = findGrantsForGrantee(grantee.getIdentifier());
for (Grant grant : grantsForGrantee) {
if (grant.getPermission().equals(permission)) {
grants.remove(grant);
}
}
return this;
}
/**
* Revoke a permission for the given group grantee, if this specific permission was granted.
*
* Note that you must be very explicit about the permissions you revoke, you cannot revoke
* partial permissions and expect this class to determine the implied remaining permissions.
* For example, if you revoke the {@link Permission#READ} permission from a grantee with
* {@link Permission#FULL_CONTROL} access, <strong>the revocation will do nothing</strong>
* and the grantee will retain full access. To change the access settings for this grantee,
* you must first remove the {@link Permission#FULL_CONTROL} permission the add back the
* {@link Permission#READ} permission.
*
* @param groupGranteeURI
* @param permission
* @return
*/
public AccessControlList revokePermission(GroupGranteeURI groupGranteeURI, Permission permission)
{
return revokePermission(new GroupGrantee(groupGranteeURI), permission);
}
/**
* Revoke all the permissions granted to the given grantee.
*
* @param grantee
* @return
*/
public AccessControlList revokeAllPermissions(Grantee grantee) {
Collection<Grant> grantsForGrantee = findGrantsForGrantee(grantee.getIdentifier());
grants.removeAll(grantsForGrantee);
return this;
}
/**
* @param granteeId
* @return
* the permissions assigned to a grantee, as identified by the given ID.
*/
public Collection<Permission> getPermissions(String granteeId) {
Collection<Grant> grantsForGrantee = findGrantsForGrantee(granteeId);
return Collections2.transform(grantsForGrantee, new Function<Grant, Permission>() {
public Permission apply(Grant g) {
return g.getPermission();
}
});
}
/**
* @param grantee
* @return
* the permissions assigned to a grantee.
*/
public Collection<Permission> getPermissions(Grantee grantee) {
return getPermissions(grantee.getIdentifier());
}
/**
* @param granteeURI
* @return
* the permissions assigned to a group grantee.
*/
public Collection<Permission> getPermissions(GroupGranteeURI granteeURI) {
return getPermissions(granteeURI.getIdentifier());
}
/**
* @param granteeId
* @param permission
* @return
* true if the grantee has the given permission.
*/
public boolean hasPermission(String granteeId, Permission permission) {
return getPermissions(granteeId).contains(permission);
}
/**
* @param grantee
* @param permission
* @return
* true if the grantee has the given permission.
*/
public boolean hasPermission(Grantee grantee, Permission permission) {
return hasPermission(grantee.getIdentifier(), permission);
}
/**
* @param granteeURI
* @param permission
* @return
* true if the grantee has the given permission.
*/
public boolean hasPermission(GroupGranteeURI granteeURI, Permission permission) {
return getPermissions(granteeURI.getIdentifier()).contains(permission);
}
/**
* Find all the grants for a given grantee, identified by an ID which allows all Grantee
* types to be searched.
*
* @param granteeId
* identifier of a canonical user, email address user, or group.
* @return
*/
protected Collection<Grant> findGrantsForGrantee(final String granteeId) {
return Collections2.filter(grants, new Predicate<Grant>() {
public boolean apply(Grant g) {
return g.getGrantee().getIdentifier().equals(granteeId);
}
});
}
/**
* Converts a canned access control policy into the equivalent access control list.
*
* @param cannedAP
* @param ownerId
* @return
*/
public static AccessControlList fromCannedAccessPolicy(
CannedAccessPolicy cannedAP, String ownerId)
{
AccessControlList acl = new AccessControlList();
acl.setOwner(new CanonicalUser(ownerId));
// Canned access policies always allow full control to the owner.
acl.addPermission(new CanonicalUserGrantee(ownerId), Permission.FULL_CONTROL);
if (CannedAccessPolicy.PRIVATE == cannedAP) {
// No more work to do.
} else if (CannedAccessPolicy.AUTHENTICATED_READ == cannedAP) {
acl.addPermission(GroupGranteeURI.AUTHENTICATED_USERS, Permission.READ);
} else if (CannedAccessPolicy.PUBLIC_READ == cannedAP) {
acl.addPermission(GroupGranteeURI.ALL_USERS, Permission.READ);
} else if (CannedAccessPolicy.PUBLIC_READ_WRITE == cannedAP) {
acl.addPermission(GroupGranteeURI.ALL_USERS, Permission.READ);
acl.addPermission(GroupGranteeURI.ALL_USERS, Permission.WRITE);
}
return acl;
}
///////////////////////////////////////////////////////////////////////////////
// Class and Enum declarations to represent Grants, Grantees and Permissions //
///////////////////////////////////////////////////////////////////////////////
public static enum Permission {
READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL;
};
public static class Grant implements Comparable<Grant> {
private final Grantee grantee;
private final Permission permission;
public Grant(Grantee grantee, Permission permission) {
this.grantee = grantee;
this.permission = permission;
}
public Grantee getGrantee() {
return grantee;
}
public Permission getPermission() {
return permission;
}
public int compareTo(org.jclouds.aws.s3.domain.AccessControlList.Grant o) {
if (this == o) {
return 0;
} else {
String myGranteeAndPermission = grantee.getIdentifier() + "\n" + permission;
String otherGranteeAndPermission = o.grantee.getIdentifier() + "\n" + o.permission;
return myGranteeAndPermission.compareTo(otherGranteeAndPermission);
}
}
}
public abstract static class Grantee implements Comparable<Grantee> {
private final String identifier;
protected Grantee(String identifier) {
this.identifier = identifier;
}
public String getIdentifier() {
return identifier;
}
public int compareTo(org.jclouds.aws.s3.domain.AccessControlList.Grantee o) {
return (this == o) ? 0 : getIdentifier().compareTo(o.getIdentifier());
}
}
public static class EmailAddressGrantee extends Grantee {
public EmailAddressGrantee(String emailAddress) {
super(emailAddress);
}
public String getEmailAddress() {
return getIdentifier();
}
}
public static class CanonicalUserGrantee extends Grantee {
private final String displayName;
public CanonicalUserGrantee(String id, String displayName) {
super(id);
this.displayName = displayName;
}
public CanonicalUserGrantee(String id) {
this(id, null);
}
public String getDisplayName() {
return displayName;
}
}
public enum GroupGranteeURI {
ALL_USERS ("http://acs.amazonaws.com/groups/global/AllUsers"),
AUTHENTICATED_USERS ("http://acs.amazonaws.com/groups/global/AuthenticatedUsers"),
LOG_DELIVERY ("http://acs.amazonaws.com/groups/s3/LogDelivery");
private final String uri;
GroupGranteeURI(String uri) {
this.uri = uri;
}
public String getIdentifier() {
return this.uri;
}
public static GroupGranteeURI fromURI(String uri) {
if (ALL_USERS.uri.equals(uri)) {
return ALL_USERS;
} else if (AUTHENTICATED_USERS.uri.equals(uri)) {
return AUTHENTICATED_USERS;
} else if (LOG_DELIVERY.uri.equals(uri)) {
return LOG_DELIVERY;
} else {
throw new IllegalArgumentException("No GroupGranteeURI constant matches " + uri);
}
}
}
public static class GroupGrantee extends Grantee {
public GroupGrantee(GroupGranteeURI groupURI) {
super(groupURI.getIdentifier());
}
}
}

View File

@ -164,11 +164,34 @@ public class RequestAuthorizeSignature implements HttpRequestFilter {
}
private static void appendUriPath(HttpRequest request, StringBuilder toSign) {
// Remove parameters from URI, because most must not be included in the signed URI...
String paramsString = null;
int queryIndex = request.getUri().indexOf('?');
if (queryIndex >= 0)
if (queryIndex >= 0) {
toSign.append(request.getUri().substring(0, queryIndex));
else
paramsString = request.getUri().substring(queryIndex + 1);
} else {
toSign.append(request.getUri());
}
// ...however, there are a few exceptions that must be included in the signed URI.
if (paramsString != null) {
StringBuilder paramsToSign = new StringBuilder("?");
String[] params = paramsString.split("&");
for (String param : params) {
String[] paramNameAndValue = param.split("=");
if ("acl".equals(paramNameAndValue[0])) {
paramsToSign.append("acl");
}
// TODO: Other special cases not yet handled: torrent, logging, location, requestPayment
}
if (paramsToSign.length() > 1) {
toSign.append(paramsToSign);
}
}
}
private static String valueOrEmpty(Collection<String> collection) {

View File

@ -31,6 +31,7 @@ import org.jclouds.aws.s3.commands.BucketExists;
import org.jclouds.aws.s3.commands.CopyObject;
import org.jclouds.aws.s3.commands.DeleteBucket;
import org.jclouds.aws.s3.commands.DeleteObject;
import org.jclouds.aws.s3.commands.GetAccessControlList;
import org.jclouds.aws.s3.commands.ListOwnedBuckets;
import org.jclouds.aws.s3.commands.GetObject;
import org.jclouds.aws.s3.commands.HeadObject;
@ -43,6 +44,7 @@ import org.jclouds.aws.s3.commands.options.GetObjectOptions;
import org.jclouds.aws.s3.commands.options.ListBucketOptions;
import org.jclouds.aws.s3.commands.options.PutBucketOptions;
import org.jclouds.aws.s3.commands.options.PutObjectOptions;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.S3Bucket.Metadata;
@ -229,4 +231,26 @@ public class LiveS3Connection implements S3Connection {
return listRequest;
}
/**
* {@inheritDoc}
*
* @see GetAccessControlList
*/
public Future<AccessControlList> getBucketACL(String bucket) {
GetAccessControlList getBucketACLRequest = factory.createGetBucketACL(bucket);
client.submit(getBucketACLRequest);
return getBucketACLRequest;
}
/**
* {@inheritDoc}
*
* @see GetAccessControlList
*/
public Future<AccessControlList> getObjectACL(String bucket, String objectKey) {
GetAccessControlList getObjectACLRequest = factory.createGetObjectACL(bucket, objectKey);
client.submit(getObjectACLRequest);
return getObjectACLRequest;
}
}

View File

@ -0,0 +1,98 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.aws.s3.xml;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.CanonicalUser;
import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.Grantee;
import org.jclouds.aws.s3.domain.AccessControlList.GroupGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI;
import org.jclouds.aws.s3.domain.AccessControlList.Permission;
import org.jclouds.http.commands.callables.xml.ParseSax;
import org.xml.sax.Attributes;
/**
* Parses the following XML document:
* <p/>
* AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/"
*
* @author James Murty
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
public class AccessControlListHandler extends ParseSax.HandlerWithResult<AccessControlList> {
private AccessControlList acl = new AccessControlList();
private StringBuilder currentText = new StringBuilder();
public AccessControlListHandler() {
}
public AccessControlList getResult() {
return acl;
}
private String currentId;
private String currentDisplayName;
private String currentGranteeType;
private String currentPermission;
private Grantee currentGrantee;
public void startElement(String uri, String name, String qName, Attributes attrs) {
if (qName.equals("Grantee")) {
currentGranteeType = attrs.getValue("xsi:type");
}
}
public void endElement(String uri, String name, String qName) {
if (qName.equals("Owner")) {
CanonicalUser owner = new CanonicalUser(currentId);
owner.setDisplayName(currentDisplayName);
acl.setOwner(owner);
} else if (qName.equals("Grantee")) {
if ("AmazonCustomerByEmail".equals(currentGranteeType)) {
currentGrantee = new EmailAddressGrantee(currentId);
} else if ("CanonicalUser".equals(currentGranteeType)) {
currentGrantee = new CanonicalUserGrantee(currentId, currentDisplayName);
} else if ("Group".equals(currentGranteeType)) {
currentGrantee = new GroupGrantee(GroupGranteeURI.fromURI(currentId));
}
} else if (qName.equals("Grant")) {
acl.addPermission(currentGrantee, Permission.valueOf(currentPermission));
}
else if (qName.equals("ID") || qName.equals("EmailAddress") || qName.equals("URI")) {
currentId = currentText.toString();
} else if (qName.equals("DisplayName")) {
currentDisplayName = currentText.toString();
} else if (qName.equals("Permission")) {
currentPermission = currentText.toString();
}
currentText = new StringBuilder();
}
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
}

View File

@ -26,6 +26,7 @@ package org.jclouds.aws.s3.xml;
import java.util.List;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.xml.ErrorHandler;
@ -101,4 +102,17 @@ public class S3ParserFactory {
return parseErrorFactory.create(errorHandlerProvider.get());
}
@Inject
private GenericParseFactory<AccessControlList> parseAccessControlListFactory;
@Inject
Provider<AccessControlListHandler> accessControlListHandlerProvider;
/**
* @return a parser used to handle {@link org.jclouds.aws.s3.commands.GetAccessControlList} responses
*/
public ParseSax<AccessControlList> createAccessControlListParser() {
return parseAccessControlListFactory.create(accessControlListHandlerProvider.get());
}
}

View File

@ -26,8 +26,10 @@ package org.jclouds.aws.s3.xml.config;
import java.util.List;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.xml.AccessControlListHandler;
import org.jclouds.aws.s3.xml.CopyObjectHandler;
import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler;
import org.jclouds.aws.s3.xml.ListBucketHandler;
@ -46,13 +48,25 @@ import com.google.inject.assistedinject.FactoryProvider;
* @author Adrian Cole
*/
public class S3ParserModule extends AbstractModule {
private final TypeLiteral<S3ParserFactory.GenericParseFactory<List<S3Bucket.Metadata>>> listBucketsTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<List<S3Bucket.Metadata>>>() {
private final TypeLiteral<S3ParserFactory.GenericParseFactory
<List<S3Bucket.Metadata>>> listBucketsTypeLiteral =
new TypeLiteral<S3ParserFactory.GenericParseFactory<List<S3Bucket.Metadata>>>() {
};
private final TypeLiteral<S3ParserFactory.GenericParseFactory<S3Bucket>> bucketTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<S3Bucket>>() {
private final TypeLiteral<S3ParserFactory.GenericParseFactory
<S3Bucket>> bucketTypeLiteral =
new TypeLiteral<S3ParserFactory.GenericParseFactory<S3Bucket>>() {
};
private final TypeLiteral<S3ParserFactory.GenericParseFactory<S3Object.Metadata>> objectMetadataTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<S3Object.Metadata>>() {
private final TypeLiteral<S3ParserFactory.GenericParseFactory
<S3Object.Metadata>> objectMetadataTypeLiteral =
new TypeLiteral<S3ParserFactory.GenericParseFactory<S3Object.Metadata>>() {
};
private final TypeLiteral<S3ParserFactory.GenericParseFactory<AWSError>> errorTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<AWSError>>() {
private final TypeLiteral<S3ParserFactory.GenericParseFactory
<AWSError>> errorTypeLiteral =
new TypeLiteral<S3ParserFactory.GenericParseFactory<AWSError>>() {
};
private final TypeLiteral<S3ParserFactory.GenericParseFactory
<AccessControlList>> accessControlListTypeLiteral =
new TypeLiteral<S3ParserFactory.GenericParseFactory<AccessControlList>>() {
};
@Override
@ -71,6 +85,8 @@ public class S3ParserModule extends AbstractModule {
}).to(CopyObjectHandler.class);
bind(new TypeLiteral<ParseSax.HandlerWithResult<AWSError>>() {
}).to(ErrorHandler.class);
bind(new TypeLiteral<ParseSax.HandlerWithResult<AccessControlList>>() {
}).to(AccessControlListHandler.class);
}
private void bindCallablesThatReturnParseResults() {
@ -88,6 +104,9 @@ public class S3ParserModule extends AbstractModule {
bind(errorTypeLiteral).toProvider(
FactoryProvider.newFactory(errorTypeLiteral, new TypeLiteral<ParseSax<AWSError>>() {
}));
bind(accessControlListTypeLiteral).toProvider(
FactoryProvider.newFactory(accessControlListTypeLiteral,
new TypeLiteral<ParseSax<AccessControlList>>() {}));
}
}

View File

@ -53,6 +53,7 @@ import org.jclouds.aws.s3.commands.options.GetObjectOptions;
import org.jclouds.aws.s3.commands.options.ListBucketOptions;
import org.jclouds.aws.s3.commands.options.PutBucketOptions;
import org.jclouds.aws.s3.commands.options.PutObjectOptions;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.S3Bucket.Metadata;
@ -81,6 +82,8 @@ public class StubS3Connection implements S3Connection {
private static Map<String, Metadata.LocationConstraint> bucketToLocation = new ConcurrentHashMap<String, Metadata.LocationConstraint>();
private static Map<String, CannedAccessPolicy> keyToAcl = new ConcurrentHashMap<String, CannedAccessPolicy>();
public static final String DEFAULT_OWNER_ID = "abc123";
/**
* @throws java.io.IOException
*/
@ -439,7 +442,7 @@ public class StubS3Connection implements S3Connection {
newMd.setMd5(md5);
newMd.setContentType(object.getMetadata().getContentType());
if (options.getAcl() != null)
keyToAcl.put(bucketName + object, options.getAcl());
keyToAcl.put(bucketName + "/" + object.getKey(), options.getAcl());
bucketToContents.get(bucketName).put(object.getKey(), new S3Object(newMd, data));
// Set HTTP headers to match metadata
@ -532,4 +535,32 @@ public class StubS3Connection implements S3Connection {
};
}
public Future<AccessControlList> getBucketACL(final String bucket) {
return new FutureBase<AccessControlList>() {
public AccessControlList get() throws InterruptedException, ExecutionException {
CannedAccessPolicy cannedAP = keyToAcl.get(bucket);
if (cannedAP == null) {
// Default to private access policy
cannedAP = CannedAccessPolicy.PRIVATE;
}
return AccessControlList.fromCannedAccessPolicy(cannedAP, DEFAULT_OWNER_ID);
}
};
}
public Future<AccessControlList> getObjectACL(final String bucket, final String objectKey) {
return new FutureBase<AccessControlList>() {
public AccessControlList get() throws InterruptedException, ExecutionException {
CannedAccessPolicy cannedAP = keyToAcl.get(bucket + "/" + objectKey);
if (cannedAP == null) {
// Default to private access policy
cannedAP = CannedAccessPolicy.PRIVATE;
}
return AccessControlList.fromCannedAccessPolicy(cannedAP, DEFAULT_OWNER_ID);
}
};
}
}

View File

@ -0,0 +1,124 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.aws.s3.commands;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.commands.options.PutObjectOptions;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI;
import org.jclouds.aws.s3.domain.AccessControlList.Permission;
import org.jclouds.aws.s3.domain.acl.CannedAccessPolicy;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.assertFalse;
/**
* Tests integrated functionality of all commands that retrieve Access Control Lists (ACLs).
*
* @author James Murty
*/
@Test(groups = {"integration", "live"}, testName = "s3.GetAccessControlListIntegrationTest")
public class GetAccessControlListIntegrationTest extends S3IntegrationTest {
@Test
void testPrivateBucketACL() throws InterruptedException, ExecutionException,
TimeoutException, IOException
{
bucketName = bucketPrefix + ".testPrivateBucketACL".toLowerCase();
createBucketAndEnsureEmpty(bucketName);
AccessControlList acl = client.getBucketACL(bucketName).get(10, TimeUnit.SECONDS);
assertEquals(acl.getGrants().size(), 1);
assertTrue(acl.getOwner() != null);
String ownerId = acl.getOwner().getId();
assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL));
}
@Test
void testObjectACL() throws InterruptedException, ExecutionException,
TimeoutException, IOException
{
bucketName = bucketPrefix + ".testObjectACL".toLowerCase();
createBucketAndEnsureEmpty(bucketName);
String privateObjectKey = "pr“vate-acl";
String publicReadObjectKey = "pŸblic-read-acl";
String publicReadWriteObjectKey = "pŸblic-read-write-acl";
// Private object
addObjectToBucket(bucketName, privateObjectKey);
AccessControlList acl = client.getObjectACL(bucketName, privateObjectKey)
.get(10, TimeUnit.SECONDS);
assertEquals(acl.getGrants().size(), 1);
assertTrue(acl.getOwner() != null);
String ownerId = acl.getOwner().getId();
assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL));
// Public Read object
client.putObject(bucketName, new S3Object(publicReadObjectKey, ""),
new PutObjectOptions().withAcl(CannedAccessPolicy.PUBLIC_READ));
acl = client.getObjectACL(bucketName, publicReadObjectKey)
.get(10, TimeUnit.SECONDS);
assertEquals(acl.getGrants().size(), 2);
assertEquals(acl.getPermissions(GroupGranteeURI.ALL_USERS).size(), 1);
assertTrue(acl.getOwner() != null);
ownerId = acl.getOwner().getId();
assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL));
assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ));
// Public Read-Write object
client.putObject(bucketName, new S3Object(publicReadWriteObjectKey, ""),
new PutObjectOptions().withAcl(CannedAccessPolicy.PUBLIC_READ_WRITE));
acl = client.getObjectACL(bucketName, publicReadWriteObjectKey)
.get(10, TimeUnit.SECONDS);
assertEquals(acl.getGrants().size(), 3);
assertEquals(acl.getPermissions(GroupGranteeURI.ALL_USERS).size(), 2);
assertTrue(acl.getOwner() != null);
ownerId = acl.getOwner().getId();
assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL));
assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ));
assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.WRITE));
assertFalse(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ_ACP));
assertFalse(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.WRITE_ACP));
assertFalse(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.FULL_CONTROL));
emptyBucket(bucketName);
}
}

View File

@ -0,0 +1,84 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.jclouds.aws.s3.xml;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import org.apache.commons.io.IOUtils;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI;
import org.jclouds.aws.s3.domain.AccessControlList.Permission;
import org.jclouds.http.HttpException;
import org.jclouds.http.commands.callables.xml.ParseSax;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "s3.AccessControlListHandlerTest")
public class AccessControlListHandlerTest extends BaseHandlerTest {
public static final String aclOwnerOnly = "<AccessControlPolicy xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Owner><ID>1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c</ID><DisplayName>jamesmurty</DisplayName></Owner><AccessControlList><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID>1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c</ID><DisplayName>jamesmurty</DisplayName></Grantee><Permission>FULL_CONTROL</Permission></Grant></AccessControlList></AccessControlPolicy>";
public static final String aclExtreme = "<AccessControlPolicy xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Owner><ID>1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c</ID><DisplayName>jamesmurty</DisplayName></Owner><AccessControlList><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"Group\"><URI>http://acs.amazonaws.com/groups/global/AuthenticatedUsers</URI></Grantee><Permission>WRITE</Permission></Grant><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"Group\"><URI>http://acs.amazonaws.com/groups/global/AuthenticatedUsers</URI></Grantee><Permission>READ_ACP</Permission></Grant><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID>1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c</ID><DisplayName>jamesmurty</DisplayName></Grantee><Permission>WRITE</Permission></Grant><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"Group\"><URI>http://acs.amazonaws.com/groups/global/AuthenticatedUsers</URI></Grantee><Permission>WRITE_ACP</Permission></Grant><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"Group\"><URI>http://acs.amazonaws.com/groups/global/AllUsers</URI></Grantee><Permission>READ</Permission></Grant><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"Group\"><URI>http://acs.amazonaws.com/groups/global/AuthenticatedUsers</URI></Grantee><Permission>READ</Permission></Grant><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"Group\"><URI>http://acs.amazonaws.com/groups/s3/LogDelivery</URI></Grantee><Permission>WRITE</Permission></Grant><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID>1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c</ID><DisplayName>jamesmurty</DisplayName></Grantee><Permission>READ</Permission></Grant><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID>1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c</ID><DisplayName>jamesmurty</DisplayName></Grantee><Permission>FULL_CONTROL</Permission></Grant></AccessControlList></AccessControlPolicy>";
@BeforeMethod
ParseSax<AccessControlList> createParser() {
ParseSax<AccessControlList> parser = parserFactory.createAccessControlListParser();
return parser;
}
@Test
public void testAccessControlListOwnerOnly() throws HttpException {
String ownerId = "1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c";
AccessControlList acl = createParser().parse(
IOUtils.toInputStream(aclOwnerOnly));
assertEquals(acl.getOwner().getId(), ownerId);
assertEquals(acl.getOwner().getDisplayName(), "jamesmurty");
assertEquals(acl.getPermissions(ownerId).size(), 1);
assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL));
assertEquals(acl.getGrants().size(), 1);
assertEquals(acl.getPermissions(GroupGranteeURI.ALL_USERS).size(), 0);
assertEquals(acl.getPermissions(GroupGranteeURI.AUTHENTICATED_USERS).size(), 0);
assertEquals(acl.getPermissions(GroupGranteeURI.LOG_DELIVERY).size(), 0);
}
@Test
public void testAccessControlListExtreme() throws HttpException {
String ownerId = "1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c";
AccessControlList acl = createParser().parse(
IOUtils.toInputStream(aclExtreme));
assertEquals(acl.getOwner().getId(), ownerId);
assertEquals(acl.getOwner().getDisplayName(), "jamesmurty");
assertEquals(acl.getPermissions(ownerId).size(), 3);
assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL));
assertTrue(acl.hasPermission(ownerId, Permission.READ));
assertTrue(acl.hasPermission(ownerId, Permission.WRITE));
assertEquals(acl.getGrants().size(), 9);
assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ));
assertTrue(acl.hasPermission(GroupGranteeURI.AUTHENTICATED_USERS, Permission.READ));
assertTrue(acl.hasPermission(GroupGranteeURI.AUTHENTICATED_USERS, Permission.WRITE));
assertTrue(acl.hasPermission(GroupGranteeURI.AUTHENTICATED_USERS, Permission.READ_ACP));
assertTrue(acl.hasPermission(GroupGranteeURI.AUTHENTICATED_USERS, Permission.WRITE_ACP));
assertTrue(acl.hasPermission(GroupGranteeURI.LOG_DELIVERY, Permission.WRITE));
}
}