From c2ddd680c138b53144ec9a7faae6aeb79ff94ef2 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 24 Apr 2024 14:23:14 +0200 Subject: [PATCH] Use GHEC-DR URL in Octokit instance --- .../ql-vscode/src/common/vscode/authentication.ts | 15 +++++---------- extensions/ql-vscode/src/common/vscode/octokit.ts | 15 +++++++++++++++ extensions/ql-vscode/src/config.ts | 11 +++++++++++ .../ql-vscode/src/databases/code-search-api.ts | 2 ++ .../ql-vscode/src/databases/database-fetcher.ts | 9 ++++++--- .../src/databases/github-databases/api.ts | 6 +++++- 6 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 extensions/ql-vscode/src/common/vscode/octokit.ts diff --git a/extensions/ql-vscode/src/common/vscode/authentication.ts b/extensions/ql-vscode/src/common/vscode/authentication.ts index 1929c0bdb..b78b8a488 100644 --- a/extensions/ql-vscode/src/common/vscode/authentication.ts +++ b/extensions/ql-vscode/src/common/vscode/authentication.ts @@ -3,6 +3,7 @@ import type { Octokit } from "@octokit/rest"; import type { Credentials } from "../authentication"; import { AppOctokit } from "../octokit"; import { hasGhecDrUri } from "../../config"; +import { getOctokitBaseUrl } from "./octokit"; // We need 'repo' scope for triggering workflows, 'gist' scope for exporting results to Gist, // and 'read:packages' for reading private CodeQL packages. @@ -15,24 +16,18 @@ const SCOPES = ["repo", "gist", "read:packages"]; */ export class VSCodeCredentials implements Credentials { /** - * A specific octokit to return, otherwise a new authenticated octokit will be created when needed. - */ - private octokit: Octokit | undefined; - - /** - * Creates or returns an instance of Octokit. + * Creates or returns an instance of Octokit. The returned instance should + * not be stored and reused, as it may become out-of-date with the current + * authentication session. * * @returns An instance of Octokit. */ async getOctokit(): Promise { - if (this.octokit) { - return this.octokit; - } - const accessToken = await this.getAccessToken(); return new AppOctokit({ auth: accessToken, + baseUrl: getOctokitBaseUrl(), }); } diff --git a/extensions/ql-vscode/src/common/vscode/octokit.ts b/extensions/ql-vscode/src/common/vscode/octokit.ts new file mode 100644 index 000000000..71ff98c10 --- /dev/null +++ b/extensions/ql-vscode/src/common/vscode/octokit.ts @@ -0,0 +1,15 @@ +import { getGitHubInstanceApiUrl } from "../../config"; + +/** + * Returns the Octokit base URL to use based on the GitHub instance URL. + * + * This is necessary because the Octokit base URL should not have a trailing + * slash, but this is included by default in a URL. + */ +export function getOctokitBaseUrl(): string { + let apiUrl = getGitHubInstanceApiUrl().toString(); + if (apiUrl.endsWith("/")) { + apiUrl = apiUrl.slice(0, -1); + } + return apiUrl; +} diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index ecf23b47e..82807531f 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -127,6 +127,7 @@ export function hasGhecDrUri(): boolean { * The URI for GitHub.com. */ export const GITHUB_URL = new URL("https://github.com"); +export const GITHUB_API_URL = new URL("https://api.github.com"); /** * If the GitHub Enterprise URI is set to something that looks like GHEC-DR, return it. @@ -148,6 +149,16 @@ export function getGitHubInstanceUrl(): URL { return GITHUB_URL; } +export function getGitHubInstanceApiUrl(): URL { + const ghecDrUri = getGhecDrUri(); + if (ghecDrUri) { + const url = new URL(ghecDrUri.toString()); + url.hostname = `api.${url.hostname}`; + return url; + } + return GITHUB_API_URL; +} + const ROOT_SETTING = new Setting("codeQL"); // Telemetry configuration diff --git a/extensions/ql-vscode/src/databases/code-search-api.ts b/extensions/ql-vscode/src/databases/code-search-api.ts index ac987a82d..677c41591 100644 --- a/extensions/ql-vscode/src/databases/code-search-api.ts +++ b/extensions/ql-vscode/src/databases/code-search-api.ts @@ -7,6 +7,7 @@ import { AppOctokit } from "../common/octokit"; import type { ProgressCallback } from "../common/vscode/progress"; import { UserCancellationException } from "../common/vscode/progress"; import type { EndpointDefaults } from "@octokit/types"; +import { getOctokitBaseUrl } from "../common/vscode/octokit"; export async function getCodeSearchRepositories( query: string, @@ -54,6 +55,7 @@ async function provideOctokitWithThrottling( const octokit = new MyOctokit({ auth, + baseUrl: getOctokitBaseUrl(), throttle: { onRateLimit: (retryAfter: number, options: EndpointDefaults): boolean => { void logger.log( diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 9bc86e509..a7b5a6a61 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -30,6 +30,7 @@ import { allowHttp, downloadTimeout, getGitHubInstanceUrl, + hasGhecDrUri, isCanary, } from "../config"; import { showAndLogInformationMessage } from "../common/logging"; @@ -151,9 +152,10 @@ export class DatabaseFetcher { maxStep: 2, }); + const instanceUrl = getGitHubInstanceUrl(); + const options: InputBoxOptions = { - title: - 'Enter a GitHub repository URL or "name with owner" (e.g. https://github.com/github/codeql or github/codeql)', + title: `Enter a GitHub repository URL or "name with owner" (e.g. https://github.com/github/codeql or github/codeql)`, placeHolder: "https://github.com// or /", ignoreFocusOut: true, }; @@ -187,7 +189,8 @@ export class DatabaseFetcher { throw new Error(`Invalid GitHub repository: ${githubRepo}`); } - const credentials = isCanary() ? this.app.credentials : undefined; + const credentials = + isCanary() || hasGhecDrUri() ? this.app.credentials : undefined; const octokit = credentials ? await credentials.getOctokit() diff --git a/extensions/ql-vscode/src/databases/github-databases/api.ts b/extensions/ql-vscode/src/databases/github-databases/api.ts index 186b1a9c2..1b101b055 100644 --- a/extensions/ql-vscode/src/databases/github-databases/api.ts +++ b/extensions/ql-vscode/src/databases/github-databases/api.ts @@ -3,6 +3,7 @@ import type { Octokit } from "@octokit/rest"; import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods"; import { showNeverAskAgainDialog } from "../../common/vscode/dialog"; import type { GitHubDatabaseConfig } from "../../config"; +import { hasGhecDrUri } from "../../config"; import type { Credentials } from "../../common/authentication"; import { AppOctokit } from "../../common/octokit"; import type { ProgressCallback } from "../../common/vscode/progress"; @@ -67,7 +68,10 @@ export async function listDatabases( credentials: Credentials, config: GitHubDatabaseConfig, ): Promise { - const hasAccessToken = !!(await credentials.getExistingAccessToken()); + // On GHEC-DR, unauthenticated requests will enver work, so we should always ask + // for authentication. + const hasAccessToken = + !!(await credentials.getExistingAccessToken()) || hasGhecDrUri(); let octokit = hasAccessToken ? await credentials.getOctokit()