Dan Young
Dan Young

Reputation: 137

Links to Azure Storage Resource Only Work First Time

We are using SAS keys to access files from our Azure server. The links are retrieved using ASP.NET controllers and displayed using HTML elements.

The first time we request the links, the images show up perfectly. The second time the images are requested (e.g. page refresh) they do not show up - the console states 403 file not found for the images. If we copy the link from the HTML element into a new browser tab, then the image shows up.

Does anyone have experience with this problem?

Thanks.

Controller

    // GET: /<controller>/edit
    public async Task<IActionResult> Edit(int id)
    {

        Checklist checklist = await AM.Checklist.FindAsync(id);

        if (checklist == null)
        {
            return StatusCode(404);
        }

        ChecklistViewModel cVM = new ChecklistViewModel(checklist);

        cVM.productPartNum = (await AM.Product.FindAsync(checklist.productNum)).partId;
        cVM.checklistTypesDropdown = await cVM.GetChecklistOptions(AM);
        List<Field> fields = await AM.Field.Where(x => x.checklistId == checklist.id).OrderBy(x=>x.section).ThenBy(x=>x.order).ToListAsync();




        //connect to the file storate account
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));

        // Create a CloudFileClient object for credentialed access to File storage.
        CloudFileClient fileClient = storageAccount.CreateCloudFileClient();

        // Get a reference to the file share
        CloudFileShare share = fileClient.GetShareReference("newfileshare");


        string policyName = "sampleSharePolicy" + DateTime.UtcNow.Ticks;
        CloudFileDirectory rootDir = null;
        CloudFileDirectory fileDir = null;

        // Ensure that the share exists.
        if (share.Exists())
        {


            // Create a new shared access policy and define its constraints.
            SharedAccessFilePolicy sharedPolicy = new SharedAccessFilePolicy()
            {
                SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5),
                SharedAccessExpiryTime = DateTime.UtcNow.AddHours(8),
                Permissions = SharedAccessFilePermissions.Read | SharedAccessFilePermissions.Write
            };

            // Get existing permissions for the share.
            FileSharePermissions permissions = await share.GetPermissionsAsync();

            //Check length of SharedAccessPolicies
            if (permissions.SharedAccessPolicies.Count >= 5)
            {
                permissions.SharedAccessPolicies.Remove(permissions.SharedAccessPolicies.Keys.First());
            }

            // Add the shared access policy to the share's policies. Note that each policy must have a unique name.
            permissions.SharedAccessPolicies.Add(policyName, sharedPolicy);
            await share.SetPermissionsAsync(permissions);

            // Generate a SAS for a file in the share and associate this access policy with it.
            rootDir = share.GetRootDirectoryReference();
        }



        cVM.changeHappened = new List<bool>();

        cVM.Fields = new List<FieldViewModel>();
        int i = 0;
        foreach(Field f in fields)
        {
            FieldViewModel newFVM = new FieldViewModel(f);

            cVM.changeHappened.Add(false);

            if (f.attachmentReq && f.attachmentData != null)
            {

                //changeHappened will keep track of what images have been changed in order to avoid resubmitting same images
                cVM.changeHappened.Add(false);



                if (f.attachmentType.Contains("image"))
                {
                    fileDir = rootDir.GetDirectoryReference("Images");
                }
                else
                {
                    fileDir = rootDir.GetDirectoryReference("files");
                }

                CloudFile file = fileDir.GetFileReference(f.attachmentData);
                string sasToken = file.GetSharedAccessSignature(null, policyName);
                string tick = $"&{ DateTimeOffset.UtcNow.Ticks}";
                Uri fileSasUri = new Uri(file.StorageUri.PrimaryUri.ToString() + sasToken + tick);

                newFVM.link = fileSasUri.AbsoluteUri;

                //// Create a new CloudFile object from the SAS, and write some text to the file.
                //CloudFile fileSas = new CloudFile(fileSasUri);
                //fileSas.UploadText("This write operation is authenticated via SAS.");
                //Console.WriteLine(fileSas.DownloadText());
            }



            if (newFVM.textReq)
            {
                newFVM.fieldDropdownOptions = await newFVM.getDropdownOptions(AM);
            }

            cVM.Fields.Add(newFVM);
        }

        return View(cVM);
    }

Div that shows the images

                                        <div class="col-sm-5">


                                            @if (Model.Fields[i].attachmentData != null)
                                            {
                                                @if (Model.Fields[i].attachmentType.Contains("image/"))
                                                {

                                                    <img src="@Model.Fields[i].link" id="image_@i" height="50" width="50" align="middle"/>
                                                    <a hidden="hidden" id="link_@i"></a>
                                                }
                                                else
                                                {
                                                    <img hidden="hidden" id="image_@i" height="50" width="50" align="middle"/>
                                                    <a id="link_@i">@Model.Fields[i].attachmentData</a>
                                                }

                                            }
                                            else
                                            {
                                                <img hidden="hidden" id="image_@i" height="50" width="50" align="middle"/>
                                                <a hidden="hidden" id="link_@i"></a>
                                            }

                                        </div>

Upvotes: 0

Views: 112

Answers (1)

Brando Zhang
Brando Zhang

Reputation: 27962

According to your description and codes, I have reproduced the issue.

I guess the reason why you face the 403 error is your MVC code send request to the file storage before the file storage have already set and enable the permission's SharedAccessPolicies .

So it will return 403 forbidden error says your SAS token is useless.

Besides, I don't suggest you add the SharedAccessPolicies each time, because if 6 persons access your website at same time, the first person's SharedAccessPolicies may be deleted. So the first person couldn't get the file image.

I suggest you could just use one SharedAccessPolicies.

You could reset the SharedAccessFilePolicy's SharedAccessExpiryTime each time.

More details, you could refer to below code sample:

    //connect to the file storate account
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(" ");

    // Create a CloudFileClient object for credentialed access to File storage.
    CloudFileClient fileClient = storageAccount.CreateCloudFileClient();

    // Get a reference to the file share
    CloudFileShare share = fileClient.GetShareReference("brandofirstsharetest");


    string policyName = "sampleSharePolicy" + DateTime.UtcNow.Ticks;
    CloudFileDirectory rootDir = null;
    CloudFileDirectory fileDir = null;

    // Ensure that the share exists.
    if (share.Exists())
    {

        // Get existing permissions for the share.
        FileSharePermissions permissions = await share.GetPermissionsAsync();
        //if the SharedAccessPolicies is exists just get the SharedAccessPolicies
        if (permissions.SharedAccessPolicies.Count > 0)
        {
                policyName = permissions.SharedAccessPolicies.First().Key;
                SharedAccessFilePolicy sharedPolicy = permissions.SharedAccessPolicies.First().Value;
                sharedPolicy.SharedAccessExpiryTime = DateTime.UtcNow.AddHours(8);
                await share.SetPermissionsAsync(permissions);                  
        }
        else
        {
            // Create a new shared access policy and define its constraints.
            SharedAccessFilePolicy sharedPolicy = new SharedAccessFilePolicy()
            {
                SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-15),
                SharedAccessExpiryTime = DateTime.UtcNow.AddHours(8),
                Permissions = SharedAccessFilePermissions.Read | SharedAccessFilePermissions.Write
            };
            permissions.SharedAccessPolicies.Add(policyName, sharedPolicy);
            await share.SetPermissionsAsync(permissions);
        }


        // Add the shared access policy to the share's policies. Note that each policy must have a unique name.


        // Generate a SAS for a file in the share and associate this access policy with it.
        rootDir = share.GetRootDirectoryReference();
    }

    CloudFile file = rootDir.GetFileReference("Penjs.png");
       string sasToken = file.GetSharedAccessSignature(null, policyName);
       string tick = $"&{ DateTimeOffset.UtcNow.Ticks}";
       Uri fileSasUri = new Uri(file.StorageUri.PrimaryUri.ToString() + sasToken + tick);

Upvotes: 1

Related Questions