Path: blob/main/src/vs/workbench/contrib/authentication/browser/authentication.contribution.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Disposable } from '../../../../base/common/lifecycle.js';6import { localize } from '../../../../nls.js';7import { registerAction2 } from '../../../../platform/actions/common/actions.js';8import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';9import { IExtensionManifest } from '../../../../platform/extensions/common/extensions.js';10import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';11import { Registry } from '../../../../platform/registry/common/platform.js';12import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js';13import { SignOutOfAccountAction } from './actions/signOutOfAccountAction.js';14import { IBrowserWorkbenchEnvironmentService } from '../../../services/environment/browser/environmentService.js';15import { Extensions, IExtensionFeatureTableRenderer, IExtensionFeaturesRegistry, IRenderedData, IRowData, ITableData } from '../../../services/extensionManagement/common/extensionFeatures.js';16import { ManageTrustedExtensionsForAccountAction } from './actions/manageTrustedExtensionsForAccountAction.js';17import { ManageAccountPreferencesForExtensionAction } from './actions/manageAccountPreferencesForExtensionAction.js';18import { IAuthenticationUsageService } from '../../../services/authentication/browser/authenticationUsageService.js';19import { ManageAccountPreferencesForMcpServerAction } from './actions/manageAccountPreferencesForMcpServerAction.js';20import { ManageTrustedMcpServersForAccountAction } from './actions/manageTrustedMcpServersForAccountAction.js';21import { RemoveDynamicAuthenticationProvidersAction } from './actions/manageDynamicAuthenticationProvidersAction.js';22import { IAuthenticationQueryService } from '../../../services/authentication/common/authenticationQuery.js';23import { IMcpRegistry } from '../../mcp/common/mcpRegistryTypes.js';24import { autorun } from '../../../../base/common/observable.js';25import { IAuthenticationService } from '../../../services/authentication/common/authentication.js';26import { Event } from '../../../../base/common/event.js';2728const codeExchangeProxyCommand = CommandsRegistry.registerCommand('workbench.getCodeExchangeProxyEndpoints', function (accessor, _) {29const environmentService = accessor.get(IBrowserWorkbenchEnvironmentService);30return environmentService.options?.codeExchangeProxyEndpoints;31});3233class AuthenticationDataRenderer extends Disposable implements IExtensionFeatureTableRenderer {3435readonly type = 'table';3637shouldRender(manifest: IExtensionManifest): boolean {38return !!manifest.contributes?.authentication;39}4041render(manifest: IExtensionManifest): IRenderedData<ITableData> {42const authentication = manifest.contributes?.authentication || [];43if (!authentication.length) {44return { data: { headers: [], rows: [] }, dispose: () => { } };45}4647const headers = [48localize('authenticationlabel', "Label"),49localize('authenticationid', "ID"),50localize('authenticationMcpAuthorizationServers', "MCP Authorization Servers")51];5253const rows: IRowData[][] = authentication54.sort((a, b) => a.label.localeCompare(b.label))55.map(auth => {56return [57auth.label,58auth.id,59(auth.authorizationServerGlobs ?? []).join(',\n')60];61});6263return {64data: {65headers,66rows67},68dispose: () => { }69};70}71}7273const extensionFeature = Registry.as<IExtensionFeaturesRegistry>(Extensions.ExtensionFeaturesRegistry).registerExtensionFeature({74id: 'authentication',75label: localize('authentication', "Authentication"),76access: {77canToggle: false78},79renderer: new SyncDescriptor(AuthenticationDataRenderer),80});8182class AuthenticationContribution extends Disposable implements IWorkbenchContribution {83static ID = 'workbench.contrib.authentication';8485constructor() {86super();87this._register(codeExchangeProxyCommand);88this._register(extensionFeature);8990this._registerActions();91}9293private _registerActions(): void {94this._register(registerAction2(SignOutOfAccountAction));95this._register(registerAction2(ManageTrustedExtensionsForAccountAction));96this._register(registerAction2(ManageAccountPreferencesForExtensionAction));97this._register(registerAction2(ManageTrustedMcpServersForAccountAction));98this._register(registerAction2(ManageAccountPreferencesForMcpServerAction));99this._register(registerAction2(RemoveDynamicAuthenticationProvidersAction));100}101}102103class AuthenticationUsageContribution implements IWorkbenchContribution {104static ID = 'workbench.contrib.authenticationUsage';105106constructor(107@IAuthenticationUsageService private readonly _authenticationUsageService: IAuthenticationUsageService,108) {109this._initializeExtensionUsageCache();110}111112private async _initializeExtensionUsageCache() {113await this._authenticationUsageService.initializeExtensionUsageCache();114}115}116117// class AuthenticationExtensionsContribution extends Disposable implements IWorkbenchContribution {118// static ID = 'workbench.contrib.authenticationExtensions';119120// constructor(121// @IExtensionService private readonly _extensionService: IExtensionService,122// @IAuthenticationQueryService private readonly _authenticationQueryService: IAuthenticationQueryService,123// @IAuthenticationService private readonly _authenticationService: IAuthenticationService124// ) {125// super();126// void this.run();127// this._register(this._extensionService.onDidChangeExtensions(this._onDidChangeExtensions, this));128// this._register(129// Event.any(130// this._authenticationService.onDidChangeDeclaredProviders,131// this._authenticationService.onDidRegisterAuthenticationProvider132// )(() => this._cleanupRemovedExtensions())133// );134// }135136// async run(): Promise<void> {137// await this._extensionService.whenInstalledExtensionsRegistered();138// this._cleanupRemovedExtensions();139// }140141// private _onDidChangeExtensions(delta: { readonly added: readonly IExtensionDescription[]; readonly removed: readonly IExtensionDescription[] }): void {142// if (delta.removed.length > 0) {143// this._cleanupRemovedExtensions(delta.removed);144// }145// }146147// private _cleanupRemovedExtensions(removedExtensions?: readonly IExtensionDescription[]): void {148// const extensionIdsToRemove = removedExtensions149// ? new Set(removedExtensions.map(e => e.identifier.value))150// : new Set(this._extensionService.extensions.map(e => e.identifier.value));151152// // If we are cleaning up specific removed extensions, we only remove those.153// const isTargetedCleanup = !!removedExtensions;154155// const providerIds = this._authenticationQueryService.getProviderIds();156// for (const providerId of providerIds) {157// this._authenticationQueryService.provider(providerId).forEachAccount(account => {158// account.extensions().forEach(extension => {159// const shouldRemove = isTargetedCleanup160// ? extensionIdsToRemove.has(extension.extensionId)161// : !extensionIdsToRemove.has(extension.extensionId);162163// if (shouldRemove) {164// extension.removeUsage();165// extension.setAccessAllowed(false);166// }167// });168// });169// }170// }171// }172173class AuthenticationMcpContribution extends Disposable implements IWorkbenchContribution {174static ID = 'workbench.contrib.authenticationMcp';175176constructor(177@IMcpRegistry private readonly _mcpRegistry: IMcpRegistry,178@IAuthenticationQueryService private readonly _authenticationQueryService: IAuthenticationQueryService,179@IAuthenticationService private readonly _authenticationService: IAuthenticationService180) {181super();182this._cleanupRemovedMcpServers();183184// Listen for MCP collections changes using autorun with observables185this._register(autorun(reader => {186// Read the collections observable to register dependency187this._mcpRegistry.collections.read(reader);188// Schedule cleanup for next tick to avoid running during observable updates189queueMicrotask(() => this._cleanupRemovedMcpServers());190}));191this._register(192Event.any(193this._authenticationService.onDidChangeDeclaredProviders,194this._authenticationService.onDidRegisterAuthenticationProvider195)(() => this._cleanupRemovedMcpServers())196);197}198199private _cleanupRemovedMcpServers(): void {200const currentServerIds = new Set(this._mcpRegistry.collections.get().flatMap(c => c.serverDefinitions.get()).map(s => s.id));201const providerIds = this._authenticationQueryService.getProviderIds();202for (const providerId of providerIds) {203this._authenticationQueryService.provider(providerId).forEachAccount(account => {204account.mcpServers().forEach(server => {205if (!currentServerIds.has(server.mcpServerId)) {206server.removeUsage();207server.setAccessAllowed(false);208}209});210});211}212}213}214215registerWorkbenchContribution2(AuthenticationContribution.ID, AuthenticationContribution, WorkbenchPhase.AfterRestored);216registerWorkbenchContribution2(AuthenticationUsageContribution.ID, AuthenticationUsageContribution, WorkbenchPhase.Eventually);217// registerWorkbenchContribution2(AuthenticationExtensionsContribution.ID, AuthenticationExtensionsContribution, WorkbenchPhase.Eventually);218registerWorkbenchContribution2(AuthenticationMcpContribution.ID, AuthenticationMcpContribution, WorkbenchPhase.Eventually);219220221