From ae1f3df4908c0ca9e9f0deec5f53d68171827da2 Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Wed, 18 Mar 2020 11:50:25 -0700 Subject: [PATCH] Fixes how azure auth is handled on the azure pane (#9654) --- extensions/azurecore/package.json | 2 +- .../azurecore/src/azureResource/commands.ts | 2 +- .../azurecore/src/azureResource/interfaces.ts | 4 +- .../providers/database/databaseService.ts | 5 +- .../database/databaseTreeDataProvider.ts | 9 +- .../databaseServerTreeDataProvider.ts | 11 +- .../postgresServerTreeDataProvider.ts | 9 +- .../postgresServerTreeDataProvider.ts | 9 +- .../providers/resourceTreeDataProviderBase.ts | 10 +- .../sqlInstanceTreeDataProvider.ts | 9 +- .../sqlInstanceArcTreeDataProvider.ts | 9 +- .../services/subscriptionService.ts | 5 +- .../azureResource/services/tenantService.ts | 24 +--- .../src/azureResource/tree/accountTreeNode.ts | 7 +- .../src/azureResource/tree/treeProvider.ts | 9 +- .../database/databaseTreeDataProvider.test.ts | 2 +- .../databaseServerTreeDataProvider.test.ts | 2 +- .../tree/accountTreeNode.test.ts | 22 ++-- extensions/azurecore/yarn.lock | 111 ++++++++++++++++-- 19 files changed, 175 insertions(+), 86 deletions(-) diff --git a/extensions/azurecore/package.json b/extensions/azurecore/package.json index 40515642a8..3db8df8505 100644 --- a/extensions/azurecore/package.json +++ b/extensions/azurecore/package.json @@ -196,7 +196,7 @@ }, "dependencies": { "@azure/arm-resourcegraph": "^2.0.0", - "@azure/arm-subscriptions": "1.0.0", + "@azure/arm-subscriptions": "^2.0.0", "adal-node": "^0.2.1", "axios": "^0.19.2", "request": "2.88.0", diff --git a/extensions/azurecore/src/azureResource/commands.ts b/extensions/azurecore/src/azureResource/commands.ts index c4fff0658e..52d8c0fa17 100644 --- a/extensions/azurecore/src/azureResource/commands.ts +++ b/extensions/azurecore/src/azureResource/commands.ts @@ -76,7 +76,7 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur const token = tokens[tenant.id].token; const tokenType = tokens[tenant.id].tokenType; - result.resourceGroups.push(...await service.getResources(subscription, new TokenCredentials(token, tokenType))); + result.resourceGroups.push(...await service.getResources(subscription, new TokenCredentials(token, tokenType), account)); } catch (err) { const error = new Error(localize('azure.accounts.getResourceGroups.queryError', "Error fetching resource groups for account {0} ({1}) subscription {2} ({3}) tenant {4} : {5}", account.displayInfo.displayName, diff --git a/extensions/azurecore/src/azureResource/interfaces.ts b/extensions/azurecore/src/azureResource/interfaces.ts index 4afc2cc5f2..8664c7ba19 100644 --- a/extensions/azurecore/src/azureResource/interfaces.ts +++ b/extensions/azurecore/src/azureResource/interfaces.ts @@ -33,7 +33,7 @@ export interface IAzureResourceCacheService { } export interface IAzureResourceTenantService { - getTenantId(subscription: azureResource.AzureResourceSubscription): Promise; + getTenantId(subscription: azureResource.AzureResourceSubscription, account: Account, credential: msRest.ServiceClientCredentials): Promise; } export interface IAzureResourceNodeWithProviderId { @@ -42,5 +42,5 @@ export interface IAzureResourceNodeWithProviderId { } export interface IAzureResourceService { - getResources(subscription: azureResource.AzureResourceSubscription, credential: msRest.ServiceClientCredentials): Promise; + getResources(subscription: azureResource.AzureResourceSubscription, credential: msRest.ServiceClientCredentials, account: Account): Promise; } diff --git a/extensions/azurecore/src/azureResource/providers/database/databaseService.ts b/extensions/azurecore/src/azureResource/providers/database/databaseService.ts index f31f81a900..b26ee45cdc 100644 --- a/extensions/azurecore/src/azureResource/providers/database/databaseService.ts +++ b/extensions/azurecore/src/azureResource/providers/database/databaseService.ts @@ -9,14 +9,15 @@ import { IAzureResourceService } from '../../interfaces'; import { serversQuery, DbServerGraphData } from '../databaseServer/databaseServerService'; import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase'; +import { Account } from 'azdata'; interface DatabaseGraphData extends GraphData { kind: string; } export class AzureResourceDatabaseService implements IAzureResourceService { - public async getResources(subscription: azureResource.AzureResourceSubscription, credential: ServiceClientCredentials): Promise { + public async getResources(subscription: azureResource.AzureResourceSubscription, credential: ServiceClientCredentials, account: Account): Promise { const databases: azureResource.AzureResourceDatabase[] = []; - const resourceClient = new ResourceGraphClient(credential); + const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); // Query servers and databases in parallel (start both promises before waiting on the 1st) let serverQueryPromise = queryGraphResources(resourceClient, subscription.id, serversQuery); diff --git a/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts index c5a0df9230..75cc4bb504 100644 --- a/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TreeItem, ExtensionNodeType } from 'azdata'; +import { TreeItem, ExtensionNodeType, Account } from 'azdata'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -27,7 +27,7 @@ export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProvi ) { super(databaseService, apiWrapper); } - protected getTreeItemForResource(database: azureResource.AzureResourceDatabase): TreeItem { + protected getTreeItemForResource(database: azureResource.AzureResourceDatabase, account: Account): TreeItem { return { id: `databaseServer_${database.serverFullName}.database_${database.name}`, label: `${database.name} (${database.serverName})`, @@ -44,13 +44,14 @@ export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProvi databaseName: database.name, userName: database.loginName, password: '', - authenticationType: 'SqlLogin', + authenticationType: 'AzureMFA', savePassword: true, groupFullName: '', groupId: '', providerName: 'MSSQL', saveProfile: false, - options: {} + options: {}, + azureAccount: account.key.accountId }, childProvider: 'MSSQL', type: ExtensionNodeType.Database diff --git a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts index 9686651d83..b3774a4bf0 100644 --- a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionNodeType, TreeItem } from 'azdata'; +import { ExtensionNodeType, TreeItem, Account } from 'azdata'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -28,7 +28,7 @@ export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDat } - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer): TreeItem { + protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: Account): TreeItem { return { id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`, label: databaseServer.name, @@ -43,15 +43,16 @@ export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDat connectionName: undefined, serverName: databaseServer.fullName, databaseName: databaseServer.defaultDatabaseName, - userName: databaseServer.loginName, + userName: '', password: '', - authenticationType: 'SqlLogin', + authenticationType: 'AzureMFA', savePassword: true, groupFullName: '', groupId: '', providerName: 'MSSQL', saveProfile: false, - options: {} + options: {}, + azureAccount: account.key.accountId }, childProvider: 'MSSQL', type: ExtensionNodeType.Server diff --git a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts index 89715925de..d9bbf61295 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionNodeType, TreeItem } from 'azdata'; +import { ExtensionNodeType, TreeItem, Account } from 'azdata'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -28,7 +28,7 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB } - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer): TreeItem { + protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: Account): TreeItem { return { id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`, label: databaseServer.name, @@ -45,7 +45,7 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB databaseName: databaseServer.defaultDatabaseName, userName: `${databaseServer.loginName}@${databaseServer.fullName}`, password: '', - authenticationType: 'SqlLogin', + authenticationType: 'AzureMFA', savePassword: true, groupFullName: '', groupId: '', @@ -54,7 +54,8 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB options: { // Set default for SSL or will get error complaining about it not being set correctly 'sslmode': 'require' - } + }, + azureAccount: account.key.accountId }, childProvider: 'PGSQL', type: ExtensionNodeType.Server diff --git a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts index 84ae264368..ae8b6c215f 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionNodeType, TreeItem } from 'azdata'; +import { ExtensionNodeType, TreeItem, Account } from 'azdata'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -28,7 +28,7 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase } - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer): TreeItem { + protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: Account): TreeItem { return { id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`, label: databaseServer.name, @@ -45,7 +45,7 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase databaseName: databaseServer.defaultDatabaseName, userName: `${databaseServer.loginName}@${databaseServer.fullName}`, password: '', - authenticationType: 'SqlLogin', + authenticationType: 'AzureMFA', savePassword: true, groupFullName: '', groupId: '', @@ -54,7 +54,8 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase options: { // Set default for SSL or will get error complaining about it not being set correctly 'sslmode': 'require' - } + }, + azureAccount: account.key.accountId }, childProvider: 'PGSQL', type: ExtensionNodeType.Server diff --git a/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts b/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts index 7cee492294..20932a9e34 100644 --- a/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts +++ b/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts @@ -36,7 +36,7 @@ export abstract class ResourceTreeDataProviderBase a.treeItem.label.localeCompare(b.treeItem.label)); } catch (error) { console.log(AzureResourceErrorMessageUtil.getErrorMessage(error)); @@ -48,11 +48,11 @@ export abstract class ResourceTreeDataProviderBase[]; + const resources: T[] = await this._resourceService.getResources(element.subscription, credential, element.account) || []; return resources; } - protected abstract getTreeItemForResource(resource: T): azdata.TreeItem; + protected abstract getTreeItemForResource(resource: T, account: azdata.Account): azdata.TreeItem; protected abstract createContainerNode(): azureResource.IAzureResourceNode; } @@ -121,9 +121,9 @@ export abstract class ResourceServiceBase { + public async getResources(subscription: azureResource.AzureResourceSubscription, credential: msRest.ServiceClientCredentials, account: azdata.Account): Promise { const convertedResources: U[] = []; - const resourceClient = new ResourceGraphClient(credential); + const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); let graphResources = await queryGraphResources(resourceClient, subscription.id, this.query); let ids = new Set(); graphResources.forEach((res) => { diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts index 4fedf297f4..a195422bc6 100644 --- a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionNodeType, TreeItem } from 'azdata'; +import { ExtensionNodeType, TreeItem, Account } from 'azdata'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -28,7 +28,7 @@ export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase { + public async getSubscriptions(account: Account, credential: any): Promise { const subscriptions: azureResource.AzureResourceSubscription[] = []; - const subClient = new SubscriptionClient(credential); + const subClient = new SubscriptionClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); const subs = await subClient.subscriptions.list(); subs.forEach((sub) => subscriptions.push({ id: sub.subscriptionId, diff --git a/extensions/azurecore/src/azureResource/services/tenantService.ts b/extensions/azurecore/src/azureResource/services/tenantService.ts index 80158ac9f7..ff1bbc8f7c 100644 --- a/extensions/azurecore/src/azureResource/services/tenantService.ts +++ b/extensions/azurecore/src/azureResource/services/tenantService.ts @@ -2,29 +2,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -import * as request from 'request'; +import { SubscriptionClient } from '@azure/arm-subscriptions'; import { azureResource } from '../azure-resource'; import { IAzureResourceTenantService } from '../interfaces'; +import { Account } from 'azdata'; export class AzureResourceTenantService implements IAzureResourceTenantService { - public async getTenantId(subscription: azureResource.AzureResourceSubscription): Promise { - const requestPromisified = new Promise((resolve, reject) => { - const url = `https://management.azure.com/subscriptions/${subscription.id}?api-version=2014-04-01`; - request(url, function (error, response, body) { - if (response.statusCode === 401) { - const tenantIdRegEx = /authorization_uri="https:\/\/login\.windows\.net\/([0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})"/; - const teantIdString = response.headers['www-authenticate']; - if (tenantIdRegEx.test(teantIdString)) { - resolve(tenantIdRegEx.exec(teantIdString)[1]); - } else { - reject(); - } - } - }); - }); + public async getTenantId(subscription: azureResource.AzureResourceSubscription, account: Account, credentials: any): Promise { + const subClient = new SubscriptionClient(credentials, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); - return await requestPromisified; + const result = await subClient.subscriptions.get(subscription.id); + return result.subscriptionId; } } diff --git a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts index 4d4568a258..d411178455 100644 --- a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts +++ b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts @@ -42,11 +42,10 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode public async getChildren(): Promise { try { let subscriptions: azureResource.AzureResourceSubscription[] = []; + const tokens = await this.appContext.apiWrapper.getSecurityToken(this.account, AzureResource.ResourceManagement); if (this._isClearingCache) { try { - const tokens = await this.appContext.apiWrapper.getSecurityToken(this.account, AzureResource.ResourceManagement); - for (const tenant of this.account.properties.tenants) { const token = tokens[tenant.id].token; const tokenType = tokens[tenant.id].tokenType; @@ -56,7 +55,6 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode } catch (error) { throw new AzureResourceCredentialError(localize('azure.resource.tree.accountTreeNode.credentialError', "Failed to get credential for account {0}. Please refresh the account.", this.account.key.accountId), error); } - this.updateCache(subscriptions); this._isClearingCache = false; @@ -82,7 +80,8 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode return [AzureResourceMessageTreeNode.create(AzureResourceAccountTreeNode.noSubscriptionsLabel, this)]; } else { let subTreeNodes = await Promise.all(subscriptions.map(async (subscription) => { - const tenantId = await this._tenantService.getTenantId(subscription); + const token = tokens[subscription.id]; + const tenantId = await this._tenantService.getTenantId(subscription, this.account, new TokenCredentials(token.token, token.tokenType)); return new AzureResourceSubscriptionTreeNode(this.account, subscription, tenantId, this.appContext, this.treeChangeHandler, this); })); diff --git a/extensions/azurecore/src/azureResource/tree/treeProvider.ts b/extensions/azurecore/src/azureResource/tree/treeProvider.ts index 8326a8d834..febf5ddfd1 100644 --- a/extensions/azurecore/src/azureResource/tree/treeProvider.ts +++ b/extensions/azurecore/src/azureResource/tree/treeProvider.ts @@ -37,13 +37,16 @@ export class AzureResourceTreeProvider implements TreeDataProvider, IA private hookAccountService(appContext: AppContext): void { this.accountService = appContext.getService(AzureResourceServiceNames.accountService); if (this.accountService) { - this.accountService.onDidChangeAccounts((e: azdata.DidChangeAccountsParams) => { + this.accountService.onDidChangeAccounts(async (e: azdata.DidChangeAccountsParams) => { + // This event sends it per provider, we need to make sure we get all the azure related accounts + let accounts = await this.accountService.getAccounts(); + accounts = accounts.filter(a => a.key.providerId.startsWith('azure')); // the onDidChangeAccounts event will trigger in many cases where the accounts didn't actually change // the notifyNodeChanged event triggers a refresh which triggers a getChildren which can trigger this callback // this below check short-circuits the infinite callback loop this.setSystemInitialized(); - if (!equals(e.accounts, this.accounts)) { - this.accounts = e.accounts; + if (!equals(accounts, this.accounts)) { + this.accounts = accounts; this.notifyNodeChanged(undefined); } }); diff --git a/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts b/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts index ded8f531b1..24a9762f10 100644 --- a/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts +++ b/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts @@ -104,7 +104,7 @@ describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void mockExtensionContext = TypeMoq.Mock.ofType(); mockApiWrapper.setup((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement)).returns(() => Promise.resolve(mockTokens)); - mockDatabaseService.setup((o) => o.getResources(mockSubscription, TypeMoq.It.isAny())).returns(() => Promise.resolve(mockDatabases)); + mockDatabaseService.setup((o) => o.getResources(mockSubscription, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockDatabases)); mockExtensionContext.setup((o) => o.asAbsolutePath(TypeMoq.It.isAnyString())).returns(() => TypeMoq.It.isAnyString()); }); diff --git a/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts b/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts index 49fb505906..075c4d20e9 100644 --- a/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts +++ b/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts @@ -104,7 +104,7 @@ describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function (): mockExtensionContext = TypeMoq.Mock.ofType(); mockApiWrapper.setup((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement)).returns(() => Promise.resolve(mockTokens)); - mockDatabaseServerService.setup((o) => o.getResources(mockSubscription, TypeMoq.It.isAny())).returns(() => Promise.resolve(mockDatabaseServers)); + mockDatabaseServerService.setup((o) => o.getResources(mockSubscription, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockDatabaseServers)); mockExtensionContext.setup((o) => o.asAbsolutePath(TypeMoq.It.isAnyString())).returns(() => TypeMoq.It.isAnyString()); }); diff --git a/extensions/azurecore/src/test/azureResource/tree/accountTreeNode.test.ts b/extensions/azurecore/src/test/azureResource/tree/accountTreeNode.test.ts index 6b3ce58f44..0ad524d8fb 100644 --- a/extensions/azurecore/src/test/azureResource/tree/accountTreeNode.test.ts +++ b/extensions/azurecore/src/test/azureResource/tree/accountTreeNode.test.ts @@ -76,10 +76,13 @@ const mockSubscriptions = [mockSubscription1, mockSubscription2]; const mockFilteredSubscriptions = [mockSubscription1]; const mockTokens: { [key: string]: any } = {}; -mockTokens[mockTenantId] = { - token: 'mock_token', - tokenType: 'Bearer' -}; + +[mockSubscription1.id, mockSubscription2.id, mockTenantId].forEach(s => { + mockTokens[s] = { + token: 'mock_token', + tokenType: 'Bearer' + }; +}); const mockCredential = new TokenCredentials(mockTokens[mockTenantId].token, mockTokens[mockTenantId].tokenType); @@ -108,7 +111,7 @@ describe('AzureResourceAccountTreeNode.info', function (): void { mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid()); mockCacheService.setup((o) => o.get(TypeMoq.It.isAnyString())).returns(() => mockSubscriptionCache); mockCacheService.setup((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns(() => mockSubscriptionCache = mockSubscriptions); - mockTenantService.setup((o) => o.getTenantId(TypeMoq.It.isAny())).returns(() => Promise.resolve(mockTenantId)); + mockTenantService.setup((o) => o.getTenantId(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockTenantId)); }); it('Should be correct when created.', async function (): Promise { @@ -196,7 +199,7 @@ describe('AzureResourceAccountTreeNode.getChildren', function (): void { mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid()); mockCacheService.setup((o) => o.get(TypeMoq.It.isAnyString())).returns(() => mockSubscriptionCache); mockCacheService.setup((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns(() => mockSubscriptionCache = mockSubscriptions); - mockTenantService.setup((o) => o.getTenantId(TypeMoq.It.isAny())).returns(() => Promise.resolve(mockTenantId)); + mockTenantService.setup((o) => o.getTenantId(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockTenantId)); }); it('Should load subscriptions from scratch and update cache when it is clearing cache.', async function (): Promise { @@ -207,7 +210,6 @@ describe('AzureResourceAccountTreeNode.getChildren', function (): void { const children = await accountTreeNode.getChildren(); - mockApiWrapper.verify((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement), TypeMoq.Times.once()); mockSubscriptionService.verify((o) => o.getSubscriptions(mockAccount, mockCredential), TypeMoq.Times.once()); mockCacheService.verify((o) => o.get(TypeMoq.It.isAnyString()), TypeMoq.Times.exactly(0)); mockCacheService.verify((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny()), TypeMoq.Times.once()); @@ -242,7 +244,7 @@ describe('AzureResourceAccountTreeNode.getChildren', function (): void { await accountTreeNode.getChildren(); const children = await accountTreeNode.getChildren(); - mockApiWrapper.verify((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement), TypeMoq.Times.once()); + mockSubscriptionService.verify((o) => o.getSubscriptions(mockAccount, mockCredential), TypeMoq.Times.once()); mockCacheService.verify((o) => o.get(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); mockCacheService.verify((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny()), TypeMoq.Times.once()); @@ -331,11 +333,11 @@ describe('AzureResourceAccountTreeNode.clearCache', function (): void { mockAppContext.registerService(AzureResourceServiceNames.subscriptionFilterService, mockSubscriptionFilterService.object); mockAppContext.registerService(AzureResourceServiceNames.tenantService, mockTenantService.object); - mockApiWrapper.setup((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement)).returns(() => Promise.resolve(mockTokens)); + mockApiWrapper.setup((o,) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement)).returns(() => Promise.resolve(mockTokens)); mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid()); mockCacheService.setup((o) => o.get(TypeMoq.It.isAnyString())).returns(() => mockSubscriptionCache); mockCacheService.setup((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns(() => mockSubscriptionCache = mockSubscriptions); - mockTenantService.setup((o) => o.getTenantId(TypeMoq.It.isAny())).returns(() => Promise.resolve(mockTenantId)); + mockTenantService.setup((o) => o.getTenantId(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockTenantId)); }); it('Should clear cache.', async function (): Promise { diff --git a/extensions/azurecore/yarn.lock b/extensions/azurecore/yarn.lock index 7c7df9940e..55ade9aaab 100644 --- a/extensions/azurecore/yarn.lock +++ b/extensions/azurecore/yarn.lock @@ -11,16 +11,16 @@ "@azure/ms-rest-js" "^1.8.1" tslib "^1.9.3" -"@azure/arm-subscriptions@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@azure/arm-subscriptions/-/arm-subscriptions-1.0.0.tgz#ab65a5cd4d8b8c878ff6621428f29137b84eb1d6" - integrity sha512-DoZFcF+ePkGhrxQ4X/LxBoWipqpkZIt9g+itXJ4HaQNExn0hNO27Cs6kNZYpIdCs5Zu+2DbjRs2i8OqG3QbE9w== +"@azure/arm-subscriptions@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@azure/arm-subscriptions/-/arm-subscriptions-2.0.0.tgz#4202740b7f65a9d0f16f7903579a615f5de45a92" + integrity sha512-+ys2glK5YgwZ9KhwWblfAQIPABtiB5OdKEpPOpcvr7B5ygYTwZuSUNObX9MRu/MyiRo1zDlUvlxHltBphq/bLQ== dependencies: - "@azure/ms-rest-azure-js" "^1.1.0" - "@azure/ms-rest-js" "^1.1.0" - tslib "^1.9.3" + "@azure/ms-rest-azure-js" "^2.0.1" + "@azure/ms-rest-js" "^2.0.4" + tslib "^1.10.0" -"@azure/ms-rest-azure-js@^1.1.0", "@azure/ms-rest-azure-js@^1.3.2": +"@azure/ms-rest-azure-js@^1.3.2": version "1.3.8" resolved "https://registry.yarnpkg.com/@azure/ms-rest-azure-js/-/ms-rest-azure-js-1.3.8.tgz#96b518223d3baa2496b2981bc07288b3d887486e" integrity sha512-AHLfDTCyIH6wBK6+CpImI6sc9mLZ17ZgUrTx3Rhwv+3Mb3Z73BxormkarfR6Stb6scrBYitxJ27FXyndXlGAYg== @@ -28,7 +28,15 @@ "@azure/ms-rest-js" "^1.8.10" tslib "^1.9.3" -"@azure/ms-rest-js@^1.1.0", "@azure/ms-rest-js@^1.8.1", "@azure/ms-rest-js@^1.8.10": +"@azure/ms-rest-azure-js@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@azure/ms-rest-azure-js/-/ms-rest-azure-js-2.0.1.tgz#fa1b38f039b3ee48a9e086a88c8a5b5b7776491c" + integrity sha512-5e+A710O7gRFISoV4KI/ZyLQbKmjXxQZ1L8Z/sx7jSUQqmswjTnN4yyIZxs5JzfLVkobU0rXxbi5/LVzaI8QXQ== + dependencies: + "@azure/ms-rest-js" "^2.0.4" + tslib "^1.10.0" + +"@azure/ms-rest-js@^1.8.1", "@azure/ms-rest-js@^1.8.10": version "1.8.13" resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-1.8.13.tgz#ed0cd86469697378cd39d79d5589e877a3bc87a6" integrity sha512-jAa6Y2XrvwbEqkaEXDHK+ReNo0WnCPS+LgQ1dRAJUUNxK4CghF5u+SXsVtPENritilVE7FVteqsLOtlhTk+haA== @@ -42,6 +50,22 @@ uuid "^3.2.1" xml2js "^0.4.19" +"@azure/ms-rest-js@^2.0.4": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-2.0.5.tgz#7ac1043b7b8c99bb8c5b09ada96b858b02db8e75" + integrity sha512-zj9/JOSsbaJleZz9k9RXKJMBPA2NBUgsus5iZxGlhK+pDeb8M2ps+lkKhJSwLnlTUj5CvrcN3PZDF2b2nSYCQQ== + dependencies: + "@types/node-fetch" "^2.3.7" + "@types/tunnel" "0.0.1" + abort-controller "^3.0.0" + form-data "^2.5.0" + node-fetch "^2.6.0" + tough-cookie "^3.0.1" + tslib "^1.10.0" + tunnel "0.0.6" + uuid "^3.3.2" + xml2js "^0.4.19" + "@types/caseless@*": version "0.12.2" resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" @@ -59,6 +83,14 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073" integrity sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww== +"@types/node-fetch@^2.3.7": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.5.tgz#cd264e20a81f4600a6c52864d38e7fef72485e92" + integrity sha512-IWwjsyYjGw+em3xTvWVQi5MgYKbRs0du57klfTaZkv/B24AEQ/p/IopNeqIYNy3EsfHOpg8ieQSDomPcsYMHpA== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node@*": version "10.14.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.4.tgz#1c586b991457cbb58fef51bc4e0cfcfa347714b5" @@ -96,6 +128,13 @@ dependencies: "@types/node" "*" +"@types/tunnel@0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.1.tgz#0d72774768b73df26f25df9184273a42da72b19c" + integrity sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A== + dependencies: + "@types/node" "*" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -106,6 +145,13 @@ abbrev@1.0.x: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + adal-node@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/adal-node/-/adal-node-0.2.1.tgz#19e401bd579977448c1a77ce0e5b4c9accdc334e" @@ -306,6 +352,13 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@2.15.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -465,6 +518,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + extend-shallow@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" @@ -514,7 +572,7 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@^2.3.2: +form-data@^2.3.2, form-data@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== @@ -523,6 +581,15 @@ form-data@^2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -662,6 +729,11 @@ inherits@2, inherits@~2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + is-buffer@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" @@ -905,6 +977,11 @@ neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +node-fetch@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + nopt@3.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -1235,6 +1312,15 @@ tough-cookie@^2.4.3: psl "^1.1.28" punycode "^2.1.1" +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== + dependencies: + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -1243,6 +1329,11 @@ tough-cookie@~2.4.3: psl "^1.1.24" punycode "^1.4.1" +tslib@^1.10.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + tslib@^1.9.2, tslib@^1.9.3: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"