mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 09:35:36 -05:00
591 lines
23 KiB
TypeScript
591 lines
23 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as azdata from 'azdata';
|
|
|
|
import { Event, Emitter } from 'vs/base/common/event';
|
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
|
import { Memento } from 'vs/workbench/common/memento';
|
|
|
|
import AccountStore from 'sql/platform/accounts/common/accountStore';
|
|
import { AccountDialogController } from 'sql/workbench/services/accountManagement/browser/accountDialogController';
|
|
import { AutoOAuthDialogController } from 'sql/workbench/services/accountManagement/browser/autoOAuthDialogController';
|
|
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
|
|
import { IAccountManagementService } from 'sql/platform/accounts/common/interfaces';
|
|
import { Deferred } from 'sql/base/common/promise';
|
|
import { localize } from 'vs/nls';
|
|
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
|
import { URI } from 'vs/base/common/uri';
|
|
import { values } from 'vs/base/common/collections';
|
|
import { ILogService } from 'vs/platform/log/common/log';
|
|
import { INotificationService, Severity, INotification } from 'vs/platform/notification/common/notification';
|
|
import { Action } from 'vs/base/common/actions';
|
|
import { DisposableStore } from 'vs/base/common/lifecycle';
|
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
|
import { ADAL_AUTH_LIBRARY, AuthLibrary, filterAccounts, MSAL_AUTH_LIBRARY } from 'sql/workbench/services/accountManagement/browser/accountDialog';
|
|
|
|
export class AccountManagementService implements IAccountManagementService {
|
|
// CONSTANTS ///////////////////////////////////////////////////////////
|
|
private static ACCOUNT_MEMENTO = 'AccountManagement';
|
|
|
|
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
|
public _providers: { [id: string]: AccountProviderWithMetadata } = {};
|
|
public _serviceBrand: undefined;
|
|
private _accountStore: AccountStore;
|
|
private _authLibrary: AuthLibrary;
|
|
private _accountDialogController?: AccountDialogController;
|
|
private _autoOAuthDialogController?: AutoOAuthDialogController;
|
|
private _mementoContext?: Memento;
|
|
protected readonly disposables = new DisposableStore();
|
|
private readonly configurationService: IConfigurationService;
|
|
|
|
// EVENT EMITTERS //////////////////////////////////////////////////////
|
|
private _addAccountProviderEmitter: Emitter<AccountProviderAddedEventParams>;
|
|
public get addAccountProviderEvent(): Event<AccountProviderAddedEventParams> { return this._addAccountProviderEmitter.event; }
|
|
|
|
private _removeAccountProviderEmitter: Emitter<azdata.AccountProviderMetadata>;
|
|
public get removeAccountProviderEvent(): Event<azdata.AccountProviderMetadata> { return this._removeAccountProviderEmitter.event; }
|
|
|
|
private _updateAccountListEmitter: Emitter<UpdateAccountListEventParams>;
|
|
public get updateAccountListEvent(): Event<UpdateAccountListEventParams> { return this._updateAccountListEmitter.event; }
|
|
|
|
// CONSTRUCTOR /////////////////////////////////////////////////////////
|
|
constructor(
|
|
@IInstantiationService private _instantiationService: IInstantiationService,
|
|
@IStorageService private _storageService: IStorageService,
|
|
@IClipboardService private _clipboardService: IClipboardService,
|
|
@IOpenerService private _openerService: IOpenerService,
|
|
@ILogService private readonly _logService: ILogService,
|
|
@INotificationService private readonly _notificationService: INotificationService,
|
|
@IConfigurationService configurationService: IConfigurationService
|
|
) {
|
|
this._mementoContext = new Memento(AccountManagementService.ACCOUNT_MEMENTO, this._storageService);
|
|
const mementoObj = this._mementoContext.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
|
|
this._accountStore = this._instantiationService.createInstance(AccountStore, mementoObj);
|
|
|
|
// Setup the event emitters
|
|
this._addAccountProviderEmitter = new Emitter<AccountProviderAddedEventParams>();
|
|
this._removeAccountProviderEmitter = new Emitter<azdata.AccountProviderMetadata>();
|
|
this._updateAccountListEmitter = new Emitter<UpdateAccountListEventParams>();
|
|
this.configurationService = configurationService;
|
|
|
|
// Determine authentication library in use, to support filtering accounts respectively.
|
|
// When this value is changed a restart is required so there isn't a need to dynamically update this value at runtime.
|
|
this._authLibrary = this.configurationService.getValue('azure.authenticationLibrary');
|
|
|
|
_storageService.onWillSaveState(() => this.shutdown());
|
|
this.registerListeners();
|
|
}
|
|
|
|
private get autoOAuthDialogController(): AutoOAuthDialogController {
|
|
// If the add account dialog hasn't been defined, create a new one
|
|
if (!this._autoOAuthDialogController) {
|
|
this._autoOAuthDialogController = this._instantiationService.createInstance(AutoOAuthDialogController);
|
|
}
|
|
return this._autoOAuthDialogController;
|
|
}
|
|
|
|
// PUBLIC METHODS //////////////////////////////////////////////////////
|
|
/**
|
|
* Called from an account provider (via extension host -> main thread interop) when an
|
|
* account's properties have been updated (usually when the account goes stale).
|
|
* @param updatedAccount Account with the updated properties
|
|
*/
|
|
public accountUpdated(updatedAccount: azdata.Account): Promise<void> {
|
|
let self = this;
|
|
|
|
// 1) Update the account in the store
|
|
// 2a) If the account was added, then the account provider incorrectly called this method.
|
|
// Remove the account
|
|
// 2b) If the account was modified, then update it in the local cache and notify any
|
|
// listeners that the account provider's list changed
|
|
// 3) Handle any errors
|
|
return this.doWithProvider(updatedAccount.key.providerId, provider => {
|
|
return self._accountStore.addOrUpdate(updatedAccount)
|
|
.then(result => {
|
|
if (result.accountAdded) {
|
|
self._accountStore.remove(updatedAccount.key);
|
|
return Promise.reject('Called with a new account!');
|
|
}
|
|
if (result.accountModified) {
|
|
self.spliceModifiedAccount(provider, result.changedAccount);
|
|
self.fireAccountListUpdate(provider, false);
|
|
}
|
|
return Promise.resolve();
|
|
});
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* Asks the requested provider to prompt for an account
|
|
* @param providerId ID of the provider to ask to prompt for an account
|
|
* @return Promise to return an account
|
|
*/
|
|
public addAccount(providerId: string): Promise<void> {
|
|
const closeAction: Action = new Action('closeAddingAccount', localize('accountManagementService.close', "Close"), undefined, true);
|
|
|
|
const loginNotification: INotification = {
|
|
severity: Severity.Info,
|
|
message: localize('loggingIn', "Adding account..."),
|
|
progress: {
|
|
infinite: true
|
|
},
|
|
actions: {
|
|
primary: [closeAction]
|
|
}
|
|
};
|
|
|
|
return this.doWithProvider(providerId, async (provider) => {
|
|
const notificationHandler = this._notificationService.notify(loginNotification);
|
|
try {
|
|
let account = await provider.provider.prompt();
|
|
if (this.isCanceledResult(account)) {
|
|
return;
|
|
}
|
|
|
|
let result = await this._accountStore.addOrUpdate(account);
|
|
if (!result) {
|
|
this._logService.error('adding account failed');
|
|
throw Error('Adding account failed, check Azure Accounts log for more info.')
|
|
}
|
|
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);
|
|
} finally {
|
|
notificationHandler.close();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Adds an account to the account store without prompting the user
|
|
* @param account account to add
|
|
*/
|
|
public addAccountWithoutPrompt(account: azdata.Account): Promise<void> {
|
|
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 (<azdata.PromptFailedResult>result).canceled;
|
|
}
|
|
|
|
/**
|
|
* Asks the requested provider to refresh an account
|
|
* @param account account to refresh
|
|
* @return Promise to return an account
|
|
*/
|
|
public refreshAccount(account: azdata.Account): Promise<azdata.Account> {
|
|
let self = this;
|
|
|
|
return this.doWithProvider(account.key.providerId, async (provider) => {
|
|
let refreshedAccount = await provider.provider.refresh(account);
|
|
if (self.isCanceledResult(refreshedAccount)) {
|
|
// Pattern here is to throw if this fails. Handled upstream.
|
|
throw new Error(localize('refreshFailed', "Refresh account was canceled by the user"));
|
|
} else {
|
|
account = refreshedAccount;
|
|
}
|
|
|
|
let result = await self._accountStore.addOrUpdate(account);
|
|
if (result.accountAdded) {
|
|
// Add the account to the list
|
|
provider.accounts.push(result.changedAccount!);
|
|
}
|
|
if (result.accountModified) {
|
|
// Find the updated account and splice the updated on in
|
|
let indexToRemove: number = provider.accounts.findIndex(account => {
|
|
return account.key.accountId === result.changedAccount!.key.accountId;
|
|
});
|
|
if (indexToRemove >= 0) {
|
|
provider.accounts.splice(indexToRemove, 1, result.changedAccount!);
|
|
}
|
|
}
|
|
|
|
self.fireAccountListUpdate(provider, result.accountAdded);
|
|
return result.changedAccount!;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Retrieves metadata of all providers that have been registered
|
|
* @returns Registered account providers
|
|
*/
|
|
public getAccountProviderMetadata(): Promise<azdata.AccountProviderMetadata[]> {
|
|
return Promise.resolve(values(this._providers).map(provider => provider.metadata));
|
|
}
|
|
|
|
/**
|
|
* Retrieves the accounts that belong to a specific provider
|
|
* @param providerId ID of the provider the returned accounts belong to
|
|
* @returns Promise to return a list of accounts
|
|
*/
|
|
public getAccountsForProvider(providerId: string): Promise<azdata.Account[]> {
|
|
let self = this;
|
|
|
|
// 1) Get the accounts from the store
|
|
// 2) Filter the accounts based on the auth library
|
|
// 3) Update our local cache of accounts
|
|
return this.doWithProvider(providerId, provider => {
|
|
return self._accountStore.getAccountsByProvider(provider.metadata.id)
|
|
.then(accounts => this._authLibrary ? filterAccounts(accounts, this._authLibrary) : accounts)
|
|
.then(accounts => {
|
|
self._providers[providerId].accounts = accounts;
|
|
return accounts;
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Retrieves all the accounts registered with ADS based on auth library in use.
|
|
*/
|
|
public getAccounts(): Promise<azdata.Account[]> {
|
|
return this._accountStore.getAllAccounts()
|
|
.then(accounts => this._authLibrary ? filterAccounts(accounts, this._authLibrary) : accounts);
|
|
}
|
|
|
|
/**
|
|
* Generates a security token by asking the account's provider
|
|
* @param account Account to generate security token for
|
|
* @param resource The resource to get the security token for
|
|
* @return Promise to return the security token
|
|
*/
|
|
public getSecurityToken(account: azdata.Account, resource: azdata.AzureResource): Promise<{} | undefined> {
|
|
return this.doWithProvider(account.key.providerId, provider => {
|
|
return Promise.resolve(provider.provider.getSecurityToken(account, resource));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Generates a security token by asking the account's provider
|
|
* @param account Account to generate security token for
|
|
* @param tenant Tenant to generate security token for
|
|
* @param resource The resource to get the security token for
|
|
* @return Promise to return the security token
|
|
*/
|
|
public getAccountSecurityToken(account: azdata.Account, tenant: string, resource: azdata.AzureResource): Promise<azdata.accounts.AccountSecurityToken | undefined> {
|
|
return this.doWithProvider(account.key.providerId, provider => {
|
|
return Promise.resolve(provider.provider.getAccountSecurityToken(account, tenant, resource));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Removes an account from the account store and clears sensitive data in the provider
|
|
* @param accountKey Key for the account to remove
|
|
* @returns Promise with result of account removal, true if account was
|
|
* removed, false otherwise.
|
|
*/
|
|
public removeAccount(accountKey: azdata.AccountKey): Promise<boolean> {
|
|
|
|
// Step 1) Remove the account
|
|
// Step 2) Clear the sensitive data from the provider (regardless of whether the account was removed)
|
|
// Step 3) Update the account cache and fire an event
|
|
return this.doWithProvider(accountKey.providerId, async provider => {
|
|
const result = await this._accountStore.remove(accountKey);
|
|
await provider.provider.clear(accountKey);
|
|
if (!result) {
|
|
return result;
|
|
}
|
|
|
|
let indexToRemove: number = provider.accounts.findIndex(account => {
|
|
return account.key.accountId === accountKey.accountId;
|
|
});
|
|
|
|
if (indexToRemove >= 0) {
|
|
provider.accounts.splice(indexToRemove, 1);
|
|
this.fireAccountListUpdate(provider, false);
|
|
}
|
|
return result;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Removes all registered accounts
|
|
*/
|
|
public async removeAccounts(): Promise<boolean> {
|
|
const accounts = await this.getAccounts();
|
|
if (accounts.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
let finalResult = true;
|
|
for (const account of accounts) {
|
|
try {
|
|
const removeResult = await this.removeAccount(account.key);
|
|
if (removeResult === false) {
|
|
this._logService.info('Error when removing %s.', account.key);
|
|
finalResult = false;
|
|
}
|
|
} catch (ex) {
|
|
this._logService.error('Error when removing an account %s. Exception: %s', account.key, JSON.stringify(ex));
|
|
}
|
|
}
|
|
return finalResult;
|
|
}
|
|
|
|
// UI METHODS //////////////////////////////////////////////////////////
|
|
/**
|
|
* Opens the account list dialog
|
|
* @return Promise that finishes when the account list dialog closes
|
|
*/
|
|
public openAccountListDialog(): Promise<void> {
|
|
let self = this;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
// If the account list dialog hasn't been defined, create a new one
|
|
if (!self._accountDialogController) {
|
|
self._accountDialogController = self._instantiationService.createInstance(AccountDialogController);
|
|
}
|
|
self._accountDialogController.openAccountDialog();
|
|
self._accountDialogController.accountDialog!.onCloseEvent(resolve);
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Begin auto OAuth device code open add account dialog
|
|
* @return Promise that finishes when the account list dialog opens
|
|
*/
|
|
public beginAutoOAuthDeviceCode(providerId: string, title: string, message: string, userCode: string, uri: string): Promise<void> {
|
|
let self = this;
|
|
|
|
return this.doWithProvider(providerId, provider => {
|
|
return self.autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* End auto OAuth Device code closes add account dialog
|
|
*/
|
|
public endAutoOAuthDeviceCode(): void {
|
|
this.autoOAuthDialogController.closeAutoOAuthDialog();
|
|
}
|
|
|
|
/**
|
|
* Called from the UI when a user cancels the auto OAuth dialog
|
|
*/
|
|
public cancelAutoOAuthDeviceCode(providerId: string): void {
|
|
void this.doWithProvider(providerId, provider => Promise.resolve(provider.provider.autoOAuthCancelled()))
|
|
.then( // Swallow errors
|
|
undefined,
|
|
err => { this._logService.warn(`Error when cancelling auto OAuth: ${err}`); }
|
|
)
|
|
.then(() => this.autoOAuthDialogController.closeAutoOAuthDialog());
|
|
}
|
|
|
|
/**
|
|
* Copy the user code to the clipboard and open a browser to the verification URI
|
|
*/
|
|
public async copyUserCodeAndOpenBrowser(userCode: string, uri: string): Promise<void> {
|
|
await this._clipboardService.writeText(userCode);
|
|
await this._openerService.open(URI.parse(uri));
|
|
|
|
}
|
|
|
|
private async _registerProvider(providerMetadata: azdata.AccountProviderMetadata, provider: azdata.AccountProvider): Promise<void> {
|
|
this._providers[providerMetadata.id] = {
|
|
metadata: providerMetadata,
|
|
provider: provider,
|
|
accounts: []
|
|
};
|
|
|
|
const accounts = await this._accountStore.getAccountsByProvider(providerMetadata.id);
|
|
const updatedAccounts = await provider.initialize(accounts);
|
|
|
|
// Don't add the accounts that are explicitly marked to be deleted to the cache.
|
|
this._providers[providerMetadata.id].accounts = updatedAccounts.filter(s => !s.delete);
|
|
|
|
const writePromises = updatedAccounts.map(async (account) => {
|
|
if (account.delete === true) {
|
|
return this._accountStore.remove(account.key);
|
|
}
|
|
return this._accountStore.addOrUpdate(account);
|
|
});
|
|
await Promise.all(writePromises);
|
|
|
|
const p = this._providers[providerMetadata.id];
|
|
this._addAccountProviderEmitter.fire({
|
|
addedProvider: p.metadata,
|
|
initialAccounts: p.accounts.slice(0) // Slice here to make sure no one can modify our cache
|
|
});
|
|
// Notify listeners that the account has been updated
|
|
this.fireAccountListUpdate(p, false);
|
|
}
|
|
|
|
// SERVICE MANAGEMENT METHODS //////////////////////////////////////////
|
|
/**
|
|
* Called by main thread to register an account provider from extension
|
|
* @param providerMetadata Metadata of the provider that is being registered
|
|
* @param provider References to the methods of the provider
|
|
*/
|
|
public registerProvider(providerMetadata: azdata.AccountProviderMetadata, provider: azdata.AccountProvider): Promise<void> {
|
|
return this._registerProvider(providerMetadata, provider);
|
|
}
|
|
|
|
/**
|
|
* Handler for when shutdown of the application occurs. Writes out the memento.
|
|
*/
|
|
private shutdown(): void {
|
|
if (this._mementoContext) {
|
|
this._mementoContext.saveMemento();
|
|
}
|
|
}
|
|
|
|
public unregisterProvider(providerMetadata: azdata.AccountProviderMetadata): void {
|
|
// Delete this account provider
|
|
delete this._providers[providerMetadata.id];
|
|
|
|
// Alert our listeners that we've removed a provider
|
|
this._removeAccountProviderEmitter.fire(providerMetadata);
|
|
}
|
|
|
|
// TODO: Support for orphaned accounts (accounts with no provider)
|
|
|
|
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
|
private doWithProvider<T>(providerId: string, op: (provider: AccountProviderWithMetadata) => Promise<T>): Promise<T> {
|
|
let provider = this._providers[providerId];
|
|
if (!provider) {
|
|
// If the provider doesn't already exist wait until it gets registered
|
|
let deferredPromise = new Deferred<T>();
|
|
let toDispose = this.addAccountProviderEvent(params => {
|
|
if (params.addedProvider.id === providerId) {
|
|
toDispose.dispose();
|
|
deferredPromise.resolve(op(this._providers[providerId]));
|
|
}
|
|
});
|
|
return deferredPromise;
|
|
}
|
|
|
|
return op(provider);
|
|
}
|
|
|
|
private fireAccountListUpdate(provider: AccountProviderWithMetadata, sort: boolean) {
|
|
// Step 1) Get and sort the list
|
|
if (sort) {
|
|
provider.accounts.sort((a: azdata.Account, b: azdata.Account) => {
|
|
if (a.displayInfo.displayName < b.displayInfo.displayName) {
|
|
return -1;
|
|
}
|
|
if (a.displayInfo.displayName > b.displayInfo.displayName) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
const authLibrary: AuthLibrary = this.configurationService.getValue('azure.authenticationLibrary');
|
|
let updatedAccounts: azdata.Account[]
|
|
if (authLibrary) {
|
|
updatedAccounts = filterAccounts(provider.accounts, authLibrary);
|
|
}
|
|
// Step 2) Fire the event
|
|
let eventArg: UpdateAccountListEventParams = {
|
|
providerId: provider.metadata.id,
|
|
accountList: updatedAccounts ?? provider.accounts
|
|
};
|
|
this._updateAccountListEmitter.fire(eventArg);
|
|
}
|
|
|
|
private spliceModifiedAccount(provider: AccountProviderWithMetadata, modifiedAccount: azdata.Account) {
|
|
// Find the updated account and splice the updated one in
|
|
let indexToRemove: number = provider.accounts.findIndex(account => {
|
|
return account.key.accountId === modifiedAccount.key.accountId;
|
|
});
|
|
if (indexToRemove >= 0) {
|
|
provider.accounts.splice(indexToRemove, 1, modifiedAccount);
|
|
}
|
|
}
|
|
|
|
private registerListeners(): void {
|
|
this.disposables.add(this.configurationService.onDidChangeConfiguration(async e => {
|
|
if (e.affectsConfiguration('azure.authenticationLibrary')) {
|
|
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<azdata.Account[]> {
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Joins together an account provider, its metadata, and its accounts, used in the provider list
|
|
*/
|
|
export interface AccountProviderWithMetadata {
|
|
metadata: azdata.AccountProviderMetadata;
|
|
provider: azdata.AccountProvider;
|
|
accounts: azdata.Account[];
|
|
}
|