-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserverConnectionToken.ts
More file actions
132 lines (106 loc) · 4.84 KB
/
Copy pathserverConnectionToken.ts
File metadata and controls
132 lines (106 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*---------------------------------------------------------------------------------------------
* 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 * 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 readonly type = ServerConnectionTokenType.None;
public validate(connectionToken: any): boolean {
return true;
}
}
export class MandatoryServerConnectionToken {
public readonly type = ServerConnectionTokenType.Mandatory;
constructor(public readonly value: string) {
}
public validate(connectionToken: any): boolean {
return (connectionToken === this.value);
}
}
export type ServerConnectionToken = NoneServerConnectionToken | MandatoryServerConnectionToken;
export class ServerConnectionTokenParseError {
constructor(
public readonly message: string
) { }
}
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') {
return new ServerConnectionTokenParseError(`Please do not use the argument '--connection-token' or '--connection-token-file' at the same time as '--without-connection-token'.`);
}
return new NoneServerConnectionToken();
}
if (typeof connectionTokenFile !== 'undefined') {
if (typeof connectionToken !== 'undefined') {
return new ServerConnectionTokenParseError(`Please do not use the argument '--connection-token' at the same time as '--connection-token-file'.`);
}
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') {
if (!connectionTokenRegex.test(connectionToken)) {
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]);
}