diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 471ab42a62d..6c4bebff701 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import * as modes from 'vs/editor/common/modes'; import * as nls from 'vs/nls'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { IAuthenticationService, AllowedExtension, readAllowedExtensions } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { IAuthenticationService, AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent } from 'vs/workbench/services/authentication/browser/authenticationService'; import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -381,7 +381,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise { - await this.extensionService.activateByEvent(`onAuthenticationRequest:${providerId}`); + await this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(providerId)); const allowList = readAllowedExtensions(this.storageService, providerId, accountName); const extensionData = allowList.find(extension => extension.id === extensionId); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 0ba2747b20b..54686714c45 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -124,7 +124,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.turningOnSyncContext = CONTEXT_TURNING_ON_STATE.bindTo(contextKeyService); this.conflictsSources = CONTEXT_CONFLICTS_SOURCES.bindTo(contextKeyService); - if (this.userDataSyncWorkbenchService.authenticationProviders.length) { + if (userDataSyncWorkbenchService.enabled) { registerConfiguration(); this.updateAccountBadge(); diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 202a92003fa..9a176e4cae3 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -16,6 +16,8 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +export function getAuthenticationProviderActivationEvent(id: string): string { return `onAuthenticationRequest${id}`; } + export const IAuthenticationService = createDecorator('IAuthenticationService'); export interface IAuthenticationService { diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index c5d3872e797..ff61d5d72e6 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -11,7 +11,7 @@ import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/edi import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { flatten, equals } from 'vs/base/common/arrays'; -import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { getAuthenticationProviderActivationEvent, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; @@ -64,7 +64,11 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat private static DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY = 'userDataSyncAccount.donotUseWorkbenchSession'; private static CACHED_SESSION_STORAGE_KEY = 'userDataSyncAccountPreference'; - readonly authenticationProviders: IAuthenticationProvider[]; + private _authenticationProviders: IAuthenticationProvider[] = []; + get enabled() { return this._authenticationProviders.length > 0; } + + private availableAuthenticationProviders: IAuthenticationProvider[] = []; + get authenticationProviders() { return this.availableAuthenticationProviders; } private _accountStatus: AccountStatus = AccountStatus.Uninitialized; get accountStatus(): AccountStatus { return this._accountStatus; } @@ -95,7 +99,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat @ILogService private readonly logService: ILogService, @IProductService productService: IProductService, @IConfigurationService configurationService: IConfigurationService, - @IExtensionService extensionService: IExtensionService, + @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @INotificationService private readonly notificationService: INotificationService, @IProgressService private readonly progressService: IProgressService, @@ -105,35 +109,36 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, ) { super(); - this.authenticationProviders = getUserDataSyncStore(productService, configurationService)?.authenticationProviders || []; + this._authenticationProviders = getUserDataSyncStore(productService, configurationService)?.authenticationProviders || []; this.syncEnablementContext = CONTEXT_SYNC_ENABLEMENT.bindTo(contextKeyService); this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); this.accountStatusContext = CONTEXT_ACCOUNT_STATE.bindTo(contextKeyService); this.activityViewsEnablementContext = CONTEXT_ENABLE_ACTIVITY_VIEWS.bindTo(contextKeyService); this.mergesViewEnablementContext = CONTEXT_ENABLE_SYNC_MERGES_VIEW.bindTo(contextKeyService); - if (this.authenticationProviders.length) { - + if (this._authenticationProviders.length) { this.syncStatusContext.set(this.userDataSyncService.status); this._register(userDataSyncService.onDidChangeStatus(status => this.syncStatusContext.set(status))); this.syncEnablementContext.set(userDataAutoSyncService.isEnabled()); this._register(userDataAutoSyncService.onDidChangeEnablement(enabled => this.syncEnablementContext.set(enabled))); - extensionService.whenInstalledExtensionsRegistered().then(() => { - if (this.authenticationProviders.every(({ id }) => authenticationService.isAuthenticationProviderRegistered(id))) { - this.initialize(); - } else { - const disposable = this.authenticationService.onDidRegisterAuthenticationProvider(() => { - if (this.authenticationProviders.every(({ id }) => authenticationService.isAuthenticationProviderRegistered(id))) { - disposable.dispose(); - this.initialize(); - } - }); - } - }); + this.waitAndInitialize(); } } + private isSupportedAuthenticationProviderId(authenticationProviderId: string): boolean { + return this._authenticationProviders.some(({ id }) => id === authenticationProviderId); + } + + private async waitAndInitialize(): Promise { + if (this._authenticationProviders.every(({ id }) => !this.authenticationService.isAuthenticationProviderRegistered(id))) { + /* None of the providers are registered -> activate providers and wait until one of them is availabe */ + await Promise.all(this._authenticationProviders.map(({ id }) => this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id)))); + await Event.toPromise(Event.filter(this.authenticationService.onDidRegisterAuthenticationProvider, ({ id }) => this.isSupportedAuthenticationProviderId(id))); + } + await this.initialize(); + } + private async initialize(): Promise { if (this.currentSessionId === undefined && this.useWorkbenchSessionId && this.environmentService.options?.authenticationSessionId) { this.currentSessionId = this.environmentService.options.authenticationSessionId; @@ -158,8 +163,11 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } private async update(): Promise { + + this.availableAuthenticationProviders = this._authenticationProviders.filter(({ id }) => this.authenticationService.isAuthenticationProviderRegistered(id)); + const allAccounts: Map = new Map(); - for (const { id } of this.authenticationProviders) { + for (const { id } of this.availableAuthenticationProviders) { const accounts = await this.getAccounts(id); allAccounts.set(id, accounts); } @@ -167,7 +175,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat this._all = allAccounts; const current = this.current; await this.updateToken(current); - this.updateAccountStatus(current); + this.updateAccountStatus(current ? AccountStatus.Available : AccountStatus.Unavailable); } private async getAccounts(authenticationProviderId: string): Promise { @@ -206,10 +214,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat await this.userDataSyncAccountService.updateAccount(value); } - private updateAccountStatus(current: UserDataSyncAccount | undefined): void { - // set status - const accountStatus: AccountStatus = current ? AccountStatus.Available : AccountStatus.Unavailable; - + private updateAccountStatus(accountStatus: AccountStatus): void { if (this._accountStatus !== accountStatus) { const previous = this._accountStatus; this.logService.debug('Sync account status changed', previous, accountStatus); @@ -381,10 +386,6 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } } - private isSupportedAuthenticationProviderId(authenticationProviderId: string): boolean { - return this.authenticationProviders.some(({ id }) => id === authenticationProviderId); - } - private isCurrentAccount(account: UserDataSyncAccount): boolean { return account.sessionId === this.currentSessionId; } @@ -414,15 +415,15 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } private async doPick(): Promise { - if (this.authenticationProviders.length === 0) { + if (this.availableAuthenticationProviders.length === 0) { return undefined; } await this.update(); // Single auth provider and no accounts available - if (this.authenticationProviders.length === 1 && !this.all.length) { - return this.authenticationProviders[0]; + if (this.availableAuthenticationProviders.length === 1 && !this.all.length) { + return this.availableAuthenticationProviders[0]; } return new Promise(async (c, e) => { @@ -454,7 +455,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat // Signed in Accounts if (this.all.length) { - const authenticationProviders = [...this.authenticationProviders].sort(({ id }) => id === this.current?.authenticationProviderId ? -1 : 1); + const authenticationProviders = [...this.availableAuthenticationProviders].sort(({ id }) => id === this.current?.authenticationProviderId ? -1 : 1); quickPickItems.push({ type: 'separator', label: localize('signed in', "Signed in") }); for (const authenticationProvider of authenticationProviders) { const accounts = (this._all.get(authenticationProvider.id) || []).sort(({ sessionId }) => sessionId === this.current?.sessionId ? -1 : 1); @@ -472,7 +473,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } // Account proviers - for (const authenticationProvider of this.authenticationProviders) { + for (const authenticationProvider of this.availableAuthenticationProviders) { const signedInForProvider = this.all.some(account => account.authenticationProviderId === authenticationProvider.id); if (!signedInForProvider || this.authenticationService.supportsMultipleAccounts(authenticationProvider.id)) { const providerName = this.authenticationService.getLabel(authenticationProvider.id); diff --git a/src/vs/workbench/services/userDataSync/common/userDataSync.ts b/src/vs/workbench/services/userDataSync/common/userDataSync.ts index 963831be05f..73c59a33130 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSync.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSync.ts @@ -44,7 +44,9 @@ export const IUserDataSyncWorkbenchService = createDecorator