Skip to content

Commit ebf4df1

Browse files
committed
Add GitHub authentication (#874)
1 parent b1e28f6 commit ebf4df1

27 files changed

Lines changed: 1255 additions & 218 deletions

extensions/ql-vscode/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module.exports = {
2222
},
2323
],
2424
"@typescript-eslint/explicit-function-return-type": "off",
25+
"@typescript-eslint/explicit-module-boundary-types": "off",
2526
"@typescript-eslint/no-non-null-assertion": "off",
2627
"@typescript-eslint/no-explicit-any": "off",
2728
"@typescript-eslint/no-floating-promises": [ "error", { ignoreVoid: true } ],

extensions/ql-vscode/package-lock.json

Lines changed: 1095 additions & 179 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/ql-vscode/package.json

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"url": "https://github.com/github/vscode-codeql"
1414
},
1515
"engines": {
16-
"vscode": "^1.43.0"
16+
"vscode": "^1.48.0"
1717
},
1818
"categories": [
1919
"Programming Languages"
@@ -28,6 +28,7 @@
2828
"onView:codeQLAstViewer",
2929
"onView:test-explorer",
3030
"onCommand:codeQL.checkForUpdatesToCLI",
31+
"onCommand:codeQL.authenticateToGitHub",
3132
"onCommand:codeQLDatabases.chooseDatabaseFolder",
3233
"onCommand:codeQLDatabases.chooseDatabaseArchive",
3334
"onCommand:codeQLDatabases.chooseDatabaseInternet",
@@ -226,6 +227,10 @@
226227
}
227228
},
228229
"commands": [
230+
{
231+
"command": "codeQL.authenticateToGitHub",
232+
"title": "CodeQL: Authenticate to GitHub"
233+
},
229234
{
230235
"command": "codeQL.runQuery",
231236
"title": "CodeQL: Run Query"
@@ -858,6 +863,7 @@
858863
"format-staged": "lint-staged"
859864
},
860865
"dependencies": {
866+
"@octokit/rest": "^18.5.6",
861867
"child-process-promise": "^2.2.1",
862868
"classnames": "~2.2.6",
863869
"fs-extra": "^9.0.1",
@@ -906,11 +912,11 @@
906912
"@types/through2": "^2.0.36",
907913
"@types/tmp": "^0.1.0",
908914
"@types/unzipper": "~0.10.1",
909-
"@types/vscode": "^1.43.0",
915+
"@types/vscode": "^1.48.0",
910916
"@types/webpack": "^4.32.1",
911917
"@types/xml2js": "~0.4.4",
912-
"@typescript-eslint/eslint-plugin": "~2.23.0",
913-
"@typescript-eslint/parser": "~2.23.0",
918+
"@typescript-eslint/eslint-plugin": "^4.26.0",
919+
"@typescript-eslint/parser": "^4.26.0",
914920
"ansi-colors": "^4.1.1",
915921
"applicationinsights": "^1.8.7",
916922
"chai": "^4.2.0",
@@ -938,7 +944,7 @@
938944
"ts-loader": "^8.1.0",
939945
"ts-node": "^8.3.0",
940946
"ts-protoc-gen": "^0.9.0",
941-
"typescript": "~3.8.3",
947+
"typescript": "^4.3.2",
942948
"typescript-formatter": "^7.2.2",
943949
"vsce": "^1.65.0",
944950
"vscode-test": "^1.4.0",

extensions/ql-vscode/src/astViewer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class AstViewerDataProvider extends DisposableObject implements TreeDataProvider
5656
}
5757

5858
refresh(): void {
59-
this._onDidChangeTreeData.fire();
59+
this._onDidChangeTreeData.fire(undefined);
6060
}
6161
getChildren(item?: AstItem): ProviderResult<AstItem[]> {
6262
const children = item ? item.children : this.roots;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import * as vscode from 'vscode';
2+
import * as Octokit from '@octokit/rest';
3+
4+
const GITHUB_AUTH_PROVIDER_ID = 'github';
5+
6+
// 'repo' scope should be enough for triggering workflows. For a comprenhensive list, see:
7+
// https://docs.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps
8+
const SCOPES = ['repo'];
9+
10+
/**
11+
* Handles authentication to GitHub, using the VS Code [authentication API](https://code.visualstudio.com/api/references/vscode-api#authentication).
12+
*/
13+
export class Credentials {
14+
private octokit: Octokit.Octokit | undefined;
15+
16+
// Explicitly make the constructor private, so that we can't accidentally call the constructor from outside the class
17+
// without also initializing the class.
18+
// eslint-disable-next-line @typescript-eslint/no-empty-function
19+
private constructor() { }
20+
21+
static async initialize(context: vscode.ExtensionContext): Promise<Credentials> {
22+
const c = new Credentials();
23+
c.registerListeners(context);
24+
c.octokit = await c.createOctokit(false);
25+
return c;
26+
}
27+
28+
private async createOctokit(createIfNone: boolean): Promise<Octokit.Octokit | undefined> {
29+
const session = await vscode.authentication.getSession(GITHUB_AUTH_PROVIDER_ID, SCOPES, { createIfNone });
30+
31+
return session
32+
? new Octokit.Octokit({
33+
auth: session.accessToken
34+
}) : undefined;
35+
}
36+
37+
registerListeners(context: vscode.ExtensionContext): void {
38+
// Sessions are changed when a user logs in or logs out.
39+
context.subscriptions.push(vscode.authentication.onDidChangeSessions(async e => {
40+
if (e.provider.id === GITHUB_AUTH_PROVIDER_ID) {
41+
this.octokit = await this.createOctokit(false);
42+
}
43+
}));
44+
}
45+
46+
async getOctokit(): Promise<Octokit.Octokit> {
47+
if (this.octokit) {
48+
return this.octokit;
49+
}
50+
51+
this.octokit = await this.createOctokit(true);
52+
// octokit shouldn't be undefined, since we've set "createIfNone: true".
53+
// The following block is mainly here to prevent a compiler error.
54+
if (!this.octokit) {
55+
throw new Error('Did not initialize Octokit.');
56+
}
57+
return this.octokit;
58+
}
59+
}

extensions/ql-vscode/src/cli.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable @typescript-eslint/camelcase */
21
import * as cpp from 'child-process-promise';
32
import * as child_process from 'child_process';
43
import * as fs from 'fs-extra';

extensions/ql-vscode/src/compare/view/Compare.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const emptyComparison: SetComparisonsMessage = {
2121
message: 'Empty comparison'
2222
};
2323

24-
export function Compare(_: {}): JSX.Element {
24+
export function Compare(_: Record<string, never>): JSX.Element {
2525
const [comparison, setComparison] = useState<SetComparisonsMessage>(
2626
emptyComparison
2727
);
@@ -66,8 +66,8 @@ export function Compare(_: {}): JSX.Element {
6666
{hasRows ? (
6767
<CompareTable comparison={comparison}></CompareTable>
6868
) : (
69-
<div className="vscode-codeql__compare-message">{message}</div>
70-
)}
69+
<div className="vscode-codeql__compare-message">{message}</div>
70+
)}
7171
</>
7272
);
7373
} catch (err) {

extensions/ql-vscode/src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ export abstract class ConfigListener extends DisposableObject {
147147

148148
protected abstract handleDidChangeConfiguration(e: ConfigurationChangeEvent): void;
149149
private updateConfiguration(): void {
150-
this._onDidChangeConfiguration.fire();
150+
this._onDidChangeConfiguration.fire(undefined);
151151
}
152152

153153
public get onDidChangeConfiguration(): Event<void> {
@@ -189,7 +189,7 @@ export class QueryServerConfigListener extends ConfigListener implements QuerySe
189189
config.push(distributionManager.onDidChangeDistribution(async () => {
190190
const codeQlPath = await distributionManager.getCodeQlPathWithoutVersionCheck();
191191
config._codeQlPath = codeQlPath!;
192-
config._onDidChangeConfiguration.fire();
192+
config._onDidChangeConfiguration.fire(undefined);
193193
}));
194194
}
195195
return config;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class DatabaseTreeDataProvider extends DisposableObject
179179

180180
public set sortOrder(newSortOrder: SortOrder) {
181181
this._sortOrder = newSortOrder;
182-
this._onDidChangeTreeData.fire();
182+
this._onDidChangeTreeData.fire(undefined);
183183
}
184184
}
185185

extensions/ql-vscode/src/extension.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ import {
7070
} from './commandRunner';
7171
import { CodeQlStatusBarHandler } from './status-bar';
7272

73+
import { Credentials } from './authentication';
74+
7375
/**
7476
* extension.ts
7577
* ------------
@@ -148,7 +150,7 @@ export interface CodeQLExtensionInterface {
148150
*
149151
* @returns CodeQLExtensionInterface
150152
*/
151-
export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionInterface | {}> {
153+
export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionInterface | Record<string, never>> {
152154
void logger.log(`Starting ${extensionId} extension`);
153155
if (extension === undefined) {
154156
throw new Error(`Can't find extension ${extensionId}`);
@@ -298,13 +300,13 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
298300

299301
async function installOrUpdateThenTryActivate(
300302
config: DistributionUpdateConfig
301-
): Promise<CodeQLExtensionInterface | {}> {
303+
): Promise<CodeQLExtensionInterface | Record<string, never>> {
302304

303305
await installOrUpdateDistribution(config);
304306

305307
// Display the warnings even if the extension has already activated.
306308
const distributionResult = await getDistributionDisplayingDistributionWarnings();
307-
let extensionInterface: CodeQLExtensionInterface | {} = {};
309+
let extensionInterface: CodeQLExtensionInterface | Record<string, never> = {};
308310
if (!beganMainExtensionActivation && distributionResult.kind !== FindDistributionResultKind.NoDistribution) {
309311
extensionInterface = await activateWithInstalledDistribution(
310312
ctx,
@@ -498,8 +500,7 @@ async function activateWithInstalledDistribution(
498500
void helpers.showAndLogErrorMessage(
499501
'Jumping from a .qlref file to the .ql file it references is not '
500502
+ 'supported with the CLI version you are running.\n'
501-
+ `Please upgrade your CLI to version ${
502-
CliVersionConstraint.CLI_VERSION_WITH_RESOLVE_QLREF
503+
+ `Please upgrade your CLI to version ${CliVersionConstraint.CLI_VERSION_WITH_RESOLVE_QLREF
503504
} or later to use this feature.`);
504505
}
505506
}
@@ -713,6 +714,18 @@ async function activateWithInstalledDistribution(
713714
void helpers.showAndLogInformationMessage(text);
714715
}));
715716

717+
/**
718+
* Credentials for authenticating to GitHub.
719+
* Currently unused, but will be useful in the future when making API calls.
720+
*/
721+
const credentials = await Credentials.initialize(ctx);
722+
723+
ctx.subscriptions.push(
724+
commandRunner('codeQL.authenticateToGitHub', async () => {
725+
const octokit = await credentials.getOctokit();
726+
const userInfo = await octokit.users.getAuthenticated();
727+
void helpers.showAndLogInformationMessage(`Authenticated to GitHub as user: ${userInfo.data.login}`);
728+
}));
716729

717730
void logger.log('Starting language server.');
718731
ctx.subscriptions.push(client.start());

0 commit comments

Comments
 (0)