Merge from vscode e3c4990c67c40213af168300d1cfeb71d680f877 (#16569)

This commit is contained in:
Cory Rivera
2021-08-25 16:28:29 -07:00
committed by GitHub
parent ab1112bfb3
commit cb7b7da0a4
1752 changed files with 59525 additions and 33878 deletions

View File

@@ -6,13 +6,11 @@
import * as vscode from 'vscode';
import { v4 as uuid } from 'uuid';
import { Keychain } from './common/keychain';
import { GitHubServer, NETWORK_ERROR } from './githubServer';
import { GitHubServer, uriHandler, NETWORK_ERROR } from './githubServer';
import Logger from './common/logger';
import { arrayEquals } from './common/utils';
import { ExperimentationTelemetry } from './experimentationService';
export const onDidChangeSessions = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
interface SessionData {
id: string;
account?: {
@@ -24,18 +22,29 @@ interface SessionData {
accessToken: string;
}
export class GitHubAuthenticationProvider {
export enum AuthProviderType {
github = 'github',
'github-enterprise' = 'github-enterprise'
}
export class GitHubAuthenticationProvider implements vscode.AuthenticationProvider {
private _sessions: vscode.AuthenticationSession[] = [];
private _sessionChangeEmitter = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
private _githubServer: GitHubServer;
private _keychain: Keychain;
constructor(context: vscode.ExtensionContext, telemetryReporter: ExperimentationTelemetry) {
this._keychain = new Keychain(context);
this._githubServer = new GitHubServer(telemetryReporter);
constructor(private context: vscode.ExtensionContext, private type: AuthProviderType, private telemetryReporter: ExperimentationTelemetry) {
this._keychain = new Keychain(context, `${type}.auth`);
this._githubServer = new GitHubServer(type, telemetryReporter);
}
public async initialize(context: vscode.ExtensionContext): Promise<void> {
get onDidChangeSessions() {
return this._sessionChangeEmitter.event;
}
public async initialize(): Promise<void> {
try {
this._sessions = await this.readSessions();
await this.verifySessions();
@@ -43,7 +52,17 @@ export class GitHubAuthenticationProvider {
// Ignore, network request failed
}
context.subscriptions.push(context.secrets.onDidChange(() => this.checkForUpdates()));
let friendlyName = 'GitHub';
if (this.type === AuthProviderType.github) {
this.context.subscriptions.push(vscode.window.registerUriHandler(uriHandler));
}
if (this.type === AuthProviderType['github-enterprise']) {
friendlyName = 'GitHub Enterprise';
}
this.context.subscriptions.push(vscode.commands.registerCommand(`${this.type}.provide-token`, () => this.manuallyProvideToken()));
this.context.subscriptions.push(vscode.authentication.registerAuthenticationProvider(this.type, friendlyName, this, { supportsMultipleAccounts: false }));
this.context.subscriptions.push(this.context.secrets.onDidChange(() => this.checkForUpdates()));
}
async getSessions(scopes?: string[]): Promise<vscode.AuthenticationSession[]> {
@@ -52,12 +71,21 @@ export class GitHubAuthenticationProvider {
: this._sessions;
}
private async afterTokenLoad(token: string): Promise<void> {
if (this.type === AuthProviderType.github) {
this._githubServer.checkIsEdu(token);
}
if (this.type === AuthProviderType['github-enterprise']) {
this._githubServer.checkEnterpriseVersion(token);
}
}
private async verifySessions(): Promise<void> {
const verifiedSessions: vscode.AuthenticationSession[] = [];
const verificationPromises = this._sessions.map(async session => {
try {
await this._githubServer.getUserInfo(session.accessToken);
this._githubServer.checkIsEdu(session.accessToken);
this.afterTokenLoad(session.accessToken);
verifiedSessions.push(session);
} catch (e) {
// Remove sessions that return unauthorized response
@@ -97,7 +125,7 @@ export class GitHubAuthenticationProvider {
}
});
this._sessions.map(session => {
this._sessions.forEach(session => {
const matchesExisting = storedSessions.some(s => s.id === session.id);
// Another window has logged out, remove from our state
if (!matchesExisting) {
@@ -112,7 +140,7 @@ export class GitHubAuthenticationProvider {
});
if (added.length || removed.length) {
onDidChangeSessions.fire({ added, removed, changed: [] });
this._sessionChangeEmitter.fire({ added, removed, changed: [] });
}
}
@@ -163,12 +191,41 @@ export class GitHubAuthenticationProvider {
return this._sessions;
}
public async createSession(scopes: string): Promise<vscode.AuthenticationSession> {
const token = await this._githubServer.login(scopes);
const session = await this.tokenToSession(token, scopes.split(' '));
this._githubServer.checkIsEdu(token);
await this.setToken(session);
return session;
public async createSession(scopes: string[]): Promise<vscode.AuthenticationSession> {
try {
/* __GDPR__
"login" : { }
*/
this.telemetryReporter?.sendTelemetryEvent('login');
const token = await this._githubServer.login(scopes.join(' '));
this.afterTokenLoad(token);
const session = await this.tokenToSession(token, scopes);
await this.setToken(session);
this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] });
Logger.info('Login success!');
return session;
} catch (e) {
// If login was cancelled, do not notify user.
if (e.message === 'Cancelled') {
/* __GDPR__
"loginCancelled" : { }
*/
this.telemetryReporter?.sendTelemetryEvent('loginCancelled');
throw e;
}
/* __GDPR__
"loginFailed" : { }
*/
this.telemetryReporter?.sendTelemetryEvent('loginFailed');
vscode.window.showErrorMessage(`Sign in failed: ${e}`);
Logger.error(e);
throw e;
}
}
public async manuallyProvideToken(): Promise<void> {
@@ -196,18 +253,33 @@ export class GitHubAuthenticationProvider {
await this.storeSessions();
}
public async removeSession(id: string): Promise<vscode.AuthenticationSession | undefined> {
Logger.info(`Logging out of ${id}`);
const sessionIndex = this._sessions.findIndex(session => session.id === id);
let session: vscode.AuthenticationSession | undefined;
if (sessionIndex > -1) {
session = this._sessions[sessionIndex];
this._sessions.splice(sessionIndex, 1);
} else {
Logger.error('Session not found');
}
public async removeSession(id: string) {
try {
/* __GDPR__
"logout" : { }
*/
this.telemetryReporter?.sendTelemetryEvent('logout');
await this.storeSessions();
return session;
Logger.info(`Logging out of ${id}`);
const sessionIndex = this._sessions.findIndex(session => session.id === id);
if (sessionIndex > -1) {
const session = this._sessions[sessionIndex];
this._sessions.splice(sessionIndex, 1);
this._sessionChangeEmitter.fire({ added: [], removed: [session], changed: [] });
} else {
Logger.error('Session not found');
}
await this.storeSessions();
} catch (e) {
/* __GDPR__
"logoutFailed" : { }
*/
this.telemetryReporter?.sendTelemetryEvent('logoutFailed');
vscode.window.showErrorMessage(`Sign out failed: ${e}`);
Logger.error(e);
throw e;
}
}
}