< Summary

Class:Microsoft.Azure.ServiceBus.Primitives.SharedAccessSignatureTokenProvider
Assembly:Microsoft.Azure.ServiceBus
File(s):C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\Primitives\SharedAccessSignatureTokenProvider.cs
Covered lines:16
Uncovered lines:44
Coverable lines:60
Total lines:174
Line coverage:26.6% (16 of 60)
Covered branches:5
Total branches:12
Branch coverage:41.6% (5 of 12)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-100%100%
.ctor(...)-0%100%
.ctor(...)-100%100%
.ctor(...)-0%100%
.ctor(...)-60%50%
GetTokenAsync(...)-0%100%
BuildSignature(...)-0%0%
NormalizeAppliesTo(...)-0%100%
BuildSignature(...)-0%100%
BuildExpiresOn(...)-0%100%
Sign(...)-0%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\Primitives\SharedAccessSignatureTokenProvider.cs

#LineLine coverage
 1// Copyright (c) Microsoft. All rights reserved.
 2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 3
 4namespace Microsoft.Azure.ServiceBus.Primitives
 5{
 6    using System;
 7    using System.Collections.Generic;
 8    using System.Diagnostics.CodeAnalysis;
 9    using System.Globalization;
 10    using System.Net;
 11    using System.Security.Cryptography;
 12    using System.Text;
 13    using System.Threading.Tasks;
 14
 15    /// <summary>
 16    /// The SharedAccessSignatureTokenProvider generates tokens using a shared access key or existing signature.
 17    /// </summary>
 18    public class SharedAccessSignatureTokenProvider : TokenProvider
 19    {
 20        const TokenScope DefaultTokenScope = TokenScope.Entity;
 21
 222        internal static readonly TimeSpan DefaultTokenTTL = TimeSpan.FromMinutes(60);
 23
 24        readonly byte[] encodedSharedAccessKey;
 25        readonly string keyName;
 26        readonly TimeSpan tokenTimeToLive;
 27        readonly TokenScope tokenScope;
 28        readonly string sharedAccessSignature;
 229        internal static readonly Func<string, byte[]> MessagingTokenProviderKeyEncoder = Encoding.UTF8.GetBytes;
 30
 031        internal SharedAccessSignatureTokenProvider(string sharedAccessSignature)
 32        {
 033            SharedAccessSignatureToken.Validate(sharedAccessSignature);
 034            this.sharedAccessSignature = sharedAccessSignature;
 035        }
 36
 37        internal SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TokenScope tokenScope = Toke
 1438            : this(keyName, sharedAccessKey, MessagingTokenProviderKeyEncoder, DefaultTokenTTL, tokenScope)
 39        {
 1440        }
 41
 42        internal SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TimeSpan tokenTimeToLive, To
 043            : this(keyName, sharedAccessKey, MessagingTokenProviderKeyEncoder, tokenTimeToLive, tokenScope)
 44        {
 045        }
 46
 47        /// <summary></summary>
 48        /// <param name="keyName"></param>
 49        /// <param name="sharedAccessKey"></param>
 50        /// <param name="customKeyEncoder"></param>
 51        /// <param name="tokenTimeToLive"></param>
 52        /// <param name="tokenScope"></param>
 1453        protected SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, Func<string, byte[]> custom
 54        {
 1455            if (string.IsNullOrEmpty(keyName))
 56            {
 057                throw new ArgumentNullException(nameof(keyName));
 58            }
 59
 1460            if (keyName.Length > SharedAccessSignatureToken.MaxKeyNameLength)
 61            {
 062                throw new ArgumentOutOfRangeException(
 063                    nameof(keyName),
 064                    Resources.ArgumentStringTooBig.FormatForUser(nameof(keyName), SharedAccessSignatureToken.MaxKeyNameL
 65            }
 66
 1467            if (string.IsNullOrEmpty(sharedAccessKey))
 68            {
 069                throw new ArgumentNullException(nameof(sharedAccessKey));
 70            }
 71
 1472            if (sharedAccessKey.Length > SharedAccessSignatureToken.MaxKeyLength)
 73            {
 074                throw new ArgumentOutOfRangeException(
 075                    nameof(sharedAccessKey),
 076                    Resources.ArgumentStringTooBig.FormatForUser(nameof(sharedAccessKey), SharedAccessSignatureToken.Max
 77            }
 78
 1479            this.keyName = keyName;
 1480            this.tokenTimeToLive = tokenTimeToLive;
 1481            this.encodedSharedAccessKey = customKeyEncoder != null ?
 1482                customKeyEncoder(sharedAccessKey) :
 1483                MessagingTokenProviderKeyEncoder(sharedAccessKey);
 1484            this.tokenScope = tokenScope;
 1485        }
 86
 87        /// <summary>
 88        /// Gets a <see cref="SecurityToken"/> for the given audience and duration.
 89        /// </summary>
 90        /// <param name="appliesTo">The URI which the access token applies to. If <see cref="SharedAccessSignatureTokenP
 91        /// is initiated with SASKeyName and SASKey, the token will be generated for this uri. If initiated with SASToke
 92        /// the value is ignored.</param>
 93        /// <param name="timeout">The timeout value for how long it takes to get the security token (not the token time 
 94        /// For SAS token, no asynchronous operation takes place and hence this timeout is ignored.</param>
 95        /// <remarks>This parameter <paramref name="timeout"/> is here for compatibility, but is not currently used.</re
 96        /// <returns><see cref="SecurityToken"/></returns>
 97        public override Task<SecurityToken> GetTokenAsync(string appliesTo, TimeSpan timeout)
 98        {
 099            TimeoutHelper.ThrowIfNegativeArgument(timeout);
 0100            appliesTo = NormalizeAppliesTo(appliesTo);
 0101            string tokenString = this.BuildSignature(appliesTo);
 0102            var securityToken = new SharedAccessSignatureToken(tokenString);
 0103            return Task.FromResult<SecurityToken>(securityToken);
 104        }
 105
 106        /// <summary></summary>
 107        /// <param name="targetUri"></param>
 108        /// <returns></returns>
 109        protected virtual string BuildSignature(string targetUri)
 110        {
 0111            return string.IsNullOrWhiteSpace(this.sharedAccessSignature)
 0112                ? SharedAccessSignatureBuilder.BuildSignature(
 0113                    this.keyName,
 0114                    this.encodedSharedAccessKey,
 0115                    targetUri,
 0116                    this.tokenTimeToLive)
 0117                : this.sharedAccessSignature;
 118        }
 119
 120        string NormalizeAppliesTo(string appliesTo)
 121        {
 0122            return ServiceBusUriHelper.NormalizeUri(appliesTo, "https", true, stripPath: this.tokenScope == TokenScope.N
 123        }
 124
 125        static class SharedAccessSignatureBuilder
 126        {
 127            [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Uris are 
 128            public static string BuildSignature(
 129                string keyName,
 130                byte[] encodedSharedAccessKey,
 131                string targetUri,
 132                TimeSpan timeToLive)
 133            {
 134                // Note that target URI is not normalized because in IoT scenario it
 135                // is case sensitive.
 0136                string expiresOn = BuildExpiresOn(timeToLive);
 0137                string audienceUri = WebUtility.UrlEncode(targetUri);
 0138                List<string> fields = new List<string> { audienceUri, expiresOn };
 139
 140                // Example string to be signed:
 141                // http://mynamespace.servicebus.windows.net/a/b/c?myvalue1=a
 142                // <Value for ExpiresOn>
 0143                string signature = Sign(string.Join("\n", fields), encodedSharedAccessKey);
 144
 145                // Example returned string:
 146                // SharedAccessKeySignature
 147                // sr=ENCODED(http://mynamespace.servicebus.windows.net/a/b/c?myvalue1=a)&sig=<Signature>&se=<ExpiresOnV
 148
 0149                return string.Format(CultureInfo.InvariantCulture, "{0} {1}={2}&{3}={4}&{5}={6}&{7}={8}",
 0150                    SharedAccessSignatureToken.SharedAccessSignature,
 0151                    SharedAccessSignatureToken.SignedResource, audienceUri,
 0152                    SharedAccessSignatureToken.Signature, WebUtility.UrlEncode(signature),
 0153                    SharedAccessSignatureToken.SignedExpiry, WebUtility.UrlEncode(expiresOn),
 0154                    SharedAccessSignatureToken.SignedKeyName, WebUtility.UrlEncode(keyName));
 155            }
 156
 157            static string BuildExpiresOn(TimeSpan timeToLive)
 158            {
 0159                DateTime expiresOn = DateTime.UtcNow.Add(timeToLive);
 0160                TimeSpan secondsFromBaseTime = expiresOn.Subtract(Constants.EpochTime);
 0161                long seconds = Convert.ToInt64(secondsFromBaseTime.TotalSeconds, CultureInfo.InvariantCulture);
 0162                return Convert.ToString(seconds, CultureInfo.InvariantCulture);
 163            }
 164
 165            static string Sign(string requestString, byte[] encodedSharedAccessKey)
 166            {
 0167                using (var hmac = new HMACSHA256(encodedSharedAccessKey))
 168                {
 0169                    return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(requestString)));
 170                }
 0171            }
 172        }
 173    }
 174}