Skip to content

Commit 6055ff7

Browse files
[aztables] adding CosmosDB support for using TokenCredentials (Azure#23596)
Just a simple matter of using the proper scope when talking with CosmosDB, which apparently didn't support token auth when this library was first created. Also includes some work to bring this up "to code", as it were: * Tests now test against TokenCredentials for both Storage and CosmosDB (previously they only tested the constructor). Bicep file also now has an example of creating a custom role that contains the privileges needed since Cosmos has it's own RBAC. * Readme and other text all updated to say "Entra ID" instead of AAD Fixes Azure#21760
1 parent 1cbed27 commit 6055ff7

20 files changed

+456
-326
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
# AzureSDKOwners: @jhendrixMSFT
4747
# ServiceLabel: %Tables
4848
# PRLabel: %Tables
49-
/sdk/data/aztables/ @jhendrixMSFT
49+
/sdk/data/aztables/ @jhendrixMSFT @richardpark-msft
5050

5151
# AzureSDKOwners: @gracewilcox
5252
# ServiceLabel: %KeyVault

eng/config.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@
7272
"Name": "keyvault/azsecrets",
7373
"CoverageGoal": 0.86
7474
},
75+
{
76+
"Name": "data/aztables",
77+
"CoverageGoal": 0.77,
78+
"EnvironmentVariables": {
79+
"TABLES_STORAGE_ACCOUNT_NAME": "fakeaccount",
80+
"TABLES_PRIMARY_STORAGE_ACCOUNT_KEY": "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==",
81+
"TABLES_SHARED_ACCESS_SIGNATURE": "?sig=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
82+
}
83+
},
7584
{
7685
"Name": "data",
7786
"CoverageGoal": 0.78,
@@ -134,4 +143,4 @@
134143
"CoverageGoal": 0.75
135144
}
136145
]
137-
}
146+
}

sdk/data/aztables/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Release History
22

3-
## 1.2.1 (Unreleased)
3+
## 1.3.0 (Unreleased)
44

55
### Features Added
6+
* Client/ServiceClient now supports `azcore.TokenCredential` authentication with Azure Cosmos DB for Table.
67

78
### Breaking Changes
89

sdk/data/aztables/README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,15 @@ func main() {
5959
For more information about table service URL's and how to configure custom domain names for Azure Storage check out the [official documentation][azure_portal_account_url]
6060

6161
#### Types of credentials
62-
The clients support different forms of authentication. Cosmos accounts can use a Shared Key Credential, Connection String, or an Shared Access Signature Token for authentication. Storage account can use the same credentials as a Cosmos account and can use the credentials in [`azidentity`](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) like `azidentity.NewDefaultAzureCredential()`.
6362

64-
The aztables package supports any of the types that implement the `azcore.TokenCredential` interface, authorization via a Connection String, or authorization with a Shared Access Signature Token.
63+
Both services (Cosmos and Storage) support the the following forms of authentication:
64+
- Microsoft Entra ID token, using one of the collection of types from the [`azidentity`](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) module, like [azidentity.DefaultAzureCredential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#readme-defaultazurecredential). Example [here](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/aztables#example-NewServiceClient).
65+
- Shared Key Credential
66+
- Connection String
67+
- Shared Access Signature Token
6568

66-
##### Creating the client with an AAD credential
67-
Use AAD authentication as the credential parameter to authenticate the client:
69+
##### Creating the client with a Microsoft Entra ID credential
70+
Use Microsoft Entra ID authentication as the credential parameter to authenticate the client:
6871
```go
6972
import (
7073
"fmt"

sdk/data/aztables/access_policy_test.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,18 @@ import (
1616
)
1717

1818
func TestSetEmptyAccessPolicy(t *testing.T) {
19-
client, delete := initClientTest(t, "storage", true, NewSpanValidator(t, SpanMatcher{
19+
client := initClientTest(t, storageEndpoint, true, NewSpanValidator(t, SpanMatcher{
2020
Name: "Client.SetAccessPolicy",
2121
}))
22-
defer delete()
2322

2423
_, err := client.SetAccessPolicy(ctx, nil)
2524
require.NoError(t, err)
2625
}
2726

2827
func TestSetAccessPolicy(t *testing.T) {
29-
client, delete := initClientTest(t, "storage", true, NewSpanValidator(t, SpanMatcher{
28+
client := initClientTest(t, storageEndpoint, true, NewSpanValidator(t, SpanMatcher{
3029
Name: "Client.GetAccessPolicy",
3130
}))
32-
defer delete()
3331

3432
start := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
3533
expiration := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
@@ -72,8 +70,7 @@ func TestSetAccessPolicy(t *testing.T) {
7270
}
7371

7472
func TestSetMultipleAccessPolicies(t *testing.T) {
75-
client, delete := initClientTest(t, "storage", true, tracing.Provider{})
76-
defer delete()
73+
client := initClientTest(t, storageEndpoint, true, tracing.Provider{})
7774

7875
id := "empty"
7976

@@ -120,11 +117,10 @@ func TestSetMultipleAccessPolicies(t *testing.T) {
120117
}
121118

122119
func TestSetTooManyAccessPolicies(t *testing.T) {
123-
client, delete := initClientTest(t, "storage", true, NewSpanValidator(t, SpanMatcher{
120+
client := initClientTest(t, storageEndpoint, true, NewSpanValidator(t, SpanMatcher{
124121
Name: "Client.SetAccessPolicy",
125122
Status: tracing.SpanStatusError,
126123
}))
127-
defer delete()
128124

129125
start := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
130126
expiration := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
@@ -155,8 +151,7 @@ func TestSetTooManyAccessPolicies(t *testing.T) {
155151
}
156152

157153
func TestSetNullAccessPolicy(t *testing.T) {
158-
client, delete := initClientTest(t, "storage", true, tracing.Provider{})
159-
defer delete()
154+
client := initClientTest(t, storageEndpoint, true, tracing.Provider{})
160155

161156
id := "null"
162157

@@ -178,8 +173,7 @@ func TestSetNullAccessPolicy(t *testing.T) {
178173
}
179174

180175
func TestSetInvalidAccessPolicy(t *testing.T) {
181-
client, delete := initClientTest(t, "storage", true, tracing.Provider{})
182-
defer delete()
176+
client := initClientTest(t, storageEndpoint, true, tracing.Provider{})
183177

184178
signedIdentifiers := make([]*SignedIdentifier, 0)
185179
signedIdentifiers = append(signedIdentifiers, &SignedIdentifier{

sdk/data/aztables/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "go",
44
"TagPrefix": "go/data/aztables",
5-
"Tag": "go/data/aztables_5a816669aa"
5+
"Tag": "go/data/aztables_7fc6e189d6"
66
}

sdk/data/aztables/client_test.go

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,20 @@ import (
1717
"github.com/stretchr/testify/require"
1818
)
1919

20-
var services = []string{"storage", "cosmos"}
20+
var services = []endpointType{
21+
storageEndpoint,
22+
cosmosEndpoint,
23+
storageTokenCredentialEndpoint,
24+
cosmosTokenCredentialEndpoint,
25+
}
2126

2227
func TestServiceErrors(t *testing.T) {
2328
for _, service := range services {
2429
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
25-
client, delete := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
30+
client := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
2631
Name: "Client.CreateTable",
2732
Status: tracing.SpanStatusError,
2833
}))
29-
defer delete()
3034

3135
// Create a duplicate table to produce an error
3236
_, err := client.CreateTable(ctx, nil)
@@ -42,10 +46,9 @@ func TestServiceErrors(t *testing.T) {
4246
func TestCreateTable(t *testing.T) {
4347
for _, service := range services {
4448
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
45-
client, delete := initClientTest(t, service, false, NewSpanValidator(t, SpanMatcher{
49+
client := initClientTest(t, service, false, NewSpanValidator(t, SpanMatcher{
4650
Name: "Client.Delete",
4751
}))
48-
defer delete()
4952

5053
_, err := client.CreateTable(ctx, nil)
5154

@@ -62,10 +65,9 @@ type mdforAddGet struct {
6265
func TestAddEntity(t *testing.T) {
6366
for _, service := range services {
6467
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
65-
client, delete := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
68+
client := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
6669
Name: "Client.AddEntity",
6770
}))
68-
defer delete()
6971

7072
simpleEntity := createSimpleEntity(1, "partition")
7173

@@ -85,8 +87,7 @@ func TestAddEntity(t *testing.T) {
8587
func TestAddComplexEntity(t *testing.T) {
8688
for _, service := range services {
8789
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
88-
client, delete := initClientTest(t, service, true, tracing.Provider{})
89-
defer delete()
90+
client := initClientTest(t, service, true, tracing.Provider{})
9091

9192
entity := createComplexEntity(1, "partition")
9293

@@ -112,10 +113,9 @@ func TestAddComplexEntity(t *testing.T) {
112113
func TestDeleteEntity(t *testing.T) {
113114
for _, service := range services {
114115
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
115-
client, delete := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
116+
client := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
116117
Name: "Client.DeleteEntity",
117118
}))
118-
defer delete()
119119

120120
simpleEntity := createSimpleEntity(1, "partition")
121121

@@ -132,8 +132,7 @@ func TestDeleteEntity(t *testing.T) {
132132
func TestDeleteEntityWithETag(t *testing.T) {
133133
for _, service := range services {
134134
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
135-
client, delete := initClientTest(t, service, true, tracing.Provider{})
136-
defer delete()
135+
client := initClientTest(t, service, true, tracing.Provider{})
137136

138137
simpleEntity := createSimpleEntity(1, "partition")
139138
simpleEntity2 := createSimpleEntity(2, "partition")
@@ -168,10 +167,9 @@ func TestDeleteEntityWithETag(t *testing.T) {
168167
func TestMergeEntity(t *testing.T) {
169168
for _, service := range services {
170169
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
171-
client, delete := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
170+
client := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
172171
Name: "Client.GetEntity",
173172
}))
174-
defer delete()
175173

176174
entityToCreate := createSimpleEntity(1, "partition")
177175
marshalled, err := json.Marshal(entityToCreate)
@@ -230,11 +228,10 @@ func TestMergeEntity(t *testing.T) {
230228
func TestMergeEntityDoesNotExist(t *testing.T) {
231229
for _, service := range services {
232230
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
233-
client, delete := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
231+
client := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
234232
Name: "Client.UpdateEntity",
235233
Status: tracing.SpanStatusError,
236234
}))
237-
defer delete()
238235

239236
entityToCreate := createSimpleEntity(1, "partition")
240237
marshalled, err := json.Marshal(entityToCreate)
@@ -253,10 +250,9 @@ func TestMergeEntityDoesNotExist(t *testing.T) {
253250
func TestInsertEntity(t *testing.T) {
254251
for _, service := range services {
255252
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
256-
client, delete := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
253+
client := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
257254
Name: "Client.UpsertEntity",
258255
}))
259-
defer delete()
260256

261257
// 1. Create Basic Entity
262258
entityToCreate := createSimpleEntityWithRowKey(1, "parti'tion", "one'")
@@ -322,8 +318,7 @@ func TestInsertEntity(t *testing.T) {
322318
func TestInsertEntityTwice(t *testing.T) {
323319
for _, service := range services {
324320
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
325-
client, delete := initClientTest(t, service, true, tracing.Provider{})
326-
defer delete()
321+
client := initClientTest(t, service, true, tracing.Provider{})
327322

328323
// 1. Create Basic Entity
329324
entityToCreate := createSimpleEntity(1, "partition")
@@ -347,10 +342,9 @@ type mdForListEntities struct {
347342
func TestQuerySimpleEntity(t *testing.T) {
348343
for _, service := range services {
349344
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
350-
client, delete := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
345+
client := initClientTest(t, service, true, NewSpanValidator(t, SpanMatcher{
351346
Name: "Pager[ListEntitiesResponse].NextPage",
352347
}))
353-
defer delete()
354348

355349
// Add 5 entities
356350
entitiesToCreate := createSimpleEntities(5, "partition")
@@ -407,8 +401,7 @@ func TestQuerySimpleEntity(t *testing.T) {
407401
func TestQueryComplexEntity(t *testing.T) {
408402
for _, service := range services {
409403
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
410-
client, delete := initClientTest(t, service, true, tracing.Provider{})
411-
defer delete()
404+
client := initClientTest(t, service, true, tracing.Provider{})
412405

413406
// Add 5 entities
414407
entitiesToCreate := createComplexEntities(5, "partition")
@@ -462,8 +455,7 @@ func TestQueryComplexEntity(t *testing.T) {
462455
func TestInvalidEntity(t *testing.T) {
463456
for _, service := range services {
464457
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
465-
client, delete := initClientTest(t, service, true, tracing.Provider{})
466-
defer delete()
458+
client := initClientTest(t, service, true, tracing.Provider{})
467459

468460
badEntity := map[string]any{
469461
"Value": 10,
@@ -483,8 +475,7 @@ func TestInvalidEntity(t *testing.T) {
483475
func TestContinuationTokens(t *testing.T) {
484476
for _, service := range services {
485477
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
486-
client, delete := initClientTest(t, service, true, tracing.Provider{})
487-
defer delete()
478+
client := initClientTest(t, service, true, tracing.Provider{})
488479

489480
err := insertNEntities("contToken", 10, client)
490481
require.NoError(t, err)
@@ -524,8 +515,7 @@ func TestContinuationTokens(t *testing.T) {
524515
func TestContinuationTokensFilters(t *testing.T) {
525516
for _, service := range services {
526517
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
527-
client, delete := initClientTest(t, service, true, tracing.Provider{})
528-
defer delete()
518+
client := initClientTest(t, service, true, tracing.Provider{})
529519

530520
err := insertNEntities("contToken", 10, client)
531521
require.NoError(t, err)

sdk/data/aztables/entity_test.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import (
1515
func TestAddBasicEntity(t *testing.T) {
1616
for _, service := range services {
1717
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
18-
client, delete := initClientTest(t, service, true, tracing.Provider{})
19-
defer delete()
18+
client := initClientTest(t, service, true, tracing.Provider{})
2019

2120
basicEntity := basicTestEntity{
2221
Entity: Entity{
@@ -65,8 +64,7 @@ func TestAddBasicEntity(t *testing.T) {
6564
func TestEdmMarshalling(t *testing.T) {
6665
for _, service := range services {
6766
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
68-
client, delete := initClientTest(t, service, true, tracing.Provider{})
69-
defer delete()
67+
client := initClientTest(t, service, true, tracing.Provider{})
7068

7169
edmEntity := createEdmEntity(1, "partition")
7270

@@ -108,8 +106,7 @@ func TestEdmMarshalling(t *testing.T) {
108106
func TestEntityQuotes(t *testing.T) {
109107
for _, service := range services {
110108
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
111-
client, delete := initClientTest(t, service, true, tracing.Provider{})
112-
defer delete()
109+
client := initClientTest(t, service, true, tracing.Provider{})
113110

114111
pk, err := createRandomName(t, "partition")
115112
require.NoError(t, err)
@@ -165,8 +162,7 @@ func TestEntityQuotes(t *testing.T) {
165162
func TestEntityUnicode(t *testing.T) {
166163
for _, service := range services {
167164
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
168-
client, delete := initClientTest(t, service, true, tracing.Provider{})
169-
defer delete()
165+
client := initClientTest(t, service, true, tracing.Provider{})
170166

171167
pk, err := createRandomName(t, "partition")
172168
require.NoError(t, err)

sdk/data/aztables/go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ module github.com/Azure/azure-sdk-for-go/sdk/data/aztables
33
go 1.18
44

55
require (
6-
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0
7-
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
6+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
7+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0
88
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0
99
github.com/stretchr/testify v1.9.0
1010
)
@@ -17,9 +17,9 @@ require (
1717
github.com/kylelemons/godebug v1.1.0 // indirect
1818
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
1919
github.com/pmezard/go-difflib v1.0.0 // indirect
20-
golang.org/x/crypto v0.25.0 // indirect
21-
golang.org/x/net v0.27.0 // indirect
22-
golang.org/x/sys v0.22.0 // indirect
23-
golang.org/x/text v0.16.0 // indirect
20+
golang.org/x/crypto v0.28.0 // indirect
21+
golang.org/x/net v0.30.0 // indirect
22+
golang.org/x/sys v0.26.0 // indirect
23+
golang.org/x/text v0.19.0 // indirect
2424
gopkg.in/yaml.v3 v3.0.1 // indirect
2525
)

0 commit comments

Comments
 (0)