2022-05-05 00:58:49 +03:00
|
|
|
Add the ability to provide a GitHub token
|
2022-03-22 23:07:14 +03:00
|
|
|
|
2022-05-05 00:58:49 +03:00
|
|
|
To test install the GitHub PR extension and start code-server with GITHUB_TOKEN
|
|
|
|
or set github-auth in the config file. The extension should be authenticated.
|
2022-03-22 23:07:14 +03:00
|
|
|
|
2022-05-05 00:58:49 +03:00
|
|
|
Index: code-server/lib/vscode/src/vs/platform/credentials/node/credentialsMainService.ts
|
2022-03-22 23:07:14 +03:00
|
|
|
===================================================================
|
2022-05-05 00:58:49 +03:00
|
|
|
--- code-server.orig/lib/vscode/src/vs/platform/credentials/node/credentialsMainService.ts
|
|
|
|
+++ code-server/lib/vscode/src/vs/platform/credentials/node/credentialsMainService.ts
|
2022-06-22 00:51:46 +03:00
|
|
|
@@ -5,9 +5,18 @@
|
2022-03-22 23:07:14 +03:00
|
|
|
|
2022-05-05 00:58:49 +03:00
|
|
|
import { InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials';
|
|
|
|
import { ILogService } from 'vs/platform/log/common/log';
|
|
|
|
-import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
|
|
|
+import { IServerEnvironmentService } from 'vs/server/node/serverEnvironmentService';
|
|
|
|
import { IProductService } from 'vs/platform/product/common/productService';
|
|
|
|
import { BaseCredentialsMainService, KeytarModule } from 'vs/platform/credentials/common/credentialsMainService';
|
|
|
|
+import { generateUuid } from 'vs/base/common/uuid';
|
2022-03-22 23:07:14 +03:00
|
|
|
+import { equals as arrayEquals } from 'vs/base/common/arrays';
|
2022-05-05 00:58:49 +03:00
|
|
|
+
|
2022-03-22 23:07:14 +03:00
|
|
|
+interface IToken {
|
|
|
|
+ accessToken: string
|
|
|
|
+ account?: { label: string }
|
|
|
|
+ id: string
|
|
|
|
+ scopes: string[]
|
|
|
|
+}
|
|
|
|
|
2022-05-05 00:58:49 +03:00
|
|
|
export class CredentialsWebMainService extends BaseCredentialsMainService {
|
2022-06-22 00:51:46 +03:00
|
|
|
// Since we fallback to the in-memory credentials provider, we do not need to surface any Keytar load errors
|
|
|
|
@@ -16,10 +25,15 @@ export class CredentialsWebMainService e
|
2022-05-05 00:58:49 +03:00
|
|
|
|
|
|
|
constructor(
|
|
|
|
@ILogService logService: ILogService,
|
|
|
|
- @INativeEnvironmentService private readonly environmentMainService: INativeEnvironmentService,
|
|
|
|
+ @IServerEnvironmentService private readonly environmentMainService: IServerEnvironmentService,
|
|
|
|
@IProductService private readonly productService: IProductService,
|
|
|
|
) {
|
|
|
|
super(logService);
|
|
|
|
+ if (this.environmentMainService.args["github-auth"]) {
|
|
|
|
+ this.storeGitHubToken(this.environmentMainService.args["github-auth"]).catch((error) => {
|
|
|
|
+ this.logService.error('Failed to store provided GitHub token', error)
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the credentials service is running on the server, we add a suffix -server to differentiate from the location that the
|
2022-06-22 00:51:46 +03:00
|
|
|
@@ -48,4 +62,59 @@ export class CredentialsWebMainService e
|
2022-05-05 00:58:49 +03:00
|
|
|
}
|
|
|
|
return this._keytarCache;
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+ private async storeGitHubToken(githubToken: string): Promise<void> {
|
|
|
|
+ const extensionId = 'vscode.github-authentication';
|
|
|
|
+ const service = `${await this.getSecretStoragePrefix()}${extensionId}`;
|
|
|
|
+ const account = 'github.auth';
|
|
|
|
+ const scopes = [['read:user', 'user:email', 'repo']]
|
|
|
|
+
|
|
|
|
+ // Oddly the scopes need to match exactly so we cannot just have one token
|
|
|
|
+ // with all the scopes, instead we have to duplicate the token for each
|
|
|
|
+ // expected set of scopes.
|
|
|
|
+ const tokens: IToken[] = scopes.map((scopes) => ({
|
|
|
|
+ id: generateUuid(),
|
|
|
|
+ scopes: scopes.sort(), // Sort for comparing later.
|
|
|
|
+ accessToken: githubToken,
|
|
|
|
+ }));
|
2022-03-22 23:07:14 +03:00
|
|
|
+
|
2022-05-05 00:58:49 +03:00
|
|
|
+ const raw = await this.getPassword(service, account)
|
2022-03-22 23:07:14 +03:00
|
|
|
+
|
2022-05-05 00:58:49 +03:00
|
|
|
+ let existing: {
|
|
|
|
+ content: IToken[]
|
|
|
|
+ } | undefined;
|
2022-03-22 23:07:14 +03:00
|
|
|
+
|
2022-05-05 00:58:49 +03:00
|
|
|
+ if (raw) {
|
|
|
|
+ try {
|
|
|
|
+ const json = JSON.parse(raw);
|
|
|
|
+ json.content = JSON.parse(json.content);
|
|
|
|
+ existing = json;
|
|
|
|
+ } catch (error) {
|
|
|
|
+ this.logService.error('Failed to parse existing GitHub credentials', error)
|
|
|
|
+ }
|
|
|
|
+ }
|
2022-03-22 23:07:14 +03:00
|
|
|
+
|
2022-05-05 00:58:49 +03:00
|
|
|
+ // Keep tokens for account and scope combinations we do not have in case
|
|
|
|
+ // there is an extension that uses scopes we have not accounted for (in
|
|
|
|
+ // these cases the user will need to manually authenticate the extension
|
|
|
|
+ // through the UI) or the user has tokens for other accounts.
|
|
|
|
+ if (existing?.content) {
|
|
|
|
+ existing.content = existing.content.filter((existingToken) => {
|
|
|
|
+ const scopes = existingToken.scopes.sort();
|
|
|
|
+ return !(tokens.find((token) => {
|
|
|
|
+ return arrayEquals(scopes, token.scopes)
|
|
|
|
+ && token.account?.label === existingToken.account?.label;
|
|
|
|
+ }))
|
2022-03-22 23:07:14 +03:00
|
|
|
+ })
|
2022-05-05 00:58:49 +03:00
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return this.setPassword(service, account, JSON.stringify({
|
|
|
|
+ extensionId,
|
|
|
|
+ ...(existing || {}),
|
|
|
|
+ content: JSON.stringify([
|
|
|
|
+ ...tokens,
|
|
|
|
+ ...(existing?.content || []),
|
|
|
|
+ ])
|
|
|
|
+ }));
|
|
|
|
+ }
|
|
|
|
}
|