< Summary

Class:Azure.Storage.Sas.SasExtensions
Assembly:Azure.Storage.Files.DataLake
File(s):C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Common\src\Shared\SasExtensions.cs
Covered lines:56
Uncovered lines:44
Coverable lines:100
Total lines:331
Line coverage:56% (56 of 100)
Covered branches:48
Total branches:94
Branch coverage:51% (48 of 94)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
ToPermissionsString(...)-100%100%
ParseResourceTypes(...)-0%0%
ToProtocolString(...)-33.33%50%
ParseProtocol(...)-0%0%
ToPermissionsString(...)-70%62.5%
ParseAccountServices(...)-0%0%
FormatTimesForSasSigning(...)-100%100%
AddToBuilder(...)-0%0%
AppendProperties(...)-79.41%78.13%
ValidateAndSanitizeRawPermissions(...)-85.71%80%

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        {
 7230            var sb = new StringBuilder();
 7231            if ((resourceTypes & AccountSasResourceTypes.Service) == AccountSasResourceTypes.Service)
 32            {
 7233                sb.Append(Constants.Sas.AccountResources.Service);
 34            }
 7235            if ((resourceTypes & AccountSasResourceTypes.Container) == AccountSasResourceTypes.Container)
 36            {
 7237                sb.Append(Constants.Sas.AccountResources.Container);
 38            }
 7239            if ((resourceTypes & AccountSasResourceTypes.Object) == AccountSasResourceTypes.Object)
 40            {
 7241                sb.Append(Constants.Sas.AccountResources.Object);
 42            }
 7243            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        {
 061            AccountSasResourceTypes types = default;
 062            foreach (var ch in s)
 63            {
 064                types |= ch switch
 065                {
 066                    Constants.Sas.AccountResources.Service => AccountSasResourceTypes.Service,
 067                    Constants.Sas.AccountResources.Container => AccountSasResourceTypes.Container,
 068                    Constants.Sas.AccountResources.Object => AccountSasResourceTypes.Object,
 069                    _ => throw Errors.InvalidResourceType(ch),
 070                };
 71            }
 072            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:
 088                    return HttpsName;
 89                case SasProtocol.HttpsAndHttp:
 090                    return HttpsAndHttpName;
 91                case SasProtocol.None:
 92                default:
 9293                    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:
 0110                    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        {
 72130            var sb = new StringBuilder();
 72131            if ((services & AccountSasServices.Blobs) == AccountSasServices.Blobs)
 132            {
 72133                sb.Append(Constants.Sas.AccountServices.Blob);
 134            }
 72135            if ((services & AccountSasServices.Files) == AccountSasServices.Files)
 136            {
 0137                sb.Append(Constants.Sas.AccountServices.File);
 138            }
 72139            if ((services & AccountSasServices.Queues) == AccountSasServices.Queues)
 140            {
 0141                sb.Append(Constants.Sas.AccountServices.Queue);
 142            }
 72143            if ((services & AccountSasServices.Tables) == AccountSasServices.Tables)
 144            {
 0145                sb.Append(Constants.Sas.AccountServices.Table);
 146            }
 72147            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        {
 0162            AccountSasServices svcs = default;
 0163            foreach (var ch in s)
 164            {
 0165                svcs |= ch switch
 0166                {
 0167                    Constants.Sas.AccountServices.Blob => AccountSasServices.Blobs,
 0168                    Constants.Sas.AccountServices.Queue => AccountSasServices.Queues,
 0169                    Constants.Sas.AccountServices.File => AccountSasServices.Files,
 0170                    Constants.Sas.AccountServices.Table => AccountSasServices.Tables,
 0171                    _ => throw Errors.InvalidService(ch),
 0172                };
 173                ;
 174            }
 0175            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"
 248186            (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        {
 640210            if (!string.IsNullOrWhiteSpace(parameters.Version))
 211            {
 640212                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Version, parameters.Version);
 213            }
 214
 640215            if (parameters.Services != null)
 216            {
 72217                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Services, parameters.Services.Value.ToPermis
 218            }
 219
 640220            if (parameters.ResourceTypes != null)
 221            {
 72222                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ResourceTypes, parameters.ResourceTypes.Valu
 223            }
 224
 640225            if (parameters.Protocol != default)
 226            {
 0227                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Protocol, parameters.Protocol.ToProtocolStri
 228            }
 229
 640230            if (parameters.StartsOn != DateTimeOffset.MinValue)
 231            {
 604232                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.StartTime, WebUtility.UrlEncode(parameters.S
 233            }
 234
 640235            if (parameters.ExpiresOn != DateTimeOffset.MinValue)
 236            {
 604237                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ExpiryTime, WebUtility.UrlEncode(parameters.
 238            }
 239
 640240            var ipr = parameters.IPRange.ToString();
 640241            if (ipr.Length > 0)
 242            {
 0243                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.IPRange, ipr);
 244            }
 245
 640246            if (!string.IsNullOrWhiteSpace(parameters.Identifier))
 247            {
 36248                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Identifier, parameters.Identifier);
 249            }
 250
 640251            if (!string.IsNullOrWhiteSpace(parameters.Resource))
 252            {
 568253                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Resource, parameters.Resource);
 254            }
 255
 640256            if (!string.IsNullOrWhiteSpace(parameters.Permissions))
 257            {
 604258                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Permissions, parameters.Permissions);
 259            }
 260
 640261            if (!string.IsNullOrWhiteSpace(parameters.CacheControl))
 262            {
 0263                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.CacheControl, WebUtility.UrlEncode(parameter
 264            }
 265
 640266            if (!string.IsNullOrWhiteSpace(parameters.ContentDisposition))
 267            {
 0268                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ContentDisposition, WebUtility.UrlEncode(par
 269            }
 270
 640271            if (!string.IsNullOrWhiteSpace(parameters.ContentEncoding))
 272            {
 0273                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ContentEncoding, WebUtility.UrlEncode(parame
 274            }
 275
 640276            if (!string.IsNullOrWhiteSpace(parameters.ContentLanguage))
 277            {
 0278                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ContentLanguage, WebUtility.UrlEncode(parame
 279            }
 280
 640281            if (!string.IsNullOrWhiteSpace(parameters.ContentType))
 282            {
 0283                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.ContentType, WebUtility.UrlEncode(parameters
 284            }
 285
 640286            if (!string.IsNullOrWhiteSpace(parameters.Signature))
 287            {
 640288                stringBuilder.AppendQueryParameter(Constants.Sas.Parameters.Signature, WebUtility.UrlEncode(parameters.S
 289            }
 640290        }
 291
 292        internal static string ValidateAndSanitizeRawPermissions(string permissions,
 293            List<char> validPermissionsInOrder)
 294        {
 8295            if (permissions == null)
 296            {
 0297                return null;
 298            }
 299
 300            // Convert permissions string to lower case.
 8301            permissions = permissions.ToLowerInvariant();
 302
 8303            HashSet<char> validPermissionsSet = new HashSet<char>(validPermissionsInOrder);
 8304            HashSet<char> permissionsSet = new HashSet<char>();
 305
 144306            foreach (char permission in permissions)
 307            {
 308                // Check that each permission is a real SAS permission.
 64309                if (!validPermissionsSet.Contains(permission))
 310                {
 0311                    throw new ArgumentException($"{permission} is not a valid SAS permission");
 312                }
 313
 314                // Add permission to permissionsSet for re-ordering.
 64315                permissionsSet.Add(permission);
 316            }
 317
 8318            StringBuilder stringBuilder = new StringBuilder();
 319
 176320            foreach (char permission in validPermissionsInOrder)
 321            {
 80322                if (permissionsSet.Contains(permission))
 323                {
 64324                    stringBuilder.Append(permission);
 325                }
 326            }
 327
 8328            return stringBuilder.ToString();
 329        }
 330    }
 331}