mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-27 20:13:32 +01:00
* Tear down the Authentication monolith Major changes: * Turn the usage functions into a proper service `AuthenticationUsageService` * Pull out the access data stuff into its own service `AuthenticationAccessService` * Pull out things that make sense as actions `ManageTrustedExtensionsForAccount` `SignOutOfAccount` * Pull out random registry stuff into a proper authentication contribution * Pull out everything else that is extension specific into its own class (and eventually it should be in MainThreadAuthentication) * Have the new `AuthenticationService` return a provider instead of having specific methods for getting the `label` or `supportsMultipleAccounts` * fix tests * fix tests
102 lines
4.8 KiB
TypeScript
102 lines
4.8 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* 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>('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);
|