/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { AllowedExtension } from 'vs/workbench/services/authentication/common/authentication'; export const IAuthenticationAccessService = createDecorator('IAuthenticationAccessService'); export interface IAuthenticationAccessService { readonly _serviceBrand: undefined; readonly onDidChangeExtensionSessionAccess: Event<{ providerId: string; accountName: string }>; /** * Check extension access to an account * @param providerId The id of the authentication provider * @param accountName The account name that access is checked for * @param extensionId The id of the extension requesting access * @returns Returns true or false if the user has opted to permanently grant or disallow access, and undefined * if they haven't made a choice yet */ isAccessAllowed(providerId: string, accountName: string, extensionId: string): boolean | undefined; readAllowedExtensions(providerId: string, accountName: string): AllowedExtension[]; updateAllowedExtensions(providerId: string, accountName: string, extensions: AllowedExtension[]): void; removeAllowedExtensions(providerId: string, accountName: string): void; } // TODO@TylerLeonhardt: Move this class to MainThreadAuthentication export class AuthenticationAccessService extends Disposable implements IAuthenticationAccessService { _serviceBrand: undefined; private _onDidChangeExtensionSessionAccess: Emitter<{ providerId: string; accountName: string }> = this._register(new Emitter<{ providerId: string; accountName: string }>()); readonly onDidChangeExtensionSessionAccess: Event<{ providerId: string; accountName: string }> = this._onDidChangeExtensionSessionAccess.event; constructor( @IStorageService private readonly _storageService: IStorageService, @IProductService private readonly _productService: IProductService ) { super(); } isAccessAllowed(providerId: string, accountName: string, extensionId: string): boolean | undefined { const trustedExtensionAuthAccess = this._productService.trustedExtensionAuthAccess; if (Array.isArray(trustedExtensionAuthAccess)) { if (trustedExtensionAuthAccess.includes(extensionId)) { return true; } } else if (trustedExtensionAuthAccess?.[providerId]?.includes(extensionId)) { return true; } const allowList = this.readAllowedExtensions(providerId, accountName); const extensionData = allowList.find(extension => extension.id === extensionId); if (!extensionData) { return undefined; } // This property didn't exist on this data previously, inclusion in the list at all indicates allowance return extensionData.allowed !== undefined ? extensionData.allowed : true; } readAllowedExtensions(providerId: string, accountName: string): AllowedExtension[] { let trustedExtensions: AllowedExtension[] = []; try { const trustedExtensionSrc = this._storageService.get(`${providerId}-${accountName}`, StorageScope.APPLICATION); if (trustedExtensionSrc) { trustedExtensions = JSON.parse(trustedExtensionSrc); } } catch (err) { } return trustedExtensions; } updateAllowedExtensions(providerId: string, accountName: string, extensions: AllowedExtension[]): void { const allowList = this.readAllowedExtensions(providerId, accountName); for (const extension of extensions) { const index = allowList.findIndex(e => e.id === extension.id); if (index === -1) { allowList.push(extension); } else { allowList[index].allowed = extension.allowed; } } this._storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.APPLICATION, StorageTarget.USER); this._onDidChangeExtensionSessionAccess.fire({ providerId, accountName }); } removeAllowedExtensions(providerId: string, accountName: string): void { this._storageService.remove(`${providerId}-${accountName}`, StorageScope.APPLICATION); this._onDidChangeExtensionSessionAccess.fire({ providerId, accountName }); } } registerSingleton(IAuthenticationAccessService, AuthenticationAccessService, InstantiationType.Delayed);