3
votes

I’ve read through and played with the sample code in the https://azure.microsoft.com/en-us/documentation/articles/storage-dotnet-shared-access-signature-part-2/#part-1-create-a-console-application-to-generate-shared-access-signatures

I then applied it to my scenario.

I write a tool to upload data from partner to Azure blob storage and then it will be consumed by some internal teams: YYYY-MM (container) (DD-GUID) (prefix) File1.zip File2.zip ……

I created 2 policies per container: 1. Write only for the partner so that they can only write blobs and nothing else. 2. List and read for our internal teams so that they can list and read(download) all the blobs in the container.

My thought is that I can simply hand the correct policy to the correct recipients; however, my implementation doesn’t work as I expected.

I created 2 policies for each container using the below method, of course with correct permission per policy:

static void CreateSharedAccessPolicy(CloudBlobClient blobClient, CloudBlobContainer container, string policyName)
    {
        //Create a new stored access policy and define its constraints.
        SharedAccessBlobPolicy sharedPolicy = new SharedAccessBlobPolicy()
        {
            SharedAccessExpiryTime = DateTime.UtcNow.AddHours(10),
            Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.List
        };

        //Get the container's existing permissions.
        BlobContainerPermissions permissions = new BlobContainerPermissions();

        //Add the new policy to the container's permissions.
        permissions.SharedAccessPolicies.Clear();
        permissions.SharedAccessPolicies.Add(policyName, sharedPolicy);
        container.SetPermissions(permissions);
    }

I created the write only policy first and then the read and list policy. What I observed is that the first policy doesn’t seem to work, everything got back a 403 Forbidden and for the second policy, the only thing that works is the List blob but not Read (I tried to download the blob but got a 404 Not Found).

It seems like I’ve missed something very basic here. Can you please help me out to see what’s wrong with my approach?

The code I used to test the permissions of the container, I also notice that the Read permission on a container doesn't really work as mentioned somewhere in Azure documentation. Here I'm trying to find an easy way to simply give people a stored access policy so that they can list and download all the blobs in the container instead of providing them a signature per blob file:

static void UseContainerSAS(string sas) { //Try performing container operations with the SAS provided.

        //Return a reference to the container using the SAS URI.
        CloudBlobContainer container = new CloudBlobContainer(new Uri(sas));

        //Create a list to store blob URIs returned by a listing operation on the container.
        List<Uri> blobUris = new List<Uri>();

        try
        {
            //Write operation: write a new blob to the container. 
            CloudBlockBlob blob = container.GetBlockBlobReference("blobCreatedViaSAS.txt");
            string blobContent = "This blob was created with a shared access signature granting write permissions to the container. ";
            MemoryStream msWrite = new MemoryStream(Encoding.UTF8.GetBytes(blobContent));
            msWrite.Position = 0;
            using (msWrite)
            {
                blob.UploadFromStream(msWrite);
            }
            Console.WriteLine("Write operation succeeded for SAS " + sas);
            Console.WriteLine();
        }
        catch (StorageException e)
        {
            Console.WriteLine("Write operation failed for SAS " + sas);
            Console.WriteLine("Additional error information: " + e.Message);
            Console.WriteLine();
        }

        try
        {
            //List operation: List the blobs in the container, including the one just added.
            foreach (ICloudBlob blobListing in container.ListBlobs())
            {
                blobUris.Add(blobListing.Uri);
            }
            Console.WriteLine("List operation succeeded for SAS " + sas);
            Console.WriteLine();
        }
        catch (StorageException e)
        {
            Console.WriteLine("List operation failed for SAS " + sas);
            Console.WriteLine("Additional error information: " + e.Message);
            Console.WriteLine();
        }

        try
        {
            CloudBlockBlob blob = container.GetBlockBlobReference(blobUris[0].ToString());

            MemoryStream msRead = new MemoryStream();
            msRead.Position = 0;
            using (msRead)
            {
                blob.DownloadToStream(msRead);
                Console.WriteLine(msRead.Length);
            }
            Console.WriteLine("Read operation succeeded for SAS " + sas);
            Console.WriteLine();
        }
        catch (StorageException e)
        {
            Console.WriteLine("Read operation failed for SAS " + sas);
            Console.WriteLine("Additional error information: " + e.Message);
            Console.WriteLine();
        }
        Console.WriteLine();

        try
        {
            //Delete operation: Delete a blob in the container.
            CloudBlockBlob blob = container.GetBlockBlobReference(blobUris[0].ToString());
            blob.Delete();
            Console.WriteLine("Delete operation succeeded for SAS " + sas);
            Console.WriteLine();
        }
        catch (StorageException e)
        {
            Console.WriteLine("Delete operation failed for SAS " + sas);
            Console.WriteLine("Additional error information: " + e.Message);
            Console.WriteLine();
        }
    }
1
sas is for the programming/database language SAS, not for this particular usage. I think azure-storage-blobs is the correct tag, but if I'm wrong, apologies, please apply the correct one. Thanks!Joe
Try setting the start time of the shared access policy to DateTime.UtcNow.AddMinutes(-5).Brendan Green
SAS is Shared Access Signature, a terminology that is being used in Azure Storage. How should I differentiate this SAS with the original one? About the start time, if you don't specify it meaning you want the policy to be effective right away which is what I want.Long Nguyen

1 Answers

7
votes

Actually, your latter operation erased what you did in the first operation. To avoid that, you ought to read the existing permissions of the container, add a new permission, then set the permissions back to container.

Following is the correct code sample:

static void CreateSharedAccessPolicy(CloudBlobClient blobClient, CloudBlobContainer container, string policyName)
{
    //Create a new stored access policy and define its constraints.
    SharedAccessBlobPolicy sharedPolicy = new SharedAccessBlobPolicy()
    {
        SharedAccessExpiryTime = DateTime.UtcNow.AddHours(10),
        Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.List
    };

    //Get the container's existing permissions.
    BlobContainerPermissions permissions = container.GetPermissions();

    //Add the new policy to the container's permissions.
    permissions.SharedAccessPolicies.Add(policyName, sharedPolicy);
    container.SetPermissions(permissions);
}

For the reason why you were confronting 404 error for reading blobs, please share you code of creating SAS by policy and how you use the created SAS to read blobs so that I can help trouble-shoot.

Following is a code sample for creating SAS and using it to read blobs: (you can copy+paste the URLs in stdout to browser directly to have a try)

        var permissions = container.GetPermissions();
        var policy = new SharedAccessBlobPolicy
        {
            Permissions = SharedAccessBlobPermissions.Read,
            SharedAccessExpiryTime = DateTime.UtcNow.AddYears(1),
        };

        string policyName = "read";
        permissions.SharedAccessPolicies.Add(policyName, policy);
        container.SetPermissions(permissions);
        string sas = container.GetSharedAccessSignature(null, policyName);
        var blobs = container.ListBlobs(null, true);
        Console.WriteLine("SAS = {0}", sas);
        Console.WriteLine("Blobs URLs with SAS:");

        foreach (var blob in blobs)
        {
            Console.WriteLine(blob.Uri.ToString() + sas);
        }