blob: 34a736660587c5552fbf415340effc2deb172ca5 [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.jclouds.s3.domain;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* 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 payload tries to access or modify an item
* in S3, the operation will be denied unless the item has ACL settings that explicitly permit that
* payload to perform that action.
*/
public class AccessControlList {
private CanonicalUser owner;
private final List<Grant> grants = Lists.newArrayList();
public void setOwner(CanonicalUser owner) {
this.owner = owner;
}
public CanonicalUser getOwner() {
return owner;
}
/**
* @return an unmodifiable set of grants represented by this ACL.
*/
public List<Grant> getGrants() {
return Collections.unmodifiableList(grants);
}
/**
* @return an unmodifiable set of grantees who have been assigned permissions in this ACL.
*/
public Set<Grantee> getGrantees() {
Set<Grantee> grantees = Sets.newTreeSet();
for (Grant grant : getGrants()) {
grantees.add(grant.getGrantee());
}
return Collections.unmodifiableSet(grantees);
}
/**
* Add a permission for the given grantee.
*
* @param grantee
* @param permission
*/
public AccessControlList addPermission(Grantee grantee, String permission) {
Grant grant = new Grant(grantee, permission);
grants.add(grant);
return this;
}
/**
* Add a permission for the given group grantee.
*
* @param groupGranteeURI
* @param permission
*/
public AccessControlList addPermission(URI groupGranteeURI, String 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
*/
public AccessControlList revokePermission(Grantee grantee, String permission) {
for (Iterator<Grant> it = grants.iterator(); it.hasNext();) {
Grant grant = it.next();
if (grant.getGrantee().equals(grantee) && grant.getPermission().equals(permission)) {
it.remove();
}
}
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
*/
public AccessControlList revokePermission(URI groupGranteeURI, String permission) {
return revokePermission(new GroupGrantee(groupGranteeURI), permission);
}
/**
* Revoke all the permissions granted to the given grantee.
*
* @param grantee
*/
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<String> getPermissions(String granteeId) {
Collection<Grant> grantsForGrantee = findGrantsForGrantee(granteeId);
return Collections2.transform(grantsForGrantee, new Function<Grant, String>() {
public String apply(Grant g) {
return g.getPermission();
}
});
}
/**
* @param grantee
* @return the permissions assigned to a grantee.
*/
public Collection<String> getPermissions(Grantee grantee) {
return getPermissions(grantee.getIdentifier());
}
/**
* @param granteeURI
* @return the permissions assigned to a group grantee.
*/
public Collection<String> getPermissions(URI granteeURI) {
return getPermissions(granteeURI.toASCIIString());
}
/**
* @param granteeId
* @param permission
* @return true if the grantee has the given permission.
*/
public boolean hasPermission(String granteeId, String permission) {
return getPermissions(granteeId).contains(permission);
}
/**
* @param grantee
* @param permission
* @return true if the grantee has the given permission.
*/
public boolean hasPermission(Grantee grantee, String permission) {
return hasPermission(grantee.getIdentifier(), permission);
}
/**
* @param granteeURI
* @param permission
* @return true if the grantee has the given permission.
*/
public boolean hasPermission(URI granteeURI, String permission) {
return getPermissions(granteeURI).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.
*/
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
*/
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 final class Permission {
public static final String READ = "READ";
public static final String WRITE = "WRITE";
public static final String READ_ACP = "READ_ACP";
public static final String WRITE_ACP = "WRITE_ACP";
public static final String FULL_CONTROL = "FULL_CONTROL";
private Permission() {
throw new AssertionError("intentionally unimplemented");
}
};
public static class Grant implements Comparable<Grant> {
private Grantee grantee;
private final String permission;
public Grant(Grantee grantee, String permission) {
this.grantee = grantee;
this.permission = permission;
}
public Grantee getGrantee() {
return grantee;
}
@VisibleForTesting
public void setGrantee(Grantee grantee) {
this.grantee = grantee;
}
public String getPermission() {
return permission;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("Grant");
sb.append("{grantee=").append(grantee);
sb.append(", permission=").append(permission);
sb.append('}');
return sb.toString();
}
public int compareTo(org.jclouds.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);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((grantee == null) ? 0 : grantee.hashCode());
result = prime * result + ((permission == null) ? 0 : permission.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Grant other = (Grant) obj;
if (grantee == null) {
if (other.grantee != null)
return false;
} else if (!grantee.equals(other.grantee))
return false;
if (permission == null) {
if (other.permission != null)
return false;
} else if (!permission.equals(other.permission))
return false;
return true;
}
}
public abstract static class Grantee implements Comparable<Grantee> {
private final String identifier;
protected Grantee(String identifier) {
this.identifier = identifier;
}
public String getIdentifier() {
return identifier;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("Grantee");
sb.append("{identifier='").append(identifier).append('\'');
sb.append('}');
return sb.toString();
}
public int compareTo(org.jclouds.s3.domain.AccessControlList.Grantee o) {
return (this == o) ? 0 : getIdentifier().compareTo(o.getIdentifier());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Grantee other = (Grantee) obj;
if (identifier == null) {
if (other.identifier != null)
return false;
} else if (!identifier.equals(other.identifier))
return false;
return true;
}
}
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 String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("CanonicalUserGrantee");
sb.append("{displayName='").append(displayName).append('\'');
sb.append(", identifier='").append(getIdentifier()).append('\'');
sb.append('}');
return sb.toString();
}
}
public static final class GroupGranteeURI {
public static final URI ALL_USERS = URI.create("http://acs.amazonaws.com/groups/global/AllUsers");
public static final URI AUTHENTICATED_USERS = URI
.create("http://acs.amazonaws.com/groups/global/AuthenticatedUsers");
public static final URI LOG_DELIVERY = URI.create("http://acs.amazonaws.com/groups/s3/LogDelivery");
private GroupGranteeURI() {
throw new AssertionError("intentionally unimplemented");
}
}
public static class GroupGrantee extends Grantee {
public GroupGrantee(URI groupURI) {
super(groupURI.toASCIIString());
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("AccessControlList");
sb.append("{owner=").append(owner);
sb.append(", grants=").append(grants);
sb.append('}');
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((grants == null) ? 0 : grants.hashCode());
result = prime * result + ((owner == null) ? 0 : owner.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AccessControlList other = (AccessControlList) obj;
if (grants == null) {
if (other.grants != null)
return false;
} else if (!grants.equals(other.grants))
return false;
if (owner == null) {
if (other.owner != null)
return false;
} else if (!owner.equals(other.owner))
return false;
return true;
}
}