mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Add MSAL Authentication Library support (#21024)
This commit is contained in:
@@ -19,7 +19,7 @@ import { FlatAccountTreeNode } from './tree/flatAccountTreeNode';
|
||||
import { ConnectionDialogTreeProvider } from './tree/connectionDialogTreeProvider';
|
||||
import { AzureResourceErrorMessageUtil } from './utils';
|
||||
|
||||
export function registerAzureResourceCommands(appContext: AppContext, azureViewTree: AzureResourceTreeProvider, connectionDialogTree: ConnectionDialogTreeProvider): void {
|
||||
export function registerAzureResourceCommands(appContext: AppContext, azureViewTree: AzureResourceTreeProvider, connectionDialogTree: ConnectionDialogTreeProvider, authLibrary: string): void {
|
||||
const trees = [azureViewTree, connectionDialogTree];
|
||||
vscode.commands.registerCommand('azure.resource.startterminal', async (node?: TreeNode) => {
|
||||
try {
|
||||
|
||||
@@ -28,7 +28,6 @@ export class AzureResourceSubscriptionService implements IAzureResourceSubscript
|
||||
const subscriptions: azureResource.AzureResourceSubscription[] = [];
|
||||
let gotSubscriptions = false;
|
||||
const errors: Error[] = [];
|
||||
|
||||
for (const tenantId of tenantIds ?? account.properties.tenants.map(t => t.id)) {
|
||||
try {
|
||||
const token = await azdata.accounts.getAccountSecurityToken(account, tenantId, azdata.AzureResource.ResourceManagement);
|
||||
@@ -42,6 +41,7 @@ export class AzureResourceSubscriptionService implements IAzureResourceSubscript
|
||||
tenant: tenantId
|
||||
};
|
||||
}));
|
||||
Logger.verbose(`AzureResourceSubscriptionService.getSubscriptions: Retrieved ${newSubs.length} subscriptions for tenant ${tenantId} / account ${account.displayInfo.displayName}`);
|
||||
gotSubscriptions = true;
|
||||
}
|
||||
else if (!account.isStale) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 || '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import { IAzureResourceSubscriptionFilterService, IAzureResourceSubscriptionServ
|
||||
import { AzureResourceGroupService } from './providers/resourceGroup/resourceGroupService';
|
||||
import { BlobServiceClient, StorageSharedKeyCredential } from '@azure/storage-blob';
|
||||
import providerSettings from '../account-provider/providerSettings';
|
||||
import * as Constants from '../constants';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -541,3 +542,18 @@ export function getProviderMetadataForAccount(account: AzureAccount): AzureAccou
|
||||
|
||||
return provider.metadata;
|
||||
}
|
||||
|
||||
// Filter accounts based on currently selected Auth Library:
|
||||
// if the account key is present, filter based on current auth library
|
||||
// if there is no account key (pre-MSAL account), then it is an ADAL account and
|
||||
// should be displayed as long as ADAL is the currently selected auth library
|
||||
export function filterAccounts(accounts: azdata.Account[], authLibrary: string): azdata.Account[] {
|
||||
let filteredAccounts = accounts.filter(account => {
|
||||
if (account.key.authLibrary) {
|
||||
return account.key.authLibrary === authLibrary;
|
||||
} else {
|
||||
return authLibrary === Constants.AuthLibrary.ADAL;
|
||||
}
|
||||
});
|
||||
return filteredAccounts;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user