From 8682c80f73aa7db541840e2e5e5e21833a75ea5c Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Thu, 7 May 2020 15:30:16 -0500 Subject: [PATCH] TableSasBuilder pattern changes (#11790) * initial draft of SasBuilder pattern changes * minor tweaks * fix TableInternalClient * pr comments * merge upstream --- .../api/Azure.Data.Tables.netstandard2.0.cs | 7 ++- .../Operations/TableInternalClient.cs | 7 --- .../src/Sas/TableSasBuilder.cs | 52 +++++++++++++++---- .../Azure.Data.Tables/src/TableClient.cs | 24 ++++++++- .../src/TableInternalClient.cs | 11 ++++ .../ValidateSasCredentials.json | 6 +-- .../ValidateSasCredentialsAsync.json | 6 +-- .../tests/TableClientLiveTests.cs | 16 +++--- 8 files changed, 95 insertions(+), 34 deletions(-) diff --git a/sdk/tables/Azure.Data.Tables/api/Azure.Data.Tables.netstandard2.0.cs b/sdk/tables/Azure.Data.Tables/api/Azure.Data.Tables.netstandard2.0.cs index a726e10d2022..b47fc3648ccb 100644 --- a/sdk/tables/Azure.Data.Tables/api/Azure.Data.Tables.netstandard2.0.cs +++ b/sdk/tables/Azure.Data.Tables/api/Azure.Data.Tables.netstandard2.0.cs @@ -9,6 +9,8 @@ protected TableClient() { } public virtual System.Threading.Tasks.Task DeleteAsync(string partitionKey, string rowKey, string eTag = "*", System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response> GetAccessPolicy(int? timeout = default(int?), string requestId = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task>> GetAccessPolicyAsync(int? timeout = default(int?), string requestId = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Data.Tables.Sas.TableSasBuilder GetSasBuilder(Azure.Data.Tables.Sas.TableSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } + public virtual Azure.Data.Tables.Sas.TableSasBuilder GetSasBuilder(string rawPermissions, System.DateTimeOffset expiresOn) { throw null; } public virtual Azure.Response> Insert(System.Collections.Generic.IDictionary entity, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task>> InsertAsync(System.Collections.Generic.IDictionary entity, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Merge(System.Collections.Generic.IDictionary entity, string eTag = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -215,7 +217,8 @@ public enum SasProtocol } public partial class TableSasBuilder { - public TableSasBuilder(string tableName) { } + public TableSasBuilder(string tableName, Azure.Data.Tables.Sas.TableSasPermissions permissions, System.DateTimeOffset expiresOn) { } + public TableSasBuilder(string tableName, string rawPermissions, System.DateTimeOffset expiresOn) { } public System.DateTimeOffset ExpiresOn { get { throw null; } set { } } public string Identifier { get { throw null; } set { } } public Azure.Data.Tables.Sas.SasIPRange IPRange { get { throw null; } set { } } @@ -227,13 +230,13 @@ public TableSasBuilder(string tableName) { } public string RowKeyStart { get { throw null; } set { } } public System.DateTimeOffset StartsOn { get { throw null; } set { } } public string TableName { get { throw null; } } - public string Version { get { throw null; } set { } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool Equals(object obj) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override int GetHashCode() { throw null; } public void SetPermissions(Azure.Data.Tables.Sas.TableSasPermissions permissions) { } public void SetPermissions(string rawPermissions) { } + public string Sign(Azure.Data.Tables.TableSharedKeyCredential sharedKeyCredential) { throw null; } public Azure.Data.Tables.Sas.TableSasQueryParameters ToSasQueryParameters(Azure.Data.Tables.TableSharedKeyCredential sharedKeyCredential) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override string ToString() { throw null; } diff --git a/sdk/tables/Azure.Data.Tables/src/Generated/Operations/TableInternalClient.cs b/sdk/tables/Azure.Data.Tables/src/Generated/Operations/TableInternalClient.cs index ec514577a176..0765e087479c 100644 --- a/sdk/tables/Azure.Data.Tables/src/Generated/Operations/TableInternalClient.cs +++ b/sdk/tables/Azure.Data.Tables/src/Generated/Operations/TableInternalClient.cs @@ -24,13 +24,6 @@ internal partial class TableInternalClient protected TableInternalClient() { } - /// Initializes a new instance of TableInternalClient. - internal TableInternalClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version = "2019-02-02") - { - RestClient = new TableInternalRestClient(clientDiagnostics, pipeline, url, version); - _clientDiagnostics = clientDiagnostics; - _pipeline = pipeline; - } /// Queries tables under the given account. /// Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the analytics logs when storage analytics logging is enabled. diff --git a/sdk/tables/Azure.Data.Tables/src/Sas/TableSasBuilder.cs b/sdk/tables/Azure.Data.Tables/src/Sas/TableSasBuilder.cs index c9020aa0aeef..927d609d546c 100644 --- a/sdk/tables/Azure.Data.Tables/src/Sas/TableSasBuilder.cs +++ b/sdk/tables/Azure.Data.Tables/src/Sas/TableSasBuilder.cs @@ -14,19 +14,35 @@ namespace Azure.Data.Tables.Sas /// public class TableSasBuilder { - - public TableSasBuilder(string tableName) + /// + /// Initializes an instance of a . + /// + /// The name of the table being made accessible with the shared access signature. + /// The permissions associated with the shared access signature. + /// The time at which the shared access signature becomes invalid. + public TableSasBuilder(string tableName, TableSasPermissions permissions, DateTimeOffset expiresOn) { Argument.AssertNotNullOrEmpty(tableName, nameof(tableName)); TableName = tableName; + ExpiresOn = expiresOn; + SetPermissions(permissions); } + /// - /// The storage service version to use to authenticate requests made - /// with this shared access signature, and the service version to use - /// when handling requests made with this shared access signature. + /// Initializes an instance of a . /// - public string Version { get; set; } + /// The name of the table being made accessible with the shared access signature. + /// The permissions associated with the shared access signature. This string should contain one or more of the following permission characters in this order: "racwdl". + /// The time at which the shared access signature becomes invalid. + public TableSasBuilder(string tableName, string rawPermissions, DateTimeOffset expiresOn) + { + Argument.AssertNotNullOrEmpty(tableName, nameof(tableName)); + + TableName = tableName; + ExpiresOn = expiresOn; + Permissions = rawPermissions; + } /// /// The optional signed protocol field specifies the protocol @@ -104,7 +120,12 @@ public TableSasBuilder(string tableName) /// public string RowKeyEnd { get; set; } - + /// + /// The storage service version to use to authenticate requests made + /// with this shared access signature, and the service version to use + /// when handling requests made with this shared access signature. + /// + internal string Version { get; set; } /// /// Sets the permissions for a table SAS. @@ -135,8 +156,7 @@ public void SetPermissions(string rawPermissions) /// The storage account's . /// /// - /// The used for authenticating - /// requests. + /// An instance of . /// public TableSasQueryParameters ToSasQueryParameters(TableSharedKeyCredential sharedKeyCredential) { @@ -180,6 +200,20 @@ public TableSasQueryParameters ToSasQueryParameters(TableSharedKeyCredential sha return p; } + /// + /// Use an account's to sign this + /// shared access signature values to produce the proper SAS query + /// parameters for authenticating requests. + /// + /// + /// The storage account's . + /// + /// + /// A URL encoded query string representing the SAS. + /// + public string Sign(TableSharedKeyCredential sharedKeyCredential) => + ToSasQueryParameters(sharedKeyCredential).ToString(); + /// /// Computes the canonical name for a table resource for SAS signing. /// diff --git a/sdk/tables/Azure.Data.Tables/src/TableClient.cs b/sdk/tables/Azure.Data.Tables/src/TableClient.cs index f1ccc2fcc867..33594c4fd434 100644 --- a/sdk/tables/Azure.Data.Tables/src/TableClient.cs +++ b/sdk/tables/Azure.Data.Tables/src/TableClient.cs @@ -7,8 +7,8 @@ using System.Threading; using System.Threading.Tasks; using Azure.Core; -using Azure.Core.Pipeline; using Azure.Data.Tables.Models; +using Azure.Data.Tables.Sas; namespace Azure.Data.Tables { @@ -36,6 +36,28 @@ internal TableClient(string table, TableInternalClient tableOperations) protected TableClient() { } + /// + /// Gets a instance scoped to the current table. + /// + /// containing the allowed permissions. + /// The time at which the shared access signature becomes invalid. + /// An instance of . + public virtual TableSasBuilder GetSasBuilder(TableSasPermissions permissions, DateTimeOffset expiresOn) + { + return new TableSasBuilder(_table, permissions, expiresOn) { Version = _tableOperations.version }; + } + + /// + /// Gets a instance scoped to the current table. + /// + /// The permissions associated with the shared access signature. This string should contain one or more of the following permission characters in this order: "racwdl". + /// The time at which the shared access signature becomes invalid. + /// An instance of . + public virtual TableSasBuilder GetSasBuilder(string rawPermissions, DateTimeOffset expiresOn) + { + return new TableSasBuilder(_table, rawPermissions, expiresOn) { Version = _tableOperations.version }; + } + /// /// Creates the table in the storage account. /// diff --git a/sdk/tables/Azure.Data.Tables/src/TableInternalClient.cs b/sdk/tables/Azure.Data.Tables/src/TableInternalClient.cs index 8cf1df8ea597..d7ffebba2b3f 100644 --- a/sdk/tables/Azure.Data.Tables/src/TableInternalClient.cs +++ b/sdk/tables/Azure.Data.Tables/src/TableInternalClient.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Azure.Core; using Azure.Data.Tables.Models; +using Azure.Core.Pipeline; namespace Azure.Data.Tables { @@ -14,6 +15,16 @@ namespace Azure.Data.Tables [CodeGenClient("TableClient")] internal partial class TableInternalClient { + internal string version { get; } + + internal TableInternalClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version = "2019-02-02") + { + RestClient = new TableInternalRestClient(clientDiagnostics, pipeline, url, version); + _clientDiagnostics = clientDiagnostics; + _pipeline = pipeline; + this.version = version; + } + /// Insert entity in a table. /// The name of the table. /// The The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations>Setting Timeouts for Queue Service Operations.</a>. diff --git a/sdk/tables/Azure.Data.Tables/tests/SessionRecords/TableClientLiveTests/ValidateSasCredentials.json b/sdk/tables/Azure.Data.Tables/tests/SessionRecords/TableClientLiveTests/ValidateSasCredentials.json index ae6b3df94119..7da918968f56 100644 --- a/sdk/tables/Azure.Data.Tables/tests/SessionRecords/TableClientLiveTests/ValidateSasCredentials.json +++ b/sdk/tables/Azure.Data.Tables/tests/SessionRecords/TableClientLiveTests/ValidateSasCredentials.json @@ -46,7 +46,7 @@ } }, { - "RequestUri": "https://chrissscratch.table.core.windows.net/testtable155063()?tn=testtable155063\u0026sv=2019-07-07\u0026se=2040-01-01T01%3A01%3A00Z\u0026sp=r\u0026sig=Sanitized\u0026%24format=application%2Fjson%3Bodata%3Dfullmetadata", + "RequestUri": "https://chrissscratch.table.core.windows.net/testtable155063()?tn=testtable155063\u0026sv=2019-02-02\u0026se=2040-01-01T01%3A01%3A00Z\u0026sp=r\u0026sig=Sanitized\u0026%24format=application%2Fjson%3Bodata%3Dfullmetadata", "RequestMethod": "GET", "RequestHeaders": { "DataServiceVersion": "3.0", @@ -80,7 +80,7 @@ } }, { - "RequestUri": "https://chrissscratch.table.core.windows.net/testtable155063(PartitionKey=\u0027partition\u0027,RowKey=\u002701\u0027)?tn=testtable155063\u0026sv=2019-07-07\u0026se=2040-01-01T01%3A01%3A00Z\u0026sp=r\u0026sig=Sanitized\u0026%24format=application%2Fjson%3Bodata%3Dfullmetadata", + "RequestUri": "https://chrissscratch.table.core.windows.net/testtable155063(PartitionKey=\u0027partition\u0027,RowKey=\u002701\u0027)?tn=testtable155063\u0026sv=2019-02-02\u0026se=2040-01-01T01%3A01%3A00Z\u0026sp=r\u0026sig=Sanitized\u0026%24format=application%2Fjson%3Bodata%3Dfullmetadata", "RequestMethod": "PUT", "RequestHeaders": { "Content-Length": "339", @@ -171,4 +171,4 @@ "TABLES_PRIMARY_STORAGE_ACCOUNT_KEY": "", "TABLES_STORAGE_ACCOUNT_NAME": "chrissscratch" } -} \ No newline at end of file +} diff --git a/sdk/tables/Azure.Data.Tables/tests/SessionRecords/TableClientLiveTests/ValidateSasCredentialsAsync.json b/sdk/tables/Azure.Data.Tables/tests/SessionRecords/TableClientLiveTests/ValidateSasCredentialsAsync.json index 0d96f6eeb333..eeec805f9ccd 100644 --- a/sdk/tables/Azure.Data.Tables/tests/SessionRecords/TableClientLiveTests/ValidateSasCredentialsAsync.json +++ b/sdk/tables/Azure.Data.Tables/tests/SessionRecords/TableClientLiveTests/ValidateSasCredentialsAsync.json @@ -46,7 +46,7 @@ } }, { - "RequestUri": "https://chrissscratch.table.core.windows.net/testtable960034()?tn=testtable960034\u0026sv=2019-07-07\u0026se=2040-01-01T01%3A01%3A00Z\u0026sp=r\u0026sig=Sanitized\u0026%24format=application%2Fjson%3Bodata%3Dfullmetadata", + "RequestUri": "https://chrissscratch.table.core.windows.net/testtable960034()?tn=testtable960034\u0026sv=2019-02-02\u0026se=2040-01-01T01%3A01%3A00Z\u0026sp=r\u0026sig=Sanitized\u0026%24format=application%2Fjson%3Bodata%3Dfullmetadata", "RequestMethod": "GET", "RequestHeaders": { "DataServiceVersion": "3.0", @@ -80,7 +80,7 @@ } }, { - "RequestUri": "https://chrissscratch.table.core.windows.net/testtable960034(PartitionKey=\u0027partition\u0027,RowKey=\u002701\u0027)?tn=testtable960034\u0026sv=2019-07-07\u0026se=2040-01-01T01%3A01%3A00Z\u0026sp=r\u0026sig=Sanitized\u0026%24format=application%2Fjson%3Bodata%3Dfullmetadata", + "RequestUri": "https://chrissscratch.table.core.windows.net/testtable960034(PartitionKey=\u0027partition\u0027,RowKey=\u002701\u0027)?tn=testtable960034\u0026sv=2019-02-02\u0026se=2040-01-01T01%3A01%3A00Z\u0026sp=r\u0026sig=Sanitized\u0026%24format=application%2Fjson%3Bodata%3Dfullmetadata", "RequestMethod": "PUT", "RequestHeaders": { "Content-Length": "339", @@ -171,4 +171,4 @@ "TABLES_PRIMARY_STORAGE_ACCOUNT_KEY": "", "TABLES_STORAGE_ACCOUNT_NAME": "chrissscratch" } -} \ No newline at end of file +} diff --git a/sdk/tables/Azure.Data.Tables/tests/TableClientLiveTests.cs b/sdk/tables/Azure.Data.Tables/tests/TableClientLiveTests.cs index eb0a59a00198..6567d340be8f 100644 --- a/sdk/tables/Azure.Data.Tables/tests/TableClientLiveTests.cs +++ b/sdk/tables/Azure.Data.Tables/tests/TableClientLiveTests.cs @@ -29,21 +29,19 @@ public TableClientLiveTests(bool isAsync) : base(isAsync /* To record tests, add [Test] public void ValidateSasCredentials() { - // Build a shared access signature with only Read permissions. - - TableSasBuilder sas = new TableSasBuilder(tableName) - { - ExpiresOn = new DateTime(2040, 1, 1, 1, 1, 0, DateTimeKind.Utc) - }; - sas.SetPermissions(TableSasPermissions.Read); - // Create a SharedKeyCredential that we can use to sign the SAS token + var credential = new TableSharedKeyCredential(TestEnvironment.AccountName, TestEnvironment.PrimaryStorageAccountKey); + // Build a shared access signature with only Read permissions. + + TableSasBuilder sas = client.GetSasBuilder(TableSasPermissions.Read, new DateTime(2040, 1, 1, 1, 1, 0, DateTimeKind.Utc)); + string token = sas.Sign(credential); + // Build a SAS URI UriBuilder sasUri = new UriBuilder(TestEnvironment.StorageUri) { - Query = sas.ToSasQueryParameters(credential).ToString() + Query = token }; // Create the TableServiceClient using the SAS URI.