Skip to content

Commit

Permalink
[data-tables] Add support for Entra ID on Cosmos (#31154)
Browse files Browse the repository at this point in the history
### Packages impacted by this PR

`@azure/data-tables`

### Describe the problem that is addressed by this PR

Adds support for using the correct scope when receiving an auth
challenge via a Cosmos endpoint.

I also fixed a central sanitizer issue that was impacting running the
playback tests.

### Are there test cases added in this PR? _(If not, why?)_

Yes

### Provide a list of related PRs _(if any)_

Azure/azure-sdk-for-net#45934

---------

Co-authored-by: Christopher Scott <[email protected]>
  • Loading branch information
xirzec and christothes authored Sep 19, 2024
1 parent ab122dc commit d8c3984
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 10 deletions.
4 changes: 3 additions & 1 deletion sdk/tables/data-tables/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Release History

## 13.2.3 (Unreleased)
## 13.3.0 (Unreleased)

### Features Added

- Added support for Entra ID credentials when targeting a Cosmos endpoint.

### Breaking Changes

### Bugs Fixed
Expand Down
2 changes: 1 addition & 1 deletion sdk/tables/data-tables/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@azure/data-tables",
"version": "13.2.3",
"version": "13.3.0",
"description": "An isomorphic client library for the Azure Tables service.",
"sdk-type": "client",
"main": "dist/index.js",
Expand Down
8 changes: 5 additions & 3 deletions sdk/tables/data-tables/src/TableClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {
isSASCredential,
isTokenCredential,
} from "@azure/core-auth";
import { STORAGE_SCOPE, TablesLoggingAllowedHeaderNames } from "./utils/constants";
import { COSMOS_SCOPE, STORAGE_SCOPE, TablesLoggingAllowedHeaderNames } from "./utils/constants";
import { decodeContinuationToken, encodeContinuationToken } from "./utils/continuationToken";
import {
deserialize,
Expand Down Expand Up @@ -224,6 +224,7 @@ export class TableClient {
) {
this.url = url;
this.tableName = tableName;
const isCosmos = isCosmosEndpoint(this.url);

const credential = isCredential(credentialOrOptions) ? credentialOrOptions : undefined;
this.credential = credential;
Expand Down Expand Up @@ -255,10 +256,11 @@ export class TableClient {
}

if (isTokenCredential(credential)) {
setTokenChallengeAuthenticationPolicy(generatedClient.pipeline, credential, STORAGE_SCOPE);
const scope = isCosmos ? COSMOS_SCOPE : STORAGE_SCOPE;
setTokenChallengeAuthenticationPolicy(generatedClient.pipeline, credential, scope);
}

if (isCosmosEndpoint(this.url)) {
if (isCosmos) {
generatedClient.pipeline.addPolicy(cosmosPatchPolicy());
}

Expand Down
7 changes: 5 additions & 2 deletions sdk/tables/data-tables/src/TableServiceClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
isSASCredential,
isTokenCredential,
} from "@azure/core-auth";
import { STORAGE_SCOPE, TablesLoggingAllowedHeaderNames } from "./utils/constants";
import { COSMOS_SCOPE, STORAGE_SCOPE, TablesLoggingAllowedHeaderNames } from "./utils/constants";
import { Service, Table } from "./generated";
import {
injectSecondaryEndpointHeader,
Expand All @@ -48,6 +48,7 @@ import { setTokenChallengeAuthenticationPolicy } from "./utils/challengeAuthenti
import { tablesNamedKeyCredentialPolicy } from "./tablesNamedCredentialPolicy";
import { tablesSASTokenPolicy } from "./tablesSASTokenPolicy";
import { tracingClient } from "./utils/tracing";
import { isCosmosEndpoint } from "./utils/isCosmosEndpoint";

/**
* A TableServiceClient represents a Client to the Azure Tables service allowing you
Expand Down Expand Up @@ -159,6 +160,7 @@ export class TableServiceClient {
options?: TableServiceClientOptions,
) {
this.url = url;
const isCosmos = isCosmosEndpoint(this.url);
const credential = isCredential(credentialOrOptions) ? credentialOrOptions : undefined;
const clientOptions =
(!isCredential(credentialOrOptions) ? credentialOrOptions : options) || {};
Expand Down Expand Up @@ -187,7 +189,8 @@ export class TableServiceClient {
}

if (isTokenCredential(credential)) {
setTokenChallengeAuthenticationPolicy(client.pipeline, credential, STORAGE_SCOPE);
const scope = isCosmos ? COSMOS_SCOPE : STORAGE_SCOPE;
setTokenChallengeAuthenticationPolicy(client.pipeline, credential, scope);
}

if (options?.version) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sdk/tables/data-tables/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const TRANSACTION_HTTP_VERSION_1_1 = "HTTP/1.1";
export const TRANSACTION_HTTP_LINE_ENDING = "\r\n";

export const STORAGE_SCOPE = "https://storage.azure.com/.default";
export const COSMOS_SCOPE = "https://cosmos.azure.com/.default";

export const HeaderConstants = {
AUTHORIZATION: "authorization",
Expand Down
2 changes: 1 addition & 1 deletion sdk/tables/data-tables/src/utils/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ import { createTracingClient } from "@azure/core-tracing";
export const tracingClient = createTracingClient({
namespace: "Microsoft.Data.Tables",
packageName: "@azure/data-tables",
packageVersion: "13.2.3",
packageVersion: "13.3.0",
});
2 changes: 1 addition & 1 deletion sdk/tables/data-tables/swagger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

```yaml
v3: true
package-version: 13.2.3
package-version: 13.3.0
package-name: "@azure/data-tables"
title: TablesClient
description: Tables Client
Expand Down
49 changes: 49 additions & 0 deletions sdk/tables/data-tables/test/public/endpoint.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { TableClient } from "../../src/TableClient";
import { TableServiceClient } from "../../src/TableServiceClient";
import { assert } from "chai";
import { TokenCredential } from "@azure/core-auth";
import { COSMOS_SCOPE } from "../../src/utils/constants";

export class FakeCredential implements TokenCredential {
public lastScopes?: string | string[];

getToken(scopes: string | string[]): Promise<null> {
this.lastScopes = scopes;
return Promise.resolve(null);
}
}

describe(`Cosmos endpoint tests`, function () {
describe("TableServiceClient", function () {
it("Sets the scope correctly on the auth policy", async function () {
const credential = new FakeCredential();
const fakeEndpointUrl = "https://localhost/";
const client = new TableServiceClient(fakeEndpointUrl, credential);
try {
await client.createTable("Test");
} catch {
// this will throw because the fake credential doesn't return a valid token,
// but we'll still invoke it with the right scope first.
}
assert.deepEqual(credential.lastScopes, [COSMOS_SCOPE]);
});
});

describe("TableClient", function () {
it("Sets the scope correctly on the auth policy", async function () {
const credential = new FakeCredential();
const fakeEndpointUrl = "https://localhost/";
const client = new TableClient(fakeEndpointUrl, "Test", credential);
try {
await client.createTable();
} catch {
// this will throw because the fake credential doesn't return a valid token,
// but we'll still invoke it with the right scope first.
}
assert.deepEqual(credential.lastScopes, [COSMOS_SCOPE]);
});
});
});
3 changes: 3 additions & 0 deletions sdk/tables/data-tables/test/public/utils/recordedClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const sanitizerOptions: SanitizerOptions = {
const recorderOptions: RecorderStartOptions = {
envSetupForPlayback: replaceableVariables,
sanitizerOptions,
removeCentralSanitizers: [
"AZSDK3493", // .name in the body is not a secret
],
};

export type CreateClientMode =
Expand Down

0 comments on commit d8c3984

Please sign in to comment.