Sat, 22 Aug 2009

Using Container-Level Access Policies in Windows Azure Storage

[UPDATE 5/2/2010] The code in this post is woefully out of date. See “Shared Access Signatures Are Easy These Days” for an updated sample.

Last week, I blogged about shared access signatures, one of the new blob storage features introduced in July.  This feature lets you embed signatures in URLs to grant granular access to containers and blobs.  In the approach I took in the blog post, you embed the access policy directly in the URL, which means there’s no way for you to modify or revoke permission after I’ve given out the URL.  To limit the scope of these unrevokable privileges, explicit access policies in the URL are limited to granting permissions for up to only one hour.

To grant longer-term permissions or to retain the ability to modify or revoke permissions after handing them out, you can use another new feature called container-level access policies (MSDN documentation).  These are named access policies that take the place of explicit policies in the URL.  In this blog post, I’ll walk you through a simple example of using a container-level access policy.

Creating the Container-Level Access Policy

The first step is to create the container-level access policy.  This is done using the “set container ACL” method (MSDN documentation).  The following code from the sample creates an access policy that has an explicit start time, expiry time, and permission set.


var req = (HttpWebRequest)(WebRequest.Create(
    string.Format("http://{0}.blob.core.windows.net/{1}?restype=container&comp=acl", account,
        containername)));
req.Method = "PUT";
req.Headers.Add("x-ms-version", "2009-07-17");
req.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R"));
req.Headers.Add("x-ms-prop-publicaccess", "false");
var body = string.Format(@"<?xml version=""1.0"" encoding=""utf-8""?>
                            <SignedIdentifiers>
                                <SignedIdentifier>
                                    <Id>{0}</Id>
                                    <AccessPolicy>
                                        <Start>{1}</Start>
                                        <Expiry>{2}</Expiry>
                                        <Permission>{3}</Permission>
                                    </AccessPolicy>
                                </SignedIdentifier>
                            </SignedIdentifiers>", policyname, yesterday, tomorrow, permissions);
req.ContentLength = body.Length;
skc.SignRequest(req, new ResourceUriComponents(account, containername, "comp=acl"));
using (var stream = new StreamWriter(req.GetRequestStream()))
{
    stream.Write(body);
    stream.Close();
}
req.GetResponse();

Building the Query String

Now that we have created an access policy, we need to construct a URL that makes use of it.  The following code creates a web request that uses our new access policy.  Note that because we’ve specified an explicit start time, expiry time, and permission set, we don’t specify them in the URL (as we did when we weren’t using a named access policy).  This tripped me up at first.  The start time, expiry time, and permissions are all optional when creating the container-level access policy, and any which are specified there should not be specified in the URL.

var sig = MakePolicySignature(account, key, containername, policyname);
string text = null;
using (var reader = 
    new StreamReader(
        WebRequest.Create(
            string.Format("http://{0}.blob.core.windows.net/{1}/{2}?sr=c&si={3}&sig={4}",
                account,
                containername,
                blobname,
                policyname,
                Uri.EscapeDataString(sig)
            )
        ).GetResponse().GetResponseStream()))
{
    text = reader.ReadToEnd();
}

Signing the Request

In the previous code snippet, we used a method called MakePolicySignature to create the signature for our request.  Here’s the source code for that method.  See the shared access signature MSDN documentation for details on what should go in the “StringToSign.”

public static string MakePolicySignature(string account, string key, string container, string policyId)
{
    string stringtosign = string.Format("\n\n\n/{0}/{1}\n{2}", account, container, policyId);
    using (var hmac = new HMACSHA256(Convert.FromBase64String(key)))
    {
        return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringtosign)));
    }
}

When to Use Container-Level Access Policies

Shared access signatures give you the ability to create granular permission sets with or without the use of a container-level access policy, so when should you use them?  Let’s take a look at some advantages and disadvantages to using container-level access policies.

Advantages:

  • Container-level access policies let you grant permissions that last more than an hour.
  • You can modify or revoke container-level access policies at any time.

Disadvantages:

  • Container-level access policies are, as the name states, at the level of the container.  You can’t use them to create permissions for a specific blob (except by putting it by itself in a container).
  • There’s a limit of up to five access policies per container.  This is a problem if you want to grant different permissions to many different users.

Because they give a greater level of control, I think container-level access policies will be my default way of granting permissions in URLs.  I’ll forgo them for short-term access, like giving a user permission to upload a blob or embedding image tags on a web page.

Download the Sample Code

The above code snippets are part of a full command-line sample.  Download the full source here.