refactored mainthreadauth with silent option and tests (#135503)

This commit is contained in:
Tyler James Leonhardt
2021-10-20 12:17:06 -07:00
committed by GitHub
parent fb0e4870b3
commit c71e201bed
6 changed files with 419 additions and 244 deletions
@@ -17,6 +17,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { fromNow } from 'vs/base/common/date';
import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import type { AuthenticationGetSessionOptions } from 'vscode';
interface TrustedExtensionsQuickPickItem {
label: string;
@@ -204,99 +205,67 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
}
private async selectSession(providerId: string, extensionId: string, extensionName: string, scopes: string[], potentialSessions: readonly modes.AuthenticationSession[], clearSessionPreference: boolean, silent: boolean, showSilentPrompt: boolean): Promise<modes.AuthenticationSession | undefined> {
if (!potentialSessions.length) {
throw new Error('No potential sessions found');
}
if (clearSessionPreference) {
this.storageService.remove(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
} else {
const existingSessionPreference = this.storageService.get(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
if (existingSessionPreference) {
const matchingSession = potentialSessions.find(session => session.id === existingSessionPreference);
if (matchingSession) {
const allowed = this.authenticationService.isAccessAllowed(providerId, matchingSession.account.label, extensionId);
if (!allowed) {
if (!silent) {
const didAcceptPrompt = await this.authenticationService.showGetSessionPrompt(providerId, matchingSession.account.label, extensionId, extensionName);
if (!didAcceptPrompt) {
throw new Error('User did not consent to login.');
}
} else {
if (showSilentPrompt) {
this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, scopes, potentialSessions);
}
return undefined;
}
}
return matchingSession;
}
}
}
if (silent) {
this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, scopes, potentialSessions);
return undefined;
}
return this.authenticationService.selectSession(providerId, extensionId, extensionName, scopes, potentialSessions);
}
async $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone: boolean, forceNewSession: boolean | { detail: string }, clearSessionPreference: boolean, solelyCheckExistence?: boolean }): Promise<modes.AuthenticationSession | undefined> {
private async doGetSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: AuthenticationGetSessionOptions): Promise<modes.AuthenticationSession | undefined> {
const sessions = await this.authenticationService.getSessions(providerId, scopes, true);
let silent = !options.createIfNone;
// TODO: remove this property and implement a proper $hasSession function.
let showSilentPrompt = !options.solelyCheckExistence;
// Error cases
if (options.forceNewSession && !sessions.length) {
throw new Error('No existing sessions found.');
}
if (options.forceNewSession && options.createIfNone) {
throw new Error('Invalid combination of options. Please remove one of the following: forceNewSession, createIfNone');
}
if (options.forceNewSession && options.silent) {
throw new Error('Invalid combination of options. Please remove one of the following: forceNewSession, silent');
}
if (options.createIfNone && options.silent) {
throw new Error('Invalid combination of options. Please remove one of the following: createIfNone, silent');
}
let session: modes.AuthenticationSession | undefined;
// Ignore existing sessions if we are forceRecreating
// Check if the sessions we have are valid
if (!options.forceNewSession && sessions.length) {
if (!this.authenticationService.supportsMultipleAccounts(providerId)) {
session = sessions[0];
const allowed = this.authenticationService.isAccessAllowed(providerId, session.account.label, extensionId);
if (!allowed) {
if (!silent) {
const didAcceptPrompt = await this.authenticationService.showGetSessionPrompt(providerId, session.account.label, extensionId, extensionName);
if (!didAcceptPrompt) {
throw new Error('User did not consent to login.');
if (this.authenticationService.supportsMultipleAccounts(providerId)) {
if (options.clearSessionPreference) {
this.storageService.remove(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
} else {
const existingSessionPreference = this.storageService.get(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
if (existingSessionPreference) {
const matchingSession = sessions.find(session => session.id === existingSessionPreference);
if (matchingSession && this.authenticationService.isAccessAllowed(providerId, matchingSession.account.label, extensionId)) {
return matchingSession;
}
} else if (allowed !== false) {
if (showSilentPrompt) {
this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, scopes, [session]);
}
return undefined;
} else {
return undefined;
}
}
} else {
return this.selectSession(providerId, extensionId, extensionName, scopes, sessions, !!options.clearSessionPreference, silent, showSilentPrompt);
}
} else {
// If we are forceRecreating, we need to show the prompt.
if (options.forceNewSession || !silent) {
const providerName = this.authenticationService.getLabel(providerId);
const detail = (typeof options.forceNewSession === 'object') ? options.forceNewSession!.detail : undefined;
const isAllowed = await this.loginPrompt(providerName, extensionName, !!options.forceNewSession, detail);
if (!isAllowed) {
throw new Error('User did not consent to login.');
}
session = await this.authenticationService.createSession(providerId, scopes, true);
await this.setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id);
} else {
if (showSilentPrompt) {
await this.authenticationService.requestNewSession(providerId, scopes, extensionId, extensionName);
}
} else if (this.authenticationService.isAccessAllowed(providerId, sessions[0].account.label, extensionId)) {
return sessions[0];
}
}
// We may need to prompt because we don't have a valid session
// modal flows
if (options.createIfNone || options.forceNewSession) {
const providerName = this.authenticationService.getLabel(providerId);
const detail = (typeof options.forceNewSession === 'object') ? options.forceNewSession!.detail : undefined;
const isAllowed = await this.loginPrompt(providerName, extensionName, !!options.forceNewSession, detail);
if (!isAllowed) {
throw new Error('User did not consent to login.');
}
const session = await this.authenticationService.createSession(providerId, scopes, true);
await this.setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id);
return session;
}
// passive flows
if (!options.silent) {
await this.authenticationService.requestNewSession(providerId, scopes, extensionId, extensionName);
}
return undefined;
}
async $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: AuthenticationGetSessionOptions): Promise<modes.AuthenticationSession | undefined> {
const session = await this.doGetSession(providerId, scopes, extensionId, extensionName, options);
if (session) {
type AuthProviderUsageClassification = {
extensionId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };