Skip to content

Commit 61bc66c

Browse files
committed
Use express
1 parent e5880a8 commit 61bc66c

File tree

7 files changed

+264
-226
lines changed

7 files changed

+264
-226
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"argon2": "^0.28.2",
6363
"applicationinsights": "1.0.8",
6464
"chokidar": "3.5.1",
65+
"cookie-parser": "^1.4.5",
6566
"express": "^4.17.1",
6667
"graceful-fs": "4.2.6",
6768
"http-proxy-agent": "^2.1.0",
@@ -97,9 +98,11 @@
9798
"@types/applicationinsights": "0.20.0",
9899
"@types/chokidar": "2.1.3",
99100
"@types/cookie": "^0.3.3",
101+
"@types/cookie-parser": "^1.4.2",
100102
"@types/copy-webpack-plugin": "^6.0.3",
101103
"@types/cssnano": "^4.0.0",
102104
"@types/debug": "4.1.5",
105+
"@types/express": "^4.17.13",
103106
"@types/graceful-fs": "4.1.2",
104107
"@types/gulp-postcss": "^8.0.0",
105108
"@types/http-proxy-agent": "^2.0.1",

src/vs/server/node/auth.ts

Lines changed: 23 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import * as http from 'http';
21
import * as crypto from 'crypto';
32
import * as argon2 from 'argon2';
3+
import * as express from 'express';
44
import { ServerParsedArgs } from 'vs/server/node/args';
5-
import { serveError } from 'vs/server/node/http';
65

76
/** Ensures that the input is sanitized by checking
87
* - it's a string
@@ -15,51 +14,24 @@ export function sanitizeString(str: string): string {
1514
return typeof str === 'string' && str.trim().length > 0 ? str.trim() : '';
1615
}
1716

18-
export const ensureAuthenticated = async (args: ServerParsedArgs, req: http.IncomingMessage, res: http.ServerResponse): Promise<boolean> => {
19-
const isAuthenticated = await authenticated(args, req);
20-
if (!isAuthenticated) {
21-
serveError(req, res, 401, 'Unauthorized');
22-
}
23-
return isAuthenticated;
24-
};
25-
2617
/**
2718
* Return true if authenticated via cookies.
2819
*/
29-
export const authenticated = async (args: ServerParsedArgs, req: http.IncomingMessage): Promise<boolean> => {
20+
export const authenticated = async (args: ServerParsedArgs, req: express.Request): Promise<boolean> => {
3021
if (!args.password && !args.hashedPassword) {
3122
return true;
3223
}
3324
const passwordMethod = getPasswordMethod(args.hashedPassword);
34-
const cookies = parseCookies(req);
3525
const isCookieValidArgs: IsCookieValidArgs = {
3626
passwordMethod,
37-
cookieKey: sanitizeString(cookies.key),
27+
cookieKey: sanitizeString(req.cookies.key),
3828
passwordFromArgs: args.password || '',
3929
hashedPasswordFromArgs: args.hashedPassword,
4030
};
4131

4232
return await isCookieValid(isCookieValidArgs);
4333
};
4434

45-
function parseCookies(request: http.IncomingMessage): Record<string, string> {
46-
const cookies: Record<string, string> = {},
47-
rc = request.headers.cookie;
48-
49-
// eslint-disable-next-line code-no-unused-expressions
50-
rc && rc.split(';').forEach(cookie => {
51-
let parts = cookie.split('=');
52-
if (parts.length > 0) {
53-
const name = parts.shift()!.trim();
54-
let value = decodeURI(parts.join('='));
55-
value = value.substring(1, value.length - 1);
56-
cookies[name] = value;
57-
}
58-
});
59-
60-
return cookies;
61-
}
62-
6335
export type PasswordMethod = 'ARGON2' | 'PLAIN_TEXT';
6436

6537
/**
@@ -88,7 +60,7 @@ type HandlePasswordValidationArgs = {
8860
/** The PasswordMethod */
8961
passwordMethod: PasswordMethod
9062
/** The password provided by the user */
91-
passwordFromRequestBody: string
63+
passwordFromRequestBody: string | undefined
9264
/** The password set in PASSWORD or config */
9365
passwordFromArgs: string | undefined
9466
/** The hashed-password set in HASHED_PASSWORD or config */
@@ -174,24 +146,26 @@ export async function handlePasswordValidation({
174146
hashedPassword: '',
175147
};
176148

177-
switch (passwordMethod) {
178-
case 'PLAIN_TEXT': {
179-
const isValid = passwordFromArgs ? safeCompare(passwordFromRequestBody, passwordFromArgs) : false;
180-
passwordValidation.isPasswordValid = isValid;
181-
182-
const hashedPassword = await hash(passwordFromRequestBody);
183-
passwordValidation.hashedPassword = hashedPassword;
184-
break;
185-
}
186-
case 'ARGON2': {
187-
const isValid = await isHashMatch(passwordFromRequestBody, hashedPasswordFromArgs || '');
188-
passwordValidation.isPasswordValid = isValid;
189-
190-
passwordValidation.hashedPassword = hashedPasswordFromArgs || '';
191-
break;
149+
if (passwordFromRequestBody) {
150+
switch (passwordMethod) {
151+
case 'PLAIN_TEXT': {
152+
const isValid = passwordFromArgs ? safeCompare(passwordFromRequestBody, passwordFromArgs) : false;
153+
passwordValidation.isPasswordValid = isValid;
154+
155+
const hashedPassword = await hash(passwordFromRequestBody);
156+
passwordValidation.hashedPassword = hashedPassword;
157+
break;
158+
}
159+
case 'ARGON2': {
160+
const isValid = await isHashMatch(passwordFromRequestBody, hashedPasswordFromArgs || '');
161+
passwordValidation.isPasswordValid = isValid;
162+
163+
passwordValidation.hashedPassword = hashedPasswordFromArgs || '';
164+
break;
165+
}
166+
default:
167+
break;
192168
}
193-
default:
194-
break;
195169
}
196170

197171
return passwordValidation;

src/vs/server/node/http.ts

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import * as fs from 'fs';
22
import * as path from 'path';
3-
import * as http from 'http';
43
import { ILogService } from 'vs/platform/log/common/log';
5-
import { parse } from 'querystring';
4+
import { Request, Response } from 'express';
65

76
// TODO is it enough?
87
const textMimeType = new Map([
@@ -33,26 +32,7 @@ export function getMediaMime(forPath: string): string | undefined {
3332
return mapExtToMediaMimes.get(ext.toLowerCase());
3433
}
3534

36-
export function collectRequestData(request: http.IncomingMessage): Promise<Record<string, string>> {
37-
return new Promise(resolve => {
38-
const FORM_URLENCODED = 'application/x-www-form-urlencoded';
39-
if (request.headers['content-type'] === FORM_URLENCODED) {
40-
let body = '';
41-
request.on('data', chunk => {
42-
body += chunk.toString();
43-
});
44-
request.on('end', () => {
45-
const item = parse(body) as Record<string, string>;
46-
resolve(item);
47-
});
48-
}
49-
else {
50-
resolve({});
51-
}
52-
});
53-
}
54-
55-
export async function serveFile(logService: ILogService, req: http.IncomingMessage, res: http.ServerResponse, filePath: string, responseHeaders: http.OutgoingHttpHeaders = {}) {
35+
export async function serveFile(logService: ILogService, req: Request, res: Response, filePath: string) {
5636
try {
5737

5838
// Sanity checks
@@ -63,15 +43,13 @@ export async function serveFile(logService: ILogService, req: http.IncomingMessa
6343
// Check if file modified since
6444
const etag = `W/"${[stat.ino, stat.size, stat.mtime.getTime()].join('-')}"`; // weak validator (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag)
6545
if (req.headers['if-none-match'] === etag) {
66-
res.writeHead(304);
46+
res.status(304);
6747
return res.end();
6848
}
6949

70-
// Headers
71-
responseHeaders['Content-Type'] = textMimeType.get(path.extname(filePath)) || getMediaMime(filePath) || 'text/plain';
72-
responseHeaders['Etag'] = etag;
73-
74-
res.writeHead(200, responseHeaders);
50+
res.header('Content-Type', textMimeType.get(path.extname(filePath)) || getMediaMime(filePath) || 'text/plain');
51+
res.header('Etag', etag);
52+
res.status(200);
7553

7654
// Data
7755
fs.createReadStream(filePath).pipe(res);
@@ -82,7 +60,7 @@ export async function serveFile(logService: ILogService, req: http.IncomingMessa
8260
}
8361
}
8462

85-
export function serveError(req: http.IncomingMessage, res: http.ServerResponse, errorCode: number, errorMessage: string): void {
63+
export function serveError(req: Request, res: Response, errorCode: number, errorMessage: string): void {
8664
res.writeHead(errorCode, { 'Content-Type': 'text/plain' });
8765
res.end(errorMessage);
8866
}

0 commit comments

Comments
 (0)