The S3 service allows you to define access control permissions to specify who can access your buckets and objects, and what kind of operations can be performed. The group of permission settings applied to an S3 resource is called an Accesss Control Policy (ACP), though more often these settings are referred to as an Accesss Control List (ACL), because this list defines the permission settings.
Every resource in S3 has an ACL associated with it. The default ACL applied to objects and buckets when they are created or updated marks these resources as private, meaning that you as the owner have full control over the resource, and no one else can access or modify it. You can update the ACL permission settings of your resources at any time.
Access Control Lists contain a set of up to 100 grant rules. Each grant rule defines the specific entity that can access a resource; this entity is called a grantee, and a single permission value describes what the grantee can do with the resource. You control the access permission settings for a resources by adding grant rules to, or removing them from, the ACL settings document associated with a resource.
ACL grant rules only grant access permissions, they cannot forbid them. Access permissions must be explicitly granted to take effect.
In Create or Replace an Object” we demonstrated how limited, “canned” access control permission settings can be applied when you create an object in S3. However, to take advantage of the full flexibility and power of the access controls available in S3, it is necessary to work with Access Control List configuration documents.
An Access Control List configuration document is an XML document that contains a set of grant rules. It includes two main sections. The first is an Owner section, which stores the ID and (optionally) the display name of the resource’s owner. The second section contains a listing of Grant XML elements that define the grant rules the document embodies.
Here is an example ACL document for a publicly accessible object:
<AccessControlPolicy xmlns='http://s3.amazonaws.com/doc/2006-03-01/'> <Owner> <ID>1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b</ID> <DisplayName>jamesmurty</DisplayName> </Owner> <AccessControlList> <Grant> <Grantee xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='CanonicalUser'> <ID>1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b</ID> <DisplayName>jamesmurty</DisplayName> </Grantee> <Permission>FULL_CONTROL</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> </AccessControlList> </AccessControlPolicy>
This ACL includes two Grant elements. The first grant specifies
that the S3 user, jamesmurty is allowed the FULL_CONTROL
permission, while the second
grant specifies that a group identified with the URI http://acs.amazonaws.com/groups/global/AllUsers is
allowed the READ
permission. Let’s
look at what these settings actually mean.
There are two kinds of grantee entities that can be allowed permissions in an ACL document: Users and Groups.
A User grantee is a specific S3 user or account holder, denoted by a canonical user-identifier value that is permanently associated with the account. A user may also have a display name associated with the account, however this name is provided only as a convenience and should not be relied upon.
The display name shown for a user grantee is entirely controlled by the user, and it may change over time as the nickname or email address on Amazon’s web site changes. Do not assume that the display name provides a reliable means for identifying S3 users.
Canonical ID strings are long, unwieldy strings that are difficult for humans to deal with. For this reason, S3 allows us to identify users by their Amazon email address instead of their canonical ID. The service converts the user’s email address into the canonical ID when the ACL is applied.
To construct an XML document fragment that identifies a
user in an ACL, you define a Grantee element with an xsi:type
value of CanonicalUser
. The
Grantee element will contain an ID element with the user’s
canonical identifier, and an optional DisplayName element will
contain the display name:
<Grantee xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='CanonicalUser’> <ID>1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b</ID> <DisplayName>jamesmurty</DisplayName> </Grantee>
To identify a user with an Amazon email address, you
define a Grantee element with an xsi:type value of AmazonCustomerByEmail
. The Grantee
element will contain an EmailAddress element with the user’s
email address:
<Grantee xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='AmazonCustomerByEmail’> <EmailAddress>someone@somewhere.com</EmailAddress> </Grantee>
A group grantee denotes a group of people to whom permissions can be assigned as a collective. Each group is identified using a special URI value that is recognized by S3.
There is no mechanism in S3 for developers to define their own groups, so the only groups available are those that have been predefined by Amazon. Here is the list of the predefined groups:
http://acs.amazonaws.com/groups/global/AllUsers: The general public, including people without S3 accounts
http://acs.amazonaws.com/groups/global/AuthenticatedUsers: People authenticated within AWS, or in other words all AWS account holders
http://acs.amazonaws.com/groups/s3/LogDelivery: A group used by the automated processes within Amazon that perform bucket-access logging (see Server Access Logging (Beta)” for more information)
To construct and XML document fragment that identifies a
group in an ACL, you define a Grantee element with an xsi:type
value of Group
. The Grantee
element will contain a URI element with the value of a URI that
identifies a predefined group:
<Grantee xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='Group’> <URI>http://acs.amazonaws.com/groups/global/AllUsers</URI> </Grantee>
As the owner of an S3 account your access to your own items is also set by user grantee rules. Whenever you create a bucket or object you become the owner of that item and S3 automatically assigns you all permissions. It is possible to alter or remove your own user grant from an ACL, leaving you with limited access to your own items. This situation could actually be desirable, for example if you are worried you will accidentally delete important objects in S3 while testing you could remove your permission to delete objects. Regardless of what ACL permissions you deny yourself, as the owner of an object or bucket the S3 service will always allow you to apply new ACL settings so it is impossible to do any lasting damage.
There are five kinds of access permission that can be assigned to a grantee in an ACL rule. Each permission type has different semantics, depending on whether the permission is applied to a bucket or an object resource.
Allows an object’s data and metadata to be read, or allows object-listings to be retrieved from a bucket.
Configures a bucket to allow objects to be created, modified, or deleted within it. This permission is not applicable to individual objects, only to an entire bucket.
Allows the access control policy of a bucket or object to be read. The owner of a bucket or object will always retain this permission regardless of what the ACL setting is.
Allows the access control policy of a bucket or object to be modified. The owner of a bucket or object will always retain this permission regardless of what the ACL setting is.
Granting the WRITE_ACP permission to third parties will effectively give them full control over your resource, because the third party will be able to increase their own permission settings at will.
A short-hand way to include all permissions in a single grant rule. This permission is the combination of all of the above permissions: READ, WRITE, READ_ACP, and WRITE_ACP.
To retrieve the Access Control List document associated
with a resource, you send a GET request with a URI that specifies the
resource and includes a single acl
parameter. The service will respond with an HTTP 200 response message
containing an ACL XML document.
Here is an example URI that will retrieve an object’s ACL:
https://my-bucket.s3.amazonaws.com/WebPage.html?acl
Example 3-15 defines a method that sends a GET
request to a resource and includes the acl
parameter. This method will receive and
interpret the ACL XML document.
Example 3-15. Get ACL: S3.rb
def get_acl(bucket_name, object_key='') uri = generate_s3_uri(bucket_name, object_key, [:acl=>nil]) response = do_rest('GET', uri) xml_doc = REXML::Document.new(response.body) grants = [] xml_doc.elements.each('//Grant') do |grant| grantee = {} grantee[:type] = grant.elements['Grantee'].attributes['type'] if grantee[:type] == 'Group' grantee[:uri] = grant.elements['Grantee/URI'].text else grantee[:id] = grant.elements['Grantee/ID'].text grantee[:display_name] = grant.elements['Grantee/DisplayName'].text end grants << { :grantee => grantee, :permission => grant.elements['Permission'].text } end return { :owner_id => xml_doc.elements['//Owner/ID'].text, :owner_name => xml_doc.elements['//Owner/DisplayName'].text, :grants => grants } end
To demonstrate this method, we can retrieve the ACL settings for
the objects we created earlier. First, let us look at the bucket
my-bucket, which has the default
private access control settings. We will store the results of the
method in a local acl
variable.
irb> acl = s3.get_acl('my-bucket') REQUEST DESCRIPTION ======= GET\n \n \n Wed, 07 Nov 2007 11:38:46 GMT\n /my-bucket/?acl REQUEST ======= Method : GET URI : https://my-bucket.s3.amazonaws.com?acl Headers: Authorization=AWS ABCDEFGHIJ1234567890:oey+xFCgVoxsV146qAPqEpNCk5M= Date=Wed, 07 Nov 2007 11:38:46 GMT Host=my-bucket.s3.amazonaws.com RESPONSE ======== Status : 200 OK Headers: x-amz-id-2=IwKcqFKPueW4DPZ3IyzIR7gTPjS8XwljyyRMnvTlW0mIswObnRNBnETT1kyNZAQj content-type=application/xml date=Wed, 07 Nov 2007 11:38:48 GMT x-amz-request-id=80BCD92A3214A9FD server=AmazonS3 transfer-encoding=chunked Body: <?xml version='1.0' encoding='UTF-8'?> <AccessControlPolicy xmlns='http://s3.amazonaws.com/doc/2006-03-01/'> <Owner> <ID>1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b</ID> <DisplayName>jamesmurty</DisplayName> </Owner> <AccessControlList> <Grant> <Grantee xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='CanonicalUser'> <ID>1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b</ID> <DisplayName>jamesmurty</DisplayName> </Grantee> <Permission>FULL_CONTROL</Permission> </Grant> </AccessControlList> </AccessControlPolicy>
We can examine the acl
variable to delve into the Access Control List settings applied to our
bucket. Notice that there is only one grantee with permissions, the
bucket’s owner jamesmurty
. The
owner has the FULL_CONTROL permission.
irb> acl[:owner_id] => "1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b" irb> acl[:grants] => [{ :permission=>"FULL_CONTROL", :grantee=>{ :id=>"1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b", :display_name=>"jamesmurty", :type=>"CanonicalUser" }}]
Now let us look at the WebPage.html object that we created to be publicly accessible. This object has two grantees: the owner, who has full control permissions and the AllUsers (public) group http://acs.amazonaws.com/groups/global/AllUsers, which is only allowed to read the object.
irb> acl = s3.get_acl('my-bucket', 'Metadata.txt') irb> acl[:grants] => [{ :permission=>"FULL_CONTROL", :grantee=>{ :id=>"1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b", :display_name=>"jamesmurty", :type=>"CanonicalUser" }}, { :permission=>"READ", :grantee=>{ :uri=>"http://acs.amazonaws.com/groups/global/AllUsers", :type=>"Group" }}]
To modify the ACL settings for a bucket or object we must
generate an XML document describing the settings we wish to apply and
upload it to S3 with a PUT request. The request will be sent to a URI
that specifies the resource the ACL belongs to and includes a single
parameter: acl
. When the request
succeeds, the service will respond with an empty HTTP 200 response
message.
Example 3-16 defines a method that builds an ACL
document containing a set of grant rules, and uploads this document to
a resource identified with a URI that includes the acl
parameter.
Example 3-16. Set ACL: S3.rb
def set_acl(owner_id, bucket_name, object_key='', grants=[owner_id=>'FULL_CONTROL']) xml_doc = REXML::Document.new("<AccessControlPolicy xmlns='#{XMLNS}'/>") xml_doc.root.add_element('Owner').add_element('ID').text = owner_id grant_list = xml_doc.root.add_element('AccessControlList') grants.each do |hash| hash.each do |grantee_id, permission| grant = grant_list.add_element('Grant') grant.add_element('Permission').text = permission # Grantee may be of type email, group, or canonical user if grantee_id.index('@') # Email grantee grantee = grant.add_element('Grantee', {'xmlns:xsi'=>'http://www.w3.org/2001/XMLSchema-instance', 'xsi:type'=>'AmazonCustomerByEmail'}) grantee.add_element('EmailAddress').text = grantee_id elsif grantee_id.index('://') # Group grantee grantee = grant.add_element('Grantee', {'xmlns:xsi'=>'http://www.w3.org/2001/XMLSchema-instance', 'xsi:type'=>'Group'}) grantee.add_element('URI').text = grantee_id else # Canonical user grantee grantee = grant.add_element('Grantee', {'xmlns:xsi'=>'http://www.w3.org/2001/XMLSchema-instance', 'xsi:type'=>'CanonicalUser'}) grantee.add_element('ID').text = grantee_id end end end uri = generate_s3_uri(bucket_name, object_key, [:acl=>nil]) do_rest('PUT', uri, xml_doc.to_s, {'Content-Type'=>'application/xml'}) return true end
This method works by accepting an array of Grantee-to-Permission
mappings in the grants
method
variable. Each of these mappings has a grantee identifier set as its
key name, and the requested permission is set as its value. The method
examines these grant items and determines whether a particular grantee
is of the type AmazonCustomerByEmail
, Group
, or CanonicalUser
by looking at the grantee
identifier. Each grantee and permission value is then added to the ACL
XML document using the appropriate document structure. For
convenience, the default grants argument includes the owner’s ID and
grants FULL_CONTROL permission to the object’s owner.
When building an ACL document, you must include the Canonical ID of the owner of the target resource. To obtain this ID value, you will generally have to get the resource’s ACL first.
Let us run through some examples to show how this method works. First, we will make our bucket my-bucket publicly readable by adding a rule that allows the group http://acs.amazonaws.com/groups/global/AllUsers the READ permission. Before we can send ACL updates, however, we must find out the canonical ID of the bucket’s owner.
# Obtain the canonical ID of the bucket's owner irb> acl = s3.get_acl('my-bucket') irb> acl[:owner_id] => "1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b" # Define a set of grants containing a single grant rule irb> grants = [{'http://acs.amazonaws.com/groups/global/AllUsers'=>'READ'}] # Apply the new ACL settings irb> s3.set_acl(acl[:owner_id], 'my-bucket', '', grants) # Confirm that the bucket's ACL settings have been updated irb> s3.get_acl('my-bucket')[:grants] => [{ :permission=>"READ", :grantee=>{ :uri=>"http://acs.amazonaws.com/groups/global/AllUsers", :type=>"Group" } }]
Notice that the newest ACL settings replace the old ones, and we therefore no longer have FULL_CONTROL access to our own bucket. This is not a serious problem, because the owner of a resource always retains the ability to update the ACL settings of that resource, whatever the ACL settings.
Now, let us further extend the ACL settings for this bucket to
grant ourselves FULL_CONTROL again. We will also allow the Amazon user
with the email address someone@somewhere.com
to view but not modify
the bucket’s ACL settings. For the sake of this example, you will have
to imagine that this email address is the author’s Amazon.com contact address.
irb> grants << {acl[:owner_id] => 'FULL_CONTROL'} irb> grants << {'someone@somewhere.com' => 'READ_ACP'} irb> grants [{"http://acs.amazonaws.com/groups/global/AllUsers"=>"READ"}, {"1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b"=> "FULL_CONTROL"}, {"someone@somewhere.com"=>"READ_ACP"}] irb> s3.set_acl(acl[:owner_id], 'my-bucket', '', grants) . . . Request Body Data: <AccessControlPolicy xmlns='http://s3.amazonaws.com/doc/2006-03-01/'> <Owner> <ID>1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b</ID> </Owner> <AccessControlList> <Grant> <Permission>READ</Permission> <Grantee xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='Group'> <URI>http://acs.amazonaws.com/groups/global/AllUsers</URI> </Grantee> </Grant> <Grant> <Permission>FULL_CONTROL</Permission> <Grantee xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='CanonicalUser'> <ID>1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b</ID> </Grantee> </Grant> <Grant> <Permission>READ_ACP</Permission> <Grantee xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='AmazonCustomerByEmail'> <EmailAddress>someone@somewhere.com</EmailAddress> </Grantee> </Grant> </AccessControlList> </AccessControlPolicy>
If we now retrieve the ACL settings we have just applied, we
will find that the AmazonCustomerByEmail
grantee has been
automatically converted into a CanonicalUser
grantee by the service. This
remapping is performed automatically by S3 every time you upload an
ACL with AmazonCustomerByEmail
grantees.
irb> s3.get_acl('my-bucket')
. . .
<AccessControlPolicy xmlns='http://s3.amazonaws.com/doc/2006-03-01/'>
. . .
<AccessControlList>
. . .
<Grant>
<Grantee xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:type='CanonicalUser'>
<ID>1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b</ID>
<DisplayName>jamesmurty</DisplayName>
</Grantee>
<Permission>READ_ACP</Permission>
</Grant>
</AccessControlList>
</AccessControlPolicy>
To reset the ACL permissions for our bucket, we would invoke the
set_acl
method with no explicit
grants variable, in which case the method will use the default grant
rule, mapping the owner to the FULL_CONTROL permission.
irb> s3.set_acl(acl[:owner_id], 'my-bucket')
Setting the access permissions of S3 resources is a two-step process when ACL documents are used to define the access rules. First, the resource is created or modified; then the desired access settings are applied to that resource by uploading an ACL document in a follow-up API operation. The S3 REST API interface provides a convenient shortcut for this process, using simplified, canned access policies that can be applied in the same API call that creates or updates a resource.
When you create an object or a bucket with the REST API
interface, you can include a special HTTP request header named
x-amz-acl
with a value matching one
of the predefined access control policies listed in Table 3-6.
Table 3-6. Canned ACL policies
Policy Name | Description |
---|---|
private | The resource’s owner is assigned the |
public-read | The owner of the resource is assigned the
|
public-read-write | The owner of the resource is assigned the
|
authenticated-read | The resource’s owner is assigned the |
log-delivery-write | The owner of the resource is assigned the
|
The previous sections have demonstrated how you can configure your resources using Access Control List XML documents. However, managing the full set of ACL settings can be an unnecessary burden if the predefined canned policies are sufficient to meet your needs. Canned policies are easier to apply, because as you do not have to define a set of grant rules or build an ACL XML document.
To apply canned access policy settings, you send a PUT request
including the x-amz-acl
header to a
resource specified by a URI that includes the acl
parameter. Example 3-17 defines a simple method that performs this
task.
Example 3-17. Set canned ACL: S3.rb
def set_canned_acl(canned_acl, bucket_name, object_key='') uri = generate_s3_uri(bucket_name, object_key, [:acl=>nil]) response = do_rest('PUT', uri, nil, {'x-amz-acl'=>canned_acl}) return true end
The following example shows how much easier it is to modify the access permissions of your resources using canned access policies instead of full ACL documents. Rather than defining a set of grant rules, we can simply apply the canned policy that defines the changes we wish to make. In this case, we will modify the access permissions for the bucket my-bucket to make it private.
irb> s3.set_canned_acl('private', 'my-bucket') REQUEST DESCRIPTION ======= PUT\n \n \n Wed, 07 Nov 2007 11:41:40 GMT\n x-amz-acl:private\n /my-bucket/?acl REQUEST ======= Method : PUT URI : https://my-bucket.s3.amazonaws.com?acl Headers: Expect=100-continue Authorization=AWS ABCDEFGHIJ1234567890:gBre2h+lA8JLWuREKGF8V9RWe/k= x-amz-acl=private Date=Wed, 07 Nov 2007 11:41:40 GMT Host=my-bucket.s3.amazonaws.com . . . # Retrieve the bucket's ACL settings to confirm it is now marked as private irb> s3.get_acl('my-bucket')[:grants] => [{ :permission=>"FULL_CONTROL", :grantee=>{ :id=>"1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b", :display_name=>"jamesmurty", :type=>"CanonicalUser" } }]