< Summary

Class:Microsoft.Azure.Batch.Protocol.BatchSharedKeyCredential
Assembly:Microsoft.Azure.Batch
File(s):C:\Git\azure-sdk-for-net\sdk\batch\Microsoft.Azure.Batch\src\Protocol\BatchSharedKeyCredential.cs
Covered lines:53
Uncovered lines:5
Coverable lines:58
Total lines:170
Line coverage:91.3% (53 of 58)
Covered branches:45
Total branches:64
Branch coverage:70.3% (45 of 64)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-100%100%
get_AccountName()-100%100%
get_KeyValue()-100%100%
.ctor(...)-100%100%
ProcessHttpRequestAsync(...)-90.2%70.31%

File(s)

C:\Git\azure-sdk-for-net\sdk\batch\Microsoft.Azure.Batch\src\Protocol\BatchSharedKeyCredential.cs

#LineLine coverage
 1//
 2// Copyright (c) Microsoft Corporation. All rights reserved.
 3// Licensed under the MIT License. See License.txt in the project root for license information.
 4//
 5
 6namespace Microsoft.Azure.Batch.Protocol
 7{
 8    using System;
 9    using System.Collections.Generic;
 10    using System.Collections.Specialized;
 11    using System.Globalization;
 12    using System.Linq;
 13    using System.Net;
 14    using System.Net.Http;
 15    using System.Net.Http.Headers;
 16    using System.Security.Cryptography;
 17    using System.Text;
 18    using System.Threading;
 19    using System.Threading.Tasks;
 20    using Microsoft.Azure.Batch.Utils;
 21    using Rest;
 22
 23    /// <summary>
 24    /// Shared key credentials for an Azure Batch account.
 25    /// </summary>
 26    public class BatchSharedKeyCredential : ServiceClientCredentials
 27    {
 28        private const string OCPDateHeaderString = "ocp-date";
 29
 130        private static readonly byte[] EmptyArray = new byte[0];
 31
 32        /// <summary>
 33        /// Gets the Batch account name.
 34        /// </summary>
 15835        public string AccountName { get; private set; }
 36
 37        /// <summary>
 38        /// Gets the account access key, as a Base64-encoded string.
 39        /// </summary>
 14940        public string KeyValue { get; private set; }
 41
 42        /// <summary>
 43        /// Initializes a new instance of the <see cref="BatchSharedKeyCredential"/> class.
 44        /// </summary>
 45        /// <param name="accountName">The name of the Batch account.</param>
 46        /// <param name="keyValue">The access key of the Batch account, as a Base64-encoded string.</param>
 14847        public BatchSharedKeyCredential(string accountName, string keyValue)
 48        {
 14849            this.AccountName = accountName;
 14850            this.KeyValue = keyValue;
 14851        }
 52
 53        /// <summary>
 54        /// Signs a HTTP request with the current credentials.
 55        /// </summary>
 56        /// <param name="httpRequest">The HTTP request</param>
 57        /// <param name="cancellationToken">A <see cref="System.Threading.CancellationToken"/> for the request.</param>
 58        /// <returns>A <see cref="Task"/> representing the asynchronous signing operation.</returns>
 59        public override Task ProcessHttpRequestAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken
 60        {
 961            if (httpRequest == null)
 62            {
 063                return Async.CompletedTask;
 64            }
 65
 66            //First set ocp-date always
 967            if (!httpRequest.Headers.Contains(OCPDateHeaderString))
 68            {
 969                httpRequest.Headers.TryAddWithoutValidation(OCPDateHeaderString, string.Format(CultureInfo.InvariantCult
 70            }
 71
 72            // Set Headers
 973            var signature = new StringBuilder();
 974            signature.Append(httpRequest.Method).Append('\n');
 975            signature.Append(httpRequest.Content != null && httpRequest.Content.Headers.Contains("Content-Encoding") ? h
 976            signature.Append(httpRequest.Content != null && httpRequest.Content.Headers.Contains("Content-Language") ? h
 77
 78            // Handle content length
 979            long? contentLength = httpRequest.Content?.Headers?.ContentLength;
 80
 981            if (contentLength == null)
 82            {
 83                // C# in .NET Framework adds a content-lenth = 0 reader for DELETE, PATH, OPTIONS, and POST, so we need 
 84                // Because of https://github.com/dotnet/corefx/issues/31172 netstandard/netcore has different behavior d
 85                // httoRequest.Content to an empty array to froce inclusion of content-length = 0 on all versions.
 986                if (httpRequest.Method == HttpMethod.Delete || httpRequest.Method == new HttpMethod("PATCH") || httpRequ
 87                {
 488                    httpRequest.Content = new ByteArrayContent(EmptyArray);
 489                    contentLength = 0;
 90                }
 91            }
 992            signature.Append(contentLength).Append('\n');
 93
 994            signature.Append(httpRequest.Content != null && httpRequest.Content.Headers.Contains("Content-MD5") ? httpRe
 995            signature.Append(httpRequest.Content != null && httpRequest.Content.Headers.Contains("Content-Type") ? httpR
 996            signature.Append(httpRequest.Headers.Contains("Date") ? httpRequest.Headers.GetValues("Date").FirstOrDefault
 997            signature.Append(httpRequest.Headers.Contains("If-Modified-Since") ? httpRequest.Headers.GetValues("If-Modif
 998            signature.Append(httpRequest.Headers.Contains("If-Match") ? httpRequest.Headers.GetValues("If-Match").FirstO
 999            signature.Append(httpRequest.Headers.Contains("If-None-Match") ? httpRequest.Headers.GetValues("If-None-Matc
 9100            signature.Append(httpRequest.Headers.Contains("If-Unmodified-Since") ? httpRequest.Headers.GetValues("If-Unm
 9101            signature.Append(httpRequest.Headers.Contains("Range") ? httpRequest.Headers.GetValues("Range").FirstOrDefau
 102
 9103            List<string> customHeaders = new List<string>();
 58104            foreach (KeyValuePair<string, IEnumerable<string>> header in httpRequest.Headers)
 105            {
 20106                if (header.Key.StartsWith("ocp-", StringComparison.OrdinalIgnoreCase))
 107                {
 9108                    customHeaders.Add(header.Key.ToLowerInvariant());
 109                }
 110            }
 111
 9112            if (httpRequest.Content != null)
 113            {
 0114                foreach (KeyValuePair<string, IEnumerable<string>> contentHeader in httpRequest.Content.Headers)
 115                {
 0116                    if (contentHeader.Key.StartsWith("ocp-", StringComparison.OrdinalIgnoreCase))
 117                    {
 0118                        customHeaders.Add(contentHeader.Key.ToLowerInvariant());
 119                    }
 120                }
 121            }
 9122            customHeaders.Sort(StringComparer.Ordinal);
 123
 36124            foreach (string canonicalHeader in customHeaders)
 125            {
 9126                string value = httpRequest.Headers.GetValues(canonicalHeader).FirstOrDefault();
 9127                value = value.Replace('\n', ' ').Replace('\r', ' ').TrimStart();
 9128                signature.Append(canonicalHeader).Append(':').Append(value).Append('\n');
 129            }
 130
 9131            signature.Append('/').Append(AccountName).Append('/').Append(httpRequest.RequestUri.AbsolutePath.TrimStart('
 9132            if (!string.IsNullOrEmpty(httpRequest.RequestUri.Query))
 133            {
 134#if FullNetFx
 135                NameValueCollection queryVariables = System.Web.HttpUtility.ParseQueryString(httpRequest.RequestUri.Quer
 136                List<string> queryVariableKeys = new List<string>(queryVariables.AllKeys);
 137#else
 2138                Dictionary<string, Extensions.Primitives.StringValues> queryVariables = Microsoft.AspNetCore.WebUtilitie
 2139                List<string> queryVariableKeys = new List<string>(queryVariables.Keys);
 140#endif
 141
 2142                queryVariableKeys.Sort(StringComparer.OrdinalIgnoreCase);
 143
 8144                foreach (string queryKey in queryVariableKeys)
 145                {
 146                    string lowercaseQueryKey;
 2147                    if (queryKey != null)
 148                    {
 2149                        lowercaseQueryKey = queryKey.ToLowerInvariant();
 150                    }
 151                    else
 152                    {
 0153                        lowercaseQueryKey = null;
 154                    }
 2155                    signature.Append('\n').Append(lowercaseQueryKey).Append(':').Append(queryVariables[queryKey]);
 156                }
 157            }
 158
 9159            string signedSignature = null;
 160
 9161            using (HashAlgorithm hashAlgorithm = new HMACSHA256(Convert.FromBase64String(this.KeyValue)))
 162            {
 9163                signedSignature = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(signature.ToSt
 9164            }
 9165            httpRequest.Headers.Authorization = new AuthenticationHeaderValue("SharedKey", this.AccountName + ":" + sign
 166
 9167            return Async.CompletedTask;
 168        }
 169    }
 170}