< Summary

Class:Azure.Storage.Sas.BlobSasBuilder
Assembly:Azure.Storage.Blobs
File(s):C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Blobs\src\Sas\BlobSasBuilder.cs
Covered lines:160
Uncovered lines:5
Coverable lines:165
Total lines:494
Line coverage:96.9% (160 of 165)
Covered branches:31
Total branches:36
Branch coverage:86.1% (31 of 36)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_Version()-100%100%
get_Protocol()-100%100%
get_StartsOn()-100%100%
get_ExpiresOn()-100%100%
get_Permissions()-100%100%
get_IPRange()-100%100%
get_Identifier()-100%100%
get_BlobContainerName()-100%100%
get_BlobName()-100%100%
get_Snapshot()-100%100%
get_BlobVersionId()-100%100%
get_Resource()-100%100%
get_CacheControl()-100%100%
get_ContentDisposition()-100%100%
get_ContentEncoding()-100%100%
get_ContentLanguage()-100%100%
get_ContentType()-100%100%
SetPermissions(...)-100%100%
SetPermissions(...)-100%100%
SetPermissions(...)-100%100%
SetPermissions(...)-100%100%
SetPermissions(...)-100%100%
SetPermissions(...)-100%100%
SetPermissions(...)-100%100%
.cctor()-100%100%
ToSasQueryParameters(...)-100%87.5%
ToSasQueryParameters(...)-100%75%
GetCanonicalName(...)-100%100%
ComputeHMACSHA256(...)-100%100%
EnsureState()-86.67%87.5%
ToString()-0%100%
Equals(...)-0%100%
GetHashCode()-0%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Blobs\src\Sas\BlobSasBuilder.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.ComponentModel;
 7using System.Security.Cryptography;
 8using System.Text;
 9using Azure.Storage.Blobs;
 10using Azure.Storage.Blobs.Models;
 11
 12namespace Azure.Storage.Sas
 13{
 14    /// <summary>
 15    /// <see cref="BlobSasBuilder"/> is used to generate a Shared Access
 16    /// Signature (SAS) for an Azure Storage container or blob.
 17    /// For more information, see
 18    /// <see href="https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas">
 19    /// Create a service SAS</see>.
 20    /// </summary>
 21    public class BlobSasBuilder
 22    {
 23        /// <summary>
 24        /// The storage service version to use to authenticate requests made
 25        /// with this shared access signature, and the service version to use
 26        /// when handling requests made with this shared access signature.
 27        /// </summary>
 78028        public string Version { get; set; }
 29
 30        /// <summary>
 31        /// The optional signed protocol field specifies the protocol
 32        /// permitted for a request made with the SAS.  Possible values are
 33        /// <see cref="SasProtocol.HttpsAndHttp"/>,
 34        /// <see cref="SasProtocol.Https"/>, and
 35        /// <see cref="SasProtocol.None"/>.
 36        /// </summary>
 55637        public SasProtocol Protocol { get; set; }
 38
 39        /// <summary>
 40        /// Optionally specify the time at which the shared access signature
 41        /// becomes valid.  If omitted when DateTimeOffset.MinValue is used,
 42        /// start time for this call is assumed to be the time when the
 43        /// storage service receives the request.
 44        /// </summary>
 56445        public DateTimeOffset StartsOn { get; set; }
 46
 47        /// <summary>
 48        /// The time at which the shared access signature becomes invalid.
 49        /// This field must be omitted if it has been specified in an
 50        /// associated stored access policy.
 51        /// </summary>
 72052        public DateTimeOffset ExpiresOn { get; set; }
 53
 54        /// <summary>
 55        /// The permissions associated with the shared access signature. The
 56        /// user is restricted to operations allowed by the permissions. This
 57        /// field must be omitted if it has been specified in an associated
 58        /// stored access policy.  The <see cref="BlobSasPermissions"/>,
 59        /// <see cref="BlobContainerSasPermissions"/>, <see cref="SnapshotSasPermissions"/>,
 60        /// or <see cref="BlobAccountSasPermissions"/> can be used to create the
 61        /// permissions string.
 62        /// </summary>
 71663        public string Permissions { get; private set; }
 64
 65        /// <summary>
 66        /// Specifies an IP address or a range of IP addresses from which to
 67        /// accept requests. If the IP address from which the request
 68        /// originates does not match the IP address or address range
 69        /// specified on the SAS token, the request is not authenticated.
 70        /// When specifying a range of IP addresses, note that the range is
 71        /// inclusive.
 72        /// </summary>
 55273        public SasIPRange IPRange { get; set; }
 74
 75        /// <summary>
 76        /// An optional unique value up to 64 characters in length that
 77        /// correlates to an access policy specified for the container.
 78        /// </summary>
 46079        public string Identifier { get; set; }
 80
 81        /// <summary>
 82        /// The name of the blob container being made accessible.
 83        /// </summary>
 38484        public string BlobContainerName { get; set; }
 85
 86        /// <summary>
 87        /// The name of the blob being made accessible, or
 88        /// <see cref="string.Empty"/> for a container SAS.
 89        /// </summary>
 55690        public string BlobName { get; set; }
 91
 92        /// <summary>
 93        /// The name of the snapshot being made accessible, or
 94        /// <see cref="string.Empty"/> for a blob SAS.
 95        /// </summary>
 47696        public string Snapshot { get; set; }
 97
 98        /// <summary>
 99        /// The name of the blob version being made accessible, or
 100        /// <see cref="string.Empty"/> for a blob SAS.
 101        /// </summary>
 428102        public string BlobVersionId { get; set; }
 103
 104        /// <summary>
 105        /// Specifies which resources are accessible via the shared access
 106        /// signature.
 107        ///
 108        /// Specify "b" if the shared resource is a blob. This grants access to
 109        /// the content and metadata of the blob.
 110        ///
 111        /// Specify "c" if the shared resource is a blob container. This grants
 112        /// access to the content and metadata of any blob in the container,
 113        /// and to the list of blobs in the container.
 114        ///
 115        /// Beginning in version 2018-11-09, specify "bs" if the shared resource
 116        /// is a blob snapshot.  This grants access to the content and
 117        /// metadata of the specific snapshot, but not the corresponding root
 118        /// blob.
 119        ///
 120        /// Beginning in version 2019-12-12, specify "bv" if the shared resource
 121        /// is a blob version.  This grants access to the content and
 122        /// metadata of the specific version, but not the corresponding root
 123        /// blob.
 124        /// </summary>
 568125        public string Resource { get; set; }
 126
 127        /// <summary>
 128        /// Override the value returned for Cache-Control response header.
 129        /// </summary>
 400130        public string CacheControl { get; set; }
 131
 132        /// <summary>
 133        /// Override the value returned for Content-Disposition response
 134        /// header.
 135        /// </summary>
 400136        public string ContentDisposition { get; set; }
 137
 138        /// <summary>
 139        /// Override the value returned for Cache-Encoding response header.
 140        /// </summary>
 400141        public string ContentEncoding { get; set; }
 142
 143        /// <summary>
 144        /// Override the value returned for Cache-Language response header.
 145        /// </summary>
 400146        public string ContentLanguage { get; set; }
 147
 148        /// <summary>
 149        /// Override the value returned for Cache-Type response header.
 150        /// </summary>
 400151        public string ContentType { get; set; }
 152
 153        /// <summary>
 154        /// Sets the permissions for a blob SAS.
 155        /// </summary>
 156        /// <param name="permissions">
 157        /// <see cref="BlobSasPermissions"/> containing the allowed permissions.
 158        /// </param>
 159        public void SetPermissions(BlobSasPermissions permissions)
 160        {
 76161            Permissions = permissions.ToPermissionsString();
 76162        }
 163
 164        /// <summary>
 165        /// Sets the permissions for a blob account level SAS.
 166        /// </summary>
 167        /// <param name="permissions">
 168        /// <see cref="BlobAccountSasPermissions"/> containing the allowed permissions.
 169        /// </param>
 170        public void SetPermissions(BlobAccountSasPermissions permissions)
 171        {
 28172            Permissions = permissions.ToPermissionsString();
 28173        }
 174
 175        /// <summary>
 176        /// Sets the permissions for a blob container SAS.
 177        /// </summary>
 178        /// <param name="permissions">
 179        /// <see cref="BlobContainerSasPermissions"/> containing the allowed permissions.
 180        /// </param>
 181        public void SetPermissions(BlobContainerSasPermissions permissions)
 182        {
 52183            Permissions = permissions.ToPermissionsString();
 52184        }
 185
 186        /// <summary>
 187        /// Sets the permissions for a Snapshot SAS.
 188        /// </summary>
 189        /// <param name="permissions">
 190        /// <see cref="SnapshotSasPermissions"/> containing the allowed permissions.
 191        /// </param>
 192        public void SetPermissions(SnapshotSasPermissions permissions)
 193        {
 12194            Permissions = permissions.ToPermissionsString();
 12195        }
 196
 197        /// <summary>
 198        /// Sets the permissions for a Version SAS.
 199        /// </summary>
 200        /// <param name="permissions">
 201        /// <see cref="SnapshotSasPermissions"/> containing the allowed permissions.
 202        /// </param>
 203        public void SetPermissions(BlobVersionSasPermissions permissions)
 204        {
 8205            Permissions = permissions.ToPermissionsString();
 8206        }
 207
 208        /// <summary>
 209        /// Sets the permissions for the SAS using a raw permissions string.
 210        /// </summary>
 211        /// <param name="rawPermissions">
 212        /// Raw permissions string for the SAS.
 213        /// </param>
 214        /// <param name="normalize">
 215        /// If the permissions should be validated and correctly ordered.
 216        /// </param>
 217        public void SetPermissions(
 218            string rawPermissions,
 219            bool normalize = default)
 220        {
 16221            if (normalize)
 222            {
 16223                rawPermissions = SasExtensions.ValidateAndSanitizeRawPermissions(
 16224                    permissions: rawPermissions,
 16225                    validPermissionsInOrder: s_validPermissionsInOrder);
 226            }
 227
 8228            SetPermissions(rawPermissions);
 8229        }
 230
 231        /// <summary>
 232        /// Sets the permissions for the SAS using a raw permissions string.
 233        /// </summary>
 234        /// <param name="rawPermissions">Raw permissions string for the SAS.</param>
 235        public void SetPermissions(string rawPermissions)
 236        {
 8237            Permissions = rawPermissions;
 8238        }
 239
 4240        private static readonly List<char> s_validPermissionsInOrder = new List<char>
 4241        {
 4242            Constants.Sas.Permissions.Read,
 4243            Constants.Sas.Permissions.Add,
 4244            Constants.Sas.Permissions.Create,
 4245            Constants.Sas.Permissions.Write,
 4246            Constants.Sas.Permissions.Delete,
 4247            Constants.Sas.Permissions.DeleteBlobVersion,
 4248            Constants.Sas.Permissions.List,
 4249            Constants.Sas.Permissions.Tag,
 4250            Constants.Sas.Permissions.Update,
 4251            Constants.Sas.Permissions.Process,
 4252            Constants.Sas.Permissions.FilterByTags,
 4253        };
 254
 255        /// <summary>
 256        /// Use an account's <see cref="StorageSharedKeyCredential"/> to sign this
 257        /// shared access signature values to produce the proper SAS query
 258        /// parameters for authenticating requests.
 259        /// </summary>
 260        /// <param name="sharedKeyCredential">
 261        /// The storage account's <see cref="StorageSharedKeyCredential"/>.
 262        /// </param>
 263        /// <returns>
 264        /// The <see cref="BlobSasQueryParameters"/> used for authenticating
 265        /// requests.
 266        /// </returns>
 267        public BlobSasQueryParameters ToSasQueryParameters(StorageSharedKeyCredential sharedKeyCredential)
 268        {
 128269            sharedKeyCredential = sharedKeyCredential ?? throw Errors.ArgumentNull(nameof(sharedKeyCredential));
 270
 124271            EnsureState();
 272
 124273            var startTime = SasExtensions.FormatTimesForSasSigning(StartsOn);
 124274            var expiryTime = SasExtensions.FormatTimesForSasSigning(ExpiresOn);
 275
 276            // See http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
 124277            var stringToSign = String.Join("\n",
 124278                Permissions,
 124279                startTime,
 124280                expiryTime,
 124281                GetCanonicalName(sharedKeyCredential.AccountName, BlobContainerName ?? String.Empty, BlobName ?? String.
 124282                Identifier,
 124283                IPRange.ToString(),
 124284                SasExtensions.ToProtocolString(Protocol),
 124285                Version,
 124286                Resource,
 124287                Snapshot ?? BlobVersionId,
 124288                CacheControl,
 124289                ContentDisposition,
 124290                ContentEncoding,
 124291                ContentLanguage,
 124292                ContentType);
 293
 124294            var signature = StorageSharedKeyCredentialInternals.ComputeSasSignature(sharedKeyCredential,stringToSign);
 295
 124296            var p = new BlobSasQueryParameters(
 124297                version: Version,
 124298                services: default,
 124299                resourceTypes: default,
 124300                protocol: Protocol,
 124301                startsOn: StartsOn,
 124302                expiresOn: ExpiresOn,
 124303                ipRange: IPRange,
 124304                identifier: Identifier,
 124305                resource: Resource,
 124306                permissions: Permissions,
 124307                signature: signature,
 124308                cacheControl: CacheControl,
 124309                contentDisposition: ContentDisposition,
 124310                contentEncoding: ContentEncoding,
 124311                contentLanguage: ContentLanguage,
 124312                contentType: ContentType);
 124313            return p;
 314        }
 315
 316        /// <summary>
 317        /// Use an account's <see cref="UserDelegationKey"/> to sign this
 318        /// shared access signature values to produce the proper SAS query
 319        /// parameters for authenticating requests.
 320        /// </summary>
 321        /// <param name="userDelegationKey">
 322        /// A <see cref="UserDelegationKey"/> returned from
 323        /// <see cref="Azure.Storage.Blobs.BlobServiceClient.GetUserDelegationKeyAsync"/>.
 324        /// </param>
 325        /// <param name="accountName">The name of the storage account.</param>
 326        /// <returns>
 327        /// The <see cref="BlobSasQueryParameters"/> used for authenticating requests.
 328        /// </returns>
 329        public BlobSasQueryParameters ToSasQueryParameters(UserDelegationKey userDelegationKey, string accountName)
 330        {
 64331            userDelegationKey = userDelegationKey ?? throw Errors.ArgumentNull(nameof(userDelegationKey));
 332
 64333            EnsureState();
 334
 64335            var startTime = SasExtensions.FormatTimesForSasSigning(StartsOn);
 64336            var expiryTime = SasExtensions.FormatTimesForSasSigning(ExpiresOn);
 64337            var signedStart = SasExtensions.FormatTimesForSasSigning(userDelegationKey.SignedStartsOn);
 64338            var signedExpiry = SasExtensions.FormatTimesForSasSigning(userDelegationKey.SignedExpiresOn);
 339
 340            // See http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
 64341            var stringToSign = String.Join("\n",
 64342                Permissions,
 64343                startTime,
 64344                expiryTime,
 64345                GetCanonicalName(accountName, BlobContainerName ?? String.Empty, BlobName ?? String.Empty),
 64346                userDelegationKey.SignedObjectId,
 64347                userDelegationKey.SignedTenantId,
 64348                signedStart,
 64349                signedExpiry,
 64350                userDelegationKey.SignedService,
 64351                userDelegationKey.SignedVersion,
 64352                IPRange.ToString(),
 64353                SasExtensions.ToProtocolString(Protocol),
 64354                Version,
 64355                Resource,
 64356                Snapshot ?? BlobVersionId,
 64357                CacheControl,
 64358                ContentDisposition,
 64359                ContentEncoding,
 64360                ContentLanguage,
 64361                ContentType);
 362
 64363            var signature = ComputeHMACSHA256(userDelegationKey.Value, stringToSign);
 364
 64365            var p = new BlobSasQueryParameters(
 64366                version: Version,
 64367                services: default,
 64368                resourceTypes: default,
 64369                protocol: Protocol,
 64370                startsOn: StartsOn,
 64371                expiresOn: ExpiresOn,
 64372                ipRange: IPRange,
 64373                identifier: null,
 64374                resource: Resource,
 64375                permissions: Permissions,
 64376                keyOid: userDelegationKey.SignedObjectId,
 64377                keyTid: userDelegationKey.SignedTenantId,
 64378                keyStart: userDelegationKey.SignedStartsOn,
 64379                keyExpiry: userDelegationKey.SignedExpiresOn,
 64380                keyService: userDelegationKey.SignedService,
 64381                keyVersion: userDelegationKey.SignedVersion,
 64382                signature: signature,
 64383                cacheControl: CacheControl,
 64384                contentDisposition: ContentDisposition,
 64385                contentEncoding: ContentEncoding,
 64386                contentLanguage: ContentLanguage,
 64387                contentType: ContentType);
 64388            return p;
 389        }
 390
 391        /// <summary>
 392        /// Computes the canonical name for a container or blob resource for SAS signing.
 393        /// Container: "/blob/account/containername"
 394        /// Blob: "/blob/account/containername/blobname"
 395        /// </summary>
 396        /// <param name="account">The name of the storage account.</param>
 397        /// <param name="containerName">The name of the container.</param>
 398        /// <param name="blobName">The name of the blob.</param>
 399        /// <returns>The canonical resource name.</returns>
 400        private static string GetCanonicalName(string account, string containerName, string blobName)
 188401            => !String.IsNullOrEmpty(blobName)
 188402               ? $"/blob/{account}/{containerName}/{blobName.Replace("\\", "/")}"
 188403               : $"/blob/{account}/{containerName}";
 404
 405        /// <summary>
 406        /// ComputeHMACSHA256 generates a base-64 hash signature string for an
 407        /// HTTP request or for a SAS.
 408        /// </summary>
 409        /// <param name="userDelegationKeyValue">
 410        /// A <see cref="UserDelegationKey.Value"/> used to sign with a key
 411        /// representing AD credentials.
 412        /// </param>
 413        /// <param name="message">The message to sign.</param>
 414        /// <returns>The signed message.</returns>
 415        private static string ComputeHMACSHA256(string userDelegationKeyValue, string message) =>
 64416            Convert.ToBase64String(
 64417                new HMACSHA256(
 64418                    Convert.FromBase64String(userDelegationKeyValue))
 64419                .ComputeHash(Encoding.UTF8.GetBytes(message)));
 420
 421        /// <summary>
 422        /// Ensure the <see cref="BlobSasBuilder"/>'s properties are in a
 423        /// consistent state.
 424        /// </summary>
 425        private void EnsureState()
 426        {
 188427            if (Identifier == default)
 428            {
 156429                if (ExpiresOn == default)
 430                {
 0431                    throw Errors.SasMissingData(nameof(ExpiresOn));
 432                }
 156433                if (string.IsNullOrEmpty(Permissions))
 434                {
 0435                    throw Errors.SasMissingData(nameof(Permissions));
 436                }
 437            }
 438
 439            // Container
 188440            if (string.IsNullOrEmpty(BlobName))
 441            {
 72442                Resource = Constants.Sas.Resource.Container;
 443            }
 444
 445            // Blob or Snapshot
 446            else
 447            {
 448                // Blob
 116449                if (string.IsNullOrEmpty(Snapshot) && string.IsNullOrEmpty(BlobVersionId))
 450                {
 88451                    Resource = Constants.Sas.Resource.Blob;
 452                }
 453                // Snapshot
 28454                else if (string.IsNullOrEmpty(BlobVersionId))
 455                {
 20456                    Resource = Constants.Sas.Resource.BlobSnapshot;
 457                }
 458                // Blob Version
 459                else
 460                {
 8461                    Resource = Constants.Sas.Resource.BlobVersion;
 462                }
 463            }
 188464            if (string.IsNullOrEmpty(Version))
 465            {
 40466                Version = SasQueryParameters.DefaultSasVersion;
 467            }
 188468        }
 469
 470        /// <summary>
 471        /// Returns a string that represents the current object.
 472        /// </summary>
 473        /// <returns>A string that represents the current object.</returns>
 474        [EditorBrowsable(EditorBrowsableState.Never)]
 475        public override string ToString() =>
 0476            base.ToString();
 477
 478        /// <summary>
 479        /// Check if two BlobSasBuilder instances are equal.
 480        /// </summary>
 481        /// <param name="obj">The instance to compare to.</param>
 482        /// <returns>True if they're equal, false otherwise.</returns>
 483        [EditorBrowsable(EditorBrowsableState.Never)]
 484        public override bool Equals(object obj)
 0485            => base.Equals(obj);
 486
 487        /// <summary>
 488        /// Get a hash code for the BlobSasBuilder.
 489        /// </summary>
 490        /// <returns>Hash code for the BlobSasBuilder.</returns>
 491        [EditorBrowsable(EditorBrowsableState.Never)]
 0492        public override int GetHashCode() => base.GetHashCode();
 493    }
 494}