src/vs/server/node/serverConnectionToken.ts
132 LOC · 84 covered · 48 uncovered · 24 ranges · 13 concepts · 13 introducers · 7 tests
File neighbourhood
The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.
Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file
In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.
Graph controls are ready.
Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.
/*---------------------------------------------------------------------------------------------
serverConnectionToken.ts ×8
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cookie from 'cookie';
import * as fs from 'fs';
import type * as http from 'http';
import * as url from 'url';
import * as path from '../../base/common/path.js';
import { generateUuid } from '../../base/common/uuid.js';
import { connectionTokenCookieName, connectionTokenQueryName } from '../../base/common/network.js';
import { ServerParsedArgs } from './serverEnvironmentService.js';
import { Promises } from '../../base/node/pfs.js';
const connectionTokenRegex = /^[0-9A-Za-z_-]+$/;
export const enum ServerConnectionTokenType {
None,
Optional,// TODO: Remove this soon
Mandatory
}
export class NoneServerConnectionToken {
public validate(connectionToken: unknown): boolean {
return true;
}
export class MandatoryServerConnectionToken {
public readonly type = ServerConnectionTokenType.Mandatory;
constructor(public readonly value: string) {
public validate(connectionToken: unknown): boolean {
return (connectionToken === this.value);
}
export type ServerConnectionToken = NoneServerConnectionToken | MandatoryServerConnectionToken;
export class ServerConnectionTokenParseError {
constructor(
) { }
export async function parseServerConnectionToken(args: ServerParsedArgs, defaultValue: () => Promise<string>): Promise<ServerConnectionToken | ServerConnectionTokenParseError> {
const withoutConnectionToken = args['without-connection-token'];
const connectionToken = args['connection-token'];
const connectionTokenFile = args['connection-token-file'];
if (withoutConnectionToken) {
if (typeof connectionToken !== 'undefined' || typeof connectionTokenFile !== 'undefined') {
serverConnectionToken.ts ×1
return new ServerConnectionTokenParseError(`Please do not use the argument '--connection-token' or '--connection-token-file' at the same time as '--without-connection-token'.`);
serverConnectionToken.ts ×1
}
}
if (typeof connectionTokenFile !== 'undefined') {
return new ServerConnectionTokenParseError(`Please do not use the argument '--connection-token' at the same time as '--connection-token-file'.`);
serverConnectionToken.ts ×1
}
let rawConnectionToken: string;
try {
rawConnectionToken = fs.readFileSync(connectionTokenFile).toString().replace(/\r?\n$/, '');
} catch (e) {
return new ServerConnectionTokenParseError(`Unable to read the connection token file at '${connectionTokenFile}'.`);
}
if (!connectionTokenRegex.test(rawConnectionToken)) {
return new ServerConnectionTokenParseError(`The connection token defined in '${connectionTokenFile} does not adhere to the characters 0-9, a-z, A-Z, _, or -.`);
}
return new MandatoryServerConnectionToken(rawConnectionToken);
}
if (typeof connectionToken !== 'undefined') {
return new ServerConnectionTokenParseError(`The connection token '${connectionToken} does not adhere to the characters 0-9, a-z, A-Z or -.`);
}
return new MandatoryServerConnectionToken(connectionToken);
}
return new MandatoryServerConnectionToken(await defaultValue());
}
export async function determineServerConnectionToken(args: ServerParsedArgs): Promise<ServerConnectionToken | ServerConnectionTokenParseError> {
const readOrGenerateConnectionToken = async () => {
if (!args['user-data-dir']) {
// No place to store it!
return generateUuid();
}
const storageLocation = path.join(args['user-data-dir'], 'token');
// First try to find a connection token
try {
const fileContents = await fs.promises.readFile(storageLocation);
const connectionToken = fileContents.toString().replace(/\r?\n$/, '');
if (connectionTokenRegex.test(connectionToken)) {
return connectionToken;
}
} catch (err) { }
// No connection token found, generate one
const connectionToken = generateUuid();
try {
// Try to store it
await Promises.writeFile(storageLocation, connectionToken, { mode: 0o600 });
} catch (err) { }
return connectionToken;
};
return parseServerConnectionToken(args, readOrGenerateConnectionToken);
}
export function requestHasValidConnectionToken(connectionToken: ServerConnectionToken, req: http.IncomingMessage, parsedUrl: url.UrlWithParsedQuery) {
// First check if there is a valid query parameter
if (connectionToken.validate(parsedUrl.query[connectionTokenQueryName])) {
return true;
}
// Otherwise, check if there is a valid cookie
const cookies = cookie.parse(req.headers.cookie || '');
return connectionToken.validate(cookies[connectionTokenCookieName]);
}