< Summary

Class:Azure.Storage.Sas.SasExtensions
Assembly:Azure.Storage.Common
File(s):C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Common\src\Shared\SasExtensions.cs
Covered lines:88
Uncovered lines:12
Coverable lines:100
Total lines:331
Line coverage:88% (88 of 100)
Covered branches:82
Total branches:94
Branch coverage:87.2% (82 of 94)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
ToPermissionsString(...)-100%100%
ParseResourceTypes(...)-90%87.5%
ToProtocolString(...)-66.67%75%
ParseProtocol(...)-25%50%
ToPermissionsString(...)-100%100%
ParseAccountServices(...)-90.91%83.33%
FormatTimesForSasSigning(...)-100%100%
AddToBuilder(...)-0%0%
AppendProperties(...)-100%100%
ValidateAndSanitizeRawPermissions(...)-92.86%90%

File(s)

C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Common\src\Shared\SasExtensions.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.Net;
 8using System.Runtime.CompilerServices;
 9using System.Text;
 10
 11namespace Azure.Storage.Sas
 12{
 13    /// <summary>
 14    /// Extension methods for Sas.
 15    /// </summary>
 16    internal static partial class SasExtensions
 17    {
 18        /// <summary>
 19        /// Creates a string representing which resource types are allowed
 20        /// for <see cref="AccountSasBuilder.ResourceTypes"/>.
 21        /// </summary>
 22        /// <returns>
 23        /// A string representing which resource types are allowed.
 24        /// </returns>
 25        /// <remarks>
 26        /// The order here matches the order used by the portal when generating SAS signatures.
 27        /// </remarks>
 28        internal static string ToPermissionsString(this AccountSasResourceTypes resourceTypes)
 29        {
 30630            var sb = new StringBuilder();
 30631            if ((resourceTypes & AccountSasResourceTypes.Service) == AccountSasResourceTypes.Service)
 32            {
 27433                sb.Append(Constants.Sas.AccountResources.Service);
 34            }
 30635            if ((resourceTypes & AccountSasResourceTypes.Container) == AccountSasResourceTypes.Container)
 36            {
 27637                sb.Append(Constants.Sas.AccountResources.Container);
 38            }
 30639            if ((resourceTypes & AccountSasResourceTypes.Object) == AccountSasResourceTypes.Object)
 40            {
 24441                sb.Append(Constants.Sas.AccountResources.Object);
 42            }
 30643            return sb.ToString();
 44        }
 45
 46        /// <summary>
 47        /// Parse a string representing which resource types are accessible
 48        /// from a shared access signature.
 49        /// </summary>
 50        /// <param name="s">
 51        /// A string representing which resource types are accessible.
 52        /// </param>
 53        /// <returns>
 54        /// An <see cref="AccountSasResourceTypes"/> instance.
 55        /// </returns>
 56        /// <remarks>
 57        /// The order here matches the order used by the portal when generating SAS signatures.
 58        /// </remarks>
 59        internal static AccountSasResourceTypes ParseResourceTypes(string s)
 60        {
 15861            AccountSasResourceTypes types = default;
 116062            foreach (var ch in s)
 63            {
 42264                types |= ch switch
 42265                {
 56866                    Constants.Sas.AccountResources.Service => AccountSasResourceTypes.Service,
 56667                    Constants.Sas.AccountResources.Container => AccountSasResourceTypes.Container,
 55468                    Constants.Sas.AccountResources.Object => AccountSasResourceTypes.Object,
 069                    _ => throw Errors.InvalidResourceType(ch),
 42270                };
 71            }
 15872            return types;
 73        }
 74
 75        private const string NoneName = null;
 76        private const string HttpsName = "https";
 77        private const string HttpsAndHttpName = "https,http";
 78
 79        /// <summary>
 80        /// Gets a string representation of the protocol.
 81        /// </summary>
 82        /// <returns>A string representation of the protocol.</returns>
 83        internal static string ToProtocolString(this SasProtocol protocol)
 84        {
 85            switch (protocol)
 86            {
 87                case SasProtocol.Https:
 10088                    return HttpsName;
 89                case SasProtocol.HttpsAndHttp:
 090                    return HttpsAndHttpName;
 91                case SasProtocol.None:
 92                default:
 10293                    return null;
 94            }
 95        }
 96
 97        /// <summary>
 98        /// Parse a string representation of a protocol.
 99        /// </summary>
 100        /// <param name="s">A string representation of a protocol.</param>
 101        /// <returns>A <see cref="SasProtocol"/>.</returns>
 102        public static SasProtocol ParseProtocol(string s)
 103        {
 104            switch (s)
 105            {
 106                case NoneName:
 107                case "":
 0108                    return SasProtocol.None;
 109                case HttpsName:
 100110                    return SasProtocol.Https;
 111                case HttpsAndHttpName:
 0112                    return SasProtocol.HttpsAndHttp;
 113                default:
 0114                    throw Errors.InvalidSasProtocol(nameof(s), nameof(SasProtocol));
 115            }
 116        }
 117
 118        /// <summary>
 119        /// Creates a string representing which services can be used for
 120        /// <see cref="AccountSasBuilder.Services"/>.
 121        /// </summary>
 122        /// <returns>
 123        /// A string representing which services are allowed.
 124        /// </returns>
 125        /// <remarks>
 126        /// The order here matches the order used by the portal when generating SAS signatures.
 127        /// </remarks>
 128        internal static string ToPermissionsString(this AccountSasServices services)
 129        {
 308130            var sb = new StringBuilder();
 308131            if ((services & AccountSasServices.Blobs) == AccountSasServices.Blobs)
 132            {
 244133                sb.Append(Constants.Sas.AccountServices.Blob);
 134            }
 308135            if ((services & AccountSasServices.Files) == AccountSasServices.Files)
 136            {
 126137                sb.Append(Constants.Sas.AccountServices.File);
 138            }
 308139            if ((services & AccountSasServices.Queues) == AccountSasServices.Queues)
 140            {
 110141                sb.Append(Constants.Sas.AccountServices.Queue);
 142            }
 308143            if ((services & AccountSasServices.Tables) == AccountSasServices.Tables)
 144            {
 86145                sb.Append(Constants.Sas.AccountServices.Table);
 146            }
 308147            return sb.ToString();
 148        }
 149
 150        /// <summary>
 151        /// Parse a string representing which services are accessible from a
 152        /// shared access signature.
 153        /// </summary>
 154        /// <param name="s">
 155        /// A string representing which services are accessible.
 156        /// </param>
 157        /// <returns>
 158        /// An <see cref="AccountSasServices"/> instance.
 159        /// </returns>
 160        internal static AccountSasServices ParseAccountServices(string s)
 161        {
 160162            AccountSasServices svcs = default;
 1132163            foreach (var ch in s)
 164            {
 406165                svcs |= ch switch
 406166                {
 554167                    Constants.Sas.AccountServices.Blob => AccountSasServices.Blobs,
 428168                    Constants.Sas.AccountServices.Queue => AccountSasServices.Queues,
 440169                    Constants.Sas.AccountServices.File => AccountSasServices.Files,
 428170                    Constants.Sas.AccountServices.Table => AccountSasServices.Tables,
 0171                    _ => throw Errors.InvalidService(ch),
 406172                };
 173                ;
 174            }
 160175            return svcs;
 176        }
 177
 178        /// <summary>
 179        /// FormatTimesForSASSigning converts a time.Time to a snapshotTimeFormat string suitable for a
 180        /// SASField's StartTime or ExpiryTime fields. Returns "" if value.IsZero().
 181        /// </summary>
 182        /// <param name="time"></param>
 183        /// <returns></returns>
 184        internal static string FormatTimesForSasSigning(DateTimeOffset time) =>
 185            // "yyyy-MM-ddTHH:mm:ssZ"
 264186            (time == new DateTimeOffset()) ? "" : time.ToString(Constants.SasTimeFormat, CultureInfo.InvariantCulture);
 187
 188        /// <summary>
 189        /// Helper method to add query param key value pairs to StringBuilder
 190        /// </summary>
 191        /// <param name="sb">StringBuilder instance</param>
 192        /// <param name="key">query key</param>
 193        /// <param name="value">query value</param>
 194        internal static void AddToBuilder(StringBuilder sb, string key, string value) =>
 0195            sb
 0196            .Append(sb.Length > 0 ? "&" : "")
 0197            .Append(key)
 0198            .Append('=')
 0199            .Append(value);
 200
 201        /// <summary>
 202        /// Builds the query parameter string for the SasQueryParameters instance.
 203        /// </summary>
 204        /// <param name="parameters"></param>
 205        /// <param name="stringBuilder">
 206        /// StringBuilder instance to add the query params to
 207        /// </param>
 208        internal static void AppendProperties(this SasQueryParameters parameters, StringBuilder stringBuilder)
 209        {
 278210            if (!string.IsNullOrWhiteSpace(parameters.Version))
 211            {
 278212                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Version, parameters.Version);
 213            }
 214
 278215            if (parameters.Services != null)
 216            {
 174217                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Services, parameters.Services.Value.ToPermis
 218            }
 219
 278220            if (parameters.ResourceTypes != null)
 221            {
 174222                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ResourceTypes, parameters.ResourceTypes.Valu
 223            }
 224
 278225            if (parameters.Protocol != default)
 226            {
 70227                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Protocol, parameters.Protocol.ToProtocolStri
 228            }
 229
 278230            if (parameters.StartsOn != DateTimeOffset.MinValue)
 231            {
 200232                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.StartTime, WebUtility.UrlEncode(parameters.S
 233            }
 234
 278235            if (parameters.ExpiresOn != DateTimeOffset.MinValue)
 236            {
 272237                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ExpiryTime, WebUtility.UrlEncode(parameters.
 238            }
 239
 278240            var ipr = parameters.IPRange.ToString();
 278241            if (ipr.Length > 0)
 242            {
 10243                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.IPRange, ipr);
 244            }
 245
 278246            if (!string.IsNullOrWhiteSpace(parameters.Identifier))
 247            {
 12248                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Identifier, parameters.Identifier);
 249            }
 250
 278251            if (!string.IsNullOrWhiteSpace(parameters.Resource))
 252            {
 90253                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Resource, parameters.Resource);
 254            }
 255
 278256            if (!string.IsNullOrWhiteSpace(parameters.Permissions))
 257            {
 272258                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Permissions, parameters.Permissions);
 259            }
 260
 278261            if (!string.IsNullOrWhiteSpace(parameters.CacheControl))
 262            {
 6263                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.CacheControl, WebUtility.UrlEncode(parameter
 264            }
 265
 278266            if (!string.IsNullOrWhiteSpace(parameters.ContentDisposition))
 267            {
 6268                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ContentDisposition, WebUtility.UrlEncode(par
 269            }
 270
 278271            if (!string.IsNullOrWhiteSpace(parameters.ContentEncoding))
 272            {
 6273                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ContentEncoding, WebUtility.UrlEncode(parame
 274            }
 275
 278276            if (!string.IsNullOrWhiteSpace(parameters.ContentLanguage))
 277            {
 6278                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ContentLanguage, WebUtility.UrlEncode(parame
 279            }
 280
 278281            if (!string.IsNullOrWhiteSpace(parameters.ContentType))
 282            {
 6283                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ContentType, WebUtility.UrlEncode(parameters
 284            }
 285
 278286            if (!string.IsNullOrWhiteSpace(parameters.Signature))
 287            {
 278288                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Signature, WebUtility.UrlEncode(parameters.S
 289            }
 278290        }
 291
 292        internal static string ValidateAndSanitizeRawPermissions(string permissions,
 293            List<char> validPermissionsInOrder)
 294        {
 42295            if (permissions == null)
 296            {
 0297                return null;
 298            }
 299
 300            // Convert permissions string to lower case.
 42301            permissions = permissions.ToLowerInvariant();
 302
 42303            HashSet<char> validPermissionsSet = new HashSet<char>(validPermissionsInOrder);
 42304            HashSet<char> permissionsSet = new HashSet<char>();
 305
 694306            foreach (char permission in permissions)
 307            {
 308                // Check that each permission is a real SAS permission.
 312309                if (!validPermissionsSet.Contains(permission))
 310                {
 14311                    throw new ArgumentException($"{permission} is not a valid SAS permission");
 312                }
 313
 314                // Add permission to permissionsSet for re-ordering.
 298315                permissionsSet.Add(permission);
 316            }
 317
 28318            StringBuilder stringBuilder = new StringBuilder();
 319
 672320            foreach (char permission in validPermissionsInOrder)
 321            {
 308322                if (permissionsSet.Contains(permission))
 323                {
 284324                    stringBuilder.Append(permission);
 325                }
 326            }
 327
 28328            return stringBuilder.ToString();
 329        }
 330    }
 331}