Skip to content

Commit b855574

Browse files
Always require credentials to download databases from github
1 parent 82e98c5 commit b855574

File tree

3 files changed

+82
-216
lines changed

3 files changed

+82
-216
lines changed

Diff for: extensions/ql-vscode/src/databases/database-fetcher.ts

+1-9
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,8 @@ import {
2828
allowHttp,
2929
downloadTimeout,
3030
getGitHubInstanceUrl,
31-
hasGhecDrUri,
32-
isCanary,
3331
} from "../config";
3432
import { showAndLogInformationMessage } from "../common/logging";
35-
import { AppOctokit } from "../common/octokit";
3633
import type { DatabaseOrigin } from "./local-databases/database-origin";
3734
import { createTimeoutSignal } from "../common/fetch-stream";
3835
import type { App } from "../common/app";
@@ -187,12 +184,7 @@ export class DatabaseFetcher {
187184
throw new Error(`Invalid GitHub repository: ${githubRepo}`);
188185
}
189186

190-
const credentials =
191-
isCanary() || hasGhecDrUri() ? this.app.credentials : undefined;
192-
193-
const octokit = credentials
194-
? await credentials.getOctokit()
195-
: new AppOctokit();
187+
const octokit = await this.app.credentials.getOctokit();
196188

197189
const result = await convertGithubNwoToDatabaseUrl(
198190
nwo,

Diff for: extensions/ql-vscode/src/databases/github-databases/api.ts

+11-45
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import { RequestError } from "@octokit/request-error";
21
import type { Octokit } from "@octokit/rest";
32
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
43
import { showNeverAskAgainDialog } from "../../common/vscode/dialog";
54
import type { GitHubDatabaseConfig } from "../../config";
6-
import { hasGhecDrUri } from "../../config";
75
import type { Credentials } from "../../common/authentication";
8-
import { AppOctokit } from "../../common/octokit";
96
import type { ProgressCallback } from "../../common/vscode/progress";
107
import { getErrorMessage } from "../../common/helpers-pure";
118
import { getLanguageDisplayName } from "../../common/query-language";
@@ -68,52 +65,21 @@ export async function listDatabases(
6865
credentials: Credentials,
6966
config: GitHubDatabaseConfig,
7067
): Promise<ListDatabasesResult | undefined> {
71-
// On GHEC-DR, unauthenticated requests will never work, so we should always ask
72-
// for authentication.
73-
const hasAccessToken =
74-
!!(await credentials.getExistingAccessToken()) || hasGhecDrUri();
68+
const hasAccessToken = !!(await credentials.getExistingAccessToken());
7569

76-
let octokit = hasAccessToken
77-
? await credentials.getOctokit()
78-
: new AppOctokit();
79-
80-
let promptedForCredentials = false;
81-
82-
let databases: CodeqlDatabase[];
83-
try {
84-
const response = await octokit.rest.codeScanning.listCodeqlDatabases({
85-
owner,
86-
repo,
87-
});
88-
databases = response.data;
89-
} catch (e) {
90-
// If we get a 404 when we don't have an access token, it might be because
91-
// the repository is private/internal. Therefore, we should ask the user
92-
// whether they want to connect to GitHub and try again.
93-
if (e instanceof RequestError && e.status === 404 && !hasAccessToken) {
94-
// Check whether the user wants to connect to GitHub
95-
if (!(await askForGitHubConnect(config))) {
96-
return;
97-
}
98-
99-
// Prompt for credentials
100-
octokit = await credentials.getOctokit();
101-
102-
promptedForCredentials = true;
103-
104-
const response = await octokit.rest.codeScanning.listCodeqlDatabases({
105-
owner,
106-
repo,
107-
});
108-
databases = response.data;
109-
} else {
110-
throw e;
111-
}
70+
if (!hasAccessToken && !(await askForGitHubConnect(config))) {
71+
return undefined;
11272
}
73+
const octokit = await credentials.getOctokit();
74+
75+
const response = await octokit.rest.codeScanning.listCodeqlDatabases({
76+
owner,
77+
repo,
78+
});
11379

11480
return {
115-
promptedForCredentials,
116-
databases,
81+
promptedForCredentials: !hasAccessToken,
82+
databases: response.data,
11783
octokit,
11884
};
11985
}

Diff for: extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/api.test.ts

+70-162
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,9 @@ import {
1111
} from "../../../../../src/databases/github-databases/api";
1212
import type { Credentials } from "../../../../../src/common/authentication";
1313
import type { Octokit } from "@octokit/rest";
14-
import { AppOctokit } from "../../../../../src/common/octokit";
1514
import { RequestError } from "@octokit/request-error";
1615
import { window } from "vscode";
1716

18-
// Mock the AppOctokit constructor to ensure we aren't making any network requests
19-
jest.mock("../../../../../src/common/octokit", () => ({
20-
AppOctokit: jest.fn(),
21-
}));
22-
const appMockListCodeqlDatabases = mockedOctokitFunction<
23-
"codeScanning",
24-
"listCodeqlDatabases"
25-
>();
26-
const appOctokit = mockedObject<Octokit>({
27-
rest: {
28-
codeScanning: {
29-
listCodeqlDatabases: appMockListCodeqlDatabases,
30-
},
31-
},
32-
});
33-
beforeEach(() => {
34-
(AppOctokit as unknown as jest.Mock).mockImplementation(() => appOctokit);
35-
});
36-
3717
describe("listDatabases", () => {
3818
const owner = "github";
3919
const repo = "codeql";
@@ -161,29 +141,59 @@ describe("listDatabases", () => {
161141
});
162142

163143
describe("when the user does not have an access token", () => {
164-
describe("when the repo is public", () => {
165-
beforeEach(() => {
166-
credentials = mockedObject<Credentials>({
167-
getExistingAccessToken: () => undefined,
168-
});
144+
beforeEach(() => {
145+
credentials = mockedObject<Credentials>({
146+
getExistingAccessToken: () => undefined,
147+
getOctokit: () => octokit,
148+
});
149+
});
169150

170-
mockListCodeqlDatabases.mockResolvedValue(undefined);
171-
appMockListCodeqlDatabases.mockResolvedValue(successfulMockApiResponse);
151+
describe("when answering connect to prompt", () => {
152+
beforeEach(() => {
153+
showNeverAskAgainDialogSpy.mockResolvedValue("Connect");
172154
});
173155

174156
it("returns the databases", async () => {
175157
const result = await listDatabases(owner, repo, credentials, config);
176158
expect(result).toEqual({
177159
databases,
178-
promptedForCredentials: false,
179-
octokit: appOctokit,
160+
promptedForCredentials: true,
161+
octokit,
162+
});
163+
expect(showNeverAskAgainDialogSpy).toHaveBeenCalled();
164+
expect(mockListCodeqlDatabases).toHaveBeenCalled();
165+
});
166+
167+
describe("when the request fails with a 404", () => {
168+
beforeEach(() => {
169+
mockListCodeqlDatabases.mockRejectedValue(
170+
new RequestError("Not found", 404, {
171+
request: {
172+
method: "GET",
173+
url: "",
174+
headers: {},
175+
},
176+
response: {
177+
status: 404,
178+
headers: {},
179+
url: "",
180+
data: {},
181+
retryCount: 0,
182+
},
183+
}),
184+
);
185+
});
186+
187+
it("throws an error", async () => {
188+
await expect(
189+
listDatabases(owner, repo, credentials, config),
190+
).rejects.toThrow("Not found");
180191
});
181-
expect(showNeverAskAgainDialogSpy).not.toHaveBeenCalled();
182192
});
183193

184194
describe("when the request fails with a 500", () => {
185195
beforeEach(() => {
186-
appMockListCodeqlDatabases.mockRejectedValue(
196+
mockListCodeqlDatabases.mockRejectedValue(
187197
new RequestError("Internal server error", 500, {
188198
request: {
189199
method: "GET",
@@ -205,151 +215,49 @@ describe("listDatabases", () => {
205215
await expect(
206216
listDatabases(owner, repo, credentials, config),
207217
).rejects.toThrow("Internal server error");
208-
expect(mockListCodeqlDatabases).not.toHaveBeenCalled();
209218
});
210219
});
211220
});
212221

213-
describe("when the repo is private", () => {
222+
describe("when cancelling prompt", () => {
214223
beforeEach(() => {
215-
credentials = mockedObject<Credentials>({
216-
getExistingAccessToken: () => undefined,
217-
getOctokit: () => octokit,
218-
});
219-
220-
appMockListCodeqlDatabases.mockRejectedValue(
221-
new RequestError("Not found", 404, {
222-
request: {
223-
method: "GET",
224-
url: "",
225-
headers: {},
226-
},
227-
response: {
228-
status: 404,
229-
headers: {},
230-
url: "",
231-
data: {},
232-
retryCount: 0,
233-
},
234-
}),
235-
);
224+
showNeverAskAgainDialogSpy.mockResolvedValue(undefined);
236225
});
237226

238-
describe("when answering connect to prompt", () => {
239-
beforeEach(() => {
240-
showNeverAskAgainDialogSpy.mockResolvedValue("Connect");
241-
});
242-
243-
it("returns the databases", async () => {
244-
const result = await listDatabases(owner, repo, credentials, config);
245-
expect(result).toEqual({
246-
databases,
247-
promptedForCredentials: true,
248-
octokit,
249-
});
250-
expect(showNeverAskAgainDialogSpy).toHaveBeenCalled();
251-
expect(appMockListCodeqlDatabases).toHaveBeenCalled();
252-
expect(mockListCodeqlDatabases).toHaveBeenCalled();
253-
});
254-
255-
describe("when the request fails with a 404", () => {
256-
beforeEach(() => {
257-
mockListCodeqlDatabases.mockRejectedValue(
258-
new RequestError("Not found", 404, {
259-
request: {
260-
method: "GET",
261-
url: "",
262-
headers: {},
263-
},
264-
response: {
265-
status: 404,
266-
headers: {},
267-
url: "",
268-
data: {},
269-
retryCount: 0,
270-
},
271-
}),
272-
);
273-
});
274-
275-
it("throws an error", async () => {
276-
await expect(
277-
listDatabases(owner, repo, credentials, config),
278-
).rejects.toThrow("Not found");
279-
});
280-
});
281-
282-
describe("when the request fails with a 500", () => {
283-
beforeEach(() => {
284-
mockListCodeqlDatabases.mockRejectedValue(
285-
new RequestError("Internal server error", 500, {
286-
request: {
287-
method: "GET",
288-
url: "",
289-
headers: {},
290-
},
291-
response: {
292-
status: 500,
293-
headers: {},
294-
url: "",
295-
data: {},
296-
retryCount: 0,
297-
},
298-
}),
299-
);
300-
});
301-
302-
it("throws an error", async () => {
303-
await expect(
304-
listDatabases(owner, repo, credentials, config),
305-
).rejects.toThrow("Internal server error");
306-
});
307-
});
227+
it("returns undefined", async () => {
228+
const result = await listDatabases(owner, repo, credentials, config);
229+
expect(result).toEqual(undefined);
230+
expect(showNeverAskAgainDialogSpy).toHaveBeenCalled();
231+
expect(mockListCodeqlDatabases).not.toHaveBeenCalled();
232+
expect(setDownload).not.toHaveBeenCalled();
308233
});
234+
});
309235

310-
describe("when cancelling prompt", () => {
311-
beforeEach(() => {
312-
showNeverAskAgainDialogSpy.mockResolvedValue(undefined);
313-
});
314-
315-
it("returns undefined", async () => {
316-
const result = await listDatabases(owner, repo, credentials, config);
317-
expect(result).toEqual(undefined);
318-
expect(showNeverAskAgainDialogSpy).toHaveBeenCalled();
319-
expect(appMockListCodeqlDatabases).toHaveBeenCalled();
320-
expect(mockListCodeqlDatabases).not.toHaveBeenCalled();
321-
expect(setDownload).not.toHaveBeenCalled();
322-
});
236+
describe("when answering not now to prompt", () => {
237+
beforeEach(() => {
238+
showNeverAskAgainDialogSpy.mockResolvedValue("Not now");
323239
});
324240

325-
describe("when answering not now to prompt", () => {
326-
beforeEach(() => {
327-
showNeverAskAgainDialogSpy.mockResolvedValue("Not now");
328-
});
329-
330-
it("returns undefined", async () => {
331-
const result = await listDatabases(owner, repo, credentials, config);
332-
expect(result).toEqual(undefined);
333-
expect(showNeverAskAgainDialogSpy).toHaveBeenCalled();
334-
expect(appMockListCodeqlDatabases).toHaveBeenCalled();
335-
expect(mockListCodeqlDatabases).not.toHaveBeenCalled();
336-
expect(setDownload).not.toHaveBeenCalled();
337-
});
241+
it("returns undefined", async () => {
242+
const result = await listDatabases(owner, repo, credentials, config);
243+
expect(result).toEqual(undefined);
244+
expect(showNeverAskAgainDialogSpy).toHaveBeenCalled();
245+
expect(mockListCodeqlDatabases).not.toHaveBeenCalled();
246+
expect(setDownload).not.toHaveBeenCalled();
338247
});
248+
});
339249

340-
describe("when answering never to prompt", () => {
341-
beforeEach(() => {
342-
showNeverAskAgainDialogSpy.mockResolvedValue("Never");
343-
});
250+
describe("when answering never to prompt", () => {
251+
beforeEach(() => {
252+
showNeverAskAgainDialogSpy.mockResolvedValue("Never");
253+
});
344254

345-
it("returns undefined and sets the config to 'never'", async () => {
346-
const result = await listDatabases(owner, repo, credentials, config);
347-
expect(result).toEqual(undefined);
348-
expect(showNeverAskAgainDialogSpy).toHaveBeenCalled();
349-
expect(appMockListCodeqlDatabases).toHaveBeenCalled();
350-
expect(mockListCodeqlDatabases).not.toHaveBeenCalled();
351-
expect(setDownload).toHaveBeenCalledWith("never");
352-
});
255+
it("returns undefined and sets the config to 'never'", async () => {
256+
const result = await listDatabases(owner, repo, credentials, config);
257+
expect(result).toEqual(undefined);
258+
expect(showNeverAskAgainDialogSpy).toHaveBeenCalled();
259+
expect(mockListCodeqlDatabases).not.toHaveBeenCalled();
260+
expect(setDownload).toHaveBeenCalledWith("never");
353261
});
354262
});
355263
});

0 commit comments

Comments
 (0)