Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Storage] Use @azure/core-http in @azure/storage-* libraries to support @azure/identity #3853

Merged
merged 15 commits into from
Jun 24, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions sdk/core/core-http/lib/axiosHttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ export class AxiosHttpClient implements HttpClient {
config.httpAgent = agent.agent;
}
}
// This hack is still required with 0.19.0 version of axios since axios tries to merge the
// Content-Type header from it's config["<method name>"] where the method name is lower-case,
// into the request header. It could be possible that the Content-Type header is not present
// in the original request and this would create problems while creating the signature for
// storage data plane sdks.
axios.interceptors.request.use((config: AxiosRequestConfig) => ({
...config,
method: (config.method as Method) && (config.method as Method).toUpperCase() as Method
}));

res = await axios.request(config);
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-http/lib/credentials/tokenCredential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ export interface AccessToken {
* @param credential The assumed TokenCredential to be tested.
*/
export function isTokenCredential(credential: any): credential is TokenCredential {
return "getToken" in credential;
return credential && typeof credential.getToken === "function";
}
6 changes: 5 additions & 1 deletion sdk/core/core-http/test/mockHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import xhrMock, { proxy } from "xhr-mock";
import MockAdapter from "axios-mock-adapter";
import { isNode, HttpMethods } from "../lib/coreHttp";
import { AxiosRequestConfig, AxiosInstance } from "axios";
import { AxiosRequestConfig, AxiosInstance, Method } from "axios";

export type UrlFilter = string | RegExp;

Expand Down Expand Up @@ -41,6 +41,10 @@ class NodeHttpMock implements HttpMockFacade {
throw new Error("Axios instance cannot be undefined");
}
this._mockAdapter = new MockAdapter(axiosInstance);
axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => ({
...config,
method: (config.method as Method) && (config.method as Method).toLowerCase() as Method
}));
}

setup(): void {
Expand Down
5 changes: 4 additions & 1 deletion sdk/storage/storage-blob/BreakingChanges.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Breaking Changes

2019.6 Version 11.0.0-preview.1
* `TokenCredential` has been renamed to `RawTokenCredential` to make way for the new `@azure/identity` library's `TokenCredential` interface.

2018.12 10.3.0

* Updated convenience layer methods enum type parameters into typescript union types, this will help reducing bundle footprint.
Expand All @@ -21,4 +24,4 @@
* `String.prototype.startsWith`
* `String.prototype.endsWith`
* `String.prototype.repeat`
* `String.prototype.includes`
* `String.prototype.includes`
3 changes: 2 additions & 1 deletion sdk/storage/storage-blob/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@
"homepage": "https://github.com/Azure/azure-sdk-for-js#readme",
"sideEffects": false,
"dependencies": {
"@azure/ms-rest-js": "^1.2.6",
"@azure/core-http": "^1.2.6",
"@azure/core-paging": "^1.0.0",
"events": "^3.0.0",
"tslib": "^1.9.3"
},
"devDependencies": {
"@azure/identity": "^0.1.0",
"@microsoft/api-extractor": "^7.1.5",
"@types/dotenv": "^6.1.0",
"@types/fs-extra": "~7.0.0",
Expand Down
3 changes: 1 addition & 2 deletions sdk/storage/storage-blob/rollup.base.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const depNames = Object.keys(pkg.dependencies);
const production = process.env.NODE_ENV === "production";

export function nodeConfig(test = false) {
const externalNodeBuiltins = ["@azure/ms-rest-js", "crypto", "fs", "events", "os", "stream"];
const externalNodeBuiltins = ["@azure/core-http", "crypto", "fs", "events", "os", "stream"];
const baseConfig = {
input: "dist-esm/src/index.js",
external: depNames.concat(externalNodeBuiltins),
Expand Down Expand Up @@ -70,7 +70,6 @@ export function nodeConfig(test = false) {
export function browserConfig(test = false, production = false) {
const baseConfig = {
input: "dist-esm/src/index.browser.js",
external: ["ms-rest-js"],
output: {
file: "browser/azure-storage-blob.js",
banner: banner,
Expand Down
4 changes: 4 additions & 0 deletions sdk/storage/storage-blob/samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ npm install @azure/storage-blob
- Note down the "AccountName", "AccountKey" obtained at **Access keys** and "AccountSAS" from **Shared access signature** under **Settings** tab.
Before running any of the samples, update with the credentials you have noted down above.

### Authenticating with Azure Active Directory

If you have [registered an application](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) with an Azure Active Directory tenant, you can [assign it to an RBAC role](https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad) in your Azure Storage account. This enables you to use the Azure.Identity library to authenticate with Azure Storage as shown in the [azureAdAuth.ts sample](./samples/azureAdAuth.ts).

## Running Samples

- Change `"../.."` to `"@azure/storage-blob"` in the samples in order to import the published package instead of using source code.
Expand Down
44 changes: 44 additions & 0 deletions sdk/storage/storage-blob/samples/javascript/azureAdAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Setup: Enter your Azure Active Directory credentials as described in main()
*/

const { BlobServiceClient } = require("../.."); // Change to "@azure/storage-blob" in your package
const { DefaultAzureCredential } = require("@azure/identity");

async function main() {
// Enter your storage account name
const account = "";

// DefaultAzureCredential will first look for Azure Active Directory (AAD)
// client secret credentials in the following environment variables:
//
// - AZURE_TENANT_ID: The ID of your AAD tenant
// - AZURE_CLIENT_ID: The ID of your AAD app registration (client)
// - AZURE_CLIENT_SECRET: The client secret for your AAD app registration
//
// If those environment variables aren't found and your application is deployed
// to an Azure VM or App Service instance, the managed service identity endpoint
// will be used as a fallback authentication source.
const defaultAzureCredential = new DefaultAzureCredential();

const blobServiceClient = new BlobServiceClient(
`https://${account}.blob.core.windows.net`,
defaultAzureCredential,
);

// Create a container
const containerName = `newcontainer${new Date().getTime()}`;
const createContainerResponse = await blobServiceClient
.createContainerClient(containerName)
.create();
console.log(`Created container ${containerName} successfully`, createContainerResponse.requestId);
}

// An async method returns a Promise object, which is compatible with then().catch() coding style.
main()
.then(() => {
console.log("Successfully executed the sample.");
})
.catch((err) => {
console.log(err.message);
});
4 changes: 2 additions & 2 deletions sdk/storage/storage-blob/samples/javascript/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const {
newPipeline,
SharedKeyCredential,
AnonymousCredential,
TokenCredential
RawTokenCredential
} = require("../.."); // Change to "@azure/storage-blob" in your package

async function main() {
Expand All @@ -19,7 +19,7 @@ async function main() {
const sharedKeyCredential = new SharedKeyCredential(account, accountKey);

// Use TokenCredential with OAuth token
const tokenCredential = new TokenCredential("token");
const tokenCredential = new RawTokenCredential("token");
tokenCredential.token = "renewedToken"; // Renew the token by updating token field of token credential

// Use AnonymousCredential when url already includes a SAS signature
Expand Down
44 changes: 44 additions & 0 deletions sdk/storage/storage-blob/samples/typescript/azureAdAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Setup: Enter your Azure Active Directory credentials as described in main()
*/

import { BlobServiceClient, SharedKeyCredential } from "../../src"; // Change to "@azure/storage-blob" in your package
import { DefaultAzureCredential } from "@azure/identity";

async function main() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main [](start = 15, length = 4)

Is "Ad" or "Aad"? is this file name azureAdAuth.ts correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I called it "azureAd" to refer to "Azure Active Directory", though I can see how having "aad" explicitly could make things clearer. I'll ask someone about this and will rename these files after the PR gets merged if necessary.

// Enter your storage account name
const account = "";

// DefaultAzureCredential will first look for Azure Active Directory (AAD)
// client secret credentials in the following environment variables:
//
// - AZURE_TENANT_ID: The ID of your AAD tenant
// - AZURE_CLIENT_ID: The ID of your AAD app registration (client)
// - AZURE_CLIENT_SECRET: The client secret for your AAD app registration
//
// If those environment variables aren't found and your application is deployed
// to an Azure VM or App Service instance, the managed service identity endpoint
// will be used as a fallback authentication source.
const defaultAzureCredential = new DefaultAzureCredential();

const blobServiceClient = new BlobServiceClient(
`https://${account}.blob.core.windows.net`,
defaultAzureCredential,
);

// Create a container
const containerName = `newcontainer${new Date().getTime()}`;
const createContainerResponse = await blobServiceClient
.createContainerClient(containerName)
.create();
console.log(`Created container ${containerName} successfully`, createContainerResponse.requestId);
}

// An async method returns a Promise object, which is compatible with then().catch() coding style.
main()
.then(() => {
console.log("Successfully executed the sample.");
})
.catch((err) => {
console.log(err.message);
});
4 changes: 2 additions & 2 deletions sdk/storage/storage-blob/samples/typescript/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
Models,
SharedKeyCredential,
newPipeline,
TokenCredential,
RawTokenCredential,
} from "../../src"; // Change to "@azure/storage-blob" in your package

async function main() {
Expand All @@ -19,7 +19,7 @@ async function main() {
const sharedKeyCredential = new SharedKeyCredential(account, accountKey);

// Use TokenCredential with OAuth token
daviwil marked this conversation as resolved.
Show resolved Hide resolved
daviwil marked this conversation as resolved.
Show resolved Hide resolved
const tokenCredential = new TokenCredential("token");
const tokenCredential = new RawTokenCredential("token");
tokenCredential.token = "renewedToken"; // Renew the token by updating token field of token credential

// Use AnonymousCredential when url already includes a SAS signature
Expand Down
2 changes: 1 addition & 1 deletion sdk/storage/storage-blob/src/Aborter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { AbortSignalLike, isNode } from "@azure/ms-rest-js";
import { AbortSignalLike, isNode } from "@azure/core-http";

/**
* An aborter instance implements AbortSignal interface, can abort HTTP requests.
Expand Down
21 changes: 15 additions & 6 deletions sdk/storage/storage-blob/src/AppendBlobClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { HttpRequestBody, TransferProgressEvent } from "@azure/ms-rest-js";
import {
HttpRequestBody,
TransferProgressEvent,
TokenCredential,
isTokenCredential
} from "@azure/core-http";

import * as Models from "./generated/lib/models";
import { Aborter } from "./Aborter";
Expand Down Expand Up @@ -143,12 +148,13 @@ export class AppendBlobClient extends BlobClient {
* Encoded URL string will NOT be escaped twice, only special characters in URL path will be escaped.
* However, if a blob name includes ? or %, blob name must be encoded in the URL.
* Such as a blob named "my?blob%", the URL should be "https://myaccount.blob.core.windows.net/mycontainer/my%3Fblob%25".
* @param {Credential} credential Such as AnonymousCredential, SharedKeyCredential or TokenCredential.
* If not specified, AnonymousCredential is used.
* @param {Credential | TokenCredential} credential Such as AnonymousCredential, SharedKeyCredential, RawTokenCredential,
* or a TokenCredential from @azure/identity. If not specified,
* AnonymousCredential is used.
* @param {NewPipelineOptions} [options] Optional. Options to configure the HTTP pipeline.
* @memberof AppendBlobClient
*/
constructor(url: string, credential: Credential, options?: NewPipelineOptions);
constructor(url: string, credential: Credential | TokenCredential, options?: NewPipelineOptions);
/**
* Creates an instance of AppendBlobClient.
* This method accepts an encoded URL or non-encoded URL pointing to an append blob.
Expand All @@ -170,7 +176,7 @@ export class AppendBlobClient extends BlobClient {
constructor(url: string, pipeline: Pipeline);
constructor(
urlOrConnectionString: string,
credentialOrPipelineOrContainerName: string | Credential | Pipeline,
credentialOrPipelineOrContainerName: string | Credential | TokenCredential | Pipeline,
blobNameOrOptions?: string | NewPipelineOptions,
options?: NewPipelineOptions
) {
Expand All @@ -179,7 +185,10 @@ export class AppendBlobClient extends BlobClient {
let pipeline: Pipeline;
if (credentialOrPipelineOrContainerName instanceof Pipeline) {
pipeline = credentialOrPipelineOrContainerName;
} else if (credentialOrPipelineOrContainerName instanceof Credential) {
} else if (
credentialOrPipelineOrContainerName instanceof Credential ||
isTokenCredential(credentialOrPipelineOrContainerName)
) {
options = blobNameOrOptions as NewPipelineOptions;
pipeline = newPipeline(credentialOrPipelineOrContainerName, options);
} else if (
Expand Down
21 changes: 15 additions & 6 deletions sdk/storage/storage-blob/src/BlobClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { isNode, TransferProgressEvent } from "@azure/ms-rest-js";
import {
isNode,
TransferProgressEvent,
TokenCredential,
isTokenCredential
} from "@azure/core-http";

import * as Models from "./generated/lib/models";
import { Aborter } from "./Aborter";
Expand Down Expand Up @@ -579,12 +584,13 @@ export class BlobClient extends StorageClient {
* @param {string} url A Client string pointing to Azure Storage blob service, such as
* "https://myaccount.blob.core.windows.net". You can append a SAS
* if using AnonymousCredential, such as "https://myaccount.blob.core.windows.net?sasString".
* @param {Credential} credential Such as AnonymousCredential, SharedKeyCredential or TokenCredential.
* If not specified, AnonymousCredential is used.
* @param {Credential | TokenCredential} credential Such as AnonymousCredential, SharedKeyCredential, RawTokenCredential,
* or a TokenCredential from @azure/identity. If not specified,
* AnonymousCredential is used.
* @param {NewPipelineOptions} [options] Optional. Options to configure the HTTP pipeline.
* @memberof BlobClient
*/
constructor(url: string, credential?: Credential, options?: NewPipelineOptions);
constructor(url: string, credential?: Credential | TokenCredential, options?: NewPipelineOptions);
/**
* Creates an instance of BlobClient.
* This method accepts an encoded URL or non-encoded URL pointing to a blob.
Expand All @@ -606,14 +612,17 @@ export class BlobClient extends StorageClient {
constructor(url: string, pipeline: Pipeline);
constructor(
urlOrConnectionString: string,
credentialOrPipelineOrContainerName?: string | Credential | Pipeline,
credentialOrPipelineOrContainerName?: string | Credential | TokenCredential | Pipeline,
blobNameOrOptions?: string | NewPipelineOptions,
options?: NewPipelineOptions
) {
let pipeline: Pipeline;
if (credentialOrPipelineOrContainerName instanceof Pipeline) {
pipeline = credentialOrPipelineOrContainerName;
} else if (credentialOrPipelineOrContainerName instanceof Credential) {
} else if (
credentialOrPipelineOrContainerName instanceof Credential ||
isTokenCredential(credentialOrPipelineOrContainerName)
) {
options = blobNameOrOptions as NewPipelineOptions;
pipeline = newPipeline(credentialOrPipelineOrContainerName, options);
} else if (
Expand Down
2 changes: 1 addition & 1 deletion sdk/storage/storage-blob/src/BlobDownloadResponse.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { HttpResponse, isNode } from "@azure/ms-rest-js";
import { HttpResponse, isNode } from "@azure/core-http";

import * as Models from "./generated/lib/models";
import { Metadata } from "./models";
Expand Down
18 changes: 13 additions & 5 deletions sdk/storage/storage-blob/src/BlobServiceClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {
TokenCredential,
isTokenCredential
} from "@azure/core-http";
import * as Models from "./generated/lib/models";
import { Aborter } from "./Aborter";
import { ListContainersIncludeType } from "./generated/lib/models/index";
Expand Down Expand Up @@ -200,12 +204,13 @@ export class BlobServiceClient extends StorageClient {
* @param {string} url A Client string pointing to Azure Storage blob service, such as
* "https://myaccount.blob.core.windows.net". You can append a SAS
* if using AnonymousCredential, such as "https://myaccount.blob.core.windows.net?sasString".
* @param {Credential} credential Such as AnonymousCredential, SharedKeyCredential or TokenCredential.
* If not specified, AnonymousCredential is used.
* @param {Credential | TokenCredential} credential Such as AnonymousCredential, SharedKeyCredential, RawTokenCredential,
* or a TokenCredential from @azure/identity. If not specified,
* AnonymousCredential is used.
* @param {NewPipelineOptions} [options] Optional. Options to configure the HTTP pipeline.
* @memberof BlobServiceClient
*/
constructor(url: string, credential?: Credential, options?: NewPipelineOptions);
constructor(url: string, credential?: Credential | TokenCredential, options?: NewPipelineOptions);
/**
* Creates an instance of BlobServiceClient.
*
Expand All @@ -219,13 +224,16 @@ export class BlobServiceClient extends StorageClient {
constructor(url: string, pipeline: Pipeline);
constructor(
url: string,
credentialOrPipeline?: Credential | Pipeline,
credentialOrPipeline?: Credential | TokenCredential | Pipeline,
options?: NewPipelineOptions
) {
let pipeline: Pipeline;
if (credentialOrPipeline instanceof Pipeline) {
pipeline = credentialOrPipeline;
} else if (credentialOrPipeline instanceof Credential) {
} else if (
credentialOrPipeline instanceof Credential ||
isTokenCredential(credentialOrPipeline)
) {
pipeline = newPipeline(credentialOrPipeline, options);
} else {
// The second parameter is undefined. Use anonymous credential
Expand Down
Loading