Access Control Lists

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.

Note

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.

User grantee (canonical or email)

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.

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>
Group 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:

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>

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.

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.

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.

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.

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.

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.

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"
  }
}]