Add MSAL Authentication Library support (#21024)

This commit is contained in:
Christopher Suh
2022-11-23 17:06:44 -05:00
committed by GitHub
parent fba47815e2
commit 86c3f315f2
32 changed files with 1502 additions and 320 deletions

View File

@@ -66,23 +66,25 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode
if (subscriptions.length === 0) {
return [AzureResourceMessageTreeNode.create(AzureResourceAccountTreeNode.noSubscriptionsLabel, this)];
} else {
// Filter out everything that we can't authenticate to.
const hasTokenResults = await Promise.all(subscriptions.map(async s => {
let token: azdata.accounts.AccountSecurityToken | undefined = undefined;
let errMsg = '';
try {
token = await azdata.accounts.getAccountSecurityToken(this.account, s.tenant!, azdata.AzureResource.ResourceManagement);
} catch (err) {
errMsg = AzureResourceErrorMessageUtil.getErrorMessage(err);
}
if (!token) {
void vscode.window.showWarningMessage(localize('azure.unableToAccessSubscription', "Unable to access subscription {0} ({1}). Please [refresh the account](command:azure.resource.signin) to try again. {2}", s.name, s.id, errMsg));
return false;
}
return true;
}));
subscriptions = subscriptions.filter((_s, i) => hasTokenResults[i]);
const authLibrary = vscode.workspace.getConfiguration('azure').get('authenticationLibrary');
if (authLibrary === 'ADAL') {
// Filter out everything that we can't authenticate to.
const hasTokenResults = await Promise.all(subscriptions.map(async s => {
let token: azdata.accounts.AccountSecurityToken | undefined = undefined;
let errMsg = '';
try {
token = await azdata.accounts.getAccountSecurityToken(this.account, s.tenant!, azdata.AzureResource.ResourceManagement);
} catch (err) {
errMsg = AzureResourceErrorMessageUtil.getErrorMessage(err);
}
if (!token) {
void vscode.window.showWarningMessage(localize('azure.unableToAccessSubscription', "Unable to access subscription {0} ({1}). Please [refresh the account](command:azure.resource.signin) to try again. {2}", s.name, s.id, errMsg));
return false;
}
return true;
}));
subscriptions = subscriptions.filter((_s, i) => hasTokenResults[i]);
}
let subTreeNodes = await Promise.all(subscriptions.map(async (subscription) => {
return new AzureResourceSubscriptionTreeNode(this.account, subscription, subscription.tenant!, this.appContext, this.treeChangeHandler, this);
}));
@@ -164,4 +166,9 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode
private _selectedSubscriptionCount = 0;
private static readonly noSubscriptionsLabel = localize('azure.resource.tree.accountTreeNode.noSubscriptionsLabel', "No Subscriptions found.");
sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}

View File

@@ -13,7 +13,7 @@ import { TreeNode } from '../treeNode';
import { AzureResourceAccountNotSignedInTreeNode } from './accountNotSignedInTreeNode';
import { AzureResourceMessageTreeNode } from '../messageTreeNode';
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
import { AzureResourceErrorMessageUtil, equals } from '../utils';
import { AzureResourceErrorMessageUtil, equals, filterAccounts } from '../utils';
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
import { FlatAccountTreeNode } from './flatAccountTreeNode';
import { Logger } from '../../utils/Logger';
@@ -26,10 +26,11 @@ export class ConnectionDialogTreeProvider implements vscode.TreeDataProvider<Tre
private _onDidChangeTreeData = new vscode.EventEmitter<TreeNode | undefined>();
private loadingAccountsPromise: Promise<void> | undefined;
public constructor(private readonly appContext: AppContext) {
public constructor(private readonly appContext: AppContext,
private readonly authLibrary: string) {
azdata.accounts.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 azdata.accounts.getAllAccounts();
let accounts = filterAccounts(await azdata.accounts.getAllAccounts(), authLibrary);
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
@@ -55,10 +56,11 @@ export class ConnectionDialogTreeProvider implements vscode.TreeDataProvider<Tre
}
if (this.accounts && this.accounts.length > 0) {
let accounts = filterAccounts(this.accounts, this.authLibrary);
const accountNodes: FlatAccountTreeNode[] = [];
const errorMessages: string[] = [];
// We are doing sequential account loading to avoid the Azure request throttling
for (const account of this.accounts) {
for (const account of accounts) {
try {
const accountNode = new FlatAccountTreeNode(account, this.appContext, this);
await accountNode.updateLabel();
@@ -85,7 +87,7 @@ export class ConnectionDialogTreeProvider implements vscode.TreeDataProvider<Tre
private async loadAccounts(): Promise<void> {
try {
this.accounts = await azdata.accounts.getAllAccounts();
this.accounts = filterAccounts(await azdata.accounts.getAllAccounts(), this.authLibrary);
// System has been initialized
this.setSystemInitialized();
this._onDidChangeTreeData.fire(undefined);

View File

@@ -12,12 +12,12 @@ const localize = nls.loadMessageBundle();
import { TreeNode } from '../treeNode';
import { AzureResourceMessageTreeNode } from '../messageTreeNode';
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
import { AzureResourceErrorMessageUtil } from '../utils';
import { AzureResourceErrorMessageUtil, filterAccounts } from '../utils';
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
import { IAzureResourceNodeWithProviderId, IAzureResourceSubscriptionService } from '../interfaces';
import { AzureResourceServiceNames } from '../constants';
import { AzureResourceService } from '../resourceService';
import { Logger } from '../../utils/Logger';
export class FlatAzureResourceTreeProvider implements vscode.TreeDataProvider<TreeNode>, IAzureResourceTreeChangeHandler {
public isSystemInitialized: boolean = false;
@@ -26,7 +26,8 @@ export class FlatAzureResourceTreeProvider implements vscode.TreeDataProvider<Tr
private resourceLoader: ResourceLoader | undefined;
public constructor(private readonly appContext: AppContext) {
public constructor(private readonly appContext: AppContext,
private readonly authLibrary: string) {
}
public async getChildren(element?: TreeNode): Promise<TreeNode[]> {
@@ -35,7 +36,7 @@ export class FlatAzureResourceTreeProvider implements vscode.TreeDataProvider<Tr
}
if (!this.resourceLoader) {
this.resourceLoader = new ResourceLoader(this.appContext);
this.resourceLoader = new ResourceLoader(this.appContext, this.authLibrary);
this.resourceLoader.onDidAddNewResource(e => this._onDidChangeTreeData.fire(e));
}
@@ -87,7 +88,8 @@ class ResourceLoader {
private readonly _onDidAddNewResource = new vscode.EventEmitter<TreeNode | undefined>();
public readonly onDidAddNewResource = this._onDidAddNewResource.event;
constructor(private readonly appContext: AppContext) {
constructor(private readonly appContext: AppContext,
private readonly authLibrary: string) {
this.subscriptionService = appContext.getService<IAzureResourceSubscriptionService>(AzureResourceServiceNames.subscriptionService);
this.resourceService = appContext.getService<AzureResourceService>(AzureResourceServiceNames.resourceService);
}
@@ -118,7 +120,7 @@ class ResourceLoader {
this._state = LoaderState.Loading;
const accounts = await azdata.accounts.getAllAccounts();
const accounts = filterAccounts(await azdata.accounts.getAllAccounts(), this.authLibrary);
for (const account of accounts) {
for (const tenant of account.properties.tenants) {
@@ -141,7 +143,7 @@ class ResourceLoader {
}
}
console.log('finished loading');
Logger.verbose('finished loading all accounts and subscriptions');
clearInterval(interval);
@@ -208,5 +210,4 @@ class AzureResourceResourceTreeNode extends TreeNode {
public get nodePathValue(): string {
return this.resourceNodeWithProviderId.resourceNode.treeItem.id || '';
}
}

View File

@@ -14,11 +14,10 @@ import { AzureResourceAccountTreeNode } from './accountTreeNode';
import { AzureResourceAccountNotSignedInTreeNode } from './accountNotSignedInTreeNode';
import { AzureResourceMessageTreeNode } from '../messageTreeNode';
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
import { AzureResourceErrorMessageUtil, equals } from '../utils';
import { AzureResourceErrorMessageUtil, equals, filterAccounts } from '../utils';
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
import { AzureAccount } from 'azurecore';
export class AzureResourceTreeProvider implements vscode.TreeDataProvider<TreeNode>, IAzureResourceTreeChangeHandler {
public isSystemInitialized: boolean = false;
@@ -26,10 +25,11 @@ export class AzureResourceTreeProvider implements vscode.TreeDataProvider<TreeNo
private _onDidChangeTreeData = new vscode.EventEmitter<TreeNode | undefined>();
private loadingAccountsPromise: Promise<void> | undefined;
public constructor(private readonly appContext: AppContext) {
public constructor(private readonly appContext: AppContext,
private readonly authLibrary: string) {
azdata.accounts.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 azdata.accounts.getAllAccounts();
let accounts = filterAccounts(await azdata.accounts.getAllAccounts(), authLibrary);
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
@@ -56,6 +56,7 @@ export class AzureResourceTreeProvider implements vscode.TreeDataProvider<TreeNo
try {
if (this.accounts && this.accounts.length > 0) {
this.accounts = filterAccounts(this.accounts, this.authLibrary);
return this.accounts.map((account) => new AzureResourceAccountTreeNode(account, this.appContext, this));
} else {
return [new AzureResourceAccountNotSignedInTreeNode()];
@@ -67,7 +68,7 @@ export class AzureResourceTreeProvider implements vscode.TreeDataProvider<TreeNo
private async loadAccounts(): Promise<void> {
try {
this.accounts = await azdata.accounts.getAllAccounts();
this.accounts = filterAccounts(await azdata.accounts.getAllAccounts(), this.authLibrary);
// System has been initialized
this.setSystemInitialized();
this._onDidChangeTreeData.fire(undefined);
@@ -96,7 +97,6 @@ export class AzureResourceTreeProvider implements vscode.TreeDataProvider<TreeNo
node.clearCache();
}
}
this._onDidChangeTreeData.fire(node);
}