< Summary

Class:Azure.Storage.StorageSharedKeyPipelinePolicy
Assembly:Azure.Storage.Queues
File(s):C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Common\src\Shared\StorageSharedKeyPipelinePolicy.cs
Covered lines:56
Uncovered lines:1
Coverable lines:57
Total lines:144
Line coverage:98.2% (56 of 57)
Covered branches:30
Total branches:32
Branch coverage:93.7% (30 of 32)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor(...)-100%100%
OnSendingRequest(...)-100%100%
BuildStringToSign(...)-100%95.45%
BuildCanonicalizedHeaders(...)-100%100%
BuildCanonicalizedResource(...)-88.89%83.33%

File(s)

C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Common\src\Shared\StorageSharedKeyPipelinePolicy.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Globalization;
 6using System.Linq;
 7using System.Net.Http.Headers;
 8using System.Text;
 9using Azure.Core;
 10using Azure.Core.Pipeline;
 11
 12namespace Azure.Storage
 13{
 14    /// <summary>
 15    /// HttpPipelinePolicy to sign requests using an Azure Storage shared key.
 16    /// </summary>
 17    internal sealed class StorageSharedKeyPipelinePolicy : HttpPipelineSynchronousPolicy
 18    {
 19        /// <summary>
 20        /// Whether to always add the x-ms-date header.
 21        /// </summary>
 22        private const bool IncludeXMsDate = true;
 23
 24        /// <summary>
 25        /// Shared key credentials used to sign requests
 26        /// </summary>
 27        private readonly StorageSharedKeyCredential _credentials;
 28
 29        /// <summary>
 30        /// Create a new SharedKeyPipelinePolicy
 31        /// </summary>
 32        /// <param name="credentials">SharedKeyCredentials to authenticate requests.</param>
 27233        public StorageSharedKeyPipelinePolicy(StorageSharedKeyCredential credentials)
 27234            => _credentials = credentials;
 35
 36        /// <summary>
 37        /// Sign the request using the shared key credentials.
 38        /// </summary>
 39        /// <param name="message">The message with the request to sign.</param>
 40        public override void OnSendingRequest(HttpMessage message)
 41        {
 72642            base.OnSendingRequest(message);
 43
 44            // Add a x-ms-date header
 45            if (IncludeXMsDate)
 46            {
 72647                var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
 72648                message.Request.Headers.SetValue(Constants.HeaderNames.Date, date);
 49            }
 50
 72651            var stringToSign = BuildStringToSign(message);
 72652            var signature = StorageSharedKeyCredentialInternals.ComputeSasSignature(_credentials, stringToSign);
 53
 72654            var key = new AuthenticationHeaderValue(Constants.HeaderNames.SharedKey, _credentials.AccountName + ":" + si
 72655            message.Request.Headers.SetValue(Constants.HeaderNames.Authorization, key);
 72656        }
 57
 58        private string BuildStringToSign(HttpMessage message)
 59        {
 60            // https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key
 61
 72662            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentEncoding, out var contentEncoding);
 72663            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentLanguage, out var contentLanguage);
 72664            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentLength, out var contentLength);
 72665            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentMD5, out var contentMD5);
 72666            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentType, out var contentType);
 72667            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfModifiedSince, out var ifModifiedSince);
 72668            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfMatch, out var ifMatch);
 72669            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfNoneMatch, out var ifNoneMatch);
 72670            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfUnmodifiedSince, out var ifUnmodifiedSince);
 72671            message.Request.Headers.TryGetValue(Constants.HeaderNames.Range, out var range);
 72
 72673            var stringToSign = string.Join("\n",
 72674                message.Request.Method.ToString().ToUpperInvariant(),
 72675                contentEncoding ?? "",
 72676                contentLanguage ?? "",
 72677                contentLength == "0" ? "" : contentLength ?? "",
 72678                contentMD5 ?? "", // todo: fix base 64 VALUE
 72679                contentType ?? "",
 72680                "", // Empty date because x-ms-date is expected (as per web page above)
 72681                ifModifiedSince ?? "",
 72682                ifMatch ?? "",
 72683                ifNoneMatch ?? "",
 72684                ifUnmodifiedSince ?? "",
 72685                range ?? "",
 72686                BuildCanonicalizedHeaders(message),
 72687                BuildCanonicalizedResource(message.Request.Uri.ToUri()));
 72688            return stringToSign;
 89        }
 90
 91        private static string BuildCanonicalizedHeaders(HttpMessage message)
 92        {
 93            // Grab all the "x-ms-*" headers, trim whitespace, lowercase, sort,
 94            // and combine them with their values (separated by a colon).
 72695            var sb = new StringBuilder();
 670296            foreach (var headerName in
 72697                message.Request.Headers
 475298                .Select(h => h.Name.ToLowerInvariant())
 475299                .Where(name => name.StartsWith(Constants.HeaderNames.XMsPrefix, StringComparison.OrdinalIgnoreCase))
 726100#pragma warning disable CA1308 // Normalize strings to uppercase
 3714101                .OrderBy(name => name.Trim()))
 102#pragma warning restore CA1308 // Normalize strings to uppercase
 103            {
 2988104                if (sb.Length > 0)
 105                {
 2262106                    sb.Append('\n');
 107                }
 2988108                message.Request.Headers.TryGetValue(headerName, out var value);
 2988109                sb.Append(headerName).Append(':').Append(value);
 110            }
 726111            return sb.ToString();
 112        }
 113
 114        private string BuildCanonicalizedResource(Uri resource)
 115        {
 116            // https://docs.microsoft.com/en-us/rest/api/storageservices/authentication-for-the-azure-storage-services
 726117            StringBuilder cr = new StringBuilder("/").Append(_credentials.AccountName);
 726118            if (resource.AbsolutePath.Length > 0)
 119            {
 120                // Any portion of the CanonicalizedResource string that is derived from
 121                // the resource's URI should be encoded exactly as it is in the URI.
 122                // -- https://msdn.microsoft.com/en-gb/library/azure/dd179428.aspx
 726123                cr.Append(resource.AbsolutePath);//EscapedPath()
 124            }
 125            else
 126            {
 127                // a slash is required to indicate the root path
 0128                cr.Append('/');
 129            }
 130
 726131            System.Collections.Generic.IDictionary<string, string> parameters = resource.GetQueryParameters(); // Return
 726132            if (parameters.Count > 0)
 133            {
 1234134                foreach (var name in parameters.Keys.OrderBy(key => key, StringComparer.Ordinal))
 135                {
 136#pragma warning disable CA1308 // Normalize strings to uppercase
 266137                    cr.Append('\n').Append(name.ToLowerInvariant()).Append(':').Append(parameters[name]);
 138#pragma warning restore CA1308 // Normalize strings to uppercase
 139                }
 140            }
 726141            return cr.ToString();
 142        }
 143    }
 144}