Simplifying Shared Access Signature Generation with the Mobile Services ResourceBroker

In my post Storing the Big Stuff in Blob Storage I showed you how to manually create a shared access signature. The Azure Mobile Services team have done a really nice job of making this even easier with the ResourceBroker NuGet package. Getting started documentation is available via GitHub (https://github.com/Azure/azure-mobile-services-resourcebroker) and of course the package, which I’ve added to my Mobile Service project, is available via NuGet.

image

The changes I needed to make to my SharedAccessSignature controller are:

– Change my Web.config to include the ResourceBrokerStorageConnectionString appSetting – the documentation talks about adding this via the Configure panel of the mobile service portal but you’ll need to add it to web.config for debugging. Also the format of this string should be similar to the following:

<add key=”ResourceBrokerStorageConnectionString”
     value=”DefaultEndpointsProtocol=https;AccountName=realestateinspector;AccountKey=LxWu0q2UvQ7ddxXvIP3UfV4ozDkLpgaSkUxkK8————————–BYHTpTrAGaHjLoynH+61ng==” />

– Change the base class of the controller to ResourcesControllerBase (I needed to add an import statement to the top of the file too)

– Add routing information to the WebApiConfig.cs file (as per the documentation on GitHub)

// Create a custom route mapping the resource type into the URI.    
var resourcesRoute = config.Routes.CreateRoute(
     routeTemplate: “api/resources/{type}”,
     defaults: new { controller = “resources” },
     constraints: null);

// Insert the ResourcesController route at the top of the collection to avoid conflicting with predefined routes.
config.Routes.Insert(0, “Resources”, resourcesRoute);

– Initially I removed the contents of my controller but then I realised that there are limitations on the ResourceControllerBase (eg the Blob container must exist and that I needed to specify an actual blob, not just a container for access), so I kept my code and modified it to work with the new connection string.

public class SharedAccessSignatureController :  ResourcesControllerBase
{
    public async Task<string> Get(string id)
    {
        var sas = string.Empty;

        if (!string.IsNullOrEmpty(id))
        {
            // Try to get the Azure storage account token from app settings. 
            string storageAccountConnectionString;

            if (Services.Settings.TryGetValue(“ResourceBrokerStorageConnectionString”, out storageAccountConnectionString) )
            {
                // Set the URI for the Blob Storage service.
                var account = CloudStorageAccount.Parse(storageAccountConnectionString);
                // Create the BLOB service client.
                var blobClient = new CloudBlobClient(account.BlobStorageUri,account.Credentials);

                // Create a container, if it doesn’t already exist.
                var container = blobClient.GetContainerReference(id);
                await container.CreateIfNotExistsAsync();

                // Create a shared access permission policy.
                var containerPermissions = new BlobContainerPermissions();

                // Enable anonymous read access to BLOBs.
                containerPermissions.PublicAccess = BlobContainerPublicAccessType.Blob;
                container.SetPermissions(containerPermissions);

                // Define a policy that gives write access to the container for 1h
                var sasPolicy = new SharedAccessBlobPolicy()
                {
                    SharedAccessStartTime = DateTime.UtcNow,
                    SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(59).AddSeconds(59),
                    Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read
                };

                sas = container.GetSharedAccessSignature(sasPolicy);
            }
        }

        return sas;
    }
}

– To get my code to work I also had to amend the route to the following, adding the id as an optional parameter

var resourcesRoute = config.Routes.CreateRoute(
        routeTemplate: “api/sharedaccesssignature/{type}/{id}”,
        defaults: new { controller = “sharedaccesssignature”, id = RouteParameter.Optional },
        constraints: null);

Calling the controller from Fiddler can be done in two ways:

GET: http://localhost:51539/api/sharedaccesssignature/blob/test

POST: http://localhost:51539/api/sharedaccesssignature/blob
Content-Type: application/json
{
    “name”: “myblob”,
    “container”: “test”,
    “permissions”: “w”,
    “expiry”: “2015-12-01T07:34:42Z”
}

Don’t forget that if during debugging you set authenticationlevel to anonymous, to make sure you change it back to User or Application before publishing.

Leave a comment