From 9708b470c780c227e3ec74ac257d9652dcd8efee Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Wed, 11 Jan 2023 16:05:20 -0800 Subject: [PATCH] Azure core | Include alternate library accounts if found (#21565) --- .../azurecore/src/azureResource/commands.ts | 4 +- .../browser/accountDialog.ts | 19 +--- .../browser/accountManagementService.ts | 95 ++++++++++++++----- 3 files changed, 77 insertions(+), 41 deletions(-) diff --git a/extensions/azurecore/src/azureResource/commands.ts b/extensions/azurecore/src/azureResource/commands.ts index 854e5ef2fb..e9167030c1 100644 --- a/extensions/azurecore/src/azureResource/commands.ts +++ b/extensions/azurecore/src/azureResource/commands.ts @@ -17,7 +17,7 @@ import { AzureResourceServiceNames } from './constants'; import { AzureAccount, Tenant, azureResource } from 'azurecore'; import { FlatAccountTreeNode } from './tree/flatAccountTreeNode'; import { ConnectionDialogTreeProvider } from './tree/connectionDialogTreeProvider'; -import { AzureResourceErrorMessageUtil } from './utils'; +import { AzureResourceErrorMessageUtil, filterAccounts } from './utils'; export function registerAzureResourceCommands(appContext: AppContext, azureViewTree: AzureResourceTreeProvider, connectionDialogTree: ConnectionDialogTreeProvider, authLibrary: string): void { const trees = [azureViewTree, connectionDialogTree]; @@ -33,7 +33,7 @@ export function registerAzureResourceCommands(appContext: AppContext, azureViewT if (node instanceof AzureResourceAccountTreeNode) { azureAccount = node.account; } else { - let accounts = await azdata.accounts.getAllAccounts(); + let accounts = filterAccounts(await azdata.accounts.getAllAccounts(), authLibrary); accounts = accounts.filter(a => a.key.providerId.startsWith('azure')); if (accounts.length === 0) { const signin = localize('azure.signIn', "Sign in"); diff --git a/src/sql/workbench/services/accountManagement/browser/accountDialog.ts b/src/sql/workbench/services/accountManagement/browser/accountDialog.ts index f0b87d6df8..c303117fff 100644 --- a/src/sql/workbench/services/accountManagement/browser/accountDialog.ts +++ b/src/sql/workbench/services/accountManagement/browser/accountDialog.ts @@ -51,10 +51,10 @@ import { IAccountManagementService } from 'sql/platform/accounts/common/interfac export const VIEWLET_ID = 'workbench.view.accountpanel'; export type AuthLibrary = 'ADAL' | 'MSAL'; +export const MSAL_AUTH_LIBRARY: AuthLibrary = 'MSAL'; // default +export const ADAL_AUTH_LIBRARY: AuthLibrary = 'ADAL'; -export class AccountPaneContainer extends ViewPaneContainer { - -} +export class AccountPaneContainer extends ViewPaneContainer { } export const ACCOUNT_VIEW_CONTAINER = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, @@ -499,19 +499,10 @@ export class AccountDialog extends Modal { export function filterAccounts(accounts: azdata.Account[], authLibrary: AuthLibrary): azdata.Account[] { let filteredAccounts = accounts.filter(account => { if (account.key.authLibrary) { - if (account.key.authLibrary === authLibrary) { - return true; - } else { - return false; - } + return account.key.authLibrary === authLibrary; } else { - if (authLibrary === 'ADAL') { - return true; - } else { - return false; - } + return authLibrary === ADAL_AUTH_LIBRARY; } }); - return filteredAccounts; } diff --git a/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts b/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts index 4f9faed4ce..128a9c5d15 100644 --- a/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts +++ b/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts @@ -26,7 +26,7 @@ import { INotificationService, Severity, INotification } from 'vs/platform/notif import { Action } from 'vs/base/common/actions'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { AuthLibrary, filterAccounts } from 'sql/workbench/services/accountManagement/browser/accountDialog'; +import { ADAL_AUTH_LIBRARY, AuthLibrary, filterAccounts, MSAL_AUTH_LIBRARY } from 'sql/workbench/services/accountManagement/browser/accountDialog'; export class AccountManagementService implements IAccountManagementService { // CONSTANTS /////////////////////////////////////////////////////////// @@ -168,6 +168,28 @@ export class AccountManagementService implements IAccountManagementService { }); } + /** + * Adds an account to the account store without prompting the user + * @param account account to add + */ + public addAccountWithoutPrompt(account: azdata.Account): Promise { + return this.doWithProvider(account.key.providerId, async (provider) => { + let result = await this._accountStore.addOrUpdate(account); + if (!result) { + this._logService.error('adding account failed'); + } + if (result.accountAdded) { + // Add the account to the list + provider.accounts.push(result.changedAccount); + } + if (result.accountModified) { + this.spliceModifiedAccount(provider, result.changedAccount); + } + + this.fireAccountListUpdate(provider, result.accountAdded); + }); + } + private isCanceledResult(result: azdata.Account | azdata.PromptFailedResult): result is azdata.PromptFailedResult { return (result).canceled; } @@ -504,35 +526,58 @@ export class AccountManagementService implements IAccountManagementService { private registerListeners(): void { this.disposables.add(this.configurationService.onDidChangeConfiguration(async e => { if (e.affectsConfiguration('azure.authenticationLibrary')) { - const authLibrary: AuthLibrary = this.configurationService.getValue('azure.authenticationLibrary'); - if (authLibrary) { - let accounts = await this._accountStore.getAllAccounts(); - if (accounts) { - let updatedAccounts = filterAccounts(accounts, authLibrary); - let eventArg: UpdateAccountListEventParams; - if (updatedAccounts.length > 0) { - updatedAccounts.forEach(account => { - if (account.key.authLibrary === 'MSAL') { - account.isStale = false; - } - }); - eventArg = { - providerId: updatedAccounts[0].key.providerId, - accountList: updatedAccounts - }; - - } else { // default to public cloud if no accounts - eventArg = { - providerId: 'azure_publicCloud', - accountList: updatedAccounts - }; - } - this._updateAccountListEmitter.fire(eventArg); + const authLibrary: AuthLibrary = this.configurationService.getValue('azure.authenticationLibrary') ?? MSAL_AUTH_LIBRARY; + let accounts = await this._accountStore.getAllAccounts(); + if (accounts) { + let updatedAccounts = await this.filterAndMergeAccounts(accounts, authLibrary); + let eventArg: UpdateAccountListEventParams; + if (updatedAccounts.length > 0) { + updatedAccounts.forEach(account => { + if (account.key.authLibrary === MSAL_AUTH_LIBRARY) { + account.isStale = false; + } + }); + eventArg = { + providerId: updatedAccounts[0].key.providerId, + accountList: updatedAccounts + }; + } else { // default to public cloud if no accounts + eventArg = { + providerId: 'azure_publicCloud', + accountList: updatedAccounts + }; } + this._updateAccountListEmitter.fire(eventArg); } } })); } + + // Filters and merges accounts from both authentication libraries + private async filterAndMergeAccounts(accounts: azdata.Account[], currentAuthLibrary: AuthLibrary): Promise { + // Fetch accounts for alternate authenticationLibrary + const altLibrary = currentAuthLibrary === MSAL_AUTH_LIBRARY ? ADAL_AUTH_LIBRARY : MSAL_AUTH_LIBRARY; + const altLibraryAccounts = filterAccounts(accounts, altLibrary); + + // Fetch accounts for current authenticationLibrary + const currentLibraryAccounts = filterAccounts(accounts, currentAuthLibrary); + + // In the list of alternate accounts, check if the accounts are present in the current library cache, + // if not, add the account and mark it stale. The original account is marked as taken so its not picked again. + for (let account of altLibraryAccounts) { + await this.removeAccount(account.key); + if (currentLibraryAccounts.find(a => account.displayInfo.email === a.displayInfo.email)) { + continue; + } else { + // TODO: Refresh access token for the account if feasible. + account.isStale = true; + account.key.authLibrary = currentAuthLibrary; + currentLibraryAccounts.push(account); + await this.addAccountWithoutPrompt(account); + } + } + return currentLibraryAccounts; + } } /**