diff --git a/sdk/storage/Azure.Storage.Blobs/CHANGELOG.md b/sdk/storage/Azure.Storage.Blobs/CHANGELOG.md index 0eb39ffdc1691..61f95dcd8d822 100644 --- a/sdk/storage/Azure.Storage.Blobs/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Blobs/CHANGELOG.md @@ -1,10 +1,14 @@ # Release History ## 12.7.0-preview.2 (Unreleased) -- Fixed bug where BobContainerClient.SetAccessPolicy() would throw an exception if signed identifier permissions were not in the correct order. +- Fixed bug where BlobContainerClient.SetAccessPolicy() would throw an exception if signed identifier permissions were not in the correct order. +- Added seekability to BaseBlobClient.OpenRead(). - Added additional info to exception messages. - Fixed bug where Blobs SDK coudn't handle SASs with start and expiry time in format other than yyyy-MM-ddTHH:mm:ssZ. - Added ability to set Position on streams created with BlobBaseClient.OpenRead(). +- Added CanGenerateSasUri property, GenerateSasUri() to BlobBaseClient, BlobClient, BlockBlobClient, AppendBlobClient, PageBlobClient and BlobContainerClient. +- Added CanAccountGenerateSasUri property, GenerateAccountSasUri() to BlobServiceClient. +- Deprecated property BlobSasBuilder.Version, so when generating SAS will always use the latest Storage Service SAS version. ## 12.7.0-preview.1 (2020-09-30) - Added support for service version 2020-02-10. diff --git a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs index 4e48cd0747c01..8d53e799f0a61 100644 --- a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs @@ -62,6 +62,7 @@ public BlobContainerClient(System.Uri blobContainerUri, Azure.Core.TokenCredenti public BlobContainerClient(System.Uri blobContainerUri, Azure.Storage.Blobs.BlobClientOptions options = null) { } public BlobContainerClient(System.Uri blobContainerUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateSasUri { get { throw null; } } public virtual string Name { get { throw null; } } public virtual System.Uri Uri { get { throw null; } } public virtual Azure.Response Create(Azure.Storage.Blobs.Models.PublicAccessType publicAccessType = Azure.Storage.Blobs.Models.PublicAccessType.None, System.Collections.Generic.IDictionary metadata = null, Azure.Storage.Blobs.Models.BlobContainerEncryptionScopeOptions encryptionScopeOptions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -87,6 +88,8 @@ public BlobContainerClient(System.Uri blobContainerUri, Azure.Storage.StorageSha public virtual System.Threading.Tasks.Task> DeleteIfExistsAsync(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Exists(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> ExistsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobContainerSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasBuilder builder) { throw null; } public virtual Azure.Response GetAccessPolicy(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessPolicyAsync(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } protected internal virtual Azure.Storage.Blobs.Specialized.AppendBlobClient GetAppendBlobClientCore(string blobName) { throw null; } @@ -116,6 +119,7 @@ public BlobServiceClient(System.Uri serviceUri, Azure.Core.TokenCredential crede public BlobServiceClient(System.Uri serviceUri, Azure.Storage.Blobs.BlobClientOptions options = null) { } public BlobServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { } public string AccountName { get { throw null; } } + public bool CanGenerateAccountSasUri { get { throw null; } } public virtual System.Uri Uri { get { throw null; } } public virtual Azure.Response CreateBlobContainer(string blobContainerName, Azure.Storage.Blobs.Models.PublicAccessType publicAccessType = Azure.Storage.Blobs.Models.PublicAccessType.None, System.Collections.Generic.IDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> CreateBlobContainerAsync(string blobContainerName, Azure.Storage.Blobs.Models.PublicAccessType publicAccessType = Azure.Storage.Blobs.Models.PublicAccessType.None, System.Collections.Generic.IDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -124,6 +128,8 @@ public BlobServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyCr public virtual System.Threading.Tasks.Task DeleteBlobContainerAsync(string blobContainerName, Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Pageable FindBlobsByTags(string tagFilterSqlExpression, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.AsyncPageable FindBlobsByTagsAsync(string tagFilterSqlExpression, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Uri GenerateAccountSasUri(Azure.Storage.Sas.AccountSasBuilder builder) { throw null; } + public System.Uri GenerateAccountSasUri(Azure.Storage.Sas.AccountSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Sas.AccountSasResourceTypes resourceTypes) { throw null; } public virtual Azure.Response GetAccountInfo(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccountInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } protected static Azure.Core.Pipeline.HttpPipelinePolicy GetAuthenticationPolicy(Azure.Storage.Blobs.BlobServiceClient client) { throw null; } @@ -1235,6 +1241,7 @@ public BlobBaseClient(System.Uri blobUri, Azure.Storage.Blobs.BlobClientOptions public BlobBaseClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { } public virtual string AccountName { get { throw null; } } public virtual string BlobContainerName { get { throw null; } } + public bool CanGenerateSasUri { get { throw null; } } public virtual string Name { get { throw null; } } public virtual System.Uri Uri { get { throw null; } } public virtual Azure.Response AbortCopyFromUri(string copyId, Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -1265,6 +1272,8 @@ public BlobBaseClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredenti public virtual System.Threading.Tasks.Task DownloadToAsync(string path, System.Threading.CancellationToken cancellationToken) { throw null; } public virtual Azure.Response Exists(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> ExistsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasBuilder builder) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } protected internal virtual Azure.Storage.Blobs.Specialized.BlobLeaseClient GetBlobLeaseClientCore(string leaseId) { throw null; } public virtual Azure.Response GetProperties(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetPropertiesAsync(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -1459,7 +1468,10 @@ public enum BlobContainerSasPermissions } public partial class BlobSasBuilder { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public BlobSasBuilder() { } + public BlobSasBuilder(Azure.Storage.Sas.BlobContainerSasPermissions permissions, System.DateTimeOffset expiresOn) { } + public BlobSasBuilder(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn) { } public string BlobContainerName { get { throw null; } set { } } public string BlobName { get { throw null; } set { } } public string BlobVersionId { get { throw null; } set { } } @@ -1478,6 +1490,7 @@ public BlobSasBuilder() { } public string Resource { get { throw null; } set { } } public string Snapshot { get { throw null; } set { } } public System.DateTimeOffset StartsOn { get { throw null; } set { } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public string Version { get { throw null; } set { } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool Equals(object obj) { throw null; } diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs index d9f067eb0a778..f182b4e59c007 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs @@ -11,6 +11,7 @@ using Azure.Core.Pipeline; using Azure.Storage.Blobs.Models; using Azure.Storage.Cryptography; +using Azure.Storage.Sas; using Azure.Storage.Shared; using Metadata = System.Collections.Generic.IDictionary; using Tags = System.Collections.Generic.IDictionary; @@ -101,6 +102,16 @@ public class BlobBaseClient /// internal virtual string EncryptionScope => _encryptionScope; + /// + /// Optional. The snapshot of the blob. + /// + private string _snapshot; + + /// + /// Optional. The version of the blob. + /// + private string _blobVersionId; + /// /// The Storage account name corresponding to the blob client. /// @@ -152,6 +163,17 @@ public virtual string Name } } + /// + /// The used to authenticate and generate SAS + /// + private StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -226,6 +248,7 @@ public BlobBaseClient(string connectionString, string blobContainerName, string _customerProvidedKey = options.CustomerProvidedKey; _clientSideEncryption = options._clientSideEncryptionOptions?.Clone(); _encryptionScope = options.EncryptionScope; + _storageSharedKeyCredential = conn.Credentials as StorageSharedKeyCredential; BlobErrors.VerifyHttpsCustomerProvidedKey(_uri, _customerProvidedKey); BlobErrors.VerifyCpkAndEncryptionScopeNotBothSet(_customerProvidedKey, _encryptionScope); } @@ -246,7 +269,7 @@ public BlobBaseClient(string connectionString, string blobContainerName, string /// every request. /// public BlobBaseClient(Uri blobUri, BlobClientOptions options = default) - : this(blobUri, (HttpPipelinePolicy)null, options) + : this(blobUri, (HttpPipelinePolicy)null, options, null) { } @@ -269,7 +292,7 @@ public BlobBaseClient(Uri blobUri, BlobClientOptions options = default) /// every request. /// public BlobBaseClient(Uri blobUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) - : this(blobUri, credential.AsPolicy(), options) + : this(blobUri, credential.AsPolicy(), options, credential) { } @@ -292,7 +315,7 @@ public BlobBaseClient(Uri blobUri, StorageSharedKeyCredential credential, BlobCl /// every request. /// public BlobBaseClient(Uri blobUri, TokenCredential credential, BlobClientOptions options = default) - : this(blobUri, credential.AsPolicy(), options) + : this(blobUri, credential.AsPolicy(), options, null) { Errors.VerifyHttpsTokenAuth(blobUri); } @@ -315,16 +338,36 @@ public BlobBaseClient(Uri blobUri, TokenCredential credential, BlobClientOptions /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal BlobBaseClient(Uri blobUri, HttpPipelinePolicy authentication, BlobClientOptions options) + /// + /// The shared key credential used to sign requests. + /// + internal BlobBaseClient( + Uri blobUri, + HttpPipelinePolicy authentication, + BlobClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) { options ??= new BlobClientOptions(); _uri = blobUri; + if (!string.IsNullOrEmpty(blobUri.Query)) + { + UriQueryParamsCollection queryParamsCollection = new UriQueryParamsCollection(blobUri.Query); + if (queryParamsCollection.ContainsKey(Constants.SnapshotParameterName)) + { + _snapshot = System.Web.HttpUtility.ParseQueryString(blobUri.Query).Get(Constants.SnapshotParameterName); + } + if (queryParamsCollection.ContainsKey(Constants.VersionIdParameterName)) + { + _blobVersionId = System.Web.HttpUtility.ParseQueryString(blobUri.Query).Get(Constants.VersionIdParameterName); + } + } _pipeline = options.Build(authentication); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); _customerProvidedKey = options.CustomerProvidedKey; _clientSideEncryption = options._clientSideEncryptionOptions?.Clone(); _encryptionScope = options.EncryptionScope; + _storageSharedKeyCredential = storageSharedKeyCredential; BlobErrors.VerifyHttpsCustomerProvidedKey(_uri, _customerProvidedKey); BlobErrors.VerifyCpkAndEncryptionScopeNotBothSet(_customerProvidedKey, _encryptionScope); } @@ -396,6 +439,7 @@ internal BlobBaseClient( /// A new instance. protected virtual BlobBaseClient WithSnapshotCore(string snapshot) { + _snapshot = snapshot; var blobUriBuilder = new BlobUriBuilder(Uri) { Snapshot = snapshot @@ -434,6 +478,7 @@ protected virtual BlobBaseClient WithSnapshotCore(string snapshot) /// A new instance. private protected virtual BlobBaseClient WithVersionCore(string versionId) { + _blobVersionId = versionId; BlobUriBuilder blobUriBuilder = new BlobUriBuilder(Uri) { VersionId = versionId @@ -1416,7 +1461,7 @@ public virtual async Task OpenReadAsync( #pragma warning disable AZC0015 // Unexpected client method return type. public virtual Stream OpenRead( #pragma warning restore AZC0015 // Unexpected client method return type. - long position = 0 , + long position = 0, int? bufferSize = default, BlobRequestConditions conditions = default, CancellationToken cancellationToken = default) @@ -1531,7 +1576,7 @@ public virtual async Task OpenReadAsync( public virtual async Task OpenReadAsync( #pragma warning restore AZC0015 // Unexpected client method return type. bool allowBlobModifications, - long position = 0 , + long position = 0, int? bufferSize = default, CancellationToken cancellationToken = default) => await OpenReadAsync( @@ -4103,6 +4148,104 @@ private async Task SetTagsInternal( } } #endregion + + #region GenerateSas + /// + /// The + /// returns a that generates a Blob Service + /// Shared Access Signature (SAS) Uri based on the Client properties and + /// parameters passed. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a service SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(BlobSasPermissions permissions, DateTimeOffset expiresOn) => + GenerateSasUri(new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = BlobContainerName, + BlobName = Name, + Snapshot = _snapshot, + BlobVersionId = _blobVersionId + }); + + /// + /// The returns a + /// that generates a Blob Service Shared Access Signature (SAS) Uri + /// based on the Client properties and and builder. The SAS is signed + /// by the shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a Service SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS). + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Uri GenerateSasUri(BlobSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.BlobContainerName.Equals(BlobContainerName, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.BlobContainerName), + nameof(BlobSasBuilder), + nameof(BlobContainerName)); + } + if (!builder.BlobName.Equals(Name, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.BlobName), + nameof(BlobSasBuilder), + nameof(Name)); + } + if (string.Compare(_snapshot, builder.Snapshot, StringComparison.InvariantCulture) != 0) + { + throw Errors.SasNamesNotMatching( + nameof(builder.Snapshot), + nameof(BlobSasBuilder)); + } + if (string.Compare(_blobVersionId, builder.BlobVersionId, StringComparison.InvariantCulture) != 0) + { + throw Errors.SasNamesNotMatching( + nameof(builder.BlobVersionId), + nameof(BlobSasBuilder)); + } + BlobUriBuilder sasUri = new BlobUriBuilder(Uri) + { + Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString() + }; + return sasUri.ToUri(); + } + #endregion } /// diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobContainerClient.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobContainerClient.cs index 2288b9bf9dab4..f4d9b384036e1 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/BlobContainerClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/BlobContainerClient.cs @@ -148,6 +148,17 @@ public virtual string Name } } + /// + /// The used to authenticate and generate SAS + /// + private readonly StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateSasUri => _storageSharedKeyCredential != null; + #region ctor /// /// Initializes a new instance of the @@ -210,6 +221,7 @@ public BlobContainerClient(string connectionString, string blobContainerName, Bl _clientDiagnostics = new ClientDiagnostics(options); _customerProvidedKey = options.CustomerProvidedKey; _encryptionScope = options.EncryptionScope; + _storageSharedKeyCredential = conn.Credentials as StorageSharedKeyCredential; BlobErrors.VerifyHttpsCustomerProvidedKey(_uri, _customerProvidedKey); BlobErrors.VerifyCpkAndEncryptionScopeNotBothSet(_customerProvidedKey, _encryptionScope); } @@ -229,7 +241,7 @@ public BlobContainerClient(string connectionString, string blobContainerName, Bl /// every request. /// public BlobContainerClient(Uri blobContainerUri, BlobClientOptions options = default) - : this(blobContainerUri, (HttpPipelinePolicy)null, options) + : this(blobContainerUri, (HttpPipelinePolicy)null, options) { } @@ -253,6 +265,7 @@ public BlobContainerClient(Uri blobContainerUri, BlobClientOptions options = def public BlobContainerClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) : this(blobContainerUri, credential.AsPolicy(), options) { + _storageSharedKeyCredential = credential; } /// @@ -2875,5 +2888,84 @@ await GetBlobClient(blobName).DeleteIfExistsAsync( .ConfigureAwait(false); #endregion DeleteBlob + + #region GenerateSas + /// + /// The + /// returns a that generates a Blob Container Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and parameters passed. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a service SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(BlobContainerSasPermissions permissions, DateTimeOffset expiresOn) => + GenerateSasUri(new BlobSasBuilder(permissions, expiresOn) { BlobContainerName = Name }); + + /// + /// The returns a + /// that generates a Blob Container Service Shared Access Signature (SAS) Uri + /// based on the Client properties and builder passed. The SAS is signed by + /// the shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a Service SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS). + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(BlobSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.BlobContainerName.Equals(Name, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.BlobContainerName), + nameof(BlobSasBuilder), + nameof(Name)); + } + if (!string.IsNullOrEmpty(builder.BlobName)) + { + throw Errors.SasBuilderEmptyParam( + nameof(builder), + nameof(builder.BlobName), + nameof(Constants.Blob.Container.Name)); + } + BlobUriBuilder sasUri = new BlobUriBuilder(Uri) + { + Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString() + }; + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobServiceClient.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobServiceClient.cs index f2ba906cca09c..eac4119d65e71 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/BlobServiceClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/BlobServiceClient.cs @@ -11,7 +11,9 @@ using Azure.Core.Pipeline; using Azure.Storage.Blobs.Models; using Azure.Storage.Cryptography; +using Azure.Storage.Sas; using Metadata = System.Collections.Generic.IDictionary; +using System.Linq; namespace Azure.Storage.Blobs { @@ -125,6 +127,17 @@ public string AccountName } } + /// + /// The used to authenticate and generate SAS + /// + private StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateAccountSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -180,6 +193,7 @@ public BlobServiceClient(string connectionString, BlobClientOptions options) _customerProvidedKey = options.CustomerProvidedKey; _clientSideEncryption = options._clientSideEncryptionOptions?.Clone(); _encryptionScope = options.EncryptionScope; + _storageSharedKeyCredential = conn.Credentials as StorageSharedKeyCredential; BlobErrors.VerifyHttpsCustomerProvidedKey(_uri, _customerProvidedKey); BlobErrors.VerifyCpkAndEncryptionScopeNotBothSet(_customerProvidedKey, _encryptionScope); } @@ -219,7 +233,7 @@ public BlobServiceClient(Uri serviceUri, BlobClientOptions options = default) /// every request. /// public BlobServiceClient(Uri serviceUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) - : this(serviceUri, credential.AsPolicy(), options ?? new BlobClientOptions()) + : this(serviceUri, credential.AsPolicy(), options ?? new BlobClientOptions(), credential) { } @@ -261,7 +275,14 @@ public BlobServiceClient(Uri serviceUri, TokenCredential credential, BlobClientO /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal BlobServiceClient(Uri serviceUri, HttpPipelinePolicy authentication, BlobClientOptions options) + /// + /// Optional storage shared key credential used to sign requests and generate sas. + /// + internal BlobServiceClient( + Uri serviceUri, + HttpPipelinePolicy authentication, + BlobClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential = default) : this( serviceUri, authentication, @@ -270,9 +291,9 @@ internal BlobServiceClient(Uri serviceUri, HttpPipelinePolicy authentication, Bl options?.CustomerProvidedKey, options?._clientSideEncryptionOptions?.Clone(), options?.EncryptionScope, - options.Build(authentication)) + options.Build(authentication), + storageSharedKeyCredential) { - } /// @@ -299,6 +320,7 @@ internal BlobServiceClient(Uri serviceUri, HttpPipelinePolicy authentication, Bl /// /// The transport pipeline used to send every request. /// + /// Storage Shared Key Credential internal BlobServiceClient( Uri serviceUri, HttpPipelinePolicy authentication, @@ -307,7 +329,8 @@ internal BlobServiceClient( CustomerProvidedKey? customerProvidedKey, ClientSideEncryptionOptions clientSideEncryption, string encryptionScope, - HttpPipeline pipeline) + HttpPipeline pipeline, + StorageSharedKeyCredential storageSharedKeyCredential = default) { _uri = serviceUri; _authenticationPolicy = authentication; @@ -317,6 +340,7 @@ internal BlobServiceClient( _customerProvidedKey = customerProvidedKey; _clientSideEncryption = clientSideEncryption?.Clone(); _encryptionScope = encryptionScope; + _storageSharedKeyCredential = storageSharedKeyCredential; BlobErrors.VerifyCpkAndEncryptionScopeNotBothSet(_customerProvidedKey, _encryptionScope); BlobErrors.VerifyHttpsCustomerProvidedKey(_uri, _customerProvidedKey); } @@ -1749,5 +1773,86 @@ internal async Task> FindBlobsByTagsInternal( } } #endregion FilterBlobs + + #region GenerateSas + /// + /// The + /// returns a that generates a Blob Account + /// Shared Access Signature (SAS) based on the Client properties + /// and parameters passed. The SAS is signed by the + /// shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing an Account SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. The time at which the shared access signature becomes invalid. + /// + /// + /// Specifies the resource types associated with the shared access signature. + /// The user is restricted to operations on the specified resources. + /// See . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public Uri GenerateAccountSasUri( + AccountSasPermissions permissions, + DateTimeOffset expiresOn, + AccountSasResourceTypes resourceTypes) => + GenerateAccountSasUri(new AccountSasBuilder( + permissions, + expiresOn, + AccountSasServices.Blobs, + resourceTypes)); + + /// + /// The returns a that + /// generates a Blob Account Shared Access Signature (SAS) based on the + /// Client properties and builder passed. The SAS is signed by the + /// shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing an Account SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS). + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public Uri GenerateAccountSasUri(AccountSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.Services.HasFlag(AccountSasServices.Blobs)) + { + throw Errors.SasServiceNotMatching( + nameof(builder.Services), + nameof(builder), + nameof(AccountSasServices.Blobs)); + } + UriBuilder sasUri = new UriBuilder(Uri); + sasUri.Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString(); + return sasUri.Uri; + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobUriBuilder.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobUriBuilder.cs index 6684ad2071b5b..2fecae43b85c2 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/BlobUriBuilder.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/BlobUriBuilder.cs @@ -34,12 +34,6 @@ public class BlobUriBuilder /// private readonly bool _isPathStyleUri; - /// - /// List of ports used for path style addressing. - /// Copied from Microsoft.Azure.Storage.Core.Util - /// - private static readonly int[] PathStylePorts = { 10000, 10001, 10002, 10003, 10004, 10100, 10101, 10102, 10103, 10104, 11000, 11001, 11002, 11003, 11004, 11100, 11101, 11102, 11103, 11104 }; - /// /// Gets or sets the scheme name of the URI. /// Example: "https" @@ -193,9 +187,9 @@ public BlobUriBuilder(Uri uri) var startIndex = 0; - _isPathStyleUri = uri.IsHostIPEndPointStyle() || PathStylePorts.Contains(uri.Port); - if (_isPathStyleUri) + if (uri.IsHostIPEndPointStyle()) { + _isPathStyleUri = true; var accountEndIndex = path.IndexOf("/", StringComparison.InvariantCulture); // Slash not found; path has account name & no container name diff --git a/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs b/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs index 4a6b4543b1bb2..5b771caee3a2d 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs @@ -25,6 +25,13 @@ public class BlobSasBuilder /// with this shared access signature, and the service version to use /// when handling requests made with this shared access signature. /// + /// + /// This property has been deprecated and we will always use the latest + /// storage SAS version of the Storage service supported. This change + /// does not have any impact on how your application generates or makes + /// use of SAS tokens. + /// + [EditorBrowsable(EditorBrowsableState.Never)] public string Version { get; set; } /// @@ -169,6 +176,61 @@ public class BlobSasBuilder /// public string CorrelationId { get; set; } + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// This constructor has been deprecated. Please consider using + /// + /// to create a Service SAS. This change does not have any impact on how + /// your application generates or makes use of SAS tokens. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public BlobSasBuilder() + { + } + + /// + /// Initializes a new instance of the + /// class to create a Blob Service Sas. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + public BlobSasBuilder(BlobSasPermissions permissions, DateTimeOffset expiresOn) + { + ExpiresOn = expiresOn; + SetPermissions(permissions); + } + + /// + /// Initializes a new instance of the + /// class to create a Blob Container Service Sas. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + public BlobSasBuilder(BlobContainerSasPermissions permissions, DateTimeOffset expiresOn) + { + ExpiresOn = expiresOn; + SetPermissions(permissions); + } + /// /// Sets the permissions for a blob SAS. /// @@ -470,10 +532,7 @@ private void EnsureState() Resource = Constants.Sas.Resource.BlobVersion; } } - if (string.IsNullOrEmpty(Version)) - { - Version = SasQueryParameters.DefaultSasVersion; - } + Version = SasQueryParameters.DefaultSasVersion; } /// diff --git a/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTests.cs b/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTests.cs index daf2073f071d5..b4847668e3f64 100644 --- a/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTests.cs +++ b/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTests.cs @@ -5728,6 +5728,293 @@ await TestHelper.AssertExpectedExceptionAsync( e => Assert.AreEqual(BlobErrorCode.BlobNotFound.ToString(), e.ErrorCode)); } + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - BlobBaseClient(string connectionString, string blobContainerName, string blobName) + BlobBaseClient blob = new BlobBaseClient( + connectionString, + GetNewContainerName(), + GetNewBlobName()); + Assert.IsTrue(blob.CanGenerateSasUri); + + // Act - BlobBaseClient(string connectionString, string blobContainerName, string blobName, BlobClientOptions options) + BlobBaseClient blob2 = new BlobBaseClient( + connectionString, + GetNewContainerName(), + GetNewBlobName(), + GetOptions()); + Assert.IsTrue(blob2.CanGenerateSasUri); + + // Act - BlobBaseClient(Uri blobContainerUri, BlobClientOptions options = default) + BlobBaseClient blob3 = new BlobBaseClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(blob3.CanGenerateSasUri); + + // Act - BlobBaseClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + BlobBaseClient blob4 = new BlobBaseClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(blob4.CanGenerateSasUri); + + // Act - BlobBaseClient(Uri blobContainerUri, TokenCredential credential, BlobClientOptions options = default) + var tokenCredentials = new DefaultAzureCredential(); + BlobBaseClient blob5 = new BlobBaseClient( + blobEndpoint, + tokenCredentials, + GetOptions()); + Assert.IsFalse(blob5.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = new BlobBaseClient( + connectionString, + containerName, + blobName, + GetOptions()); + + //Act + Uri sasUri = blobClient.GenerateSasUri(permissions, expiresOn); + + // Assert + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(blobEndpoint) + { + BlobContainerName = containerName, + BlobName = blobName, + Sas = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + BlobBaseClient blobClient = new BlobBaseClient( + connectionString, + containerName, + blobName, + GetOptions()); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + StartsOn = startsOn + }; + + // Act + Uri sasUri = blobClient.GenerateSasUri(sasBuilder); + + // Assert + BlobSasBuilder sasBuilder2 = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + StartsOn = startsOn + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(blobEndpoint) + { + BlobContainerName = containerName, + BlobName = blobName, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongContainerName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string blobName = GetNewBlobName(); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewContainerName() + "/" + blobName; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = new BlobBaseClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = GetNewContainerName(), // set a different containerName + BlobName = blobName, + Resource = "b", + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None) + }; + + // Act + try + { + Uri sasUri = blobClient.GenerateSasUri(sasBuilder); + + Assert.Fail("BlobBaseClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderWrongBlobName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string containerName = GetNewContainerName(); + blobUriBuilder.Path += constants.Sas.Account + "/" + containerName + "/" + GetNewBlobName(); + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = new BlobBaseClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = GetNewBlobName(), // set a different blobName + Resource = "b", + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None) + }; + + // Act + try + { + blobClient.GenerateSasUri(sasBuilder); + + Assert.Fail("BlobBaseClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderWrongSnapshot() + { + // Arrange + var constants = new TestConstants(this); + string snapshot = "2020-07-03T12:45:46.1234567Z"; + string differentSnapshot = "2019-07-03T12:45:46.1234567Z"; + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + Uri blobEndpoint = new Uri($"http://127.0.0.1/{constants.Sas.Account}/{containerName}/{Uri.EscapeDataString(blobName)}?snapshot={snapshot}"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + blobUriBuilder.Path += constants.Sas.Account + "/" + containerName + "/" + blobName; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = new BlobBaseClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + Resource = "bs", + Snapshot = differentSnapshot, + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None) + }; + + // Act + try + { + blobClient.GenerateSasUri(sasBuilder); + + Assert.Fail("BlobBaseClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderWrongVersion() + { + // Arrange + var constants = new TestConstants(this); + string blobVersionId = "2020-07-03T12:45:46.1234567Z"; + string diffBlobVersionId = "2019-07-03T12:45:46.1234567Z"; + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + Uri blobEndpoint = new Uri($"http://127.0.0.1/{constants.Sas.Account}/{containerName}/{Uri.EscapeDataString(blobName)}?snapshot={blobVersionId}"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + blobUriBuilder.Path += constants.Sas.Account + "/" + containerName + "/" + blobName; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = new BlobBaseClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + Resource = "bs", + BlobVersionId = diffBlobVersionId, + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None) + }; + + // Act + try + { + blobClient.GenerateSasUri(sasBuilder); + + Assert.Fail("BlobBaseClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion + //[Test] //public async Task SetTierAsync_Batch() //{ diff --git a/sdk/storage/Azure.Storage.Blobs/tests/BlobSasBuilderTests.cs b/sdk/storage/Azure.Storage.Blobs/tests/BlobSasBuilderTests.cs index ca6aea5198c91..72a45d44af450 100644 --- a/sdk/storage/Azure.Storage.Blobs/tests/BlobSasBuilderTests.cs +++ b/sdk/storage/Azure.Storage.Blobs/tests/BlobSasBuilderTests.cs @@ -262,7 +262,6 @@ public void ToSasQueryParameters_IdentifierTest() Assert.AreEqual(constants.Sas.Identifier, sasQueryParameters.Identifier); Assert.AreEqual(SasProtocol.Https, sasQueryParameters.Protocol); Assert.AreEqual(resource, sasQueryParameters.Resource); - Assert.AreEqual(constants.Sas.Version, sasQueryParameters.Version); } [Test] diff --git a/sdk/storage/Azure.Storage.Blobs/tests/ContainerClientTests.cs b/sdk/storage/Azure.Storage.Blobs/tests/ContainerClientTests.cs index af11285b48754..0edf759f5243d 100644 --- a/sdk/storage/Azure.Storage.Blobs/tests/ContainerClientTests.cs +++ b/sdk/storage/Azure.Storage.Blobs/tests/ContainerClientTests.cs @@ -2593,6 +2593,153 @@ public async Task GetBlobClients_SpecialCharacters(string blobName) Assert.AreEqual(blobName, pageBlobClientFromConnectionString.Name); } + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - BlobContainerClient(string connectionString, string blobContainerName) + BlobContainerClient container = new BlobContainerClient( + connectionString, + GetNewContainerName()); + Assert.IsTrue(container.CanGenerateSasUri); + + // Act - BlobContainerClient(string connectionString, string blobContainerName, BlobClientOptions options) + BlobContainerClient container2 = new BlobContainerClient( + connectionString, + GetNewContainerName(), + GetOptions()); + Assert.IsTrue(container2.CanGenerateSasUri); + + // Act - BlobContainerClient(Uri blobContainerUri, BlobClientOptions options = default) + BlobContainerClient container3 = new BlobContainerClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(container3.CanGenerateSasUri); + + // Act - BlobContainerClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + BlobContainerClient container4 = new BlobContainerClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(container4.CanGenerateSasUri); + + // Act - BlobContainerClient(Uri blobContainerUri, TokenCredential credential, BlobClientOptions options = default) + var tokenCredentials = new DefaultAzureCredential(); + BlobContainerClient container5 = new BlobContainerClient( + blobEndpoint, + tokenCredentials, + GetOptions()); + Assert.IsFalse(container5.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string containerName = GetNewContainerName(); + BlobContainerSasPermissions permissions = BlobContainerSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobContainerClient containerClient = new BlobContainerClient(connectionString, containerName, GetOptions()); + + //Act + Uri sasUri = containerClient.GenerateSasUri(permissions, expiresOn); + + // Assert + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(blobEndpoint) + { + BlobContainerName = containerName, + Sas = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string containerName = GetNewContainerName(); + BlobContainerSasPermissions permissions = BlobContainerSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobContainerClient containerClient = + new BlobContainerClient(connectionString, containerName, GetOptions()); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName + }; + + // Act + Uri sasUri = containerClient.GenerateSasUri(sasBuilder); + + // Assert + BlobSasBuilder sasBuilder2 = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(blobEndpoint) + { + BlobContainerName = containerName, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewContainerName(); + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobContainerClient containerClient = new BlobContainerClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = GetNewContainerName(), // set a different containerName + Resource = "b", + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None) + }; + + // Act + try + { + containerClient.GenerateSasUri(sasBuilder); + + Assert.Fail("BlobContainerClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion + [Test] public void CanMockBlobClientsRetrieval() { diff --git a/sdk/storage/Azure.Storage.Blobs/tests/ServiceClientTests.cs b/sdk/storage/Azure.Storage.Blobs/tests/ServiceClientTests.cs index 42467e0281b6e..fa5d6d24895e9 100644 --- a/sdk/storage/Azure.Storage.Blobs/tests/ServiceClientTests.cs +++ b/sdk/storage/Azure.Storage.Blobs/tests/ServiceClientTests.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Azure.Core.TestFramework; +using Azure.Identity; using Azure.Storage.Blobs.Models; using Azure.Storage.Blobs.Specialized; using Azure.Storage.Sas; @@ -683,5 +684,139 @@ await TestHelper.AssertExpectedExceptionAsync( service.UndeleteBlobContainerAsync(GetNewBlobName(), "01D60F8BB59A4652"), e => Assert.AreEqual(BlobErrorCode.ContainerNotFound.ToString(), e.ErrorCode)); } + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - BlobServiceClient(string connectionString) + BlobServiceClient container = new BlobServiceClient( + connectionString); + Assert.IsTrue(container.CanGenerateAccountSasUri); + + // Act - BlobServiceClient(string connectionString, string blobContainerName, BlobClientOptions options) + BlobServiceClient container2 = new BlobServiceClient( + connectionString, + GetOptions()); + Assert.IsTrue(container2.CanGenerateAccountSasUri); + + // Act - BlobServiceClient(Uri blobContainerUri, BlobClientOptions options = default) + BlobServiceClient container3 = new BlobServiceClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(container3.CanGenerateAccountSasUri); + + // Act - BlobServiceClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + BlobServiceClient container4 = new BlobServiceClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(container4.CanGenerateAccountSasUri); + + // Act - BlobServiceClient(Uri blobContainerUri, TokenCredential credential, BlobClientOptions options = default) + var tokenCredentials = new DefaultAzureCredential(); + BlobServiceClient container5 = new BlobServiceClient( + blobEndpoint, + tokenCredentials, + GetOptions()); + Assert.IsFalse(container5.CanGenerateAccountSasUri); + } + + [Test] + public void GenerateAccountSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + BlobServiceClient serviceClient = new BlobServiceClient(connectionString, GetOptions()); + + // Act + Uri sasUri = serviceClient.GenerateAccountSasUri( + permissions: permissions, + expiresOn: expiresOn, + resourceTypes: resourceTypes); + + // Assert + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, AccountSasServices.Blobs, resourceTypes); + UriBuilder expectedUri = new UriBuilder(blobEndpoint); + expectedUri.Query += sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential).ToString(); + Assert.AreEqual(expectedUri.Uri.ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateAccountSas_Builder() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AccountSasServices services = AccountSasServices.Blobs; + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + BlobServiceClient serviceClient = new BlobServiceClient(connectionString, GetOptions()); + + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None), + StartsOn = Recording.UtcNow.AddHours(-1) + }; + + // Act + Uri sasUri = serviceClient.GenerateAccountSasUri(sasBuilder); + + // Assert + UriBuilder expectedUri = new UriBuilder(blobEndpoint); + expectedUri.Query += sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential).ToString(); + Assert.AreEqual(expectedUri.Uri.ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateAccountSas_WrongService_Service() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AccountSasServices services = AccountSasServices.Files; // Wrong Service + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + BlobServiceClient serviceClient = new BlobServiceClient(connectionString, GetOptions()); + + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None), + StartsOn = Recording.UtcNow.AddHours(-1) + }; + + // Act + try + { + Uri sasUri = serviceClient.GenerateAccountSasUri(sasBuilder); + + Assert.Fail("BlobContainerClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + // the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..fb96b2b7e4db1 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.2997397-07:00", + "RandomSeed": "2090294214" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..402ab1c86bcba --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.4618823-07:00", + "RandomSeed": "437729744" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..0af09edcdea2f --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.3816201-07:00", + "RandomSeed": "759338175" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..331896c896f1a --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.4643956-07:00", + "RandomSeed": "994442021" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongBlobName.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongBlobName.json new file mode 100644 index 0000000000000..1be18b5c603e5 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongBlobName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T14:14:34.9296278-07:00", + "RandomSeed": "1737966333" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongBlobNameAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongBlobNameAsync.json new file mode 100644 index 0000000000000..c91a495b4ec7c --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongBlobNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.4659997-07:00", + "RandomSeed": "766441096" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongContainerName.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongContainerName.json new file mode 100644 index 0000000000000..d4f98adda8729 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongContainerName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T14:14:34.9815683-07:00", + "RandomSeed": "1762754898" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongContainerNameAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongContainerNameAsync.json new file mode 100644 index 0000000000000..d2ce4619a0f91 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongContainerNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.4673695-07:00", + "RandomSeed": "716421888" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongSnapshot.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongSnapshot.json new file mode 100644 index 0000000000000..bc050f03279d2 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongSnapshot.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.4528217-07:00", + "RandomSeed": "1729983564" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongSnapshotAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongSnapshotAsync.json new file mode 100644 index 0000000000000..bcfaed1b493df --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongSnapshotAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.4692062-07:00", + "RandomSeed": "1247505208" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongVersion.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongVersion.json new file mode 100644 index 0000000000000..985f89153381a --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongVersion.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T14:49:40.7921491-07:00", + "RandomSeed": "558581438" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongVersionAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongVersionAsync.json new file mode 100644 index 0000000000000..fae4c9612b6b8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_BuilderWrongVersionAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T14:49:40.8813254-07:00", + "RandomSeed": "1036320819" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..50e12963ad606 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.4550664-07:00", + "RandomSeed": "322953682" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..50dbad95882ea --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/BlobBaseClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:41:11.4706849-07:00", + "RandomSeed": "835399276" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..e9c502daa0d0b --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:42:06.3258294-07:00", + "RandomSeed": "2081581446" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..aa4134a7fead8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:42:06.4496190-07:00", + "RandomSeed": "1959121824" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..423bf0b388c3d --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:42:06.4163209-07:00", + "RandomSeed": "600990451" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..aaad5163bd679 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:42:06.4518710-07:00", + "RandomSeed": "951008844" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_BuilderWrongName.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_BuilderWrongName.json new file mode 100644 index 0000000000000..286ebd863b879 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_BuilderWrongName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:42:06.4387500-07:00", + "RandomSeed": "1148132338" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_BuilderWrongNameAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_BuilderWrongNameAsync.json new file mode 100644 index 0000000000000..8e3493a914f29 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_BuilderWrongNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:42:06.4555807-07:00", + "RandomSeed": "1326719516" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..d16cb0fe2563a --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:42:06.4416620-07:00", + "RandomSeed": "301148535" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..a38bd40b42009 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ContainerClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:42:06.4571558-07:00", + "RandomSeed": "713686816" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..d43146a304261 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:43:05.3955383-07:00", + "RandomSeed": "1062401034" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..b02d1bced956a --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:43:05.4764540-07:00", + "RandomSeed": "231317741" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json new file mode 100644 index 0000000000000..1d9c706ede93e --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:43:27.1651089-07:00", + "RandomSeed": "1514693036" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json new file mode 100644 index 0000000000000..4f5b02a5f447e --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:43:27.2153009-07:00", + "RandomSeed": "880974664" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParameters.json new file mode 100644 index 0000000000000..9bb6d4bd7c351 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:43:27.1975317-07:00", + "RandomSeed": "560055838" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..6cb41d8786870 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:43:27.2166461-07:00", + "RandomSeed": "830583297" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json new file mode 100644 index 0000000000000..09f3c73d140d6 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:43:27.2003798-07:00", + "RandomSeed": "902777024" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json new file mode 100644 index 0000000000000..ba9bf5223b208 --- /dev/null +++ b/sdk/storage/Azure.Storage.Blobs/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:43:27.2180480-07:00", + "RandomSeed": "455074574" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.netstandard2.0.cs b/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.netstandard2.0.cs index b30c32785ab91..b4d9bff59f605 100644 --- a/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.netstandard2.0.cs @@ -45,7 +45,9 @@ namespace Azure.Storage.Sas { public partial class AccountSasBuilder { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public AccountSasBuilder() { } + public AccountSasBuilder(Azure.Storage.Sas.AccountSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Sas.AccountSasServices services, Azure.Storage.Sas.AccountSasResourceTypes resourceTypes) { } public System.DateTimeOffset ExpiresOn { get { throw null; } set { } } public Azure.Storage.Sas.SasIPRange IPRange { get { throw null; } set { } } public string Permissions { get { throw null; } } diff --git a/sdk/storage/Azure.Storage.Common/src/Sas/AccountSasBuilder.cs b/sdk/storage/Azure.Storage.Common/src/Sas/AccountSasBuilder.cs index 3675dccfaf56b..2dcf12089cda9 100644 --- a/sdk/storage/Azure.Storage.Common/src/Sas/AccountSasBuilder.cs +++ b/sdk/storage/Azure.Storage.Common/src/Sas/AccountSasBuilder.cs @@ -78,6 +78,55 @@ public class AccountSasBuilder /// public AccountSasResourceTypes ResourceTypes { get; set; } + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// This constructor has been deprecated. Please consider using + /// + /// to create a Service SAS. This change does not have any impact on how + /// your application generates or makes use of SAS tokens. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public AccountSasBuilder() + { + } + + /// + /// Initializes a new instance of the + /// class to create a Blob Container Service Sas. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// Specifies the services accessible from an account level shared access + /// signature. + /// + /// + /// Specifies the resource types accessible from an account level shared + /// access signature. + /// + public AccountSasBuilder( + AccountSasPermissions permissions, + DateTimeOffset expiresOn, + AccountSasServices services, + AccountSasResourceTypes resourceTypes) + { + ExpiresOn = expiresOn; + SetPermissions(permissions); + Services = services; + ResourceTypes = resourceTypes; + } + /// /// Sets the permissions for an account SAS. /// diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs b/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs index 912a9b4b2ea87..19781a07e8577 100644 --- a/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs +++ b/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs @@ -85,6 +85,9 @@ internal static class Constants public const string PercentSign = "%"; public const string EncodedPercentSign = "%25"; + public const string FalseName = "false"; + public const string TrueName = "true"; + /// /// Storage Connection String constant values. /// @@ -192,6 +195,7 @@ internal static class Page internal static class Container { + public const string Name = "Blob Container"; /// /// The Azure Storage name used to identify a storage account's root container. /// @@ -248,6 +252,11 @@ internal static class Errors public const string LeaseNotPresentWithFileOperation = "LeaseNotPresentWithFileOperation"; } + + internal static class Share + { + public const string Name = "Share"; + } } /// @@ -306,6 +315,8 @@ internal static class DataLake /// Metadata key for isFolder property. /// public const string IsDirectoryKey = "hdi_isFolder"; + + public const string FileSystemName = "FileSystem"; } /// @@ -528,6 +539,12 @@ internal static class AccountResources Sas.Permissions.Move, Sas.Permissions.Execute }; + + /// + /// List of ports used for path style addressing. + /// Copied from Microsoft.Azure.Storage.Core.Util + /// + internal static readonly int[] PathStylePorts = { 10000, 10001, 10002, 10003, 10004, 10100, 10101, 10102, 10103, 10104, 11000, 11001, 11002, 11003, 11004, 11100, 11101, 11102, 11103, 11104 }; } internal static class ClientSideEncryption diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs b/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs index e262702dfdc16..ab39ef16554e0 100644 --- a/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs +++ b/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Linq; using System.Security.Authentication; +using System.Xml.Serialization; using Azure.Core.Pipeline; namespace Azure.Storage @@ -55,6 +56,28 @@ public static InvalidOperationException SasDataNotAllowed(string paramName, stri public static InvalidOperationException SasDataInConjunction(string paramName, string paramName2) => new InvalidOperationException($"SAS cannot have the following parameters specified in conjunction: {paramName}, {paramName2}"); + public static InvalidOperationException SasNamesNotMatching(string builderParam, string builderName, string clientParam) + => new InvalidOperationException($"SAS Uri cannot be generated. {builderName}.{builderParam} does not match {clientParam} in the Client. " + + $"{builderName}.{builderParam} must either be left empty or match the {clientParam} in the Client"); + + public static InvalidOperationException SasNamesNotMatching(string builderParam, string builderName) + => new InvalidOperationException($"SAS Uri cannot be generated. {builderName}.{builderParam} does not match snapshot value in the URI in the Client. " + + $"{builderName}.{builderParam} must either be left empty or match the snapshot value in the URI in the Client"); + + public static InvalidOperationException SasServiceNotMatching(string builderParam, string builderName, string expectedService) + => new InvalidOperationException($"SAS Uri cannot be generated. {builderName}.{builderParam} does specify {expectedService}. " + + $"{builderName}.{builderParam} must either specify {expectedService} or specify all Services are accessible in the value."); + + public static InvalidOperationException SasClientMissingData(string paramName) + => new InvalidOperationException($"SAS Uri cannot be generated. {paramName} in the client has not been set"); + + public static InvalidOperationException SasBuilderEmptyParam(string builderName, string paramName, string sasType) + => new InvalidOperationException($"SAS Uri cannot be generated. {builderName}.{paramName} cannot be set to create a {sasType} SAS."); + + public static InvalidOperationException SasIncorrectResourceType(string builderName, string builderParam, string value, string clientName) + => new InvalidOperationException($"SAS Uri cannot be generated. Expected {builderName}.{builderParam} to be set to {value} to generate" + + $"the respective SAS for the client, {clientName}"); + public static ArgumentException InvalidPermission(char s) => new ArgumentException($"Invalid permission: '{s}'"); diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/UriExtensions.cs b/sdk/storage/Azure.Storage.Common/src/Shared/UriExtensions.cs index 3f1c399d21fc2..0785cbb8214a8 100644 --- a/sdk/storage/Azure.Storage.Common/src/Shared/UriExtensions.cs +++ b/sdk/storage/Azure.Storage.Common/src/Shared/UriExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Net; using System.Text; @@ -119,9 +120,10 @@ public static string GetPath(this Uri uri) => /// The Uri. /// True if using IP Endpoint style. public static bool IsHostIPEndPointStyle(this Uri uri) => - !string.IsNullOrEmpty(uri.Host) && + (!string.IsNullOrEmpty(uri.Host) && uri.Host.IndexOf(".", StringComparison.InvariantCulture) >= 0 && - IPAddress.TryParse(uri.Host, out _); + IPAddress.TryParse(uri.Host, out _)) || + Constants.Sas.PathStylePorts.Contains(uri.Port); /// /// Appends a query parameter to the string builder. diff --git a/sdk/storage/Azure.Storage.Common/src/StorageSharedKeyCredential.cs b/sdk/storage/Azure.Storage.Common/src/StorageSharedKeyCredential.cs index 2e8818f084537..54fda26948c1a 100644 --- a/sdk/storage/Azure.Storage.Common/src/StorageSharedKeyCredential.cs +++ b/sdk/storage/Azure.Storage.Common/src/StorageSharedKeyCredential.cs @@ -2,6 +2,9 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; using System.Security.Cryptography; using System.Text; using System.Threading; diff --git a/sdk/storage/Azure.Storage.Common/tests/Shared/StorageTestBase.cs b/sdk/storage/Azure.Storage.Common/tests/Shared/StorageTestBase.cs index 0bfa59c0739d4..4fd26f8eea035 100644 --- a/sdk/storage/Azure.Storage.Common/tests/Shared/StorageTestBase.cs +++ b/sdk/storage/Azure.Storage.Common/tests/Shared/StorageTestBase.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Text; using System.Threading; using System.Threading.Tasks; using Azure.Core; @@ -482,8 +483,8 @@ protected async Task CreateLimitedMemoryStream(long size, long maxMemory return stream; } - private class StorageTestTokenCredential : TokenCredential - { + private class StorageTestTokenCredential : TokenCredential + { public override ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) { return new ValueTask(GetToken(requestContext, cancellationToken)); @@ -494,5 +495,55 @@ public override AccessToken GetToken(TokenRequestContext requestContext, Cancell return new AccessToken("TEST TOKEN " + string.Join(" ", requestContext.Scopes), DateTimeOffset.MaxValue); } } + + public string AccountSasPermissionsToPermissionsString(AccountSasPermissions permissions) + { + var sb = new StringBuilder(); + if ((permissions & AccountSasPermissions.Read) == AccountSasPermissions.Read) + { + sb.Append(Constants.Sas.Permissions.Read); + } + if ((permissions & AccountSasPermissions.Write) == AccountSasPermissions.Write) + { + sb.Append(Constants.Sas.Permissions.Write); + } + if ((permissions & AccountSasPermissions.Delete) == AccountSasPermissions.Delete) + { + sb.Append(Constants.Sas.Permissions.Delete); + } + if ((permissions & AccountSasPermissions.DeleteVersion) == AccountSasPermissions.DeleteVersion) + { + sb.Append(Constants.Sas.Permissions.DeleteBlobVersion); + } + if ((permissions & AccountSasPermissions.List) == AccountSasPermissions.List) + { + sb.Append(Constants.Sas.Permissions.List); + } + if ((permissions & AccountSasPermissions.Add) == AccountSasPermissions.Add) + { + sb.Append(Constants.Sas.Permissions.Add); + } + if ((permissions & AccountSasPermissions.Create) == AccountSasPermissions.Create) + { + sb.Append(Constants.Sas.Permissions.Create); + } + if ((permissions & AccountSasPermissions.Update) == AccountSasPermissions.Update) + { + sb.Append(Constants.Sas.Permissions.Update); + } + if ((permissions & AccountSasPermissions.Process) == AccountSasPermissions.Process) + { + sb.Append(Constants.Sas.Permissions.Process); + } + if ((permissions & AccountSasPermissions.Tag) == AccountSasPermissions.Tag) + { + sb.Append(Constants.Sas.Permissions.Tag); + } + if ((permissions & AccountSasPermissions.Filter) == AccountSasPermissions.Filter) + { + sb.Append(Constants.Sas.Permissions.FilterByTags); + } + return sb.ToString(); + } } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/CHANGELOG.md b/sdk/storage/Azure.Storage.Files.DataLake/CHANGELOG.md index e8b1dbd671f28..30679977764aa 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Files.DataLake/CHANGELOG.md @@ -6,6 +6,8 @@ - Added DataLakeDirectoryClient.GetPaths(). - Fixed bug where Data Lake SDK coudn't handle SASs with start and expiry time in format other than yyyy-MM-ddTHH:mm:ssZ. - Added ability to set Position on streams created with DataLakeFileClient.OpenRead(). +- Added CanGenerateSasUri property and GenerateSasUri() to DataLakePathClient, DataLakeFileClient, DataLakeDirectoryClient and DataLakeFileSystemClient. +- Added CanGenerateAccountSasUri property and GenerateAccountSasUri() to DataLakeServiceClient. ## 12.5.0-preview.1 (2020-09-30) - Added support for service version 2020-02-10. diff --git a/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.netstandard2.0.cs b/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.netstandard2.0.cs index dd7da689d7f70..680af235760c4 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.netstandard2.0.cs @@ -38,6 +38,8 @@ public DataLakeDirectoryClient(System.Uri directoryUri, Azure.Storage.StorageSha public virtual System.Threading.Tasks.Task> DeleteIfExistsAsync(Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response DeleteSubDirectory(string path, string continuation = null, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task DeleteSubDirectoryAsync(string path, string continuation = null, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder) { throw null; } + public override System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } public override Azure.Response GetAccessControl(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override System.Threading.Tasks.Task> GetAccessControlAsync(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Storage.Files.DataLake.DataLakeFileClient GetFileClient(string fileName) { throw null; } @@ -152,6 +154,7 @@ public DataLakeFileSystemClient(System.Uri fileSystemUri, Azure.Storage.Files.Da public DataLakeFileSystemClient(System.Uri fileSystemUri, Azure.Storage.StorageSharedKeyCredential credential) { } public DataLakeFileSystemClient(System.Uri fileSystemUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Files.DataLake.DataLakeClientOptions options) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateSasUri { get { throw null; } } public virtual string Name { get { throw null; } } public virtual System.Uri Uri { get { throw null; } } public virtual Azure.Response Create(Azure.Storage.Files.DataLake.Models.PublicAccessType publicAccessType = Azure.Storage.Files.DataLake.Models.PublicAccessType.None, System.Collections.Generic.IDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -172,6 +175,8 @@ public DataLakeFileSystemClient(System.Uri fileSystemUri, Azure.Storage.StorageS public virtual System.Threading.Tasks.Task> DeleteIfExistsAsync(Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Exists(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> ExistsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeFileSystemSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder) { throw null; } public virtual Azure.Response GetAccessPolicy(Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessPolicyAsync(Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Storage.Files.DataLake.DataLakeDirectoryClient GetDirectoryClient(string directoryName) { throw null; } @@ -220,6 +225,7 @@ public DataLakePathClient(System.Uri pathUri, Azure.Storage.Files.DataLake.DataL public DataLakePathClient(System.Uri pathUri, Azure.Storage.StorageSharedKeyCredential credential) { } public DataLakePathClient(System.Uri pathUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Files.DataLake.DataLakeClientOptions options) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateSasUri { get { throw null; } } public virtual string FileSystemName { get { throw null; } } public virtual string Name { get { throw null; } } public virtual string Path { get { throw null; } } @@ -234,6 +240,8 @@ public DataLakePathClient(System.Uri pathUri, Azure.Storage.StorageSharedKeyCred public virtual System.Threading.Tasks.Task> DeleteIfExistsAsync(bool? recursive = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Exists(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> ExistsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } public virtual Azure.Response GetAccessControl(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessControlAsync(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response GetProperties(Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -265,11 +273,14 @@ public DataLakeServiceClient(System.Uri serviceUri, Azure.Storage.Files.DataLake public DataLakeServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyCredential credential) { } public DataLakeServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Files.DataLake.DataLakeClientOptions options) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateAccountSasUri { get { throw null; } } public virtual System.Uri Uri { get { throw null; } } public virtual Azure.Response CreateFileSystem(string fileSystemName, Azure.Storage.Files.DataLake.Models.PublicAccessType publicAccessType = Azure.Storage.Files.DataLake.Models.PublicAccessType.None, System.Collections.Generic.IDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> CreateFileSystemAsync(string fileSystemName, Azure.Storage.Files.DataLake.Models.PublicAccessType publicAccessType = Azure.Storage.Files.DataLake.Models.PublicAccessType.None, System.Collections.Generic.IDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response DeleteFileSystem(string fileSystemName, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task DeleteFileSystemAsync(string fileSystemName, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Uri GenerateAccountSasUri(Azure.Storage.Sas.AccountSasBuilder builder) { throw null; } + public System.Uri GenerateAccountSasUri(Azure.Storage.Sas.AccountSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Sas.AccountSasResourceTypes resourceTypes) { throw null; } public virtual Azure.Storage.Files.DataLake.DataLakeFileSystemClient GetFileSystemClient(string fileSystemName) { throw null; } public virtual Azure.Pageable GetFileSystems(Azure.Storage.Files.DataLake.Models.FileSystemTraits traits = Azure.Storage.Files.DataLake.Models.FileSystemTraits.None, string prefix = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.AsyncPageable GetFileSystemsAsync(Azure.Storage.Files.DataLake.Models.FileSystemTraits traits = Azure.Storage.Files.DataLake.Models.FileSystemTraits.None, string prefix = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -845,7 +856,10 @@ public enum DataLakeFileSystemSasPermissions } public partial class DataLakeSasBuilder { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public DataLakeSasBuilder() { } + public DataLakeSasBuilder(Azure.Storage.Sas.DataLakeFileSystemSasPermissions permissions, System.DateTimeOffset expiresOn) { } + public DataLakeSasBuilder(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn) { } public string AgentObjectId { get { throw null; } set { } } public string CacheControl { get { throw null; } set { } } public string ContentDisposition { get { throw null; } set { } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeDirectoryClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeDirectoryClient.cs index 1e4db30a68cfe..022b89102319c 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeDirectoryClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeDirectoryClient.cs @@ -9,6 +9,7 @@ using Azure.Core.Pipeline; using Azure.Storage.Blobs.Specialized; using Azure.Storage.Files.DataLake.Models; +using Azure.Storage.Sas; using Metadata = System.Collections.Generic.IDictionary; namespace Azure.Storage.Files.DataLake @@ -37,7 +38,7 @@ protected DataLakeDirectoryClient() /// directory. /// public DataLakeDirectoryClient(Uri directoryUri) - : this(directoryUri, (HttpPipelinePolicy)null, null) + : this(directoryUri, (HttpPipelinePolicy)null, null, null) { } @@ -56,7 +57,7 @@ public DataLakeDirectoryClient(Uri directoryUri) /// applied to every request. /// public DataLakeDirectoryClient(Uri directoryUri, DataLakeClientOptions options) - : this(directoryUri, (HttpPipelinePolicy)null, options) + : this(directoryUri, (HttpPipelinePolicy)null, options, null) { } @@ -73,7 +74,7 @@ public DataLakeDirectoryClient(Uri directoryUri, DataLakeClientOptions options) /// The shared key credential used to sign requests. /// public DataLakeDirectoryClient(Uri directoryUri, StorageSharedKeyCredential credential) - : this(directoryUri, credential.AsPolicy(), null) + : this(directoryUri, credential.AsPolicy(), null, credential) { } @@ -95,7 +96,7 @@ public DataLakeDirectoryClient(Uri directoryUri, StorageSharedKeyCredential cred /// every request. /// public DataLakeDirectoryClient(Uri directoryUri, StorageSharedKeyCredential credential, DataLakeClientOptions options) - : this(directoryUri, credential.AsPolicy(), options) + : this(directoryUri, credential.AsPolicy(), options, credential) { } @@ -112,7 +113,7 @@ public DataLakeDirectoryClient(Uri directoryUri, StorageSharedKeyCredential cred /// The token credential used to sign requests. /// public DataLakeDirectoryClient(Uri directoryUri, TokenCredential credential) - : this(directoryUri, credential.AsPolicy(), null) + : this(directoryUri, credential.AsPolicy(), null, null) { Errors.VerifyHttpsTokenAuth(directoryUri); } @@ -135,7 +136,7 @@ public DataLakeDirectoryClient(Uri directoryUri, TokenCredential credential) /// every request. /// public DataLakeDirectoryClient(Uri directoryUri, TokenCredential credential, DataLakeClientOptions options) - : this(directoryUri, credential.AsPolicy(), options) + : this(directoryUri, credential.AsPolicy(), options, null) { Errors.VerifyHttpsTokenAuth(directoryUri); } @@ -157,8 +158,15 @@ public DataLakeDirectoryClient(Uri directoryUri, TokenCredential credential, Dat /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal DataLakeDirectoryClient(Uri directoryUri, HttpPipelinePolicy authentication, DataLakeClientOptions options) - : base(directoryUri, authentication, options) + /// + /// The shared key credential used to sign requests. + /// + internal DataLakeDirectoryClient + (Uri directoryUri, + HttpPipelinePolicy authentication, + DataLakeClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) + : base(directoryUri, authentication, options, storageSharedKeyCredential) { } @@ -2278,5 +2286,98 @@ public virtual AsyncPageable GetPathsAsync( $"{nameof(DataLakeDirectoryClient)}.{nameof(GetPaths)}") .ToAsyncCollection(cancellationToken); #endregion Get Paths + + #region GenerateSas + /// + /// The + /// returns a that generates a DataLake Directory Service + /// Shared Access Signature (SAS) Uri based on the Client properties and + /// parameters passed. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a service SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public override Uri GenerateSasUri(DataLakeSasPermissions permissions, DateTimeOffset expiresOn) => + GenerateSasUri(new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = FileSystemName, + Path = Path, + IsDirectory = true + }); + + /// + /// The returns a + /// that generates a DataLake Directory Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and and builder. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a Service SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS). + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public override Uri GenerateSasUri(DataLakeSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.IsDirectory.GetValueOrDefault(false)) + { + throw Errors.SasIncorrectResourceType( + nameof(builder), + nameof(builder.IsDirectory), + Constants.TrueName, + nameof(this.GetType)); + } + if (!builder.FileSystemName.Equals(FileSystemName, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.FileSystemName), + nameof(DataLakeSasBuilder), + nameof(FileSystemName)); + } + if (!builder.Path.Equals(Path, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.Path), + nameof(DataLakeSasBuilder), + nameof(Path)); + } + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) + { + Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString() + }; + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileClient.cs index 4763a1351ea2a..b21e6337d65f4 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileClient.cs @@ -15,6 +15,7 @@ using Azure.Core.Pipeline; using Azure.Storage.Blobs.Models; using Azure.Storage.Files.DataLake.Models; +using Azure.Storage.Sas; using Metadata = System.Collections.Generic.IDictionary; namespace Azure.Storage.Files.DataLake @@ -61,7 +62,7 @@ protected DataLakeFileClient() /// file. /// public DataLakeFileClient(Uri fileUri) - : this(fileUri, (HttpPipelinePolicy)null, null) + : this(fileUri, (HttpPipelinePolicy)null, null, null) { } @@ -79,7 +80,7 @@ public DataLakeFileClient(Uri fileUri) /// applied to every request. /// public DataLakeFileClient(Uri fileUri, DataLakeClientOptions options) - : this(fileUri, (HttpPipelinePolicy)null, options) + : this(fileUri, (HttpPipelinePolicy)null, options, null) { } @@ -95,7 +96,7 @@ public DataLakeFileClient(Uri fileUri, DataLakeClientOptions options) /// The shared key credential used to sign requests. /// public DataLakeFileClient(Uri fileUri, StorageSharedKeyCredential credential) - : this(fileUri, credential.AsPolicy(), null) + : this(fileUri, credential.AsPolicy(), null, credential) { } @@ -116,7 +117,7 @@ public DataLakeFileClient(Uri fileUri, StorageSharedKeyCredential credential) /// applied to every request. /// public DataLakeFileClient(Uri fileUri, StorageSharedKeyCredential credential, DataLakeClientOptions options) - : this(fileUri, credential.AsPolicy(), options) + : this(fileUri, credential.AsPolicy(), options, credential) { } @@ -132,7 +133,7 @@ public DataLakeFileClient(Uri fileUri, StorageSharedKeyCredential credential, Da /// The token credential used to sign requests. /// public DataLakeFileClient(Uri fileUri, TokenCredential credential) - : this(fileUri, credential.AsPolicy(), null) + : this(fileUri, credential.AsPolicy(), null, null) { Errors.VerifyHttpsTokenAuth(fileUri); } @@ -154,7 +155,7 @@ public DataLakeFileClient(Uri fileUri, TokenCredential credential) /// applied to every request. /// public DataLakeFileClient(Uri fileUri, TokenCredential credential, DataLakeClientOptions options) - : this(fileUri, credential.AsPolicy(), options) + : this(fileUri, credential.AsPolicy(), options, null) { Errors.VerifyHttpsTokenAuth(fileUri); } @@ -176,8 +177,15 @@ public DataLakeFileClient(Uri fileUri, TokenCredential credential, DataLakeClien /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal DataLakeFileClient(Uri fileUri, HttpPipelinePolicy authentication, DataLakeClientOptions options) - : base(fileUri, authentication, options) + /// + /// The shared key credential used to sign requests. + /// + internal DataLakeFileClient( + Uri fileUri, + HttpPipelinePolicy authentication, + DataLakeClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) + : base(fileUri, authentication, options, storageSharedKeyCredential) { } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileSystemClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileSystemClient.cs index 87a24a027c0ab..7048c1fe9abeb 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileSystemClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileSystemClient.cs @@ -14,6 +14,7 @@ using System.Text.Json; using System.Collections.Generic; using Azure.Storage.Shared; +using Azure.Storage.Sas; using System.ComponentModel; namespace Azure.Storage.Files.DataLake @@ -122,6 +123,17 @@ public virtual string Name } } + /// + /// The used to authenticate and generate SAS + /// + private readonly StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -140,7 +152,7 @@ protected DataLakeFileSystemClient() /// name of the account and the name of the file system. /// public DataLakeFileSystemClient(Uri fileSystemUri) - : this(fileSystemUri, (HttpPipelinePolicy)null, null) + : this(fileSystemUri, (HttpPipelinePolicy)null, null, null) { } @@ -158,7 +170,7 @@ public DataLakeFileSystemClient(Uri fileSystemUri) /// every request. /// public DataLakeFileSystemClient(Uri fileSystemUri, DataLakeClientOptions options) - : this(fileSystemUri, (HttpPipelinePolicy)null, options) + : this(fileSystemUri, (HttpPipelinePolicy)null, options, null) { } @@ -174,7 +186,7 @@ public DataLakeFileSystemClient(Uri fileSystemUri, DataLakeClientOptions options /// The shared key credential used to sign requests. /// public DataLakeFileSystemClient(Uri fileSystemUri, StorageSharedKeyCredential credential) - : this(fileSystemUri, credential.AsPolicy(), null) + : this(fileSystemUri, credential.AsPolicy(), null, credential) { } @@ -195,7 +207,7 @@ public DataLakeFileSystemClient(Uri fileSystemUri, StorageSharedKeyCredential cr /// every request. /// public DataLakeFileSystemClient(Uri fileSystemUri, StorageSharedKeyCredential credential, DataLakeClientOptions options) - : this(fileSystemUri, credential.AsPolicy(), options) + : this(fileSystemUri, credential.AsPolicy(), options, credential) { } @@ -211,7 +223,7 @@ public DataLakeFileSystemClient(Uri fileSystemUri, StorageSharedKeyCredential cr /// The token credential used to sign requests. /// public DataLakeFileSystemClient(Uri fileSystemUri, TokenCredential credential) - : this(fileSystemUri, credential.AsPolicy(), null) + : this(fileSystemUri, credential.AsPolicy(), null, null) { Errors.VerifyHttpsTokenAuth(fileSystemUri); } @@ -233,7 +245,7 @@ public DataLakeFileSystemClient(Uri fileSystemUri, TokenCredential credential) /// every request. /// public DataLakeFileSystemClient(Uri fileSystemUri, TokenCredential credential, DataLakeClientOptions options) - : this(fileSystemUri, credential.AsPolicy(), options) + : this(fileSystemUri, credential.AsPolicy(), options, null) { Errors.VerifyHttpsTokenAuth(fileSystemUri); } @@ -254,7 +266,14 @@ public DataLakeFileSystemClient(Uri fileSystemUri, TokenCredential credential, D /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal DataLakeFileSystemClient(Uri fileSystemUri, HttpPipelinePolicy authentication, DataLakeClientOptions options) + /// + /// The shared key credential used to sign requests. + /// + internal DataLakeFileSystemClient( + Uri fileSystemUri, + HttpPipelinePolicy authentication, + DataLakeClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) { DataLakeUriBuilder uriBuilder = new DataLakeUriBuilder(fileSystemUri); options ??= new DataLakeClientOptions(); @@ -264,6 +283,7 @@ internal DataLakeFileSystemClient(Uri fileSystemUri, HttpPipelinePolicy authenti _pipeline = options.Build(authentication); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = storageSharedKeyCredential; _containerClient = BlobContainerClientInternals.Create(_blobUri, _pipeline, Version.AsBlobsVersion(), _clientDiagnostics); } @@ -2277,5 +2297,86 @@ public virtual async Task> SetAccessPolicyAsync( } } #endregion SetAccessPolicy + + #region GenerateSas + /// + /// The + /// returns a that generates a DataLake FileSystem Service + /// Shared Access Signature (SAS) Uri based on the + /// properties and parameters passed. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a service SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(DataLakeFileSystemSasPermissions permissions, DateTimeOffset expiresOn) => + GenerateSasUri(new DataLakeSasBuilder(permissions, expiresOn) { FileSystemName = Name }); + + /// + /// The returns a + /// that generates a DataLake FileSystem Service Shared Access Signature (SAS) + /// Uri based on the Client properties and builder passed. + /// The SAS is signed by the shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a Service SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS). + /// + /// + /// A on successfully deleting. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Uri GenerateSasUri( + DataLakeSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.FileSystemName.Equals(Name, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.FileSystemName), + nameof(DataLakeSasBuilder), + nameof(Name)); + } + if (!string.IsNullOrEmpty(builder.Path)) + { + throw Errors.SasBuilderEmptyParam( + nameof(builder), + nameof(builder.Path), + nameof(Constants.DataLake.FileSystemName)); + } + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) + { + Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString() + }; + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakePathClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakePathClient.cs index b8e8e28c7cd61..4b846cb8b8665 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakePathClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakePathClient.cs @@ -176,6 +176,17 @@ public virtual string Name } } + /// + /// The used to authenticate and generate SAS + /// + internal readonly StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -195,7 +206,7 @@ protected DataLakePathClient() /// resource. /// public DataLakePathClient(Uri pathUri) - : this(pathUri, (HttpPipelinePolicy)null, null) + : this(pathUri, (HttpPipelinePolicy)null, null, null) { } @@ -214,7 +225,7 @@ public DataLakePathClient(Uri pathUri) /// applied to every request. /// public DataLakePathClient(Uri pathUri, DataLakeClientOptions options) - : this(pathUri, (HttpPipelinePolicy)null, options) + : this(pathUri, (HttpPipelinePolicy)null, options, null) { } @@ -231,7 +242,7 @@ public DataLakePathClient(Uri pathUri, DataLakeClientOptions options) /// The shared key credential used to sign requests. /// public DataLakePathClient(Uri pathUri, StorageSharedKeyCredential credential) - : this(pathUri, credential.AsPolicy(), null) + : this(pathUri, credential.AsPolicy(), null, credential) { } @@ -253,7 +264,7 @@ public DataLakePathClient(Uri pathUri, StorageSharedKeyCredential credential) /// every request. /// public DataLakePathClient(Uri pathUri, StorageSharedKeyCredential credential, DataLakeClientOptions options) - : this(pathUri, credential.AsPolicy(), options) + : this(pathUri, credential.AsPolicy(), options, credential) { } @@ -270,7 +281,7 @@ public DataLakePathClient(Uri pathUri, StorageSharedKeyCredential credential, Da /// The token credential used to sign requests. /// public DataLakePathClient(Uri pathUri, TokenCredential credential) - : this(pathUri, credential.AsPolicy(), null) + : this(pathUri, credential.AsPolicy(), null, null) { Errors.VerifyHttpsTokenAuth(pathUri); } @@ -293,7 +304,7 @@ public DataLakePathClient(Uri pathUri, TokenCredential credential) /// every request. /// public DataLakePathClient(Uri pathUri, TokenCredential credential, DataLakeClientOptions options) - : this(pathUri, credential.AsPolicy(), options) + : this(pathUri, credential.AsPolicy(), options, null) { Errors.VerifyHttpsTokenAuth(pathUri); } @@ -329,7 +340,14 @@ public DataLakePathClient(DataLakeFileSystemClient fileSystemClient, string path /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal DataLakePathClient(Uri pathUri, HttpPipelinePolicy authentication, DataLakeClientOptions options) + /// + /// The shared key credential used to sign requests. + /// + internal DataLakePathClient( + Uri pathUri, + HttpPipelinePolicy authentication, + DataLakeClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) { DataLakeUriBuilder uriBuilder = new DataLakeUriBuilder(pathUri); options ??= new DataLakeClientOptions(); @@ -339,6 +357,7 @@ internal DataLakePathClient(Uri pathUri, HttpPipelinePolicy authentication, Data _pipeline = options.Build(authentication); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = storageSharedKeyCredential; _blockBlobClient = BlockBlobClientInternals.Create(_blobUri, _pipeline, Version.AsBlobsVersion(), _clientDiagnostics); } @@ -3207,5 +3226,97 @@ public virtual async Task> SetMetadataAsync( } } #endregion Set Metadata + + #region GenerateSas + /// + /// The + /// returns a that generates a DataLake Path Service + /// Shared Access Signature (SAS) Uri based on the Client properties and + /// parameters passed. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a service SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(DataLakeSasPermissions permissions, DateTimeOffset expiresOn) => + GenerateSasUri(new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = FileSystemName, + Path = Path + }); + + /// + /// The returns a + /// that generates a DataLake File Service Shared Access Signature (SAS) Uri + /// based on the Client properties and and builder. The SAS is signed + /// by the shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a Service SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS). + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Uri GenerateSasUri(DataLakeSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (builder.IsDirectory.GetValueOrDefault(false)) + { + throw Errors.SasIncorrectResourceType( + nameof(builder), + nameof(builder.IsDirectory), + nameof(Constants.FalseName), + nameof(this.GetType)); + } + if (!builder.FileSystemName.Equals(FileSystemName, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.FileSystemName), + nameof(DataLakeSasBuilder), + nameof(FileSystemName)); + } + if (!builder.Path.Equals(Path, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.Path), + nameof(DataLakeSasBuilder), + nameof(Path)); + } + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) + { + Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString() + }; + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeServiceClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeServiceClient.cs index 598ac38c7c259..3df9d6a636b34 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeServiceClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeServiceClient.cs @@ -10,6 +10,7 @@ using Azure.Core.Pipeline; using Azure.Storage.Blobs; using Azure.Storage.Files.DataLake.Models; +using Azure.Storage.Sas; using Metadata = System.Collections.Generic.IDictionary; namespace Azure.Storage.Files.DataLake @@ -95,6 +96,17 @@ public virtual string AccountName } } + /// + /// The used to authenticate and generate SAS + /// + private StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateAccountSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -112,7 +124,7 @@ protected DataLakeServiceClient() /// A referencing the Data Lake service. /// public DataLakeServiceClient(Uri serviceUri) - : this(serviceUri, (HttpPipelinePolicy)null, null) + : this(serviceUri, (HttpPipelinePolicy)null, null, null) { } @@ -129,7 +141,7 @@ public DataLakeServiceClient(Uri serviceUri) /// every request. /// public DataLakeServiceClient(Uri serviceUri, DataLakeClientOptions options) - : this(serviceUri, (HttpPipelinePolicy)null, options) + : this(serviceUri, (HttpPipelinePolicy)null, options, null) { } @@ -144,7 +156,7 @@ public DataLakeServiceClient(Uri serviceUri, DataLakeClientOptions options) /// The shared key credential used to sign requests. /// public DataLakeServiceClient(Uri serviceUri, StorageSharedKeyCredential credential) - : this(serviceUri, credential.AsPolicy(), null) + : this(serviceUri, credential.AsPolicy(), null, credential) { } @@ -164,7 +176,7 @@ public DataLakeServiceClient(Uri serviceUri, StorageSharedKeyCredential credenti /// every request. /// public DataLakeServiceClient(Uri serviceUri, StorageSharedKeyCredential credential, DataLakeClientOptions options) - : this(serviceUri, credential.AsPolicy(), options) + : this(serviceUri, credential.AsPolicy(), options, null, credential) { } @@ -179,7 +191,7 @@ public DataLakeServiceClient(Uri serviceUri, StorageSharedKeyCredential credenti /// The token credential used to sign requests. /// public DataLakeServiceClient(Uri serviceUri, TokenCredential credential) - : this(serviceUri, credential.AsPolicy(), null) + : this(serviceUri, credential.AsPolicy(), null, null) { Errors.VerifyHttpsTokenAuth(serviceUri); } @@ -200,7 +212,7 @@ public DataLakeServiceClient(Uri serviceUri, TokenCredential credential) /// every request. /// public DataLakeServiceClient(Uri serviceUri, TokenCredential credential, DataLakeClientOptions options) - : this(serviceUri, credential.AsPolicy(), options) + : this(serviceUri, credential.AsPolicy(), options, null) { Errors.VerifyHttpsTokenAuth(serviceUri); } @@ -220,8 +232,15 @@ public DataLakeServiceClient(Uri serviceUri, TokenCredential credential, DataLak /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal DataLakeServiceClient(Uri serviceUri, HttpPipelinePolicy authentication, DataLakeClientOptions options) - : this(serviceUri, authentication, options, null) + /// + /// The shared key credential used to sign requests. + /// + internal DataLakeServiceClient( + Uri serviceUri, + HttpPipelinePolicy authentication, + DataLakeClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) + : this(serviceUri, authentication, options, null, storageSharedKeyCredential) { } @@ -242,11 +261,15 @@ internal DataLakeServiceClient(Uri serviceUri, HttpPipelinePolicy authentication /// every request. /// /// + /// + /// The shared key credential used to sign requests. + /// internal DataLakeServiceClient( Uri serviceUri, HttpPipelinePolicy authentication, DataLakeClientOptions options, - ClientDiagnostics clientDiagnostics) + ClientDiagnostics clientDiagnostics, + StorageSharedKeyCredential storageSharedKeyCredential) { options ??= new DataLakeClientOptions(); _pipeline = options.Build(authentication); @@ -254,6 +277,7 @@ internal DataLakeServiceClient( _blobUri = new DataLakeUriBuilder(serviceUri).ToBlobUri(); _version = options.Version; _clientDiagnostics = clientDiagnostics ?? new ClientDiagnostics(options); + _storageSharedKeyCredential = storageSharedKeyCredential; _blobServiceClient = BlobServiceClientInternals.Create( _blobUri, _pipeline, @@ -268,7 +292,12 @@ internal DataLakeServiceClient( /// private class BlobServiceClientInternals : BlobServiceClient { - public static BlobServiceClient Create(Uri uri, HttpPipeline pipeline, HttpPipelinePolicy authentication, BlobClientOptions.ServiceVersion version, ClientDiagnostics diagnostics) + public static BlobServiceClient Create( + Uri uri, + HttpPipeline pipeline, + HttpPipelinePolicy authentication, + BlobClientOptions.ServiceVersion version, + ClientDiagnostics diagnostics) { return BlobServiceClient.CreateClient( uri, @@ -731,5 +760,87 @@ public virtual async Task DeleteFileSystemAsync( } } #endregion Delete File System + + #region GenerateSas + /// + /// The + /// returns a that generates a DataLake Account + /// Shared Access Signature (SAS) based on the Client properties + /// and parameters passed. The SAS is signed by the + /// shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing an Account SAS. + /// + /// + /// Specifies the list of permissions that can be set in the SasBuilder + /// See . + /// + /// + /// Specifies when to set the expires time in the sas builder. + /// + /// + /// Specifies the resource types associated with the shared access signature. + /// The user is restricted to operations on the specified resources. + /// See . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public Uri GenerateAccountSasUri( + AccountSasPermissions permissions, + DateTimeOffset expiresOn, + AccountSasResourceTypes resourceTypes) => + GenerateAccountSasUri(new AccountSasBuilder( + permissions, + expiresOn, + AccountSasServices.Blobs, + resourceTypes)); + + /// + /// The returns a + /// that generates a DataLake Account Shared Access Signature (SAS) + /// based on the Client properties and builder passed. + /// The SAS is signed by the shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing an Account SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS). + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public Uri GenerateAccountSasUri( + AccountSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.Services.HasFlag(AccountSasServices.Blobs)) + { + throw Errors.SasServiceNotMatching( + nameof(builder.Services), + nameof(builder), + nameof(AccountSasServices.Blobs)); + } + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri); + sasUri.Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString(); + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Sas/DataLakeSasBuilder.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/Sas/DataLakeSasBuilder.cs index 1cbc1f2f267a5..96732dd9ab753 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/Sas/DataLakeSasBuilder.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Sas/DataLakeSasBuilder.cs @@ -194,6 +194,61 @@ public class DataLakeSasBuilder /// private int? _directoryDepth; + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// This constructor has been deprecated. Please consider using + /// + /// to create a Service SAS. This change does not have any impact on how + /// your application generates or makes use of SAS tokens. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public DataLakeSasBuilder() + { + } + + /// + /// Initializes a new instance of the + /// class to create a Blob Service Sas. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + public DataLakeSasBuilder(DataLakeSasPermissions permissions, DateTimeOffset expiresOn) + { + ExpiresOn = expiresOn; + SetPermissions(permissions); + } + + /// + /// Initializes a new instance of the + /// class to create a Blob Service Sas. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + public DataLakeSasBuilder(DataLakeFileSystemSasPermissions permissions, DateTimeOffset expiresOn) + { + ExpiresOn = expiresOn; + SetPermissions(permissions); + } + /// /// Sets the permissions for a file SAS. /// @@ -513,7 +568,7 @@ public override string ToString() => base.ToString(); /// - /// Check if two BlobSasBuilder instances are equal. + /// Check if two DataLakeSasBuilder instances are equal. /// /// The instance to compare to. /// True if they're equal, false otherwise. @@ -522,9 +577,9 @@ public override bool Equals(object obj) => base.Equals(obj); /// - /// Get a hash code for the BlobSasBuilder. + /// Get a hash code for the DataLakeSasBuilder. /// - /// Hash code for the BlobSasBuilder. + /// Hash code for the DataLakeSasBuilder. [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() => base.GetHashCode(); } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/DirectoryClientTests.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/DirectoryClientTests.cs index 08e3aae053dfc..6a012410651f2 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/DirectoryClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/DirectoryClientTests.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Azure.Core; using Azure.Core.TestFramework; +using Azure.Identity; using Azure.Storage.Files.DataLake.Models; using Azure.Storage.Sas; using Azure.Storage.Test; @@ -4834,5 +4835,215 @@ private async Task SetUpDirectoryForListing(DataLakeDirectoryClient directoryCli await directory.CreateIfNotExistsAsync(); } } + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + + // Act - DataLakeDirectoryClient(Uri blobContainerUri, fileClientOptions options = default) + DataLakeDirectoryClient directory = new DataLakeDirectoryClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(directory.CanGenerateSasUri); + + // Act - DataLakeDirectoryClient(Uri blobContainerUri, StorageSharedKeyCredential credential, fileClientOptions options = default) + DataLakeDirectoryClient directory2 = new DataLakeDirectoryClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(directory2.CanGenerateSasUri); + + // Act - DataLakeDirectoryClient(Uri blobContainerUri, TokenCredential credential, fileClientOptions options = default) + var tokenCredentials = new DefaultAzureCredential(); + DataLakeDirectoryClient directory3 = new DataLakeDirectoryClient( + blobEndpoint, + tokenCredentials, + GetOptions()); + Assert.IsFalse(directory3.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewDirectoryName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakeDirectoryClient directoryClient = new DataLakeDirectoryClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + + // Act + Uri sasUri = directoryClient.GenerateSasUri(permissions, expiresOn); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true + }; + expectedUri.Sas = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewDirectoryName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakeDirectoryClient fileClient = new DataLakeDirectoryClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true, + StartsOn = startsOn + }; + + // Act + Uri sasUri = fileClient.GenerateSasUri(sasBuilder); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true, + StartsOn = startsOn + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongFileSystemName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string directoryName = GetNewDirectoryName(); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewFileSystemName() + "/" + directoryName; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient fileClient = new DataLakeDirectoryClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = GetNewFileSystemName(), // different filesytem name + Path = directoryName, + IsDirectory = true + }; + + // Act + try + { + fileClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakeDirectoryClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderWrongDirectoryName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string fileSystemName = GetNewFileSystemName(); + blobUriBuilder.Path += constants.Sas.Account + "/" + fileSystemName + "/" + GetNewDirectoryName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient containerClient = new DataLakeDirectoryClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = GetNewDirectoryName(), // different directory name + IsDirectory = true, + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None) + }; + + // Act + try + { + containerClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakeDirectoryClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderIsDirectoryError() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string fileSystemName = GetNewFileSystemName(); + string directoryName = GetNewDirectoryName(); + blobUriBuilder.Path += constants.Sas.Account + "/" + fileSystemName + "/" + directoryName; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient containerClient = new DataLakeDirectoryClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = directoryName, + IsDirectory = false, + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None), + }; + + // Act + try + { + containerClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakeFileClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/FileClientTests.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/FileClientTests.cs index 8a43e127eb5ef..b54ad575a1f97 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/FileClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/FileClientTests.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Azure.Core; using Azure.Core.TestFramework; +using Azure.Identity; using Azure.Storage.Files.DataLake.Models; using Azure.Storage.Sas; using Azure.Storage.Test; @@ -4708,5 +4709,211 @@ public async Task OpenWriteAsync_Close() await stream.CopyToAsync(openWriteStream); await openWriteStream.FlushAsync(); } + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + + // Act - DataLakeFileClient(Uri blobContainerUri, fileClientOptions options = default) + DataLakeFileClient file = new DataLakeFileClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(file.CanGenerateSasUri); + + // Act - DataLakeFileClient(Uri blobContainerUri, StorageSharedKeyCredential credential, fileClientOptions options = default) + DataLakeFileClient file2 = new DataLakeFileClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(file2.CanGenerateSasUri); + + // Act - DataLakeFileClient(Uri blobContainerUri, TokenCredential credential, fileClientOptions options = default) + var tokenCredentials = new DefaultAzureCredential(); + DataLakeFileClient file3 = new DataLakeFileClient( + blobEndpoint, + tokenCredentials, + GetOptions()); + Assert.IsFalse(file3.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakeFileClient fileClient = new DataLakeFileClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + + // Act + Uri sasUri = fileClient.GenerateSasUri(permissions, expiresOn); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakeFileClient fileClient = new DataLakeFileClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn + }; + + // Act + Uri sasUri = fileClient.GenerateSasUri(sasBuilder); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongFileSystemName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewFileSystemName() + "/" + path; + DataLakeFileClient fileClient = new DataLakeFileClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = GetNewFileSystemName(), // different filesystem name + Path = path, + }; + + // Act + try + { + fileClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakeFileClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderWrongFileName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string fileSystemName = GetNewFileSystemName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + fileSystemName + "/" + GetNewFileName(); + DataLakeFileClient fileClient = new DataLakeFileClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = GetNewFileName(), // different path + }; + + // Act + try + { + fileClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakeFileClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderIsDirectoryError() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string fileSystemName = GetNewFileSystemName(); + string fileName = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + fileSystemName + "/" + fileName; + + DataLakeFileClient fileClient = new DataLakeFileClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = GetNewFileName(), + IsDirectory = true, + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None), + ExpiresOn = Recording.UtcNow.AddHours(+1) + }; + + // Act + try + { + fileClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakeFileClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/FileSystemClientTests.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/FileSystemClientTests.cs index e890da2695ceb..e93c2da85f0bd 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/FileSystemClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/FileSystemClientTests.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Azure.Core; using Azure.Core.TestFramework; +using Azure.Identity; using Azure.Storage.Files.DataLake.Models; using Azure.Storage.Sas; using Azure.Storage.Test; @@ -1962,6 +1963,142 @@ public async Task GetFileClient_SpecialCharacters(string fileName) Assert.AreEqual(blobUri, dataLakeUriBuilder.ToUri()); } + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - DataLakeFileSystemClient(Uri blobContainerUri, BlobClientOptions options = default) + DataLakeFileSystemClient container3 = new DataLakeFileSystemClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(container3.CanGenerateSasUri); + + // Act - DataLakeFileSystemClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + DataLakeFileSystemClient container4 = new DataLakeFileSystemClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(container4.CanGenerateSasUri); + + // Act - DataLakeFileSystemClient(Uri blobContainerUri, TokenCredential credential, BlobClientOptions options = default) + var tokenCredentials = new DefaultAzureCredential(); + DataLakeFileSystemClient container5 = new DataLakeFileSystemClient( + blobEndpoint, + tokenCredentials, + GetOptions()); + Assert.IsFalse(container5.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + string fileSystemName = GetNewFileSystemName(); + DataLakeFileSystemSasPermissions permissions = DataLakeFileSystemSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + DataLakeFileSystemClient fileSystemClient = new DataLakeFileSystemClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + + // Act + Uri sasUri = fileSystemClient.GenerateSasUri(permissions, expiresOn); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint) + { + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + string fileSystemName = GetNewFileSystemName(); + DataLakeFileSystemSasPermissions permissions = DataLakeFileSystemSasPermissions.Read; + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + DataLakeFileSystemClient fileSystemClient = new DataLakeFileSystemClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemClient.Name, + StartsOn = startsOn + }; + + // Act + Uri sasUri = fileSystemClient.GenerateSasUri(sasBuilder); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + StartsOn = startsOn + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewFileSystemName(); + DataLakeFileSystemSasPermissions permissions = DataLakeFileSystemSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeFileSystemClient fileSystemClient = new DataLakeFileSystemClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = GetNewFileSystemName(), // different filesytem name + }; + + // Act + try + { + fileSystemClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakeFileSystemClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion + private IEnumerable Conditions_Data => new[] { diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/PathClientTests.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/PathClientTests.cs index 356e323d7ac85..334152a0cc543 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/PathClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/PathClientTests.cs @@ -4,6 +4,8 @@ using System; using System.Threading.Tasks; using Azure.Core; +using Azure.Identity; +using Azure.Storage.Files.DataLake.Models; using Azure.Storage.Sas; using Azure.Storage.Test; using NUnit.Framework; @@ -116,5 +118,213 @@ public async Task Ctor_FileSystemAndPath() await pathClient.GetPropertiesAsync(); await pathClient.GetAccessControlAsync(); } + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - DataLakePathClient(Uri blobContainerUri, BlobClientOptions options = default) + DataLakePathClient blob3 = new DataLakePathClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(blob3.CanGenerateSasUri); + + // Act - DataLakePathClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + DataLakePathClient blob4 = new DataLakePathClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(blob4.CanGenerateSasUri); + + // Act - DataLakePathClient(Uri blobContainerUri, TokenCredential credential, BlobClientOptions options = default) + var tokenCredentials = new DefaultAzureCredential(); + DataLakePathClient blob5 = new DataLakePathClient( + blobEndpoint, + tokenCredentials, + GetOptions()); + Assert.IsFalse(blob5.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakePathClient pathClient = new DataLakePathClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + + // Act + Uri sasUri = pathClient.GenerateSasUri(permissions, expiresOn); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint) + { + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakePathClient pathClient = new DataLakePathClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn + }; + + // Act + Uri sasUri = pathClient.GenerateSasUri(sasBuilder); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongFileSystemName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewFileSystemName() + "/" + path; + DataLakePathClient pathClient = new DataLakePathClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = GetNewFileSystemName(), // different filesystem name + Path = path, + }; + + // Act + try + { + pathClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakePathClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderWrongPath() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string fileSystemName = GetNewFileSystemName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + fileSystemName + "/" + GetNewFileName(); + DataLakePathClient containerClient = new DataLakePathClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = GetNewFileName(), // different path + }; + + // Act + try + { + containerClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakePathClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderIsDirectoryError() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string fileSystemName = GetNewFileSystemName(); + string fileName = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + fileSystemName + "/" + fileName; + DataLakePathClient containerClient = new DataLakePathClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = fileName, + IsDirectory = true, + }; + + // Act + try + { + containerClient.GenerateSasUri(sasBuilder); + + Assert.Fail("DataLakeFileClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/ServiceClientTests.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/ServiceClientTests.cs index e7be6af91a658..25d775c0c3a21 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/ServiceClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/ServiceClientTests.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Azure.Core; using Azure.Core.TestFramework; +using Azure.Identity; using Azure.Storage.Files.DataLake.Models; using Azure.Storage.Sas; using Azure.Storage.Test; @@ -251,5 +252,143 @@ public async Task DeleteFileSystemAsync() Assert.ThrowsAsync( async () => await fileSystem.GetPropertiesAsync()); } + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var uriEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (uriEndpoint, blobSecondaryEndpoint)); + + // Act - DataLakeServiceClient(Uri blobContainerUri, BlobClientOptions options = default) + DataLakeServiceClient serviceClient = new DataLakeServiceClient( + uriEndpoint, + GetOptions()); + Assert.IsFalse(serviceClient.CanGenerateAccountSasUri); + + // Act - DataLakeServiceClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + DataLakeServiceClient serviceClient2 = new DataLakeServiceClient( + uriEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(serviceClient2.CanGenerateAccountSasUri); + + // Act - DataLakeServiceClient(Uri blobContainerUri, TokenCredential credential, BlobClientOptions options = default) + var tokenCredentials = new DefaultAzureCredential(); + DataLakeServiceClient serviceClient3 = new DataLakeServiceClient( + uriEndpoint, + tokenCredentials, + GetOptions()); + Assert.IsFalse(serviceClient3.CanGenerateAccountSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + AccountSasPermissions permissions = AccountSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + DataLakeServiceClient serviceClient = new DataLakeServiceClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, GetOptions()); + + // Act + Uri sasUri = serviceClient.GenerateAccountSasUri( + permissions: permissions, + expiresOn: expiresOn, + resourceTypes: resourceTypes); + + // Assert + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, AccountSasServices.Blobs, resourceTypes); + UriBuilder expectedUri = new UriBuilder(blobEndpoint) + { + Query = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential).ToString() + }; + Assert.AreEqual(expectedUri.Uri.ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateAccountSas_Builder() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + AccountSasServices services = AccountSasServices.Blobs; + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + DataLakeServiceClient serviceClient = new DataLakeServiceClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, GetOptions()); + + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + StartsOn = startsOn + }; + + // Act + Uri sasUri = serviceClient.GenerateAccountSasUri(sasBuilder); + + // Assert + AccountSasBuilder sasBuilder2 = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + StartsOn = startsOn + }; + UriBuilder expectedUri = new UriBuilder(blobEndpoint); + expectedUri.Query += sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential).ToString(); + Assert.AreEqual(expectedUri.Uri.ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateAccountSas_WrongService_Service() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, blobStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AccountSasServices services = AccountSasServices.Files; // Wrong Service + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + DataLakeServiceClient serviceClient = new DataLakeServiceClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, GetOptions()); + + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None), + StartsOn = Recording.UtcNow.AddHours(-1) + }; + + // Add more properties on the builder + sasBuilder.SetPermissions(permissions); + + // Act + try + { + Uri sasUri = serviceClient.GenerateAccountSasUri(sasBuilder); + + Assert.Fail("BlobContainerClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + // the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..4aa9b24104433 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.5681169-07:00", + "RandomSeed": "1609952630" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..da47d8a91891a --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.7045000-07:00", + "RandomSeed": "1601302736" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..aa0ca97fd58bb --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.6721722-07:00", + "RandomSeed": "1131627253" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..51d701648e474 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.7065605-07:00", + "RandomSeed": "1313815364" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderIsDirectoryError.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderIsDirectoryError.json new file mode 100644 index 0000000000000..0015467bdde68 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderIsDirectoryError.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.6907812-07:00", + "RandomSeed": "1028753354" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderIsDirectoryErrorAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderIsDirectoryErrorAsync.json new file mode 100644 index 0000000000000..05f0ceee489bb --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderIsDirectoryErrorAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.7080903-07:00", + "RandomSeed": "772559032" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryName.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryName.json new file mode 100644 index 0000000000000..39da4033307f7 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.6930117-07:00", + "RandomSeed": "1950625490" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryNameAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryNameAsync.json new file mode 100644 index 0000000000000..9a6aae0af1d85 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.7097166-07:00", + "RandomSeed": "248100406" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongFileSystemName.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongFileSystemName.json new file mode 100644 index 0000000000000..cc88d6cc78741 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongFileSystemName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.6952629-07:00", + "RandomSeed": "345545757" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongFileSystemNameAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongFileSystemNameAsync.json new file mode 100644 index 0000000000000..c1fcb1406e0e5 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongFileSystemNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.7114620-07:00", + "RandomSeed": "1343927153" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..b01a5bf385282 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.6975479-07:00", + "RandomSeed": "502781499" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..f711442bf02b3 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:45:10.7146681-07:00", + "RandomSeed": "699978417" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..d262869e7f74c --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.7892933-07:00", + "RandomSeed": "439830383" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..5df9e8bbdf9bb --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.8939895-07:00", + "RandomSeed": "1852953517" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..e4e16395e8f2b --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.8606799-07:00", + "RandomSeed": "896147841" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..80b9f8b287bac --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.8978812-07:00", + "RandomSeed": "111647834" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderIsDirectoryError.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderIsDirectoryError.json new file mode 100644 index 0000000000000..1f5fa74816d06 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderIsDirectoryError.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.8795735-07:00", + "RandomSeed": "1799203689" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderIsDirectoryErrorAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderIsDirectoryErrorAsync.json new file mode 100644 index 0000000000000..200d455aaac50 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderIsDirectoryErrorAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.8994127-07:00", + "RandomSeed": "1458720450" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileName.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileName.json new file mode 100644 index 0000000000000..6d54d61404163 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.8819520-07:00", + "RandomSeed": "187026098" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileNameAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileNameAsync.json new file mode 100644 index 0000000000000..e5ad76c57a556 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.9010416-07:00", + "RandomSeed": "1674547547" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileSystemName.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileSystemName.json new file mode 100644 index 0000000000000..f45174ba8a1e8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileSystemName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.8840917-07:00", + "RandomSeed": "735974075" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileSystemNameAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileSystemNameAsync.json new file mode 100644 index 0000000000000..c68cd3bd5ef65 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileSystemNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.9028645-07:00", + "RandomSeed": "1156186517" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..702bdd7750681 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.8865625-07:00", + "RandomSeed": "933761021" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..54f3bb4348dcf --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:02.9044040-07:00", + "RandomSeed": "180091856" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..1ca6e638bdf1f --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:50.1288221-07:00", + "RandomSeed": "556560023" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..579f2c2caad59 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:50.2790673-07:00", + "RandomSeed": "1208338826" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..cdba9d0180f8f --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:50.2104770-07:00", + "RandomSeed": "518847496" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..f20f8596f1f23 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:50.2812771-07:00", + "RandomSeed": "1455146223" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_BuilderWrongName.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_BuilderWrongName.json new file mode 100644 index 0000000000000..414085ae1bb88 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_BuilderWrongName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:50.2320188-07:00", + "RandomSeed": "2030320879" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_BuilderWrongNameAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_BuilderWrongNameAsync.json new file mode 100644 index 0000000000000..50330fc6ead61 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_BuilderWrongNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:50.2827929-07:00", + "RandomSeed": "266003690" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..5e734d864bd70 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:50.2344265-07:00", + "RandomSeed": "1584069686" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..74c714191a1c9 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/FileSystemClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:46:50.2845883-07:00", + "RandomSeed": "1676173674" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..94fd5f547c1e6 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.3924835-07:00", + "RandomSeed": "1665025125" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..25755f2617602 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.5044936-07:00", + "RandomSeed": "629504787" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..ff24b6e5d283a --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.4702017-07:00", + "RandomSeed": "1417335127" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..d795d100cd855 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.5071671-07:00", + "RandomSeed": "1802821819" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderIsDirectoryError.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderIsDirectoryError.json new file mode 100644 index 0000000000000..012cdebb6449c --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderIsDirectoryError.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.4890199-07:00", + "RandomSeed": "831901457" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderIsDirectoryErrorAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderIsDirectoryErrorAsync.json new file mode 100644 index 0000000000000..e4466e919be04 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderIsDirectoryErrorAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.5093354-07:00", + "RandomSeed": "1786382219" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongFileSystemName.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongFileSystemName.json new file mode 100644 index 0000000000000..565c58e76fc79 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongFileSystemName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.4932649-07:00", + "RandomSeed": "219537188" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongFileSystemNameAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongFileSystemNameAsync.json new file mode 100644 index 0000000000000..0e087550a1bd8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongFileSystemNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.5114128-07:00", + "RandomSeed": "39665277" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongPath.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongPath.json new file mode 100644 index 0000000000000..7b4ca68bacb05 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongPath.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.4952829-07:00", + "RandomSeed": "1564994969" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongPathAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongPathAsync.json new file mode 100644 index 0000000000000..b9a01a037a9a8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_BuilderWrongPathAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.5134742-07:00", + "RandomSeed": "182280210" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..5736a1c9d04a0 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.4972281-07:00", + "RandomSeed": "128211097" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..ffd1e042bb753 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/PathClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:48:11.5149606-07:00", + "RandomSeed": "2142192057" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..3f1fff0a69d5c --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:49:13.3772419-07:00", + "RandomSeed": "1060659427" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..8486ae826fa8a --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:49:13.4798029-07:00", + "RandomSeed": "575311937" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json new file mode 100644 index 0000000000000..76f173ad6bd37 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:49:13.4492011-07:00", + "RandomSeed": "1623521437" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json new file mode 100644 index 0000000000000..f9756c7a6f1e7 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:49:13.4817700-07:00", + "RandomSeed": "1694626914" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json new file mode 100644 index 0000000000000..54a682dfe7f43 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:49:13.4695227-07:00", + "RandomSeed": "400485902" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json new file mode 100644 index 0000000000000..483915101cf66 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:49:13.4831902-07:00", + "RandomSeed": "1578061301" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..3a743978209ef --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:49:13.4720467-07:00", + "RandomSeed": "151229253" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..6a0c6f0a6047b --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/SessionRecords/ServiceClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:49:13.4846779-07:00", + "RandomSeed": "932549557" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/CHANGELOG.md b/sdk/storage/Azure.Storage.Files.Shares/CHANGELOG.md index 72f58aff81671..59cb26bc63460 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Files.Shares/CHANGELOG.md @@ -8,6 +8,8 @@ - Removed ability to create a ShareLeaseClient for a Share or Share Snapshot. This feature has been rescheduled for future release. - Fixed bug where File Share SDK coudn't handle SASs with start and expiry time in format other than yyyy-MM-ddTHH:mm:ssZ. - Added ability to set Position on streams created with ShareFileClient.OpenRead(). +- Added CanGenerateSasUri property and GenerateSasUri() to ShareFileClient, ShareDirectoryClient and ShareClient. +- Added CanGenerateSasUri property and GenerateAccountSasUri() to ShareServiceClient. ## 12.5.0-preview.1 (2020-09-30) - Added support for service version 2020-02-10. diff --git a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs index 84a6128095559..444853e92a2c7 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs @@ -8,6 +8,7 @@ public ShareClient(string connectionString, string shareName, Azure.Storage.File public ShareClient(System.Uri shareUri, Azure.Storage.Files.Shares.ShareClientOptions options = null) { } public ShareClient(System.Uri shareUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Files.Shares.ShareClientOptions options = null) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateSasUri { get { throw null; } } public virtual string Name { get { throw null; } } public virtual System.Uri Uri { get { throw null; } } public virtual Azure.Response Create(Azure.Storage.Files.Shares.Models.ShareCreateOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -43,6 +44,8 @@ public ShareClient(System.Uri shareUri, Azure.Storage.StorageSharedKeyCredential public virtual System.Threading.Tasks.Task> DeleteIfExistsAsync(bool includeSnapshots = true, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Exists(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> ExistsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.ShareSasBuilder builder) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.ShareSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } public virtual Azure.Response> GetAccessPolicy(Azure.Storage.Files.Shares.Models.ShareFileRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response> GetAccessPolicy(System.Threading.CancellationToken cancellationToken) { throw null; } @@ -109,6 +112,7 @@ public ShareDirectoryClient(string connectionString, string shareName, string di public ShareDirectoryClient(System.Uri directoryUri, Azure.Storage.Files.Shares.ShareClientOptions options = null) { } public ShareDirectoryClient(System.Uri directoryUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Files.Shares.ShareClientOptions options = null) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateSasUri { get { throw null; } } public virtual string Name { get { throw null; } } public virtual string Path { get { throw null; } } public virtual string ShareName { get { throw null; } } @@ -143,6 +147,8 @@ public ShareDirectoryClient(System.Uri directoryUri, Azure.Storage.StorageShared public virtual System.Threading.Tasks.Task ForceCloseAllHandlesAsync(bool? recursive = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response ForceCloseHandle(string handleId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> ForceCloseHandleAsync(string handleId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.ShareFileSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.ShareSasBuilder builder) { throw null; } public virtual Azure.Storage.Files.Shares.ShareFileClient GetFileClient(string fileName) { throw null; } public virtual Azure.Pageable GetFilesAndDirectories(string prefix = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.AsyncPageable GetFilesAndDirectoriesAsync(string prefix = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -165,6 +171,7 @@ public ShareFileClient(string connectionString, string shareName, string filePat public ShareFileClient(System.Uri fileUri, Azure.Storage.Files.Shares.ShareClientOptions options = null) { } public ShareFileClient(System.Uri fileUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Files.Shares.ShareClientOptions options = null) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateSasUri { get { throw null; } } public virtual string Name { get { throw null; } } public virtual string Path { get { throw null; } } public virtual string ShareName { get { throw null; } } @@ -203,6 +210,8 @@ public ShareFileClient(System.Uri fileUri, Azure.Storage.StorageSharedKeyCredent public virtual System.Threading.Tasks.Task ForceCloseAllHandlesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response ForceCloseHandle(string handleId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> ForceCloseHandleAsync(string handleId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.ShareFileSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.ShareSasBuilder builder) { throw null; } public virtual Azure.Pageable GetHandles(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.AsyncPageable GetHandlesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response GetProperties(Azure.Storage.Files.Shares.Models.ShareFileRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -281,6 +290,7 @@ public ShareServiceClient(string connectionString, Azure.Storage.Files.Shares.Sh public ShareServiceClient(System.Uri serviceUri, Azure.Storage.Files.Shares.ShareClientOptions options = null) { } public ShareServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Files.Shares.ShareClientOptions options = null) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateAccountSasUri { get { throw null; } } public virtual System.Uri Uri { get { throw null; } } public virtual Azure.Response CreateShare(string shareName, Azure.Storage.Files.Shares.Models.ShareCreateOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -294,6 +304,8 @@ public ShareServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyC public virtual System.Threading.Tasks.Task DeleteShareAsync(string shareName, Azure.Storage.Files.Shares.Models.ShareDeleteOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task DeleteShareAsync(string shareName, bool includeSnapshots = true, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Uri GenerateAccountSasUri(Azure.Storage.Sas.AccountSasBuilder builder) { throw null; } + public System.Uri GenerateAccountSasUri(Azure.Storage.Sas.AccountSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Sas.AccountSasResourceTypes resourceTypes) { throw null; } public virtual Azure.Response GetProperties(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetPropertiesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Storage.Files.Shares.ShareClient GetShareClient(string shareName) { throw null; } @@ -953,7 +965,10 @@ public enum ShareFileSasPermissions } public partial class ShareSasBuilder { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public ShareSasBuilder() { } + public ShareSasBuilder(Azure.Storage.Sas.ShareFileSasPermissions permissions, System.DateTimeOffset expiresOn) { } + public ShareSasBuilder(Azure.Storage.Sas.ShareSasPermissions permissions, System.DateTimeOffset expiresOn) { } public string CacheControl { get { throw null; } set { } } public string ContentDisposition { get { throw null; } set { } } public string ContentEncoding { get { throw null; } set { } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Sas/ShareSasBuilder.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Sas/ShareSasBuilder.cs index fffb7666033a2..ec80fdb40a943 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Sas/ShareSasBuilder.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Sas/ShareSasBuilder.cs @@ -125,6 +125,61 @@ public class ShareSasBuilder /// public string ContentType { get; set; } + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// This constructor has been deprecated. Please consider using + /// + /// to create a Service SAS. This change does not have any impact on how + /// your application generates or makes use of SAS tokens. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ShareSasBuilder() + { + } + + /// + /// Initializes a new instance of the + /// class to create a Blob Service Sas. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + public ShareSasBuilder(ShareFileSasPermissions permissions, DateTimeOffset expiresOn) + { + ExpiresOn = expiresOn; + SetPermissions(permissions); + } + + /// + /// Initializes a new instance of the + /// class to create a Blob Container Service Sas. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + public ShareSasBuilder(ShareSasPermissions permissions, DateTimeOffset expiresOn) + { + ExpiresOn = expiresOn; + SetPermissions(permissions); + } + /// /// Sets the permissions for a file SAS. /// diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareClient.cs index 350cc7f0df394..f5a3e3e5b0906 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareClient.cs @@ -12,6 +12,7 @@ using Azure.Core; using Azure.Core.Pipeline; using Azure.Storage.Files.Shares.Models; +using Azure.Storage.Sas; using Metadata = System.Collections.Generic.IDictionary; namespace Azure.Storage.Files.Shares @@ -100,6 +101,17 @@ public virtual string Name } } + /// + /// The used to authenticate and generate SAS + /// + private readonly StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -160,6 +172,7 @@ public ShareClient(string connectionString, string shareName, ShareClientOptions _pipeline = options.Build(conn.Credentials); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = conn.Credentials as StorageSharedKeyCredential; } /// @@ -176,7 +189,7 @@ public ShareClient(string connectionString, string shareName, ShareClientOptions /// every request. /// public ShareClient(Uri shareUri, ShareClientOptions options = default) - : this(shareUri, (HttpPipelinePolicy)null, options) + : this(shareUri, (HttpPipelinePolicy)null, options, null) { } @@ -197,7 +210,7 @@ public ShareClient(Uri shareUri, ShareClientOptions options = default) /// every request. /// public ShareClient(Uri shareUri, StorageSharedKeyCredential credential, ShareClientOptions options = default) - : this(shareUri, credential.AsPolicy(), options) + : this(shareUri, credential.AsPolicy(), options, credential) { } @@ -217,13 +230,21 @@ public ShareClient(Uri shareUri, StorageSharedKeyCredential credential, ShareCli /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal ShareClient(Uri shareUri, HttpPipelinePolicy authentication, ShareClientOptions options) + /// + /// The shared key credential used to sign requests. + /// + internal ShareClient( + Uri shareUri, + HttpPipelinePolicy authentication, + ShareClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) { options ??= new ShareClientOptions(); _uri = shareUri; _pipeline = options.Build(authentication); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = storageSharedKeyCredential; } /// @@ -3147,5 +3168,84 @@ await GetDirectoryClient(directoryName) operationName: $"{nameof(ShareClient)}.{nameof(DeleteDirectory)}") .ConfigureAwait(false); #endregion DeleteDirectory + + #region GenerateSas + /// + /// The + /// returns a that generates a Share Service + /// Shared Access Signature (SAS) Uri based on the + /// Client properties and parameters passed. + /// The SAS is signed by the shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a service SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(ShareSasPermissions permissions, DateTimeOffset expiresOn) => + GenerateSasUri(new ShareSasBuilder(permissions, expiresOn) { ShareName = Name }); + + /// + /// The returns a + /// that generates a Blob Container Service Shared Access Signature (SAS) Uri + /// based on the Client properties and builder passed. + /// The SAS is signed by the shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a Service SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS) + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(ShareSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.ShareName.Equals(Name, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.ShareName), + nameof(ShareSasBuilder), + nameof(Name)); + } + if (!string.IsNullOrEmpty(builder.FilePath)) + { + throw Errors.SasBuilderEmptyParam( + nameof(builder), + nameof(builder.FilePath), + nameof(Constants.File.Share.Name)); + } + ShareUriBuilder sasUri = new ShareUriBuilder(Uri) + { + Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString() + }; + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareDirectoryClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareDirectoryClient.cs index 88ef8ea7d503a..5bf41f8afecb8 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareDirectoryClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareDirectoryClient.cs @@ -9,6 +9,7 @@ using Azure.Core; using Azure.Core.Pipeline; using Azure.Storage.Files.Shares.Models; +using Azure.Storage.Sas; using Azure.Storage.Shared; using Metadata = System.Collections.Generic.IDictionary; @@ -131,6 +132,17 @@ public virtual string Path } } + /// + /// The used to authenticate and generate SAS + /// + internal readonly StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -200,6 +212,7 @@ public ShareDirectoryClient(string connectionString, string shareName, string di _pipeline = options.Build(conn.Credentials); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = conn.Credentials as StorageSharedKeyCredential; } /// @@ -217,7 +230,7 @@ public ShareDirectoryClient(string connectionString, string shareName, string di /// applied to every request. /// public ShareDirectoryClient(Uri directoryUri, ShareClientOptions options = default) - : this(directoryUri, (HttpPipelinePolicy)null, options) + : this(directoryUri, (HttpPipelinePolicy)null, options, null) { } @@ -239,7 +252,7 @@ public ShareDirectoryClient(Uri directoryUri, ShareClientOptions options = defau /// every request. /// public ShareDirectoryClient(Uri directoryUri, StorageSharedKeyCredential credential, ShareClientOptions options = default) - : this(directoryUri, credential.AsPolicy(), options) + : this(directoryUri, credential.AsPolicy(), options, credential) { } @@ -260,13 +273,21 @@ public ShareDirectoryClient(Uri directoryUri, StorageSharedKeyCredential credent /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal ShareDirectoryClient(Uri directoryUri, HttpPipelinePolicy authentication, ShareClientOptions options) + /// + /// The shared key credential used to sign requests. + /// + internal ShareDirectoryClient( + Uri directoryUri, + HttpPipelinePolicy authentication, + ShareClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) { options ??= new ShareClientOptions(); _uri = directoryUri; _pipeline = options.Build(authentication); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = storageSharedKeyCredential; } /// @@ -2609,5 +2630,89 @@ await GetFileClient(fileName) cancellationToken) .ConfigureAwait(false); #endregion DeleteFile + + #region GenerateSas + /// + /// The + /// returns a that generates a Share Directory Service + /// Shared Access Signature (SAS) Uri based on the Client properties and + /// parameters passed. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a service SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(ShareFileSasPermissions permissions, DateTimeOffset expiresOn) => + GenerateSasUri(new ShareSasBuilder(permissions, expiresOn) + { + ShareName = ShareName, + FilePath = Path + }); + + /// + /// The returns a + /// that generates a Share Directory Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and and builder. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a Service SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS) + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(ShareSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.ShareName.Equals(ShareName, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.ShareName), + nameof(ShareSasBuilder), + nameof(ShareName)); + } + if (!builder.FilePath.Equals(Path, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.FilePath), + nameof(ShareSasBuilder), + nameof(Path)); + } + ShareUriBuilder sasUri = new ShareUriBuilder(Uri) + { + Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString() + }; + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareFileClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareFileClient.cs index 8273a440a13ac..f2ae75dbd1e73 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareFileClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareFileClient.cs @@ -16,6 +16,7 @@ using Azure.Core.Pipeline; using Azure.Storage.Files.Shares.Models; using Azure.Storage.Shared; +using Azure.Storage.Sas; using Metadata = System.Collections.Generic.IDictionary; namespace Azure.Storage.Files.Shares @@ -137,6 +138,17 @@ public virtual string Path } } + /// + /// The used to authenticate and generate SAS + /// + internal readonly StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateSasUri => _storageSharedKeyCredential != null; + //const string fileType = "file"; //// FileMaxUploadRangeBytes indicates the maximum number of bytes that can be sent in a call to UploadRange. @@ -214,6 +226,7 @@ public ShareFileClient(string connectionString, string shareName, string filePat _pipeline = options.Build(conn.Credentials); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = conn.Credentials as StorageSharedKeyCredential; } /// @@ -230,7 +243,7 @@ public ShareFileClient(string connectionString, string shareName, string filePat /// applied to every request. /// public ShareFileClient(Uri fileUri, ShareClientOptions options = default) - : this(fileUri, (HttpPipelinePolicy)null, options) + : this(fileUri, (HttpPipelinePolicy)null, options, null) { } @@ -251,7 +264,7 @@ public ShareFileClient(Uri fileUri, ShareClientOptions options = default) /// applied to every request. /// public ShareFileClient(Uri fileUri, StorageSharedKeyCredential credential, ShareClientOptions options = default) - : this(fileUri, credential.AsPolicy(), options) + : this(fileUri, credential.AsPolicy(), options, credential) { } @@ -272,13 +285,21 @@ public ShareFileClient(Uri fileUri, StorageSharedKeyCredential credential, Share /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal ShareFileClient(Uri fileUri, HttpPipelinePolicy authentication, ShareClientOptions options) + /// + /// The shared key credential used to sign requests. + /// + internal ShareFileClient( + Uri fileUri, + HttpPipelinePolicy authentication, + ShareClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) { options ??= new ShareClientOptions(); _uri = fileUri; _pipeline = options.Build(authentication); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = storageSharedKeyCredential; } /// @@ -5183,5 +5204,90 @@ private async Task OpenWriteInternal( } } #endregion OpenWrite + + #region GenerateSas + /// + /// The + /// returns a that generates a Share File Service + /// Shared Access Signature (SAS) Uri based on the Client properties and + /// parameters passed. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a service SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(ShareFileSasPermissions permissions, DateTimeOffset expiresOn) => + GenerateSasUri(new ShareSasBuilder(permissions, expiresOn) + { + ShareName = ShareName, + FilePath = Path + }); + + /// + /// The returns a + /// that generates a Share File Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and and builder. The SAS is signed by the shared key credential + /// of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing a Service SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS) + /// + /// + /// A on successfully deleting. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Uri GenerateSasUri(ShareSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.ShareName.Equals(ShareName, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.ShareName), + nameof(ShareSasBuilder), + nameof(ShareName)); + } + if (!builder.FilePath.Equals(Path, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.FilePath), + nameof(ShareSasBuilder), + nameof(Path)); + } + ShareUriBuilder sasUri = new ShareUriBuilder(Uri) + { + Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString() + }; + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareServiceClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareServiceClient.cs index 58b4abaf39bba..6ae387076c585 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareServiceClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareServiceClient.cs @@ -10,6 +10,7 @@ using Azure.Core; using Azure.Core.Pipeline; using Azure.Storage.Files.Shares.Models; +using Azure.Storage.Sas; namespace Azure.Storage.Files.Shares { @@ -84,6 +85,17 @@ public virtual string AccountName } } + /// + /// The used to authenticate and generate SAS + /// + private StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateAccountSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -137,6 +149,7 @@ public ShareServiceClient(string connectionString, ShareClientOptions options) _pipeline = options.Build(conn.Credentials); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = conn.Credentials as StorageSharedKeyCredential; } /// @@ -152,7 +165,7 @@ public ShareServiceClient(string connectionString, ShareClientOptions options) /// every request. /// public ShareServiceClient(Uri serviceUri, ShareClientOptions options = default) - : this(serviceUri, (HttpPipelinePolicy)null, options) + : this(serviceUri, (HttpPipelinePolicy)null, options, null) { } @@ -172,7 +185,7 @@ public ShareServiceClient(Uri serviceUri, ShareClientOptions options = default) /// every request. /// public ShareServiceClient(Uri serviceUri, StorageSharedKeyCredential credential, ShareClientOptions options = default) - : this(serviceUri, credential.AsPolicy(), options) + : this(serviceUri, credential.AsPolicy(), options, credential) { } @@ -191,13 +204,21 @@ public ShareServiceClient(Uri serviceUri, StorageSharedKeyCredential credential, /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal ShareServiceClient(Uri serviceUri, HttpPipelinePolicy authentication, ShareClientOptions options) + /// + /// The shared key credential used to sign requests. + /// + internal ShareServiceClient( + Uri serviceUri, + HttpPipelinePolicy authentication, + ShareClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) { options ??= new ShareClientOptions(); _uri = serviceUri; _pipeline = options.Build(authentication); _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); + _storageSharedKeyCredential = storageSharedKeyCredential; } #endregion ctors @@ -1145,5 +1166,88 @@ private async Task> UndeleteShareInternal( } } #endregion + + #region GenerateSas + /// + /// The + /// returns a that generates a Share Account + /// Shared Access Signature (SAS) based on the Client properties + /// and parameters passed. The SAS is signed by the + /// shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing an Account SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. The time at which the shared access signature becomes invalid. + /// + /// + /// Specifies the resource types associated with the shared access signature. + /// The user is restricted to operations on the specified resources. + /// See . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + public Uri GenerateAccountSasUri( + AccountSasPermissions permissions, + DateTimeOffset expiresOn, + AccountSasResourceTypes resourceTypes) => + GenerateAccountSasUri(new AccountSasBuilder( + permissions, + expiresOn, + AccountSasServices.Files, + resourceTypes)); + + /// + /// The + /// returns a that generates a Share Account + /// Shared Access Signature (SAS) based on the Client properties + /// and builder passed. The SAS is signed by the + /// shared key credential of the client. + /// + /// To check if the client is able to sign a Service Sas see + /// . + /// + /// For more information, see + /// + /// Constructing an Account SAS. + /// + /// + /// Used to generate a Shared Access Signature (SAS) + /// + /// + /// A on successfully deleting. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public Uri GenerateAccountSasUri(AccountSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.Services.HasFlag(AccountSasServices.Files)) + { + throw Errors.SasServiceNotMatching( + nameof(builder.Services), + nameof(builder), + nameof(AccountSasServices.Files)); + } + UriBuilder sasUri = new UriBuilder(Uri); + sasUri.Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString(); + return sasUri.Uri; + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareUriBuilder.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareUriBuilder.cs index a578ddbb48ad0..d437530a990a5 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareUriBuilder.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareUriBuilder.cs @@ -30,10 +30,9 @@ public class ShareUriBuilder private Uri _uri; /// - /// Whether the Uri is an IP Uri as determined by - /// . + /// Whether the Uri is a path-style Uri (i.e. it is an IP Uri or the domain includes a port that is used by the local emulator). /// - private readonly bool _isIPStyleUri; + private readonly bool _isPathStyleUri; /// /// Gets or sets the scheme name of the URI. @@ -158,6 +157,8 @@ public string Query /// public ShareUriBuilder(Uri uri) { + uri = uri ?? throw new ArgumentNullException(nameof(uri)); + Scheme = uri.Scheme; Host = uri.Host; Port = uri.Port; @@ -173,14 +174,13 @@ public ShareUriBuilder(Uri uri) if (!string.IsNullOrEmpty(uri.AbsolutePath)) { // If path starts with a slash, remove it - var path = uri.GetPath(); var startIndex = 0; if (uri.IsHostIPEndPointStyle()) { - _isIPStyleUri = true; + _isPathStyleUri = true; var accountEndIndex = path.IndexOf("/", StringComparison.InvariantCulture); // Slash not found; path has account name & no share name @@ -281,7 +281,7 @@ private RequestUriBuilder BuildUri() var path = new StringBuilder(""); // only append the account name to the path for Ip style Uri. // regular style Uri will already have account name in domain - if (_isIPStyleUri && !string.IsNullOrWhiteSpace(AccountName)) + if (_isPathStyleUri && !string.IsNullOrWhiteSpace(AccountName)) { path.Append("/").Append(AccountName); } diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/DirectoryClientTests.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/DirectoryClientTests.cs index d41efba04d0f5..66296cea84419 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/DirectoryClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/DirectoryClientTests.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading.Tasks; using Azure.Core.TestFramework; +using Azure.Identity; using Azure.Storage.Files.Shares.Models; using Azure.Storage.Files.Shares.Tests; using Azure.Storage.Sas; @@ -1114,5 +1115,195 @@ public async Task GetSubDirectoryClient_SpecialCharacters(string directoryName, Assert.AreEqual(path, shareUriBuilder.DirectoryOrFilePath); Assert.AreEqual(expectedUri, shareUriBuilder.ToUri()); } + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - ShareDirectoryClient(string connectionString, string blobContainerName, string blobName) + ShareDirectoryClient directory = new ShareDirectoryClient( + connectionString, + GetNewShareName(), + GetNewDirectoryName()); + Assert.IsTrue(directory.CanGenerateSasUri); + + // Act - ShareDirectoryClient(string connectionString, string blobContainerName, string blobName, BlobClientOptions options) + ShareDirectoryClient directory2 = new ShareDirectoryClient( + connectionString, + GetNewShareName(), + GetNewDirectoryName(), + GetOptions()); + Assert.IsTrue(directory2.CanGenerateSasUri); + + // Act - ShareDirectoryClient(Uri blobContainerUri, BlobClientOptions options = default) + ShareDirectoryClient directory3 = new ShareDirectoryClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(directory3.CanGenerateSasUri); + + // Act - ShareDirectoryClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + ShareDirectoryClient directory4 = new ShareDirectoryClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(directory4.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + ShareFileSasPermissions permissions =ShareFileSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string shareName = GetNewShareName(); + string directoryName = GetNewDirectoryName(); + ShareDirectoryClient directoryClient = new ShareDirectoryClient( + connectionString, + shareName, + directoryName, + GetOptions()); + + // Act + Uri sasUri = directoryClient.GenerateSasUri(permissions, expiresOn); + + // Assert + ShareSasBuilder sasBuilder = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = shareName, + FilePath = directoryName, + }; + ShareUriBuilder expectedUri = new ShareUriBuilder(blobEndpoint) + { + ShareName = shareName, + DirectoryOrFilePath = directoryName, + Sas = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + ShareFileSasPermissions permissions = ShareFileSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string shareName = GetNewShareName(); + string directoryName = GetNewDirectoryName(); + + ShareDirectoryClient directoryClient = new ShareDirectoryClient( + connectionString, + shareName, + directoryName, + GetOptions()); + + ShareSasBuilder sasBuilder = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = shareName, + FilePath = directoryName, + StartsOn = startsOn + }; + + // Act + Uri sasUri = directoryClient.GenerateSasUri(sasBuilder); + + // Assert + ShareSasBuilder sasBuilder2 = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = shareName, + FilePath = directoryName, + StartsOn = startsOn + }; + ShareUriBuilder expectedUri = new ShareUriBuilder(blobEndpoint) + { + ShareName = shareName, + DirectoryOrFilePath = directoryName, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongShareName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string directoryName = GetNewDirectoryName(); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewShareName() + "/" + directoryName; + ShareDirectoryClient directoryClient = new ShareDirectoryClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + ShareSasBuilder sasBuilder = new ShareSasBuilder(ShareFileSasPermissions.All, Recording.UtcNow.AddHours(+1)) + { + ShareName = GetNewShareName(), // different share name + FilePath = directoryName + }; + + // Act + try + { + Uri sasUri = directoryClient.GenerateSasUri(sasBuilder); + + Assert.Fail("ShareDirectoryClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderWrongDirectoryName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string shareName = GetNewShareName(); + blobUriBuilder.Path += constants.Sas.Account + "/" + shareName + "/" + GetNewDirectoryName(); + ShareDirectoryClient directoryClient = new ShareDirectoryClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + ShareSasBuilder sasBuilder = new ShareSasBuilder(ShareFileSasPermissions.All, Recording.UtcNow.AddHours(+1)) + { + ShareName = shareName, + FilePath = GetNewDirectoryName() // different directory name + }; + + // Act + try + { + Uri sasUri = directoryClient.GenerateSasUri(sasBuilder); + + Assert.Fail("ShareDirectoryClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/FileClientTests.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/FileClientTests.cs index 08a7cd60c9822..33d0fbc5df44b 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/FileClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/FileClientTests.cs @@ -3973,6 +3973,196 @@ await TestHelper.AssertExpectedExceptionAsync( e => Assert.AreEqual("LeaseNotPresentWithFileOperation", e.ErrorCode)); } + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - ShareDirectoryClient(string connectionString, string blobContainerName, string blobName) + ShareFileClient directory = new ShareFileClient( + connectionString, + GetNewShareName(), + GetNewDirectoryName()); + Assert.IsTrue(directory.CanGenerateSasUri); + + // Act - ShareFileClient(string connectionString, string blobContainerName, string blobName, BlobClientOptions options) + ShareFileClient directory2 = new ShareFileClient( + connectionString, + GetNewShareName(), + GetNewDirectoryName(), + GetOptions()); + Assert.IsTrue(directory2.CanGenerateSasUri); + + // Act - ShareFileClient(Uri blobContainerUri, BlobClientOptions options = default) + ShareFileClient directory3 = new ShareFileClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(directory3.CanGenerateSasUri); + + // Act - ShareFileClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + ShareFileClient directory4 = new ShareFileClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(directory4.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + ShareFileSasPermissions permissions = ShareFileSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string shareName = GetNewShareName(); + string fileName = GetNewFileName(); + ShareFileClient fileClient = new ShareFileClient( + connectionString, + shareName, + fileName, + GetOptions()); + + // Act + Uri sasUri = fileClient.GenerateSasUri(permissions, expiresOn); + + // Assert + ShareSasBuilder sasBuilder = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = shareName, + FilePath = fileName, + }; + ShareUriBuilder expectedUri = new ShareUriBuilder(blobEndpoint) + { + ShareName = shareName, + DirectoryOrFilePath = fileName, + Sas = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + ShareFileSasPermissions permissions = ShareFileSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string shareName = GetNewShareName(); + string fileName = GetNewFileName(); + + ShareFileClient directoryClient = new ShareFileClient( + connectionString, + shareName, + fileName, + GetOptions()); + + ShareSasBuilder sasBuilder = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = shareName, + FilePath = fileName, + StartsOn = startsOn + }; + + // Act + Uri sasUri = directoryClient.GenerateSasUri(sasBuilder); + + // Assert + ShareSasBuilder sasBuilder2 = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = shareName, + FilePath = fileName, + StartsOn = startsOn + }; + ShareUriBuilder expectedUri = new ShareUriBuilder(blobEndpoint) + { + ShareName = shareName, + DirectoryOrFilePath = fileName, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongShareName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder uriBuilder = new UriBuilder(blobEndpoint); + string fileName = GetNewFileName(); + uriBuilder.Path += constants.Sas.Account + "/" + GetNewShareName() + "/" + fileName; + ShareFileClient fileClient = new ShareFileClient( + uriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + ShareSasBuilder sasBuilder = new ShareSasBuilder(ShareFileSasPermissions.All, Recording.UtcNow.AddHours(+1)) + { + ShareName = GetNewShareName(), // different share name + FilePath = fileName + }; + + // Act + try + { + fileClient.GenerateSasUri(sasBuilder); + + Assert.Fail("ShareFileClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + + [Test] + public void GenerateSas_BuilderWrongFileName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + string shareName = GetNewShareName(); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + blobUriBuilder.Path += constants.Sas.Account + "/" + shareName + "/" + GetNewFileName(); + ShareFileClient containerClient = new ShareFileClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + ShareSasBuilder sasBuilder = new ShareSasBuilder(ShareFileSasPermissions.All, Recording.UtcNow.AddHours(+1)) + { + ShareName = shareName, + FilePath = GetNewFileName() // different file name + }; + + // Act + try + { + containerClient.GenerateSasUri(sasBuilder); + + Assert.Fail("ShareFileClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion + private async Task WaitForCopy(ShareFileClient file, int milliWait = 200) { CopyStatus status = CopyStatus.Pending; diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/FileSasBuilderTests.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/FileSasBuilderTests.cs index 84024c4cc9b99..4e7218883f4cf 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/FileSasBuilderTests.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/FileSasBuilderTests.cs @@ -215,6 +215,57 @@ public async Task SharePermissionsRawPermissions_Invalid() new ArgumentException("s is not a valid SAS permission")); } + [Test] + public void ShareUriBuilder_LocalDockerUrl_PortTest() + { + // Arrange + // BlobEndpoint from https://docs.microsoft.com/en-us/azure/storage/common/storage-use-emulator#connect-to-the-emulator-account-using-the-well-known-account-name-and-key + var uriString = "http://docker_container:10000/devstoreaccount1/sharename"; + var originalUri = new UriBuilder(uriString); + + // Act + var fileUriBuilder = new ShareUriBuilder(originalUri.Uri); + Uri newUri = fileUriBuilder.ToUri(); + + // Assert + Assert.AreEqual("http", fileUriBuilder.Scheme); + Assert.AreEqual("docker_container", fileUriBuilder.Host); + Assert.AreEqual("devstoreaccount1", fileUriBuilder.AccountName); + Assert.AreEqual("sharename", fileUriBuilder.ShareName); + Assert.AreEqual("", fileUriBuilder.DirectoryOrFilePath); + Assert.AreEqual("", fileUriBuilder.Snapshot); + Assert.IsNull(fileUriBuilder.Sas); + Assert.AreEqual("", fileUriBuilder.Query); + Assert.AreEqual(10000, fileUriBuilder.Port); + + Assert.AreEqual(originalUri, newUri); + } + + [Test] + public void ShareUriBuilder_CustomUri_AccountShareFileTest() + { + // Arrange + var uriString = "https://www.mycustomname.com/sharename/filename"; + var originalUri = new UriBuilder(uriString); + + // Act + var fileUriBuilder = new ShareUriBuilder(originalUri.Uri); + Uri newUri = fileUriBuilder.ToUri(); + + // Assert + Assert.AreEqual("https", fileUriBuilder.Scheme); + Assert.AreEqual("www.mycustomname.com", fileUriBuilder.Host); + Assert.AreEqual(String.Empty, fileUriBuilder.AccountName); + Assert.AreEqual("sharename", fileUriBuilder.ShareName); + Assert.AreEqual("filename", fileUriBuilder.DirectoryOrFilePath); + Assert.AreEqual("", fileUriBuilder.Snapshot); + Assert.IsNull(fileUriBuilder.Sas); + Assert.AreEqual("", fileUriBuilder.Query); + Assert.AreEqual(443, fileUriBuilder.Port); + + Assert.AreEqual(originalUri, newUri); + } + private ShareSasBuilder BuildFileSasBuilder(bool includeVersion, bool includeFilePath, TestConstants constants, string shareName, string filePath) { var fileSasBuilder = new ShareSasBuilder diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/ServiceClientTests.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/ServiceClientTests.cs index 63ff8ccedbae7..c2841c88d3531 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/ServiceClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/ServiceClientTests.cs @@ -8,6 +8,7 @@ using Azure.Core.TestFramework; using Azure.Storage.Files.Shares.Models; using Azure.Storage.Files.Shares.Tests; +using Azure.Storage.Sas; using Azure.Storage.Test; using NUnit.Framework; @@ -375,5 +376,137 @@ await TestHelper.AssertExpectedExceptionAsync( service.UndeleteShareAsync(GetNewShareName(), fakeVersion), e => Assert.AreEqual(ShareErrorCode.ShareNotFound.ToString(), e.ErrorCode)); } + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - ShareServiceClient(string connectionString) + ShareServiceClient share = new ShareServiceClient( + connectionString); + Assert.IsTrue(share.CanGenerateAccountSasUri); + + // Act - ShareServiceClient(string connectionString, string blobContainerName, BlobClientOptions options) + ShareServiceClient share2 = new ShareServiceClient( + connectionString, + GetOptions()); + Assert.IsTrue(share2.CanGenerateAccountSasUri); + + // Act - ShareServiceClient(Uri blobContainerUri, BlobClientOptions options = default) + ShareServiceClient share3 = new ShareServiceClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(share3.CanGenerateAccountSasUri); + + // Act - ShareServiceClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + ShareServiceClient share4 = new ShareServiceClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(share4.CanGenerateAccountSasUri); + } + + [Test] + public void GenerateAccountSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + var fileEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var fileSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (fileEndpoint, fileSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + ShareServiceClient serviceClient = new ShareServiceClient(connectionString, GetOptions()); + + // Act + Uri sasUri = serviceClient.GenerateAccountSasUri( + permissions: permissions, + expiresOn: expiresOn, + resourceTypes: resourceTypes); + + // Assert + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, AccountSasServices.Files, resourceTypes); + UriBuilder expectedUri = new UriBuilder(fileEndpoint) + { + Query = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential).ToString() + }; + Assert.AreEqual(expectedUri.Uri.ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateAccountSas_Builder() + { + var constants = new TestConstants(this); + var fileEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var fileSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (fileEndpoint, fileSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + AccountSasServices services = AccountSasServices.Files; + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + ShareServiceClient serviceClient = new ShareServiceClient(connectionString, GetOptions()); + + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + StartsOn = startsOn + }; + + // Act + Uri sasUri = serviceClient.GenerateAccountSasUri(sasBuilder); + + // Assert + AccountSasBuilder sasBuilder2 = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + StartsOn = startsOn + }; + UriBuilder expectedUri = new UriBuilder(fileEndpoint); + expectedUri.Query += sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential).ToString(); + Assert.AreEqual(expectedUri.Uri.ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateAccountSas_WrongService_Service() + { + var constants = new TestConstants(this); + var fileEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var fileSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (fileEndpoint, fileSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AccountSasServices services = AccountSasServices.Blobs; // Wrong Service + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + ShareServiceClient serviceClient = new ShareServiceClient(connectionString, GetOptions()); + + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None), + StartsOn = Recording.UtcNow.AddHours(-1) + }; + + // Act + try + { + Uri sasUri = serviceClient.GenerateAccountSasUri(sasBuilder); + + Assert.Fail("FileServiceClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + // the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..09cc007d256bc --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.3468243-07:00", + "RandomSeed": "638918712" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..7948ec7d1f2b7 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.4463249-07:00", + "RandomSeed": "2027360444" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..83c925aabcfe5 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.4161596-07:00", + "RandomSeed": "432158912" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..5acf7b2e6bf0a --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.4495215-07:00", + "RandomSeed": "6111685" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryName.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryName.json new file mode 100644 index 0000000000000..cb983e90eb505 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.4350804-07:00", + "RandomSeed": "147352617" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryNameAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryNameAsync.json new file mode 100644 index 0000000000000..8ceba7909ee7c --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongDirectoryNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.4509451-07:00", + "RandomSeed": "684711219" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongShareName.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongShareName.json new file mode 100644 index 0000000000000..ecba04787d902 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongShareName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.4372290-07:00", + "RandomSeed": "1369303298" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongShareNameAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongShareNameAsync.json new file mode 100644 index 0000000000000..2252e020ed5f1 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_BuilderWrongShareNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.4522965-07:00", + "RandomSeed": "353754564" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..5fb59f1aa3b32 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.4391602-07:00", + "RandomSeed": "256896015" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..485e1434ef92f --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/DirectoryClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:50:42.4539118-07:00", + "RandomSeed": "1553157681" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..007169851bbc7 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:34.9281814-07:00", + "RandomSeed": "77949684" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..8e2bc0e6fb31e --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:35.0257639-07:00", + "RandomSeed": "969725351" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..c166b442bfa02 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:34.9944229-07:00", + "RandomSeed": "1457055881" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..4d1fe1b5259fc --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:35.0278373-07:00", + "RandomSeed": "559197698" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileName.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileName.json new file mode 100644 index 0000000000000..bdcd409b7c85b --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:35.0132503-07:00", + "RandomSeed": "748472894" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileNameAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileNameAsync.json new file mode 100644 index 0000000000000..dbd37f3a70ecf --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongFileNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:35.0301164-07:00", + "RandomSeed": "1201934260" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongShareName.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongShareName.json new file mode 100644 index 0000000000000..0f4b07a66b560 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongShareName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:35.0157729-07:00", + "RandomSeed": "1112775380" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongShareNameAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongShareNameAsync.json new file mode 100644 index 0000000000000..f75934eac5d32 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_BuilderWrongShareNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:35.0317148-07:00", + "RandomSeed": "886210647" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..786d2c07610fe --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:35.0180586-07:00", + "RandomSeed": "874331698" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..6e2e2a4538a70 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:51:35.0330569-07:00", + "RandomSeed": "1282596919" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_CustomUri_AccountShareFileTest.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_CustomUri_AccountShareFileTest.json new file mode 100644 index 0000000000000..20e5015d169f8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_CustomUri_AccountShareFileTest.json @@ -0,0 +1,4 @@ +{ + "Entries": [], + "Variables": {} +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_CustomUri_AccountShareFileTestAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_CustomUri_AccountShareFileTestAsync.json new file mode 100644 index 0000000000000..20e5015d169f8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_CustomUri_AccountShareFileTestAsync.json @@ -0,0 +1,4 @@ +{ + "Entries": [], + "Variables": {} +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_LocalDockerUrl_PortTest.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_LocalDockerUrl_PortTest.json new file mode 100644 index 0000000000000..20e5015d169f8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_LocalDockerUrl_PortTest.json @@ -0,0 +1,4 @@ +{ + "Entries": [], + "Variables": {} +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_LocalDockerUrl_PortTestAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_LocalDockerUrl_PortTestAsync.json new file mode 100644 index 0000000000000..20e5015d169f8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/FileSasBuilderTests/ShareUriBuilder_LocalDockerUrl_PortTestAsync.json @@ -0,0 +1,4 @@ +{ + "Entries": [], + "Variables": {} +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..b4425444e8cce --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:52:38.0860651-07:00", + "RandomSeed": "1469355743" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..be23b63cc0aa7 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:52:38.2381728-07:00", + "RandomSeed": "436117886" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json new file mode 100644 index 0000000000000..c23116c4295ce --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:52:38.1482096-07:00", + "RandomSeed": "577356753" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json new file mode 100644 index 0000000000000..1c5e266429bd6 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:52:38.2399527-07:00", + "RandomSeed": "665860816" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParameters.json new file mode 100644 index 0000000000000..2cc4dea9588c4 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:07.2392294-07:00", + "RandomSeed": "1218487035" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..8b7847a630b17 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:07.2775169-07:00", + "RandomSeed": "1718887448" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json new file mode 100644 index 0000000000000..41905afd8e7d1 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:52:38.2310958-07:00", + "RandomSeed": "1573702727" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json new file mode 100644 index 0000000000000..8457677d9d7fb --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:52:38.2425149-07:00", + "RandomSeed": "1360682290" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..de860392da0e6 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:46.0002302-07:00", + "RandomSeed": "492750640" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..53ffe5261254c --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:46.0977026-07:00", + "RandomSeed": "755522867" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..5352d9e291725 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:46.0717308-07:00", + "RandomSeed": "725704909" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..4ec0c7a571270 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:46.0997349-07:00", + "RandomSeed": "326104069" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_BuilderWrongName.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_BuilderWrongName.json new file mode 100644 index 0000000000000..63a000eca94b1 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_BuilderWrongName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:46.0878949-07:00", + "RandomSeed": "1586481795" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_BuilderWrongNameAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_BuilderWrongNameAsync.json new file mode 100644 index 0000000000000..2e1cfa6d8fee4 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_BuilderWrongNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:46.1011321-07:00", + "RandomSeed": "1707855079" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..dfdf7403f4bd5 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:46.0900300-07:00", + "RandomSeed": "2065268560" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..ecb11a2c06e3f --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/SessionRecords/ShareClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:53:46.1031475-07:00", + "RandomSeed": "798897421" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTests.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTests.cs index 9cff3fe37bdd5..c67fa385e01ff 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTests.cs @@ -15,6 +15,7 @@ using Azure.Storage.Test; using NUnit.Framework; using System.Threading; +using Azure.Identity; namespace Azure.Storage.Files.Shares.Test { @@ -1974,5 +1975,144 @@ await TestHelper.AssertExpectedExceptionAsync( leaseClient.RenewAsync(), e => Assert.AreEqual(ShareErrorCode.ShareNotFound.ToString(), e.ErrorCode)); } + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - ShareClient(string connectionString, string blobContainerName) + ShareClient container = new ShareClient( + connectionString, + GetNewShareName()); + Assert.IsTrue(container.CanGenerateSasUri); + + // Act - ShareClient(string connectionString, string blobContainerName, BlobClientOptions options) + ShareClient container2 = new ShareClient( + connectionString, + GetNewShareName(), + GetOptions()); + Assert.IsTrue(container2.CanGenerateSasUri); + + // Act - ShareClient(Uri blobContainerUri, BlobClientOptions options = default) + ShareClient container3 = new ShareClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(container3.CanGenerateSasUri); + + // Act - ShareClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + ShareClient container4 = new ShareClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(container4.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string shareName = GetNewShareName(); + ShareSasPermissions permissions = ShareSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + ShareClient shareClient = new ShareClient(connectionString, shareName, GetOptions()); + + // Act + Uri sasUri = shareClient.GenerateSasUri(permissions, expiresOn); + + // Assert + ShareSasBuilder sasBuilder = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = shareName + }; + ShareUriBuilder expectedUri = new ShareUriBuilder(blobEndpoint) + { + ShareName = shareName, + Sas = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, fileStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string shareName = GetNewShareName(); + ShareSasPermissions permissions = ShareSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + ShareClient shareClient = new ShareClient(connectionString, shareName, GetOptions()); + + ShareSasBuilder sasBuilder = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = shareName, + StartsOn = startsOn + }; + + // Act + Uri sasUri = shareClient.GenerateSasUri(sasBuilder); + + // Assert + ShareSasBuilder sasBuilder2 = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = shareName, + StartsOn = startsOn + }; + ShareUriBuilder expectedUri = new ShareUriBuilder(blobEndpoint) + { + ShareName = shareName, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + ShareSasPermissions permissions = ShareSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewShareName(); + ShareClient shareCLient = new ShareClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + ShareSasBuilder sasBuilder = new ShareSasBuilder(permissions, expiresOn) + { + ShareName = GetNewShareName() + }; + + // Act + try + { + Uri sasUri = shareCLient.GenerateSasUri(sasBuilder); + + Assert.Fail("ShareClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Queues/CHANGELOG.md b/sdk/storage/Azure.Storage.Queues/CHANGELOG.md index e273134ec792b..d5fbc227467be 100644 --- a/sdk/storage/Azure.Storage.Queues/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Queues/CHANGELOG.md @@ -3,6 +3,8 @@ ## 12.5.0-preview.2 (Unreleased) - Fixed a bug where QueueServiceClient.SetProperties and QueueService.GetProperties where the creating/parsing XML Service Queue Properties CorsRules incorrectly causing Invalid XML Errors - Fixed bug where Queues SDK coudn't handle SASs with start and expiry time in format other than yyyy-MM-ddTHH:mm:ssZ. +- Added CanGenerateSasUri property, GenerateSasUri() to QueueClient. +- Added CanGenerateAccountSasUri property, GenerateAccountSasUri() to QueueServiceClient. ## 12.5.0-preview.1 (2020-09-30) - This preview contains bug fixes to improve quality. diff --git a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.0.cs b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.0.cs index db2f11bef95ec..9e8373626423c 100644 --- a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.0.cs @@ -9,6 +9,7 @@ public QueueClient(System.Uri queueUri, Azure.Core.TokenCredential credential, A public QueueClient(System.Uri queueUri, Azure.Storage.Queues.QueueClientOptions options = null) { } public QueueClient(System.Uri queueUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Queues.QueueClientOptions options = null) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateSasUri { get { throw null; } } public virtual int MaxPeekableMessages { get { throw null; } } public virtual int MessageMaxBytes { get { throw null; } } protected virtual System.Uri MessagesUri { get { throw null; } } @@ -28,6 +29,8 @@ public QueueClient(System.Uri queueUri, Azure.Storage.StorageSharedKeyCredential public virtual System.Threading.Tasks.Task DeleteMessageAsync(string messageId, string popReceipt, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Exists(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> ExistsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.QueueSasBuilder builder) { throw null; } + public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } public virtual Azure.Response> GetAccessPolicy(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task>> GetAccessPolicyAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response GetProperties(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -79,11 +82,14 @@ public QueueServiceClient(System.Uri serviceUri, Azure.Core.TokenCredential cred public QueueServiceClient(System.Uri serviceUri, Azure.Storage.Queues.QueueClientOptions options = null) { } public QueueServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Queues.QueueClientOptions options = null) { } public virtual string AccountName { get { throw null; } } + public bool CanGenerateAccountSasUri { get { throw null; } } public virtual System.Uri Uri { get { throw null; } } public virtual Azure.Response CreateQueue(string queueName, System.Collections.Generic.IDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> CreateQueueAsync(string queueName, System.Collections.Generic.IDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response DeleteQueue(string queueName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task DeleteQueueAsync(string queueName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Uri GenerateAccountSasUri(Azure.Storage.Sas.AccountSasBuilder builder) { throw null; } + public System.Uri GenerateAccountSasUri(Azure.Storage.Sas.AccountSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Sas.AccountSasResourceTypes resourceTypes) { throw null; } public virtual Azure.Response GetProperties(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetPropertiesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Storage.Queues.QueueClient GetQueueClient(string queueName) { throw null; } @@ -359,7 +365,10 @@ public enum QueueAccountSasPermissions } public partial class QueueSasBuilder { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public QueueSasBuilder() { } + public QueueSasBuilder(Azure.Storage.Sas.QueueAccountSasPermissions permissions, System.DateTimeOffset expiresOn) { } + public QueueSasBuilder(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn) { } public System.DateTimeOffset ExpiresOn { get { throw null; } set { } } public string Identifier { get { throw null; } set { } } public Azure.Storage.Sas.SasIPRange IPRange { get { throw null; } set { } } diff --git a/sdk/storage/Azure.Storage.Queues/src/QueueClient.cs b/sdk/storage/Azure.Storage.Queues/src/QueueClient.cs index 3225a2e51835f..ea4261e108c37 100644 --- a/sdk/storage/Azure.Storage.Queues/src/QueueClient.cs +++ b/sdk/storage/Azure.Storage.Queues/src/QueueClient.cs @@ -12,6 +12,7 @@ using Azure.Storage.Cryptography; using Azure.Storage.Queues.Models; using Azure.Storage.Queues.Specialized; +using Azure.Storage.Sas; using Metadata = System.Collections.Generic.IDictionary; #pragma warning disable SA1402 // File may only contain a single type @@ -132,6 +133,17 @@ public virtual string Name } } + /// + /// The used to authenticate and generate SAS + /// + private StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -186,11 +198,10 @@ public QueueClient(string connectionString, string queueName) public QueueClient(string connectionString, string queueName, QueueClientOptions options) { var conn = StorageConnectionString.Parse(connectionString); - var builder = - new QueueUriBuilder(conn.QueueEndpoint) - { - QueueName = queueName - }; + var builder = new QueueUriBuilder(conn.QueueEndpoint) + { + QueueName = queueName + }; _uri = builder.ToUri(); _messagesUri = _uri.AppendToPath(Constants.Queue.MessagesUri); options ??= new QueueClientOptions(); @@ -198,6 +209,7 @@ public QueueClient(string connectionString, string queueName, QueueClientOptions _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); _clientSideEncryption = QueueClientSideEncryptionOptions.CloneFrom(options._clientSideEncryptionOptions); + _storageSharedKeyCredential = conn.Credentials as StorageSharedKeyCredential; } /// @@ -215,7 +227,7 @@ public QueueClient(string connectionString, string queueName, QueueClientOptions /// every request. /// public QueueClient(Uri queueUri, QueueClientOptions options = default) - : this(queueUri, (HttpPipelinePolicy)null, options) + : this(queueUri, (HttpPipelinePolicy)null, options, null) { } @@ -237,7 +249,7 @@ public QueueClient(Uri queueUri, QueueClientOptions options = default) /// every request. /// public QueueClient(Uri queueUri, StorageSharedKeyCredential credential, QueueClientOptions options = default) - : this(queueUri, credential.AsPolicy(), options) + : this(queueUri, credential.AsPolicy(), options, credential) { } @@ -259,7 +271,7 @@ public QueueClient(Uri queueUri, StorageSharedKeyCredential credential, QueueCli /// every request. /// public QueueClient(Uri queueUri, TokenCredential credential, QueueClientOptions options = default) - : this(queueUri, credential.AsPolicy(), options) + : this(queueUri, credential.AsPolicy(), options, null) { Errors.VerifyHttpsTokenAuth(queueUri); } @@ -281,7 +293,14 @@ public QueueClient(Uri queueUri, TokenCredential credential, QueueClientOptions /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal QueueClient(Uri queueUri, HttpPipelinePolicy authentication, QueueClientOptions options) + /// + /// The shared key credential used to sign requests. + /// + internal QueueClient( + Uri queueUri, + HttpPipelinePolicy authentication, + QueueClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) { _uri = queueUri; _messagesUri = queueUri.AppendToPath(Constants.Queue.MessagesUri); @@ -290,6 +309,7 @@ internal QueueClient(Uri queueUri, HttpPipelinePolicy authentication, QueueClien _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); _clientSideEncryption = QueueClientSideEncryptionOptions.CloneFrom(options._clientSideEncryptionOptions); + _storageSharedKeyCredential = storageSharedKeyCredential; } /// @@ -2422,6 +2442,73 @@ private async Task> UpdateMessageInternal( } } #endregion UpdateMessage + + #region GenerateSas + /// + /// The + /// returns a that generates a Queue Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and parameters passed. + /// + /// For more information, see + /// + /// Constructing a Service SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// A on successfully deleting. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri(QueueSasPermissions permissions, DateTimeOffset expiresOn) + => GenerateSasUri(new QueueSasBuilder(permissions, expiresOn) { QueueName = Name }); + + /// + /// The returns a + /// that generates a Queue Service SAS Uri based + /// on the Client properties and builder passed. + /// + /// For more information, see + /// + /// Constructing a Service SAS + /// + /// + /// Used to generate a Shared Access Signature (SAS) + /// + /// + /// A on successfully deleting. + /// + /// + /// A will be thrown if a failure occurs. + /// + public virtual Uri GenerateSasUri( + QueueSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.QueueName.Equals(Name, StringComparison.InvariantCulture)) + { + // TODO: throw proper exception for non-matching builder name + // e.g. containerName doesn't match or leave the containerName in builder + // should be left empty. Or should we always default to the client's ContainerName + // and chug along if they don't match? + throw Errors.SasNamesNotMatching( + nameof(builder.QueueName), + nameof(QueueSasBuilder), + nameof(Name)); + } + QueueUriBuilder sasUri = new QueueUriBuilder(Uri); + sasUri.Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString(); + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Queues/src/QueueServiceClient.cs b/sdk/storage/Azure.Storage.Queues/src/QueueServiceClient.cs index 72577f806e76e..38a11b0b6ed6e 100644 --- a/sdk/storage/Azure.Storage.Queues/src/QueueServiceClient.cs +++ b/sdk/storage/Azure.Storage.Queues/src/QueueServiceClient.cs @@ -11,6 +11,7 @@ using Azure.Storage.Cryptography; using Azure.Storage.Queues.Models; using Azure.Storage.Queues.Specialized; +using Azure.Storage.Sas; namespace Azure.Storage.Queues { @@ -91,6 +92,17 @@ public virtual string AccountName } } + /// + /// The used to authenticate and generate SAS + /// + private StorageSharedKeyCredential _storageSharedKeyCredential; + + /// + /// Determines whether the client is able to generate a SAS. + /// If the client is authenticated with a . + /// + public bool CanGenerateAccountSasUri => _storageSharedKeyCredential != null; + #region ctors /// /// Initializes a new instance of the @@ -145,6 +157,7 @@ public QueueServiceClient(string connectionString, QueueClientOptions options) _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); _clientSideEncryption = QueueClientSideEncryptionOptions.CloneFrom(options._clientSideEncryptionOptions); + _storageSharedKeyCredential = conn.Credentials as StorageSharedKeyCredential; } /// @@ -161,7 +174,7 @@ public QueueServiceClient(string connectionString, QueueClientOptions options) /// every request. /// public QueueServiceClient(Uri serviceUri, QueueClientOptions options = default) - : this(serviceUri, (HttpPipelinePolicy)null, options) + : this(serviceUri, (HttpPipelinePolicy)null, options, null) { } @@ -182,7 +195,7 @@ public QueueServiceClient(Uri serviceUri, QueueClientOptions options = default) /// every request. /// public QueueServiceClient(Uri serviceUri, StorageSharedKeyCredential credential, QueueClientOptions options = default) - : this(serviceUri, credential.AsPolicy(), options) + : this(serviceUri, credential.AsPolicy(), options, credential) { } @@ -203,7 +216,7 @@ public QueueServiceClient(Uri serviceUri, StorageSharedKeyCredential credential, /// every request. /// public QueueServiceClient(Uri serviceUri, TokenCredential credential, QueueClientOptions options = default) - : this(serviceUri, credential.AsPolicy(), options) + : this(serviceUri, credential.AsPolicy(), options, null) { Errors.VerifyHttpsTokenAuth(serviceUri); } @@ -224,7 +237,14 @@ public QueueServiceClient(Uri serviceUri, TokenCredential credential, QueueClien /// policies for authentication, retries, etc., that are applied to /// every request. /// - internal QueueServiceClient(Uri serviceUri, HttpPipelinePolicy authentication, QueueClientOptions options) + /// + /// The shared key credential used to sign requests. + /// + internal QueueServiceClient( + Uri serviceUri, + HttpPipelinePolicy authentication, + QueueClientOptions options, + StorageSharedKeyCredential storageSharedKeyCredential) { _uri = serviceUri; options ??= new QueueClientOptions(); @@ -232,6 +252,7 @@ internal QueueServiceClient(Uri serviceUri, HttpPipelinePolicy authentication, Q _version = options.Version; _clientDiagnostics = new ClientDiagnostics(options); _clientSideEncryption = QueueClientSideEncryptionOptions.CloneFrom(options._clientSideEncryptionOptions); + _storageSharedKeyCredential = storageSharedKeyCredential; } #endregion ctors @@ -804,5 +825,80 @@ await GetQueueClient(queueName) .DeleteAsync(cancellationToken) .ConfigureAwait(false); #endregion DeleteQueue + + #region GenerateSas + /// + /// The + /// returns a that generates a Queue + /// Account Shared Access Signature based on the + /// Client properties and parameters passed. The SAS is signed by the + /// shared key credential of the client. + /// + /// For more information, see + /// + /// Constructing a Service SAS + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. The time at which the shared access signature becomes invalid. + /// + /// + /// Specifies the resource types associated with the shared access signature. + /// The user is restricted to operations on the specified resources. + /// See . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public Uri GenerateAccountSasUri( + AccountSasPermissions permissions, + DateTimeOffset expiresOn, + AccountSasResourceTypes resourceTypes) => + GenerateAccountSasUri(new AccountSasBuilder( + permissions, + expiresOn, + AccountSasServices.Queues, + resourceTypes)); + + /// + /// The returns a Uri that + /// generates a Service SAS based on the Client properties and builder passed. + /// + /// For more information, see + /// + /// Constructing a Service SAS + /// + /// + /// Used to generate a Shared Access Signature (SAS). + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public Uri GenerateAccountSasUri(AccountSasBuilder builder) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + if (!builder.Services.HasFlag(AccountSasServices.Queues)) + { + throw Errors.SasServiceNotMatching( + nameof(builder.Services), + nameof(builder), + nameof(AccountSasServices.Queues)); + } + QueueUriBuilder sasUri = new QueueUriBuilder(Uri); + sasUri.Query = builder.ToSasQueryParameters(_storageSharedKeyCredential).ToString(); + return sasUri.ToUri(); + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Queues/src/QueueUriBuilder.cs b/sdk/storage/Azure.Storage.Queues/src/QueueUriBuilder.cs index 1ba93320e726e..c2650e2d27b67 100644 --- a/sdk/storage/Azure.Storage.Queues/src/QueueUriBuilder.cs +++ b/sdk/storage/Azure.Storage.Queues/src/QueueUriBuilder.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Linq; using System.Net; using System.Text; using Azure.Core; @@ -28,10 +29,9 @@ public class QueueUriBuilder private Uri _uri; /// - /// Whether the Uri is an IP Uri as determined by - /// . + /// Whether the Uri is a path-style Uri (i.e. it is an IP Uri or the domain includes a port that is used by the local emulator). /// - private readonly bool _isIPStyleUri; + private readonly bool _isPathStyleUri; /// /// Gets or sets the scheme name of the URI. @@ -141,6 +141,8 @@ public string Query /// public QueueUriBuilder(Uri uri) { + uri = uri ?? throw new ArgumentNullException(nameof(uri)); + Scheme = uri.Scheme; Host = uri.Host; Port = uri.Port; @@ -159,7 +161,7 @@ public QueueUriBuilder(Uri uri) if (uri.IsHostIPEndPointStyle()) { - _isIPStyleUri = true; + _isPathStyleUri = true; var accountEndIndex = path.IndexOf("/", StringComparison.InvariantCulture); // Slash not found; path has account name & no queue name @@ -257,7 +259,7 @@ private RequestUriBuilder BuildUri() var path = new StringBuilder(""); // only append the account name to the path for Ip style Uri. // regular style Uri will already have account name in domain - if (_isIPStyleUri && !string.IsNullOrWhiteSpace(AccountName)) + if (_isPathStyleUri && !string.IsNullOrWhiteSpace(AccountName)) { path.Append("/").Append(AccountName); } diff --git a/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasBuilder.cs b/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasBuilder.cs index faa10a595ec1c..63913699787f0 100644 --- a/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasBuilder.cs +++ b/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasBuilder.cs @@ -81,6 +81,63 @@ public class QueueSasBuilder /// public string QueueName { get; set; } + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// This constructor has been deprecated. Please consider using + /// + /// to create a Service SAS or + /// + /// to create an Account SAS. This change does not have any impact on how + /// your application generates or makes use of SAS tokens. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public QueueSasBuilder() + { + } + + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + public QueueSasBuilder(QueueSasPermissions permissions, DateTimeOffset expiresOn) + { + ExpiresOn = expiresOn; + SetPermissions(permissions); + } + + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + /// + /// The time at which the shared access signature becomes invalid. + /// This field must be omitted if it has been specified in an + /// associated stored access policy. + /// + public QueueSasBuilder(QueueAccountSasPermissions permissions, DateTimeOffset expiresOn) + { + ExpiresOn = expiresOn; + SetPermissions(permissions); + } + /// /// Sets the permissions for a queue SAS. /// diff --git a/sdk/storage/Azure.Storage.Queues/tests/QueueClientTests.cs b/sdk/storage/Azure.Storage.Queues/tests/QueueClientTests.cs index 7338d8bb92aa5..7ec56146ac838 100644 --- a/sdk/storage/Azure.Storage.Queues/tests/QueueClientTests.cs +++ b/sdk/storage/Azure.Storage.Queues/tests/QueueClientTests.cs @@ -1209,5 +1209,144 @@ public async Task UpdateMessageAsync_UpdateVisibilityTimeoutOnlyPreservesContent Assert.AreEqual(enqueuedMessage.MessageId, receivedMessage.MessageId); Assert.AreEqual(message, receivedMessage.MessageText); } + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, queueStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - QueueClient(string connectionString, string blobContainerName) + QueueClient container = new QueueClient( + connectionString, + GetNewQueueName()); + Assert.IsTrue(container.CanGenerateSasUri); + + // Act - QueueClient(string connectionString, string blobContainerName, BlobClientOptions options) + QueueClient container2 = new QueueClient( + connectionString, + GetNewQueueName(), + GetOptions()); + Assert.IsTrue(container2.CanGenerateSasUri); + + // Act - QueueClient(Uri blobContainerUri, BlobClientOptions options = default) + QueueClient container3 = new QueueClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(container3.CanGenerateSasUri); + + // Act - QueueClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + QueueClient container4 = new QueueClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(container4.CanGenerateSasUri); + } + + [Test] + public void GenerateSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, queueStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string queueName = GetNewQueueName(); + QueueSasPermissions permissions = QueueSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + QueueClient queueClient = new QueueClient(connectionString, queueName, GetOptions()); + + // Act + Uri sasUri = queueClient.GenerateSasUri(permissions, expiresOn); + + // Assert + QueueSasBuilder sasBuilder = new QueueSasBuilder(permissions, expiresOn) + { + QueueName = queueName + }; + QueueUriBuilder expectedUri = new QueueUriBuilder(blobEndpoint) + { + QueueName = queueName, + Sas = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_Builder() + { + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, queueStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + string queueName = GetNewQueueName(); + QueueSasPermissions permissions = QueueSasPermissions.Read; + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + QueueClient queueClient = new QueueClient(connectionString, queueName, GetOptions()); + + QueueSasBuilder sasBuilder = new QueueSasBuilder(permissions, expiresOn) + { + QueueName = queueName, + StartsOn = startsOn + }; + + // Act + Uri sasUri = queueClient.GenerateSasUri(sasBuilder); + + // Assert + QueueSasBuilder sasBuilder2 = new QueueSasBuilder(permissions, expiresOn) + { + QueueName = queueName, + StartsOn = startsOn + }; + QueueUriBuilder expectedUri = new QueueUriBuilder(blobEndpoint) + { + QueueName = queueName, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateSas_BuilderWrongName() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewQueueName(); + QueueSasPermissions permissions = QueueSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + QueueClient queueClient = new QueueClient( + blobUriBuilder.Uri, + constants.Sas.SharedKeyCredential, + GetOptions()); + + QueueSasBuilder sasBuilder = new QueueSasBuilder(permissions, expiresOn) + { + QueueName = GetNewQueueName() //different queueName + }; + + // Act + try + { + queueClient.GenerateSasUri(sasBuilder); + + Assert.Fail("QueueClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + //the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Queues/tests/QueueSasBuilderTests.cs b/sdk/storage/Azure.Storage.Queues/tests/QueueSasBuilderTests.cs index 9ec91fc0fa20a..93b4d04820c1e 100644 --- a/sdk/storage/Azure.Storage.Queues/tests/QueueSasBuilderTests.cs +++ b/sdk/storage/Azure.Storage.Queues/tests/QueueSasBuilderTests.cs @@ -210,6 +210,53 @@ public async Task QueuePermissionsRawPermissions_Invalid() new ArgumentException("s is not a valid SAS permission")); } + [Test] + public void QueueUriBuilder_LocalDockerUrl_PortTest() + { + // Arrange + // BlobEndpoint from https://docs.microsoft.com/en-us/azure/storage/common/storage-use-emulator#connect-to-the-emulator-account-using-the-well-known-account-name-and-key + var uriString = "http://docker_container:10000/devstoreaccount1/sharename"; + var originalUri = new UriBuilder(uriString); + + // Act + var fileUriBuilder = new QueueUriBuilder(originalUri.Uri); + Uri newUri = fileUriBuilder.ToUri(); + + // Assert + Assert.AreEqual("http", fileUriBuilder.Scheme); + Assert.AreEqual("docker_container", fileUriBuilder.Host); + Assert.AreEqual("devstoreaccount1", fileUriBuilder.AccountName); + Assert.AreEqual("sharename", fileUriBuilder.QueueName); + Assert.IsNull(fileUriBuilder.Sas); + Assert.AreEqual("", fileUriBuilder.Query); + Assert.AreEqual(10000, fileUriBuilder.Port); + + Assert.AreEqual(originalUri, newUri); + } + + [Test] + public void QueueUriBuilder_CustomUri_AccountShareFileTest() + { + // Arrange + var uriString = "https://www.mycustomname.com/queuename"; + var originalUri = new UriBuilder(uriString); + + // Act + var fileUriBuilder = new QueueUriBuilder(originalUri.Uri); + Uri newUri = fileUriBuilder.ToUri(); + + // Assert + Assert.AreEqual("https", fileUriBuilder.Scheme); + Assert.AreEqual("www.mycustomname.com", fileUriBuilder.Host); + Assert.AreEqual(String.Empty, fileUriBuilder.AccountName); + Assert.AreEqual("queuename", fileUriBuilder.QueueName); + Assert.IsNull(fileUriBuilder.Sas); + Assert.AreEqual("", fileUriBuilder.Query); + Assert.AreEqual(443, fileUriBuilder.Port); + + Assert.AreEqual(originalUri, newUri); + } + private QueueSasBuilder BuildQueueSasBuilder(TestConstants constants, string queueName, bool includeVersion) { var queueSasBuilder = new QueueSasBuilder diff --git a/sdk/storage/Azure.Storage.Queues/tests/ServiceClientTests.cs b/sdk/storage/Azure.Storage.Queues/tests/ServiceClientTests.cs index f36008bb3a699..90042cfcc4f2c 100644 --- a/sdk/storage/Azure.Storage.Queues/tests/ServiceClientTests.cs +++ b/sdk/storage/Azure.Storage.Queues/tests/ServiceClientTests.cs @@ -2,16 +2,16 @@ // Licensed under the MIT License. using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Azure.Core; using Azure.Core.TestFramework; -using Azure.Storage.Test; using Azure.Storage.Queues.Models; using Azure.Storage.Queues.Tests; +using Azure.Storage.Sas; +using Azure.Storage.Test; using NUnit.Framework; -using Azure.Core; namespace Azure.Storage.Queues.Test { @@ -328,5 +328,139 @@ await TestHelper.AssertExpectedExceptionAsync( e => { }); } #endregion + + #region GenerateSasTests + [Test] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = new TestConstants(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + var blobSecondaryEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, queueStorageUri: (blobEndpoint, blobSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + + // Act - QueueServiceClient(string connectionString) + QueueServiceClient share = new QueueServiceClient( + connectionString); + Assert.IsTrue(share.CanGenerateAccountSasUri); + + // Act - QueueServiceClient(string connectionString, string blobContainerName, BlobClientOptions options) + QueueServiceClient share2 = new QueueServiceClient( + connectionString, + GetOptions()); + Assert.IsTrue(share2.CanGenerateAccountSasUri); + + // Act - QueueServiceClient(Uri blobContainerUri, BlobClientOptions options = default) + QueueServiceClient share3 = new QueueServiceClient( + blobEndpoint, + GetOptions()); + Assert.IsFalse(share3.CanGenerateAccountSasUri); + + // Act - QueueServiceClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions options = default) + QueueServiceClient share4 = new QueueServiceClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions()); + Assert.IsTrue(share4.CanGenerateAccountSasUri); + } + + [Test] + public void GenerateAccountSas_RequiredParameters() + { + // Arrange + var constants = new TestConstants(this); + var queueEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var queueSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, queueStorageUri: (queueEndpoint, queueSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + QueueServiceClient serviceClient = new QueueServiceClient(connectionString, GetOptions()); + + // Act + Uri sasUri = serviceClient.GenerateAccountSasUri( + permissions: permissions, + expiresOn: expiresOn, + resourceTypes: resourceTypes); + + // Assert + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, AccountSasServices.Queues, resourceTypes); + UriBuilder expectedUri = new UriBuilder(queueEndpoint) + { + Query = sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential).ToString() + }; + Assert.AreEqual(expectedUri.Uri.ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateAccountSas_Builder() + { + var constants = new TestConstants(this); + var queueEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var queueSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, queueStorageUri: (queueEndpoint, queueSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + AccountSasServices services = AccountSasServices.Queues; + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + QueueServiceClient serviceClient = new QueueServiceClient(connectionString, GetOptions()); + + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + StartsOn = startsOn + }; + // Add more properties on the builder + sasBuilder.SetPermissions(permissions); + + // Act + Uri sasUri = serviceClient.GenerateAccountSasUri(sasBuilder); + + // Assert + AccountSasBuilder sasBuilder2 = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + StartsOn = startsOn + }; + UriBuilder expectedUri = new UriBuilder(queueEndpoint); + expectedUri.Query += sasBuilder.ToSasQueryParameters(constants.Sas.SharedKeyCredential).ToString(); + Assert.AreEqual(expectedUri.Uri.ToString(), sasUri.ToString()); + } + + [Test] + public void GenerateAccountSas_WrongService_Service() + { + var constants = new TestConstants(this); + var queueEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account); + var queueSecondaryEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "-secondary"); + var storageConnectionString = new StorageConnectionString(constants.Sas.SharedKeyCredential, queueStorageUri: (queueEndpoint, queueSecondaryEndpoint)); + string connectionString = storageConnectionString.ToString(true); + AccountSasPermissions permissions = AccountSasPermissions.Read | AccountSasPermissions.Write; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AccountSasServices services = AccountSasServices.Blobs; // Wrong Service + AccountSasResourceTypes resourceTypes = AccountSasResourceTypes.All; + QueueServiceClient serviceClient = new QueueServiceClient(connectionString, GetOptions()); + + AccountSasBuilder sasBuilder = new AccountSasBuilder(permissions, expiresOn, services, resourceTypes) + { + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None), + StartsOn = Recording.UtcNow.AddHours(-1) + }; + + // Act + try + { + Uri sasUri = serviceClient.GenerateAccountSasUri(sasBuilder); + + Assert.Fail("BlobContainerClient.GenerateSasUri should have failed with an ArgumentException."); + } + catch (InvalidOperationException) + { + // the correct exception came back + } + } + #endregion } } diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..d36ced7efa7ed --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:54:48.6632762-07:00", + "RandomSeed": "899832615" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..a8fc9e5be0afa --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:54:48.7803875-07:00", + "RandomSeed": "1318316201" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_Builder.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_Builder.json new file mode 100644 index 0000000000000..42d793af52192 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:54:48.7511428-07:00", + "RandomSeed": "378279287" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_BuilderAsync.json new file mode 100644 index 0000000000000..64ef93da956bb --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:54:48.7821839-07:00", + "RandomSeed": "1451277645" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_BuilderWrongName.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_BuilderWrongName.json new file mode 100644 index 0000000000000..b69c308f809dd --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_BuilderWrongName.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:54:48.7702151-07:00", + "RandomSeed": "1719988164" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_BuilderWrongNameAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_BuilderWrongNameAsync.json new file mode 100644 index 0000000000000..c0951f1df5553 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_BuilderWrongNameAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:54:48.7836708-07:00", + "RandomSeed": "24890774" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_RequiredParameters.json new file mode 100644 index 0000000000000..99af5cf7ca9df --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:54:48.7727816-07:00", + "RandomSeed": "1132518569" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..cd34aa5dff5cd --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueClientTests/GenerateSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:54:48.7848737-07:00", + "RandomSeed": "44278463" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_CustomUri_AccountShareFileTest.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_CustomUri_AccountShareFileTest.json new file mode 100644 index 0000000000000..20e5015d169f8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_CustomUri_AccountShareFileTest.json @@ -0,0 +1,4 @@ +{ + "Entries": [], + "Variables": {} +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_CustomUri_AccountShareFileTestAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_CustomUri_AccountShareFileTestAsync.json new file mode 100644 index 0000000000000..20e5015d169f8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_CustomUri_AccountShareFileTestAsync.json @@ -0,0 +1,4 @@ +{ + "Entries": [], + "Variables": {} +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_LocalDockerUrl_PortTest.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_LocalDockerUrl_PortTest.json new file mode 100644 index 0000000000000..20e5015d169f8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_LocalDockerUrl_PortTest.json @@ -0,0 +1,4 @@ +{ + "Entries": [], + "Variables": {} +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_LocalDockerUrl_PortTestAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_LocalDockerUrl_PortTestAsync.json new file mode 100644 index 0000000000000..20e5015d169f8 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/QueueSasBuilderTests/QueueUriBuilder_LocalDockerUrl_PortTestAsync.json @@ -0,0 +1,4 @@ +{ + "Entries": [], + "Variables": {} +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json new file mode 100644 index 0000000000000..2707ee8ed6ab5 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructors.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:55:19.2304094-07:00", + "RandomSeed": "262281468" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json new file mode 100644 index 0000000000000..ae570f48aa19d --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/CanGenerateSas_ClientConstructorsAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:55:19.4233585-07:00", + "RandomSeed": "2115047994" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json new file mode 100644 index 0000000000000..b03f6426e8156 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_Builder.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:55:19.3044233-07:00", + "RandomSeed": "611900299" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json new file mode 100644 index 0000000000000..05efa9e1661af --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_BuilderAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:55:19.4258111-07:00", + "RandomSeed": "1094405914" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParameters.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParameters.json new file mode 100644 index 0000000000000..f96c89855b307 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParameters.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:55:41.1108914-07:00", + "RandomSeed": "719598740" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParametersAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParametersAsync.json new file mode 100644 index 0000000000000..1de586a518437 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_RequiredParametersAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:55:41.1487732-07:00", + "RandomSeed": "2139896492" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json new file mode 100644 index 0000000000000..a38a22cedb003 --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_Service.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:55:19.4144802-07:00", + "RandomSeed": "227042832" + } +} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json new file mode 100644 index 0000000000000..5796706f894af --- /dev/null +++ b/sdk/storage/Azure.Storage.Queues/tests/SessionRecords/ServiceClientTests/GenerateAccountSas_WrongService_ServiceAsync.json @@ -0,0 +1,7 @@ +{ + "Entries": [], + "Variables": { + "DateTimeOffsetNow": "2020-10-23T12:55:19.4291539-07:00", + "RandomSeed": "746410799" + } +} \ No newline at end of file