diff --git a/common-npm-packages/az-blobstorage-provider/azureBlobStorageProvider.ts b/common-npm-packages/az-blobstorage-provider/azureBlobStorageProvider.ts index 39dfe664..1e367844 100644 --- a/common-npm-packages/az-blobstorage-provider/azureBlobStorageProvider.ts +++ b/common-npm-packages/az-blobstorage-provider/azureBlobStorageProvider.ts @@ -9,6 +9,7 @@ import store = require('artifact-engine/Store'); import tl = require('azure-pipelines-task-lib/task'); import { BlobItem, BlobServiceClient, ContainerClient, StorageSharedKeyCredential } from '@azure/storage-blob'; import abortController = require("@azure/abort-controller"); +import { ClientSecretCredential } from '@azure/identity'; const resourcePath: string = path.join(__dirname, 'module.json'); tl.setResourcePath(resourcePath); @@ -24,26 +25,44 @@ export class AzureBlobProvider implements models.IArtifactProvider { private _containerClient: ContainerClient; private _blobServiceClient: BlobServiceClient; private _addPrefixToDownloadedItems: boolean = false; + private _clientSecretCredential: ClientSecretCredential; - constructor(storageAccount: string, containerName: string, accessKey: string, prefixFolderPath?: string, host?: string, addPrefixToDownloadedItems?: boolean) { + constructor(storageAccount: string, containerName: string, accessKey: string, prefixFolderPath?: string, host?: string, addPrefixToDownloadedItems?: boolean); + constructor(storageAccount: string, containerName: string, clientSecretCredential: ClientSecretCredential, prefixFolderPath?: string, host?: string, addPrefixToDownloadedItems?: boolean); + + // Generic Constructor + constructor(storageAccount: string, containerName: string, credential: string | ClientSecretCredential, prefixFolderPath?: string, host?: string, addPrefixToDownloadedItems?: boolean) { this._storageAccount = storageAccount; - this._accessKey = accessKey; this._containerName = containerName; + if (typeof credential === 'string') { + this._accessKey = credential; + const sharedKeyCredential = new StorageSharedKeyCredential(this._storageAccount, this._accessKey); + this._blobServiceClient = new BlobServiceClient(this.getStorageUrl(storageAccount, host), sharedKeyCredential); + } else { + this._clientSecretCredential = credential; + this._blobServiceClient = new BlobServiceClient(this.getStorageUrl(storageAccount, host), this._clientSecretCredential); + } + if (!!prefixFolderPath) { this._prefixFolderPath = prefixFolderPath.endsWith("/") ? prefixFolderPath : prefixFolderPath + "/"; } else { this._prefixFolderPath = ""; } - const sharedKeyCredential = new StorageSharedKeyCredential(this._storageAccount, this._accessKey); - - this._blobServiceClient = new BlobServiceClient(this.getStorageUrl(this._storageAccount), sharedKeyCredential); - this._containerClient = this._blobServiceClient.getContainerClient(this._containerName); - this._addPrefixToDownloadedItems = !!addPrefixToDownloadedItems; } + + // Factory method for Storage Access Key + public static createWithStorageAccountAccessKey(storageAccount: string, containerName: string, accessKey: string, prefixFolderPath?: string, host?: string, addPrefixToDownloadedItems?: boolean): AzureBlobProvider { + return new AzureBlobProvider(storageAccount, containerName, accessKey, prefixFolderPath, host, addPrefixToDownloadedItems); + } + + // Factory method for ClientSecretCredential + public static createWithClientSecretCredential(storageAccount: string, containerName: string, clientSecretCredential: ClientSecretCredential, prefixFolderPath?: string, host?: string, addPrefixToDownloadedItems?: boolean): AzureBlobProvider { + return new AzureBlobProvider(storageAccount, containerName, clientSecretCredential, prefixFolderPath, host, addPrefixToDownloadedItems); + } public async putArtifactItem(item: models.ArtifactItem, readStream: Readable): Promise { await this._containerClient.createIfNotExists(); @@ -164,7 +183,7 @@ export class AzureBlobProvider implements models.IArtifactProvider { return artifactItems; } - private getStorageUrl(storageAccount: string): string { - return `https://${storageAccount}.blob.core.windows.net`; + private getStorageUrl(storageAccount: string, host?: string): string { + return host ? `https://${storageAccount}.${host}` : `https://${storageAccount}.blob.core.windows.net`; } } \ No newline at end of file diff --git a/common-npm-packages/az-blobstorage-provider/blobservice.ts b/common-npm-packages/az-blobstorage-provider/blobservice.ts index 890b7965..30cd2c4f 100644 --- a/common-npm-packages/az-blobstorage-provider/blobservice.ts +++ b/common-npm-packages/az-blobstorage-provider/blobservice.ts @@ -2,25 +2,51 @@ import artifactProviders = require('artifact-engine/Providers'); import azureBlobProvider = require('./azureBlobStorageProvider'); import artifactProcessor = require('artifact-engine/Engine'); import models = require('artifact-engine/Models'); +import { ClientSecretCredential } from '@azure/identity'; export class BlobService { private _storageAccountName: string; private _storageAccessKey: string; + private _credential?: ClientSecretCredential; private _host: string; + /** + * @deprecated + * Use `createWithStorageAccountAccessKey` or `createWithClientSecretCredential` instead. + */ public constructor(storageAccountName: string, storageAccessKey: string, host?: string) { this._storageAccountName = storageAccountName; this._storageAccessKey = storageAccessKey; this._host = host; } + // Static factory method for using Storage Account Access Key + public static createWithStorageAccountAccessKey(storageAccountName: string, storageAccountAccessKey: string, host?: string): BlobService { + return new BlobService(storageAccountName, storageAccountAccessKey, host); + } + + // Static factory method for using ClientSecretCredential + public static createWithClientSecretCredential(storageAccountName: string, credential: ClientSecretCredential, host?: string): BlobService { + // Create an instance using a dummy access key and then set the credential + const instance = new BlobService(storageAccountName, undefined, host); + instance._credential = credential; + return instance; + } + public async uploadBlobs(source: string, container: string, prefixFolderPath?: string, itemPattern?: string): Promise { var fileProvider = new artifactProviders.FilesystemProvider(source); - var azureProvider = new azureBlobProvider.AzureBlobProvider(this._storageAccountName, container, this._storageAccessKey, prefixFolderPath, this._host); + var processor = new artifactProcessor.ArtifactEngine(); var processorOptions = new artifactProcessor.ArtifactEngineOptions(); - if (itemPattern) { - processorOptions.itemPattern = itemPattern; + + //TODO: Check if _credential is populated. If yes, create AzureBlobProvider with it. + let azureProvider: azureBlobProvider.AzureBlobProvider; + if (this._credential) { + // Create AzureBlobProvider using ClientSecretCredential + //azureProvider = new azureBlobProvider.AzureBlobProvider(this._storageAccountName, container, this._credential, prefixFolderPath, this._host); + } else { + // Use the storage access key if no credential is provided + azureProvider = new azureBlobProvider.AzureBlobProvider(this._storageAccountName, container, this._storageAccessKey, prefixFolderPath, this._host); } var uploadedItemTickets = await processor.processItems(fileProvider, azureProvider); @@ -37,6 +63,7 @@ export class BlobService { public async downloadBlobs(destination: string, container: string, prefixFolderPath?: string, itemPattern?: string, addPrefixToDownloadedItems?: boolean): Promise { var fileProvider = new artifactProviders.FilesystemProvider(destination); + //TODO: Revise this init too var azureProvider = new azureBlobProvider.AzureBlobProvider(this._storageAccountName, container, this._storageAccessKey, prefixFolderPath, this._host, !!addPrefixToDownloadedItems); var processor = new artifactProcessor.ArtifactEngine(); var processorOptions = new artifactProcessor.ArtifactEngineOptions(); diff --git a/common-npm-packages/az-blobstorage-provider/package-lock.json b/common-npm-packages/az-blobstorage-provider/package-lock.json index 20ad2fa6..b2d1a427 100644 --- a/common-npm-packages/az-blobstorage-provider/package-lock.json +++ b/common-npm-packages/az-blobstorage-provider/package-lock.json @@ -1646,4 +1646,4 @@ "integrity": "sha1-vpuuHIoEbnazESdyY0fQrXACvrM=" } } -} +} \ No newline at end of file diff --git a/common-npm-packages/az-blobstorage-provider/package.json b/common-npm-packages/az-blobstorage-provider/package.json index e5462432..865c6f18 100644 --- a/common-npm-packages/az-blobstorage-provider/package.json +++ b/common-npm-packages/az-blobstorage-provider/package.json @@ -26,4 +26,4 @@ "scripts": { "build": "cd ../build-scripts && npm install && cd ../az-blobstorage-provider && node make.js" } -} +} \ No newline at end of file