< Summary

Class:Azure.Data.Tables.TableSharedKeyPipelinePolicy
Assembly:Azure.Data.Tables
File(s):C:\Git\azure-sdk-for-net\sdk\tables\Azure.Data.Tables\src\TableSharedKeyPipelinePolicy.cs
Covered lines:36
Uncovered lines:5
Coverable lines:41
Total lines:139
Line coverage:87.8% (36 of 41)
Covered branches:15
Total branches:18
Branch coverage:83.3% (15 of 18)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-0%100%
.ctor()-0%100%
GetSas(...)-100%100%
.ctor(...)-100%100%
OnSendingRequest(...)-100%100%
BuildStringToSign(...)-100%100%
BuildCanonicalizedResource(...)-90%87.5%
GetQueryParameters(...)-91.67%80%

File(s)

C:\Git\azure-sdk-for-net\sdk\tables\Azure.Data.Tables\src\TableSharedKeyPipelinePolicy.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Globalization;
 7using System.Linq;
 8using System.Net;
 9using System.Net.Http.Headers;
 10using System.Text;
 11using Azure.Core;
 12using Azure.Core.Pipeline;
 13
 14namespace Azure.Data.Tables
 15{
 16    /// <summary>
 17    /// HttpPipelinePolicy to sign requests using an Azure Storage shared key.
 18    /// </summary>
 19    internal sealed class TableSharedKeyPipelinePolicy : HttpPipelineSynchronousPolicy
 20    {
 21        private class InternalStorageCredential : TableSharedKeyCredential
 22        {
 023            public static InternalStorageCredential Instance = new InternalStorageCredential();
 024            public InternalStorageCredential() : base(string.Empty, string.Empty)
 25            {
 026            }
 27
 28            public static string GetSas(TableSharedKeyCredential credential, string message)
 29            {
 400830                return ComputeSasSignature(credential, message);
 31            }
 32        }
 33        /// <summary>
 34        /// Shared key credentials used to sign requests
 35        /// </summary>
 36        private readonly TableSharedKeyCredential _credentials;
 37
 38        /// <summary>
 39        /// Create a new SharedKeyPipelinePolicy
 40        /// </summary>
 41        /// <param name="credentials">SharedKeyCredentials to authenticate requests.</param>
 41642        public TableSharedKeyPipelinePolicy(TableSharedKeyCredential credentials)
 41643            => _credentials = credentials;
 44
 45        /// <summary>
 46        /// Sign the request using the shared key credentials.
 47        /// </summary>
 48        /// <param name="message">The message with the request to sign.</param>
 49        public override void OnSendingRequest(HttpMessage message)
 50        {
 400851            base.OnSendingRequest(message);
 52
 400853            var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
 400854            message.Request.Headers.SetValue(TableConstants.HeaderNames.Date, date);
 55
 400856            var stringToSign = BuildStringToSign(message);
 400857            var signature = InternalStorageCredential.GetSas(_credentials, stringToSign);
 58
 400859            var key = new AuthenticationHeaderValue(TableConstants.HeaderNames.SharedKey, _credentials.AccountName + ":"
 400860            message.Request.Headers.SetValue(TableConstants.HeaderNames.Authorization, key);
 400861        }
 62
 63        private string BuildStringToSign(HttpMessage message)
 64        {
 65            // https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key
 66
 400867            message.Request.Headers.TryGetValue(TableConstants.HeaderNames.Date, out var date);
 68
 400869            var stringToSign = string.Join("\n",
 400870                date,
 400871                BuildCanonicalizedResource(message.Request.Uri.ToUri()));
 400872            return stringToSign;
 73        }
 74
 75        private string BuildCanonicalizedResource(Uri resource)
 76        {
 77            // https://docs.microsoft.com/en-us/rest/api/storageservices/authentication-for-the-azure-storage-services
 400878            StringBuilder cr = new StringBuilder("/").Append(_credentials.AccountName);
 400879            if (resource.AbsolutePath.Length > 0)
 80            {
 81                // Any portion of the CanonicalizedResource string that is derived from
 82                // the resource's URI should be encoded exactly as it is in the URI.
 83                // -- https://msdn.microsoft.com/en-gb/library/azure/dd179428.aspx
 400884                cr.Append(resource.AbsolutePath);//EscapedPath()
 85            }
 86            else
 87            {
 88                // a slash is required to indicate the root path
 089                cr.Append('/');
 90            }
 91
 400892            System.Collections.Generic.IDictionary<string, string> parameters = GetQueryParameters(resource); // Returns
 400893            if (parameters.Count > 0)
 94            {
 1974095                foreach (var name in parameters.Keys.OrderBy(key => key, StringComparer.Ordinal))
 96                {
 97                    // If the request URI addresses a component of the resource, append the appropriate query string.
 98                    // The query string should include the question mark and the comp parameter (for example, ?comp=meta
 99                    // No other parameters should be included on the query string.
 100                    // https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key#shared-key-li
 4324101                    if (name == "comp")
 102                    {
 103#pragma warning disable CA1308 // Normalize strings to uppercase
 24104                        cr.Append('?').Append(name.ToLowerInvariant()).Append('=').Append(parameters[name]);
 105#pragma warning restore CA1308 // Normalize strings to uppercase
 106                    }
 107                }
 108            }
 4008109            return cr.ToString();
 110        }
 111        public static IDictionary<string, string> GetQueryParameters(Uri uri)
 112        {
 4008113            var parameters = new Dictionary<string, string>();
 4008114            var query = uri.Query ?? "";
 4008115            if (!string.IsNullOrEmpty(query))
 116            {
 3384117                if (query.StartsWith("?", true, CultureInfo.InvariantCulture))
 118                {
 3384119                    query = query.Substring(1);
 120                }
 15416121                foreach (var param in query.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries))
 122                {
 4324123                    var parts = param.Split(new[] { '=' }, 2);
 4324124                    var name = WebUtility.UrlDecode(parts[0]);
 4324125                    if (parts.Length == 1)
 126                    {
 0127                        parameters.Add(name, default);
 128                    }
 129                    else
 130                    {
 4324131                        parameters.Add(name, WebUtility.UrlDecode(parts[1]));
 132                    }
 133                }
 134            }
 4008135            return parameters;
 136        }
 137
 138    }
 139}