Skip to content

Commit ad6cbe6

Browse files
Merge pull request #1989 from github/robertbrignull/credentials_in_app
Add credentials to App
2 parents 64e73c6 + 9e25d19 commit ad6cbe6

34 files changed

+284
-265
lines changed

extensions/ql-vscode/src/authentication.ts

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from "vscode";
22
import * as Octokit from "@octokit/rest";
33
import { retry } from "@octokit/plugin-retry";
4+
import { Credentials } from "./common/authentication";
45

56
const GITHUB_AUTH_PROVIDER_ID = "github";
67

@@ -13,41 +14,12 @@ const SCOPES = ["repo", "gist", "read:packages"];
1314
/**
1415
* Handles authentication to GitHub, using the VS Code [authentication API](https://code.visualstudio.com/api/references/vscode-api#authentication).
1516
*/
16-
export class Credentials {
17+
export class VSCodeCredentials implements Credentials {
1718
/**
1819
* A specific octokit to return, otherwise a new authenticated octokit will be created when needed.
1920
*/
2021
private octokit: Octokit.Octokit | undefined;
2122

22-
// Explicitly make the constructor private, so that we can't accidentally call the constructor from outside the class
23-
// without also initializing the class.
24-
private constructor(octokit?: Octokit.Octokit) {
25-
this.octokit = octokit;
26-
}
27-
28-
/**
29-
* Initializes a Credentials instance. This will generate octokit instances
30-
* authenticated as the user. If there is not already an authenticated GitHub
31-
* session available then the user will be prompted to log in.
32-
*
33-
* @returns An instance of credentials.
34-
*/
35-
static async initialize(): Promise<Credentials> {
36-
return new Credentials();
37-
}
38-
39-
/**
40-
* Initializes an instance of credentials with an octokit instance using
41-
* a specific known token. This method is meant to be used in
42-
* non-interactive environments such as tests.
43-
*
44-
* @param overrideToken The GitHub token to use for authentication.
45-
* @returns An instance of credentials.
46-
*/
47-
static async initializeWithToken(overrideToken: string) {
48-
return new Credentials(new Octokit.Octokit({ auth: overrideToken, retry }));
49-
}
50-
5123
/**
5224
* Creates or returns an instance of Octokit.
5325
*

extensions/ql-vscode/src/cli.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { Logger, ProgressReporter } from "./common";
2727
import { CompilationMessage } from "./pure/legacy-messages";
2828
import { sarifParser } from "./sarif-parser";
2929
import { dbSchemeToLanguage, walkDirectory } from "./helpers";
30-
import { Credentials } from "./authentication";
30+
import { App } from "./common/app";
3131

3232
/**
3333
* The version of the SARIF format that we are using.
@@ -197,6 +197,7 @@ export class CodeQLCliServer implements Disposable {
197197
public quiet = false;
198198

199199
constructor(
200+
private readonly app: App,
200201
private distributionProvider: DistributionProvider,
201202
private cliConfig: CliConfig,
202203
private logger: Logger,
@@ -618,9 +619,7 @@ export class CodeQLCliServer implements Disposable {
618619
addFormat = true,
619620
progressReporter?: ProgressReporter,
620621
): Promise<OutputType> {
621-
const credentials = await Credentials.initialize();
622-
623-
const accessToken = await credentials.getExistingAccessToken();
622+
const accessToken = await this.app.credentials.getExistingAccessToken();
624623

625624
const extraArgs = accessToken ? ["--github-auth-stdin"] : [];
626625

@@ -633,7 +632,7 @@ export class CodeQLCliServer implements Disposable {
633632
async (line) => {
634633
if (line.startsWith("Enter value for --github-auth-stdin")) {
635634
try {
636-
return await credentials.getAccessToken();
635+
return await this.app.credentials.getAccessToken();
637636
} catch (e) {
638637
// If the user cancels the authentication prompt, we still need to give a value to the CLI.
639638
// By giving a potentially invalid value, the user will just get a 401/403 when they try to access a

extensions/ql-vscode/src/common/app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Credentials } from "./authentication";
12
import { Disposable } from "../pure/disposable-object";
23
import { AppEventEmitter } from "./events";
34
import { Logger } from "./logging";
@@ -13,6 +14,7 @@ export interface App {
1314
readonly globalStoragePath: string;
1415
readonly workspaceStoragePath?: string;
1516
readonly workspaceState: Memento;
17+
readonly credentials: Credentials;
1618
}
1719

1820
export enum AppMode {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as Octokit from "@octokit/rest";
2+
3+
/**
4+
* An interface providing methods for obtaining access tokens
5+
* or an octokit instance for making HTTP requests.
6+
*/
7+
export interface Credentials {
8+
/**
9+
* Returns an authenticated instance of Octokit.
10+
* May prompt the user to log in and grant permission to use their
11+
* token, if they have not already done so.
12+
*
13+
* @returns An instance of Octokit.
14+
*/
15+
getOctokit(): Promise<Octokit.Octokit>;
16+
17+
/**
18+
* Returns an OAuth access token.
19+
* May prompt the user to log in and grant permission to use their
20+
* token, if they have not already done so.
21+
*
22+
* @returns An OAuth access token.
23+
*/
24+
getAccessToken(): Promise<string>;
25+
26+
/**
27+
* Returns an OAuth access token if one is available.
28+
* If a token is not available this will return undefined and
29+
* will not prompt the user to log in.
30+
*
31+
* @returns An OAuth access token, or undefined.
32+
*/
33+
getExistingAccessToken(): Promise<string | undefined>;
34+
}

extensions/ql-vscode/src/common/vscode/vscode-app.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as vscode from "vscode";
2+
import { VSCodeCredentials } from "../../authentication";
23
import { Disposable } from "../../pure/disposable-object";
34
import { App, AppMode } from "../app";
45
import { AppEventEmitter } from "../events";
@@ -7,9 +8,13 @@ import { Memento } from "../memento";
78
import { VSCodeAppEventEmitter } from "./events";
89

910
export class ExtensionApp implements App {
11+
public readonly credentials: VSCodeCredentials;
12+
1013
public constructor(
1114
public readonly extensionContext: vscode.ExtensionContext,
12-
) {}
15+
) {
16+
this.credentials = new VSCodeCredentials();
17+
}
1318

1419
public get extensionPath(): string {
1520
return this.extensionContext.extensionPath;

extensions/ql-vscode/src/databaseFetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ import { DatabaseManager, DatabaseItem } from "./databases";
2020
import { showAndLogInformationMessage, tmpDir } from "./helpers";
2121
import { reportStreamProgress, ProgressCallback } from "./commandRunner";
2222
import { extLogger } from "./common";
23-
import { Credentials } from "./authentication";
2423
import { getErrorMessage } from "./pure/helpers-pure";
2524
import {
2625
getNwoFromGitHubUrl,
2726
isValidGitHubNwo,
2827
} from "./common/github-url-identifier-helper";
28+
import { Credentials } from "./common/authentication";
2929

3030
/**
3131
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.

extensions/ql-vscode/src/databases-ui.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ import {
3535
promptImportInternetDatabase,
3636
} from "./databaseFetcher";
3737
import { asyncFilter, getErrorMessage } from "./pure/helpers-pure";
38-
import { Credentials } from "./authentication";
3938
import { QueryRunner } from "./queryRunner";
4039
import { isCanary } from "./config";
40+
import { App } from "./common/app";
41+
import { Credentials } from "./common/authentication";
4142

4243
type ThemableIconPath = { light: string; dark: string } | string;
4344

@@ -220,11 +221,11 @@ export class DatabaseUI extends DisposableObject {
220221
private treeDataProvider: DatabaseTreeDataProvider;
221222

222223
public constructor(
224+
private app: App,
223225
private databaseManager: DatabaseManager,
224226
private readonly queryServer: QueryRunner | undefined,
225227
private readonly storagePath: string,
226228
readonly extensionPath: string,
227-
private readonly getCredentials: () => Promise<Credentials>,
228229
) {
229230
super();
230231

@@ -297,9 +298,7 @@ export class DatabaseUI extends DisposableObject {
297298
commandRunnerWithProgress(
298299
"codeQLDatabases.chooseDatabaseGithub",
299300
async (progress: ProgressCallback, token: CancellationToken) => {
300-
const credentials = isCanary()
301-
? await this.getCredentials()
302-
: undefined;
301+
const credentials = isCanary() ? this.app.credentials : undefined;
303302
await this.handleChooseDatabaseGithub(credentials, progress, token);
304303
},
305304
{

extensions/ql-vscode/src/extension.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ import {
102102
} from "./commandRunner";
103103
import { CodeQlStatusBarHandler } from "./status-bar";
104104

105-
import { Credentials } from "./authentication";
106105
import { RemoteQueriesManager } from "./remote-queries/remote-queries-manager";
107106
import { RemoteQueryResult } from "./remote-queries/remote-query-result";
108107
import { URLSearchParams } from "url";
@@ -546,6 +545,8 @@ async function activateWithInstalledDistribution(
546545
// of activation.
547546
errorStubs.forEach((stub) => stub.dispose());
548547

548+
const app = new ExtensionApp(ctx);
549+
549550
void extLogger.log("Initializing configuration listener...");
550551
const qlConfigurationListener =
551552
await QueryServerConfigListener.createQueryServerConfigListener(
@@ -555,6 +556,7 @@ async function activateWithInstalledDistribution(
555556

556557
void extLogger.log("Initializing CodeQL cli server...");
557558
const cliServer = new CodeQLCliServer(
559+
app,
558560
distributionManager,
559561
new CliConfigListener(),
560562
extLogger,
@@ -587,11 +589,11 @@ async function activateWithInstalledDistribution(
587589
ctx.subscriptions.push(dbm);
588590
void extLogger.log("Initializing database panel.");
589591
const databaseUI = new DatabaseUI(
592+
app,
590593
dbm,
591594
qs,
592595
getContextStoragePath(ctx),
593596
ctx.extensionPath,
594-
() => Credentials.initialize(),
595597
);
596598
databaseUI.init();
597599
ctx.subscriptions.push(databaseUI);
@@ -623,8 +625,6 @@ async function activateWithInstalledDistribution(
623625

624626
void extLogger.log("Initializing variant analysis manager.");
625627

626-
const app = new ExtensionApp(ctx);
627-
628628
const dbModule = await DbModule.initialize(app);
629629

630630
const variantAnalysisStorageDir = join(
@@ -633,12 +633,14 @@ async function activateWithInstalledDistribution(
633633
);
634634
await ensureDir(variantAnalysisStorageDir);
635635
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
636+
app.credentials,
636637
cliServer,
637638
extLogger,
638639
);
639640

640641
const variantAnalysisManager = new VariantAnalysisManager(
641642
ctx,
643+
app,
642644
cliServer,
643645
variantAnalysisStorageDir,
644646
variantAnalysisResultsManager,
@@ -656,6 +658,7 @@ async function activateWithInstalledDistribution(
656658
void extLogger.log("Initializing remote queries manager.");
657659
const rqm = new RemoteQueriesManager(
658660
ctx,
661+
app,
659662
cliServer,
660663
queryStorageDir,
661664
extLogger,
@@ -664,6 +667,7 @@ async function activateWithInstalledDistribution(
664667

665668
void extLogger.log("Initializing query history.");
666669
const qhm = new QueryHistoryManager(
670+
app,
667671
qs,
668672
dbm,
669673
localQueryResultsView,
@@ -1224,7 +1228,7 @@ async function activateWithInstalledDistribution(
12241228
commandRunner(
12251229
"codeQL.exportRemoteQueryResults",
12261230
async (queryId: string) => {
1227-
await exportRemoteQueryResults(qhm, rqm, queryId);
1231+
await exportRemoteQueryResults(qhm, rqm, queryId, app.credentials);
12281232
},
12291233
),
12301234
);
@@ -1242,6 +1246,7 @@ async function activateWithInstalledDistribution(
12421246
variantAnalysisManager,
12431247
variantAnalysisId,
12441248
filterSort,
1249+
app.credentials,
12451250
progress,
12461251
token,
12471252
);
@@ -1342,9 +1347,7 @@ async function activateWithInstalledDistribution(
13421347
commandRunnerWithProgress(
13431348
"codeQL.chooseDatabaseGithub",
13441349
async (progress: ProgressCallback, token: CancellationToken) => {
1345-
const credentials = isCanary()
1346-
? await Credentials.initialize()
1347-
: undefined;
1350+
const credentials = isCanary() ? app.credentials : undefined;
13481351
await databaseUI.handleChooseDatabaseGithub(
13491352
credentials,
13501353
progress,
@@ -1398,8 +1401,7 @@ async function activateWithInstalledDistribution(
13981401
* Credentials for authenticating to GitHub.
13991402
* These are used when making API calls.
14001403
*/
1401-
const credentials = await Credentials.initialize();
1402-
const octokit = await credentials.getOctokit();
1404+
const octokit = await app.credentials.getOctokit();
14031405
const userInfo = await octokit.users.getAuthenticated();
14041406
void showAndLogInformationMessage(
14051407
`Authenticated to GitHub as user: ${userInfo.data.login}`,

extensions/ql-vscode/src/query-history/query-history.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ import {
5656
import { pathExists } from "fs-extra";
5757
import { CliVersionConstraint } from "../cli";
5858
import { HistoryItemLabelProvider } from "./history-item-label-provider";
59-
import { Credentials } from "../authentication";
6059
import { cancelRemoteQuery } from "../remote-queries/gh-api/gh-actions-api-client";
6160
import { RemoteQueriesManager } from "../remote-queries/remote-queries-manager";
6261
import { RemoteQueryHistoryItem } from "../remote-queries/remote-query-history-item";
@@ -70,6 +69,7 @@ import { QueryRunner } from "../queryRunner";
7069
import { VariantAnalysisManager } from "../remote-queries/variant-analysis-manager";
7170
import { VariantAnalysisHistoryItem } from "./variant-analysis-history-item";
7271
import { getTotalResultCount } from "../remote-queries/shared/variant-analysis";
72+
import { App } from "../common/app";
7373

7474
/**
7575
* query-history.ts
@@ -362,6 +362,7 @@ export class QueryHistoryManager extends DisposableObject {
362362
readonly onDidCompleteQuery = this._onDidCompleteQuery.event;
363363

364364
constructor(
365+
private readonly app: App,
365366
private readonly qs: QueryRunner,
366367
private readonly dbm: DatabaseManager,
367368
private readonly localQueriesResultsView: ResultsView,
@@ -604,10 +605,6 @@ export class QueryHistoryManager extends DisposableObject {
604605
this._onDidCompleteQuery.fire(info);
605606
}
606607

607-
private getCredentials() {
608-
return Credentials.initialize();
609-
}
610-
611608
/**
612609
* Register and create the history scrubber.
613610
*/
@@ -1314,8 +1311,7 @@ export class QueryHistoryManager extends DisposableObject {
13141311
void showAndLogInformationMessage(
13151312
"Cancelling variant analysis. This may take a while.",
13161313
);
1317-
const credentials = await this.getCredentials();
1318-
await cancelRemoteQuery(credentials, item.remoteQuery);
1314+
await cancelRemoteQuery(this.app.credentials, item.remoteQuery);
13191315
} else if (item.t === "variant-analysis") {
13201316
await commands.executeCommand(
13211317
"codeQL.cancelVariantAnalysis",

0 commit comments

Comments
 (0)