From 2dab7cfcdfd28a43cb4d39364c9dbf75f8441e56 Mon Sep 17 00:00:00 2001
From: Baptiste Augrain <daiyam@zokugun.org>
Date: Thu, 3 Feb 2022 21:46:31 +0100
Subject: [PATCH] fix(1.64): update patch (#988)

---
 build/linux/appimage/build.sh |   4 +-
 patches/use-github-pat.patch  | 247 +++++++++++++++++++++++++++-------
 2 files changed, 198 insertions(+), 53 deletions(-)

diff --git a/build/linux/appimage/build.sh b/build/linux/appimage/build.sh
index 1b126b7..3b297a1 100755
--- a/build/linux/appimage/build.sh
+++ b/build/linux/appimage/build.sh
@@ -19,10 +19,10 @@ if [[ "$VSCODE_ARCH" == "x64" ]]; then
   sed -i 's/grep docker/# grep docker/' pkg2appimage.AppDir/usr/share/pkg2appimage/functions.sh
 
   bash -ex pkg2appimage.AppDir/AppRun recipe.yml
-  
+
   rm -f pkg2appimage-*.AppImage
   rm -rf pkg2appimage.AppDir
   rm -rf VSCodium
 fi
 
-cd "${CALLER_DIR}"
\ No newline at end of file
+cd "${CALLER_DIR}"
diff --git a/patches/use-github-pat.patch b/patches/use-github-pat.patch
index 81af3c5..5e84cac 100644
--- a/patches/use-github-pat.patch
+++ b/patches/use-github-pat.patch
@@ -1,9 +1,12 @@
 diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts
-index fe0fbfd..656d8bb 100644
+index 3002e937c81..9674d8d2089 100644
 --- a/extensions/github-authentication/src/githubServer.ts
 +++ b/extensions/github-authentication/src/githubServer.ts
-@@ -6,8 +6,6 @@
- import * as nls from 'vscode-nls';
+@@ -3,41 +3,13 @@
+  *  Licensed under the MIT License. See License.txt in the project root for license information.
+  *--------------------------------------------------------------------------------------------*/
+ 
+-import * as nls from 'vscode-nls';
  import * as vscode from 'vscode';
  import fetch, { Response } from 'node-fetch';
 -import { v4 as uuid } from 'uuid';
@@ -11,19 +14,26 @@ index fe0fbfd..656d8bb 100644
  import { ExperimentationTelemetry } from './experimentationService';
  import { AuthProviderType } from './github';
  import { Log } from './common/logger';
-@@ -15,8 +13,6 @@ import { Log } from './common/logger';
- const localize = nls.loadMessageBundle();
+-import { isSupportedEnvironment } from './common/env';
+-
+-const localize = nls.loadMessageBundle();
+-const CLIENT_ID = '01ab8ac9400c4e429b23';
  
  const NETWORK_ERROR = 'network error';
 -const AUTH_RELAY_SERVER = 'vscode-auth.github.com';
 -// const AUTH_RELAY_STAGING_SERVER = 'client-auth-staging-14a768b.herokuapp.com';
- 
- class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler {
- 	constructor(private readonly Logger: Log) {
-@@ -29,14 +25,6 @@ class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.
- 	}
- }
- 
+-
+-class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler {
+-	constructor(private readonly Logger: Log) {
+-		super();
+-	}
+-
+-	public handleUri(uri: vscode.Uri) {
+-		this.Logger.trace('Handling Uri...');
+-		this.fire(uri);
+-	}
+-}
+-
 -function parseQuery(uri: vscode.Uri) {
 -	return uri.query.split('&').reduce((prev: any, current) => {
 -		const queryString = current.split('=');
@@ -31,44 +41,64 @@ index fe0fbfd..656d8bb 100644
 -		return prev;
 -	}, {});
 -}
--
+ 
  export interface IGitHubServer extends vscode.Disposable {
  	login(scopes: string): Promise<string>;
- 	getUserInfo(token: string): Promise<{ id: string, accountName: string }>;
-@@ -96,11 +84,7 @@ async function getUserInfo(token: string, serverUri: vscode.Uri, logger: Log): P
+@@ -47,13 +19,6 @@ export interface IGitHubServer extends vscode.Disposable {
+ 	type: AuthProviderType;
+ }
+ 
+-interface IGitHubDeviceCodeResponse {
+-	device_code: string;
+-	user_code: string;
+-	verification_uri: string;
+-	interval: number;
+-}
+-
+ async function getScopes(token: string, serverUri: vscode.Uri, logger: Log): Promise<string[]> {
+ 	try {
+ 		logger.info('Getting token scopes...');
+@@ -115,315 +80,49 @@ async function getUserInfo(token: string, serverUri: vscode.Uri, logger: Log): P
  export class GitHubServer implements IGitHubServer {
  	friendlyName = 'GitHub';
  	type = AuthProviderType.github;
 -	private _statusBarItem: vscode.StatusBarItem | undefined;
 -	private _onDidManuallyProvideToken = new vscode.EventEmitter<string | undefined>();
- 
+-
 -	private _pendingStates = new Map<string, string[]>();
 -	private _codeExchangePromises = new Map<string, { promise: Promise<string>, cancel: vscode.EventEmitter<void> }>();
- 	private _statusBarCommandId = `${this.type}.provide-manually`;
- 	private _disposable: vscode.Disposable;
- 	private _uriHandler = new UriEventHandler(this._logger);
-@@ -115,137 +99,35 @@ export class GitHubServer implements IGitHubServer {
- 		this._disposable.dispose();
+-	private _statusBarCommandId = `${this.type}.provide-manually`;
+-	private _disposable: vscode.Disposable;
+-	private _uriHandler = new UriEventHandler(this._logger);
+ 
+ 	constructor(private readonly _supportDeviceCodeFlow: boolean, private readonly _logger: Log, private readonly _telemetryReporter: ExperimentationTelemetry) {
+-		this._disposable = vscode.Disposable.from(
+-			vscode.commands.registerCommand(this._statusBarCommandId, () => this.manuallyProvideUri()),
+-			vscode.window.registerUriHandler(this._uriHandler));
++		this._supportDeviceCodeFlow;
  	}
  
--	private isTestEnvironment(url: vscode.Uri): boolean {
--		return /\.azurewebsites\.net$/.test(url.authority) || url.authority.startsWith('localhost:');
+ 	dispose() {
+-		this._disposable.dispose();
 -	}
 -
 -	// TODO@joaomoreno TODO@TylerLeonhardt
 -	private async isNoCorsEnvironment(): Promise<boolean> {
 -		const uri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/dummy`));
 -		return (uri.scheme === 'https' && /^((insiders\.)?vscode|github)\./.test(uri.authority)) || (uri.scheme === 'http' && /^localhost/.test(uri.authority));
--	}
--
+ 	}
+ 
  	public async login(scopes: string): Promise<string> {
  		this._logger.info(`Logging in for the following scopes: ${scopes}`);
  
 -		const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
 -
--		if (this.isTestEnvironment(callbackUri)) {
--			const token = await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true });
--			if (!token) { throw new Error('Sign in failed: No token provided'); }
+-		if (!isSupportedEnvironment(callbackUri)) {
+-			const token = this._supportDeviceCodeFlow
+-				? await this.doDeviceCodeFlow(scopes)
+-				: await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true });
+-
+-			if (!token) { throw new Error('No token provided'); }
 -
 -			const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user']
 -			const scopesList = scopes.split(' '); // Example: 'read:user repo user:email'
@@ -77,22 +107,14 @@ index fe0fbfd..656d8bb 100644
 -				if (included || !scope.includes(':')) {
 -					return included;
 -				}
-+		const token = await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true });
-+		if (!token) { throw new Error('Sign in failed: No token provided'); }
- 
+-
 -				return scope.split(':').some(splitScopes => {
 -					return tokenScopes.includes(splitScopes);
 -				});
 -			})) {
 -				throw new Error(`The provided token does not match the requested scopes: ${scopes}`);
-+		const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user']
-+		const scopesList = scopes.split(' '); // Example: 'read:user repo user:email'
-+		if (!scopesList.every(scope => {
-+			const included = tokenScopes.includes(scope);
-+			if (included || !scope.includes(':')) {
-+				return included;
- 			}
- 
+-			}
+-
 -			return token;
 -		}
 -
@@ -111,13 +133,8 @@ index fe0fbfd..656d8bb 100644
 -		if (!codeExchangePromise) {
 -			codeExchangePromise = promiseFromEvent(this._uriHandler.event, this.exchangeCodeForToken(scopes));
 -			this._codeExchangePromises.set(scopes, codeExchangePromise);
-+			return scope.split(':').some(splitScopes => {
-+				return tokenScopes.includes(splitScopes);
-+			});
-+		})) {
-+			throw new Error(`The provided token does not match the requested scopes: ${scopes}`);
- 		}
- 
+-		}
+-
 -		return Promise.race([
 -			codeExchangePromise.promise,
 -			promiseFromEvent<string | undefined, string>(this._onDidManuallyProvideToken.event, (token: string | undefined, resolve, reject): void => {
@@ -134,6 +151,110 @@ index fe0fbfd..656d8bb 100644
 -			this._codeExchangePromises.delete(scopes);
 -			this.updateStatusBarItem(false);
 -		});
+-	}
+-
+-	private async doDeviceCodeFlow(scopes: string): Promise<string> {
+-		// Get initial device code
+-		const uri = `https://github.com/login/device/code?client_id=${CLIENT_ID}&scope=${scopes}`;
+-		const result = await fetch(uri, {
+-			method: 'POST',
+-			headers: {
+-				Accept: 'application/json'
+-			}
+-		});
+-		if (!result.ok) {
+-			throw new Error(`Failed to get one-time code: ${await result.text()}`);
+-		}
+-
+-		const json = await result.json() as IGitHubDeviceCodeResponse;
+-
+-
+-		const modalResult = await vscode.window.showInformationMessage(
+-			localize('code.title', "Your Code: {0}", json.user_code),
+-			{
+-				modal: true,
+-				detail: localize('code.detail', "To finish authenticating, navigate to GitHub and paste in the above one-time code.")
+-			}, 'Copy & Continue to GitHub');
+-
+-		if (modalResult !== 'Copy & Continue to GitHub') {
+-			throw new Error('Cancelled');
+-		}
+-
+-		await vscode.env.clipboard.writeText(json.user_code);
+-
+-		const uriToOpen = await vscode.env.asExternalUri(vscode.Uri.parse(json.verification_uri));
+-		await vscode.env.openExternal(uriToOpen);
+-
+-		return await vscode.window.withProgress<string>({
+-			location: vscode.ProgressLocation.Notification,
+-			cancellable: true,
+-			title: localize(
+-				'progress',
+-				"Open [{0}]({0}) in a new tab and paste your one-time code: {1}",
+-				json.verification_uri,
+-				json.user_code)
+-		}, async (_, token) => {
+-			return await this.waitForDeviceCodeAccessToken(json, token);
+-		});
+-	}
+-
+-	private async waitForDeviceCodeAccessToken(
+-		json: IGitHubDeviceCodeResponse,
+-		token: vscode.CancellationToken
+-	): Promise<string> {
+-
+-		const refreshTokenUri = `https://github.com/login/oauth/access_token?client_id=${CLIENT_ID}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code`;
+-
+-		// Try for 2 minutes
+-		const attempts = 120 / json.interval;
+-		for (let i = 0; i < attempts; i++) {
+-			await new Promise(resolve => setTimeout(resolve, json.interval * 1000));
+-			if (token.isCancellationRequested) {
+-				throw new Error('Cancelled');
+-			}
+-			let accessTokenResult;
+-			try {
+-				accessTokenResult = await fetch(refreshTokenUri, {
+-					method: 'POST',
+-					headers: {
+-						Accept: 'application/json'
+-					}
+-				});
+-			} catch {
+-				continue;
+-			}
+-
+-			if (!accessTokenResult.ok) {
+-				continue;
+-			}
++		const token = await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true });
+ 
+-			const accessTokenJson = await accessTokenResult.json();
++		if (!token) { throw new Error('No token provided'); }
+ 
+-			if (accessTokenJson.error === 'authorization_pending') {
+-				continue;
+-			}
+-
+-			if (accessTokenJson.error) {
+-				throw new Error(accessTokenJson.error_description);
++		const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user']
++		const scopesList = scopes.split(' '); // Example: 'read:user repo user:email'
++		if (!scopesList.every(scope => {
++			const included = tokenScopes.includes(scope);
++			if (included || !scope.includes(':')) {
++				return included;
+ 			}
+ 
+-			return accessTokenJson.access_token;
++			return scope.split(':').some(splitScopes => {
++				return tokenScopes.includes(splitScopes);
++			});
++		})) {
++			throw new Error(`The provided token does not match the requested scopes: ${scopes}`);
+ 		}
+ 
+-		throw new Error('Cancelled');
 +		return token;
  	}
  
@@ -196,10 +317,34 @@ index fe0fbfd..656d8bb 100644
 -		}
 -	}
 -
- 	private async manuallyProvideUri() {
- 		const uri = await vscode.window.showInputBox({
- 			prompt: 'Uri',
-@@ -277,44 +159,7 @@ export class GitHubServer implements IGitHubServer {
+-	private async manuallyProvideUri() {
+-		const uri = await vscode.window.showInputBox({
+-			prompt: 'Uri',
+-			ignoreFocusOut: true,
+-			validateInput(value) {
+-				if (!value) {
+-					return undefined;
+-				}
+-				const error = localize('validUri', "Please enter a valid Uri from the GitHub login page.");
+-				try {
+-					const uri = vscode.Uri.parse(value.trim());
+-					if (!uri.scheme || uri.scheme === 'file') {
+-						return error;
+-					}
+-				} catch (e) {
+-					return error;
+-				}
+-				return undefined;
+-			}
+-		});
+-		if (!uri) {
+-			return;
+-		}
+-
+-		this._uriHandler.handleUri(vscode.Uri.parse(uri.trim()));
+-	}
+-
+ 	public getUserInfo(token: string): Promise<{ id: string, accountName: string }> {
  		return getUserInfo(token, this.getServerUri('/user'), this._logger);
  	}
  
@@ -241,7 +386,7 @@ index fe0fbfd..656d8bb 100644
 -		} catch (e) {
 -			// No-op
 -		}
-+	public async sendAdditionalTelemetryInfo(_token: string): Promise<void> {
++	public async sendAdditionalTelemetryInfo(_: string): Promise<void> {
  	}
  
  	public async checkEnterpriseVersion(token: string): Promise<void> {