Remove 'Show features using this account' picker, show timestamps in 'Manage trusted extensions', fixes #96367, fixes #96357

This commit is contained in:
Rachel Macfarlane
2020-05-13 15:11:17 -07:00
parent e8abe35269
commit b2e0c85030

View File

@@ -18,26 +18,51 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
const accountUsages = new Map<string, { [accountName: string]: string[] }>();
import { fromNow } from 'vs/base/common/date';
const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git'];
function addAccountUsage(providerId: string, accountName: string, extensionOrFeatureName: string) {
const providerAccountUsage = accountUsages.get(providerId);
if (!providerAccountUsage) {
accountUsages.set(providerId, { [accountName]: [extensionOrFeatureName] });
} else {
if (providerAccountUsage[accountName]) {
if (!providerAccountUsage[accountName].includes(extensionOrFeatureName)) {
providerAccountUsage[accountName].push(extensionOrFeatureName);
}
} else {
providerAccountUsage[accountName] = [extensionOrFeatureName];
}
interface IAccountUsage {
extensionId: string;
extensionName: string;
lastUsed: number;
}
accountUsages.set(providerId, providerAccountUsage);
function readAccountUsages(storageService: IStorageService, providerId: string, accountName: string,): IAccountUsage[] {
const accountKey = `${providerId}-${accountName}-usages`;
const storedUsages = storageService.get(accountKey, StorageScope.GLOBAL);
let usages: IAccountUsage[] = [];
if (storedUsages) {
try {
usages = JSON.parse(storedUsages);
} catch (e) {
// ignore
}
}
return usages;
}
function addAccountUsage(storageService: IStorageService, providerId: string, accountName: string, extensionId: string, extensionName: string) {
const accountKey = `${providerId}-${accountName}-usages`;
const usages = readAccountUsages(storageService, providerId, accountName);
const existingUsageIndex = usages.findIndex(usage => usage.extensionId === extensionId);
if (existingUsageIndex > -1) {
usages.splice(existingUsageIndex, 1, {
extensionId,
extensionName,
lastUsed: Date.now()
});
} else {
usages.push({
extensionId,
extensionName,
lastUsed: Date.now()
});
}
storageService.store(accountKey, JSON.stringify(usages), StorageScope.GLOBAL);
}
export class MainThreadAuthenticationProvider extends Disposable {
@@ -50,7 +75,8 @@ export class MainThreadAuthenticationProvider extends Disposable {
public readonly id: string,
public readonly displayName: string,
private readonly notificationService: INotificationService,
private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
private readonly storageService: IStorageService
) {
super();
}
@@ -64,12 +90,17 @@ export class MainThreadAuthenticationProvider extends Disposable {
}
private manageTrustedExtensions(quickInputService: IQuickInputService, storageService: IStorageService, accountName: string) {
const quickPick = quickInputService.createQuickPick<{ label: string, extension: AllowedExtension }>();
const quickPick = quickInputService.createQuickPick<{ label: string, description: string, extension: AllowedExtension }>();
quickPick.canSelectMany = true;
const allowedExtensions = readAllowedExtensions(storageService, this.id, accountName);
const usages = readAccountUsages(storageService, this.id, accountName);
const items = allowedExtensions.map(extension => {
const usage = usages.find(usage => extension.id === usage.extensionId);
return {
label: extension.name,
description: usage
? nls.localize('accountLastUsedDate', "Last used this account {0}", fromNow(usage.lastUsed, true))
: nls.localize('notUsed', "Has not used this account"),
extension
};
});
@@ -93,24 +124,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
quickPick.show();
}
private showUsage(quickInputService: IQuickInputService, accountName: string) {
const quickPick = quickInputService.createQuickPick();
const providerUsage = accountUsages.get(this.id);
const accountUsage = (providerUsage || {})[accountName] || [];
quickPick.items = accountUsage.map(extensionOrFeature => {
return {
label: extensionOrFeature
};
});
quickPick.onDidHide(() => {
quickPick.dispose();
});
quickPick.show();
}
private async registerCommandsAndContextMenuItems(): Promise<void> {
const sessions = await this._proxy.$getSessions(this.id);
sessions.forEach(session => this.registerSession(session));
@@ -146,10 +159,9 @@ export class MainThreadAuthenticationProvider extends Disposable {
const dialogService = accessor.get(IDialogService);
const quickPick = quickInputService.createQuickPick();
const showUsage = nls.localize('showUsage', "Show Extensions and Features Using This Account");
const manage = nls.localize('manageTrustedExtensions', "Manage Trusted Extensions");
const signOut = nls.localize('signOut', "Sign Out");
const items = ([{ label: showUsage }, { label: manage }, { label: signOut }]);
const items = ([{ label: manage }, { label: signOut }]);
quickPick.items = items;
@@ -163,10 +175,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
this.manageTrustedExtensions(quickInputService, storageService, session.account.displayName);
}
if (selected.label === showUsage) {
this.showUsage(quickInputService, session.account.displayName);
}
quickPick.dispose();
});
@@ -182,24 +190,21 @@ export class MainThreadAuthenticationProvider extends Disposable {
}
async signOut(dialogService: IDialogService, session: modes.AuthenticationSession): Promise<void> {
const providerUsage = accountUsages.get(this.id);
const accountUsage = (providerUsage || {})[session.account.displayName] || [];
const accountUsages = readAccountUsages(this.storageService, this.id, session.account.displayName);
const sessionsForAccount = this._accounts.get(session.account.displayName);
// Skip dialog if nothing is using the account
if (!accountUsage.length) {
accountUsages.set(this.id, { [session.account.displayName]: [] });
if (!accountUsages.length) {
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
return;
}
const result = await dialogService.confirm({
title: nls.localize('signOutConfirm', "Sign out of {0}", session.account.displayName),
message: nls.localize('signOutMessage', "The account {0} is currently used by: \n\n{1}\n\n Sign out of these features?", session.account.displayName, accountUsage.join('\n'))
message: nls.localize('signOutMessage', "The account {0} is has been used by: \n\n{1}\n\n Sign out of these features?", session.account.displayName, accountUsages.map(usage => usage.extensionName).join('\n'))
});
if (result.confirmed) {
accountUsages.set(this.id, { [session.account.displayName]: [] });
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
}
}
@@ -211,7 +216,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
account: session.account,
scopes: session.scopes,
getAccessToken: () => {
addAccountUsage(this.id, session.account.displayName, nls.localize('sync', "Preferences Sync"));
addAccountUsage(this.storageService, this.id, session.account.displayName, 'preferencessync', nls.localize('sync', "Preferences Sync"));
return this._proxy.$getSessionAccessToken(this.id, session.id);
}
};
@@ -287,7 +292,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
}
async $registerAuthenticationProvider(id: string, displayName: string): Promise<void> {
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, this.notificationService, this.storageKeysSyncRegistryService);
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, this.notificationService, this.storageKeysSyncRegistryService, this.storageService);
await provider.initialize();
this.authenticationService.registerAuthenticationProvider(id, provider);
}
@@ -374,16 +379,16 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
}
async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
addAccountUsage(providerId, accountName, extensionName);
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
const extensionData = allowList.find(extension => extension.id === extensionId);
if (extensionData) {
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
return true;
}
const remoteConnection = this.remoteAgentService.getConnection();
if (remoteConnection && remoteConnection.remoteAuthority && remoteConnection.remoteAuthority.startsWith('vsonline') && VSO_ALLOWED_EXTENSIONS.includes(extensionId)) {
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
return true;
}
@@ -398,6 +403,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
const allow = choice === 0;
if (allow) {
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
allowList.push({ id: extensionId, name: extensionName });
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
}