< Summary

Class:Azure.Storage.StorageSharedKeyPipelinePolicy
Assembly:Azure.Storage.Files.DataLake
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>
 278633        public StorageSharedKeyPipelinePolicy(StorageSharedKeyCredential credentials)
 278634            => _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        {
 1239042            base.OnSendingRequest(message);
 43
 44            // Add a x-ms-date header
 45            if (IncludeXMsDate)
 46            {
 1239047                var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
 1239048                message.Request.Headers.SetValue(Constants.HeaderNames.Date, date);
 49            }
 50
 1239051            var stringToSign = BuildStringToSign(message);
 1239052            var signature = StorageSharedKeyCredentialInternals.ComputeSasSignature(_credentials, stringToSign);
 53
 1239054            var key = new AuthenticationHeaderValue(Constants.HeaderNames.SharedKey, _credentials.AccountName + ":" + si
 1239055            message.Request.Headers.SetValue(Constants.HeaderNames.Authorization, key);
 1239056        }
 57
 58        private string BuildStringToSign(HttpMessage message)
 59        {
 60            // https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key
 61
 1239062            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentEncoding, out var contentEncoding);
 1239063            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentLanguage, out var contentLanguage);
 1239064            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentLength, out var contentLength);
 1239065            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentMD5, out var contentMD5);
 1239066            message.Request.Headers.TryGetValue(Constants.HeaderNames.ContentType, out var contentType);
 1239067            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfModifiedSince, out var ifModifiedSince);
 1239068            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfMatch, out var ifMatch);
 1239069            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfNoneMatch, out var ifNoneMatch);
 1239070            message.Request.Headers.TryGetValue(Constants.HeaderNames.IfUnmodifiedSince, out var ifUnmodifiedSince);
 1239071            message.Request.Headers.TryGetValue(Constants.HeaderNames.Range, out var range);
 72
 1239073            var stringToSign = string.Join("\n",
 1239074                message.Request.Method.ToString().ToUpperInvariant(),
 1239075                contentEncoding ?? "",
 1239076                contentLanguage ?? "",
 1239077                contentLength == "0" ? "" : contentLength ?? "",
 1239078                contentMD5 ?? "", // todo: fix base 64 VALUE
 1239079                contentType ?? "",
 1239080                "", // Empty date because x-ms-date is expected (as per web page above)
 1239081                ifModifiedSince ?? "",
 1239082                ifMatch ?? "",
 1239083                ifNoneMatch ?? "",
 1239084                ifUnmodifiedSince ?? "",
 1239085                range ?? "",
 1239086                BuildCanonicalizedHeaders(message),
 1239087                BuildCanonicalizedResource(message.Request.Uri.ToUri()));
 1239088            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).
 1239095            var sb = new StringBuilder();
 12737896            foreach (var headerName in
 1239097                message.Request.Headers
 8459098                .Select(h => h.Name.ToLowerInvariant())
 8459099                .Where(name => name.StartsWith(Constants.HeaderNames.XMsPrefix, StringComparison.OrdinalIgnoreCase))
 12390100#pragma warning disable CA1308 // Normalize strings to uppercase
 69884101                .OrderBy(name => name.Trim()))
 102#pragma warning restore CA1308 // Normalize strings to uppercase
 103            {
 57494104                if (sb.Length > 0)
 105                {
 45104106                    sb.Append('\n');
 107                }
 57494108                message.Request.Headers.TryGetValue(headerName, out var value);
 57494109                sb.Append(headerName).Append(':').Append(value);
 110            }
 12390111            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
 12390117            StringBuilder cr = new StringBuilder("/").Append(_credentials.AccountName);
 12390118            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
 12390123                cr.Append(resource.AbsolutePath);//EscapedPath()
 124            }
 125            else
 126            {
 127                // a slash is required to indicate the root path
 0128                cr.Append('/');
 129            }
 130
 12390131            System.Collections.Generic.IDictionary<string, string> parameters = resource.GetQueryParameters(); // Return
 12390132            if (parameters.Count > 0)
 133            {
 60832134                foreach (var name in parameters.Keys.OrderBy(key => key, StringComparer.Ordinal))
 135                {
 136#pragma warning disable CA1308 // Normalize strings to uppercase
 12700137                    cr.Append('\n').Append(name.ToLowerInvariant()).Append(':').Append(parameters[name]);
 138#pragma warning restore CA1308 // Normalize strings to uppercase
 139                }
 140            }
 12390141            return cr.ToString();
 142        }
 143    }
 144}