< Summary

Class:Azure.Storage.StorageSharedKeyPipelinePolicy
Assembly:Azure.Storage.Files.Shares
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:31
Total branches:32
Branch coverage:96.8% (31 of 32)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor(...)-100%100%
OnSendingRequest(...)-100%100%
BuildStringToSign(...)-100%100%
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>
 53933        public StorageSharedKeyPipelinePolicy(StorageSharedKeyCredential credentials)
 53934            => _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        {
 329242            base.OnSendingRequest(message);
 43
 44            // Add a x-ms-date header
 45            if (IncludeXMsDate)
 46            {
 329247                var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
 329248                message.Request.Headers.SetValue(Constants.HeaderNames.Date, date);
 49            }
 50
 329251            var stringToSign = BuildStringToSign(message);
 329252            var signature = StorageSharedKeyCredentialInternals.ComputeSasSignature(_credentials, stringToSign);
 53
 329254            var key = new AuthenticationHeaderValue(Constants.HeaderNames.SharedKey, _credentials.AccountName + ":" + si
 329255            message.Request.Headers.SetValue(Constants.HeaderNames.Authorization, key);
 329256        }
 57
 58        private string BuildStringToSign(HttpMessage message)
 59        {
 60            // https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key
 61
 329262            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentEncoding, out var contentEncoding);
 329263            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentLanguage, out var contentLanguage);
 329264            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentLength, out var contentLength);
 329265            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentMD5, out var contentMD5);
 329266            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentType, out var contentType);
 329267            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfModifiedSince, out var ifModifiedSince);
 329268            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfMatch, out var ifMatch);
 329269            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfNoneMatch, out var ifNoneMatch);
 329270            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfUnmodifiedSince, out var ifUnmodifiedSince);
 329271            message.Request.Headers.TryGetValue(Constants.HeaderNames.Range, out var range);
 72
 329273            var stringToSign = string.Join("\n",
 329274                message.Request.Method.ToString().ToUpperInvariant(),
 329275                contentEncoding ?? "",
 329276                contentLanguage ?? "",
 329277                contentLength == "0" ? "" : contentLength ?? "",
 329278                contentMD5 ?? "", // todo: fix base 64 VALUE
 329279                contentType ?? "",
 329280                "", // Empty date because x-ms-date is expected (as per web page above)
 329281                ifModifiedSince ?? "",
 329282                ifMatch ?? "",
 329283                ifNoneMatch ?? "",
 329284                ifUnmodifiedSince ?? "",
 329285                range ?? "",
 329286                BuildCanonicalizedHeaders(message),
 329287                BuildCanonicalizedResource(message.Request.Uri.ToUri()));
 329288            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).
 329295            var sb = new StringBuilder();
 4244096            foreach (var headerName in
 329297                message.Request.Headers
 2752898                .Select(h => h.Name.ToLowerInvariant())
 2752899                .Where(name => name.StartsWith(Constants.HeaderNames.XMsPrefix, StringComparison.OrdinalIgnoreCase))
 3292100#pragma warning disable CA1308 // Normalize strings to uppercase
 22866101                .OrderBy(name => name.Trim()))
 102#pragma warning restore CA1308 // Normalize strings to uppercase
 103            {
 19574104                if (sb.Length > 0)
 105                {
 16282106                    sb.Append('\n');
 107                }
 19574108                message.Request.Headers.TryGetValue(headerName, out var value);
 19574109                sb.Append(headerName).Append(':').Append(value);
 110            }
 3292111            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
 3292117            StringBuilder cr = new StringBuilder("/").Append(_credentials.AccountName);
 3292118            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
 3292123                cr.Append(resource.AbsolutePath);//EscapedPath()
 124            }
 125            else
 126            {
 127                // a slash is required to indicate the root path
 0128                cr.Append('/');
 129            }
 130
 3292131            System.Collections.Generic.IDictionary<string, string> parameters = resource.GetQueryParameters(); // Return
 3292132            if (parameters.Count > 0)
 133            {
 14929134                foreach (var name in parameters.Keys.OrderBy(key => key, StringComparer.Ordinal))
 135                {
 136#pragma warning disable CA1308 // Normalize strings to uppercase
 3055137                    cr.Append('\n').Append(name.ToLowerInvariant()).Append(':').Append(parameters[name]);
 138#pragma warning restore CA1308 // Normalize strings to uppercase
 139                }
 140            }
 3292141            return cr.ToString();
 142        }
 143    }
 144}