mirror of https://github.com/coder/code-server.git
Enable secret storage (#6450)
* Remove unused dependency patch * Enable secret storage based on local storage * Remove unnecessary GitHub auth patch It works now without the patch.
This commit is contained in:
parent
468cf5c6ce
commit
a1131fadf2
|
@ -265,15 +265,35 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
|
||||||
}
|
}
|
||||||
|
|
||||||
private startListening(): void {
|
private startListening(): void {
|
||||||
@@ -569,7 +570,7 @@ function readCookie(name: string): strin
|
@@ -550,17 +551,6 @@ class WorkspaceProvider implements IWork
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-function readCookie(name: string): string | undefined {
|
||||||
|
- const cookies = document.cookie.split('; ');
|
||||||
|
- for (const cookie of cookies) {
|
||||||
|
- if (cookie.startsWith(name + '=')) {
|
||||||
|
- return cookie.substring(name.length + 1);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return undefined;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
// Find config by checking for DOM
|
||||||
|
@@ -569,8 +559,8 @@ function readCookie(name: string): strin
|
||||||
if (!configElement || !configElementAttribute) {
|
if (!configElement || !configElementAttribute) {
|
||||||
throw new Error('Missing web configuration element');
|
throw new Error('Missing web configuration element');
|
||||||
}
|
}
|
||||||
- const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = JSON.parse(configElementAttribute);
|
- const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = JSON.parse(configElementAttribute);
|
||||||
|
- const secretStorageKeyPath = readCookie('vscode-secret-key-path');
|
||||||
+ const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = { ...JSON.parse(configElementAttribute), remoteAuthority: location.host }
|
+ const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = { ...JSON.parse(configElementAttribute), remoteAuthority: location.host }
|
||||||
const secretStorageKeyPath = readCookie('vscode-secret-key-path');
|
+ const secretStorageKeyPath = (window.location.pathname + "/mint-key").replace(/\/\/+/g, "/");
|
||||||
const secretStorageCrypto = secretStorageKeyPath && ServerKeyedAESCrypto.supported()
|
const secretStorageCrypto = secretStorageKeyPath && ServerKeyedAESCrypto.supported()
|
||||||
? new ServerKeyedAESCrypto(secretStorageKeyPath) : new TransparentCrypto();
|
? new ServerKeyedAESCrypto(secretStorageKeyPath) : new TransparentCrypto();
|
||||||
|
|
||||||
Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
|
Index: code-server/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
|
||||||
===================================================================
|
===================================================================
|
||||||
--- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
|
--- code-server.orig/lib/vscode/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
Modify VS Code dependencies
|
|
||||||
|
|
||||||
1. Kerberos: this is not building in our cross-compile step. It does not look
|
|
||||||
like something code-server uses right now anyway.
|
|
||||||
|
|
||||||
Index: code-server/lib/vscode/remote/package.json
|
|
||||||
===================================================================
|
|
||||||
--- code-server.orig/lib/vscode/remote/package.json
|
|
||||||
+++ code-server/lib/vscode/remote/package.json
|
|
||||||
@@ -18,7 +18,6 @@
|
|
||||||
"http-proxy-agent": "^2.1.0",
|
|
||||||
"https-proxy-agent": "^2.2.3",
|
|
||||||
"jschardet": "3.0.0",
|
|
||||||
- "kerberos": "^2.0.1",
|
|
||||||
"keytar": "7.9.0",
|
|
||||||
"minimist": "^1.2.6",
|
|
||||||
"native-watchdog": "^1.4.1",
|
|
||||||
Index: code-server/lib/vscode/remote/yarn.lock
|
|
||||||
===================================================================
|
|
||||||
--- code-server.orig/lib/vscode/remote/yarn.lock
|
|
||||||
+++ code-server/lib/vscode/remote/yarn.lock
|
|
||||||
@@ -454,15 +454,6 @@ jschardet@3.0.0:
|
|
||||||
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882"
|
|
||||||
integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==
|
|
||||||
|
|
||||||
-kerberos@^2.0.1:
|
|
||||||
- version "2.0.1"
|
|
||||||
- resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.0.1.tgz#663b0b46883b4da84495f60f2e9e399a43a33ef5"
|
|
||||||
- integrity sha512-O/jIgbdGK566eUhFwIcgalbqirYU/r76MW7/UFw06Fd9x5bSwgyZWL/Vm26aAmezQww/G9KYkmmJBkEkPk5HLw==
|
|
||||||
- dependencies:
|
|
||||||
- bindings "^1.5.0"
|
|
||||||
- node-addon-api "^4.3.0"
|
|
||||||
- prebuild-install "7.1.1"
|
|
||||||
-
|
|
||||||
keytar@7.9.0:
|
|
||||||
version "7.9.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb"
|
|
||||||
@@ -604,24 +595,6 @@ picomatch@^2.3.1:
|
|
||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
|
||||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
|
||||||
|
|
||||||
-prebuild-install@7.1.1:
|
|
||||||
- version "7.1.1"
|
|
||||||
- resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45"
|
|
||||||
- integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==
|
|
||||||
- dependencies:
|
|
||||||
- detect-libc "^2.0.0"
|
|
||||||
- expand-template "^2.0.3"
|
|
||||||
- github-from-package "0.0.0"
|
|
||||||
- minimist "^1.2.3"
|
|
||||||
- mkdirp-classic "^0.5.3"
|
|
||||||
- napi-build-utils "^1.0.1"
|
|
||||||
- node-abi "^3.3.0"
|
|
||||||
- pump "^3.0.0"
|
|
||||||
- rc "^1.2.7"
|
|
||||||
- simple-get "^4.0.0"
|
|
||||||
- tar-fs "^2.0.0"
|
|
||||||
- tunnel-agent "^0.6.0"
|
|
||||||
-
|
|
||||||
prebuild-install@^7.0.1:
|
|
||||||
version "7.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870"
|
|
|
@ -1,106 +0,0 @@
|
||||||
Add the ability to provide a GitHub token
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Index: code-server/lib/vscode/src/vs/platform/credentials/node/credentialsMainService.ts
|
|
||||||
===================================================================
|
|
||||||
--- code-server.orig/lib/vscode/src/vs/platform/credentials/node/credentialsMainService.ts
|
|
||||||
+++ code-server/lib/vscode/src/vs/platform/credentials/node/credentialsMainService.ts
|
|
||||||
@@ -5,9 +5,18 @@
|
|
||||||
|
|
||||||
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';
|
|
||||||
+import { equals as arrayEquals } from 'vs/base/common/arrays';
|
|
||||||
+
|
|
||||||
+interface IToken {
|
|
||||||
+ accessToken: string
|
|
||||||
+ account?: { label: string }
|
|
||||||
+ id: string
|
|
||||||
+ scopes: string[]
|
|
||||||
+}
|
|
||||||
|
|
||||||
export class CredentialsWebMainService extends BaseCredentialsMainService {
|
|
||||||
// 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
|
|
||||||
|
|
||||||
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
|
|
||||||
@@ -48,4 +62,59 @@ export class CredentialsWebMainService e
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
+ }));
|
|
||||||
+
|
|
||||||
+ const raw = await this.getPassword(service, account)
|
|
||||||
+
|
|
||||||
+ let existing: {
|
|
||||||
+ content: IToken[]
|
|
||||||
+ } | undefined;
|
|
||||||
+
|
|
||||||
+ 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)
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // 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;
|
|
||||||
+ }))
|
|
||||||
+ })
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return this.setPassword(service, account, JSON.stringify({
|
|
||||||
+ extensionId,
|
|
||||||
+ ...(existing || {}),
|
|
||||||
+ content: JSON.stringify([
|
|
||||||
+ ...tokens,
|
|
||||||
+ ...(existing?.content || []),
|
|
||||||
+ ])
|
|
||||||
+ }));
|
|
||||||
+ }
|
|
||||||
}
|
|
|
@ -9,7 +9,6 @@ update-check.diff
|
||||||
logout.diff
|
logout.diff
|
||||||
store-socket.diff
|
store-socket.diff
|
||||||
proxy-uri.diff
|
proxy-uri.diff
|
||||||
github-auth.diff
|
|
||||||
unique-db.diff
|
unique-db.diff
|
||||||
local-storage.diff
|
local-storage.diff
|
||||||
service-worker.diff
|
service-worker.diff
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { logger } from "@coder/logger"
|
import { logger } from "@coder/logger"
|
||||||
|
import * as crypto from "crypto"
|
||||||
import * as express from "express"
|
import * as express from "express"
|
||||||
|
import { promises as fs } from "fs"
|
||||||
import * as http from "http"
|
import * as http from "http"
|
||||||
import * as net from "net"
|
import * as net from "net"
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
|
@ -32,6 +34,7 @@ export class CodeServerRouteWrapper {
|
||||||
private _wsRouterWrapper = WsRouter()
|
private _wsRouterWrapper = WsRouter()
|
||||||
private _socketProxyProvider = new SocketProxyProvider()
|
private _socketProxyProvider = new SocketProxyProvider()
|
||||||
public router = express.Router()
|
public router = express.Router()
|
||||||
|
private mintKeyPromise: Promise<Buffer> | undefined
|
||||||
|
|
||||||
public get wsRouter() {
|
public get wsRouter() {
|
||||||
return this._wsRouterWrapper.router
|
return this._wsRouterWrapper.router
|
||||||
|
@ -66,6 +69,33 @@ export class CodeServerRouteWrapper {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private mintKey: express.Handler = async (req, res, next) => {
|
||||||
|
if (!this.mintKeyPromise) {
|
||||||
|
this.mintKeyPromise = new Promise(async (resolve) => {
|
||||||
|
const keyPath = path.join(req.args["user-data-dir"], "serve-web-key-half")
|
||||||
|
logger.debug(`Reading server web key half from ${keyPath}`)
|
||||||
|
try {
|
||||||
|
resolve(await fs.readFile(keyPath))
|
||||||
|
return
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code !== "ENOENT") {
|
||||||
|
logError(logger, `read ${keyPath}`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// VS Code wants 256 bits.
|
||||||
|
const key = crypto.randomBytes(32)
|
||||||
|
try {
|
||||||
|
await fs.writeFile(keyPath, key)
|
||||||
|
} catch (error: any) {
|
||||||
|
logError(logger, `write ${keyPath}`, error)
|
||||||
|
}
|
||||||
|
resolve(key)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const key = await this.mintKeyPromise
|
||||||
|
res.end(key)
|
||||||
|
}
|
||||||
|
|
||||||
private $root: express.Handler = async (req, res, next) => {
|
private $root: express.Handler = async (req, res, next) => {
|
||||||
const isAuthenticated = await authenticated(req)
|
const isAuthenticated = await authenticated(req)
|
||||||
const NO_FOLDER_OR_WORKSPACE_QUERY = !req.query.folder && !req.query.workspace
|
const NO_FOLDER_OR_WORKSPACE_QUERY = !req.query.folder && !req.query.workspace
|
||||||
|
@ -173,6 +203,7 @@ export class CodeServerRouteWrapper {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.router.get("/", this.ensureCodeServerLoaded, this.$root)
|
this.router.get("/", this.ensureCodeServerLoaded, this.$root)
|
||||||
this.router.get("/manifest.json", this.manifest)
|
this.router.get("/manifest.json", this.manifest)
|
||||||
|
this.router.post("/mint-key", this.mintKey)
|
||||||
this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest)
|
this.router.all("*", ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest)
|
||||||
this._wsRouterWrapper.ws("*", ensureOrigin, ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
|
this._wsRouterWrapper.ws("*", ensureOrigin, ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue