1
- import * as http from 'http' ;
2
1
import * as crypto from 'crypto' ;
3
- import * as argon2 from 'argon2 ' ;
2
+ import * as express from 'express ' ;
4
3
import { ServerParsedArgs } from 'vs/server/node/args' ;
5
- import { serveError } from 'vs/server/node/http' ;
6
4
7
5
/** Ensures that the input is sanitized by checking
8
6
* - it's a string
@@ -15,92 +13,39 @@ export function sanitizeString(str: string): string {
15
13
return typeof str === 'string' && str . trim ( ) . length > 0 ? str . trim ( ) : '' ;
16
14
}
17
15
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
-
26
16
/**
27
17
* Return true if authenticated via cookies.
28
18
*/
29
- export const authenticated = async ( args : ServerParsedArgs , req : http . IncomingMessage ) : Promise < boolean > => {
19
+ export const authenticated = async ( args : ServerParsedArgs , req : express . Request ) : Promise < boolean > => {
30
20
if ( ! args . password && ! args . hashedPassword ) {
31
21
return true ;
32
22
}
33
- const passwordMethod = getPasswordMethod ( args . hashedPassword ) ;
34
- const cookies = parseCookies ( req ) ;
35
23
const isCookieValidArgs : IsCookieValidArgs = {
36
- passwordMethod,
37
- cookieKey : sanitizeString ( cookies . key ) ,
38
- passwordFromArgs : args . password || '' ,
39
- hashedPasswordFromArgs : args . hashedPassword ,
24
+ cookieKey : sanitizeString ( req . cookies . key ) ,
25
+ passwordFromArgs : args . password || ''
40
26
} ;
41
27
42
28
return await isCookieValid ( isCookieValidArgs ) ;
43
29
} ;
44
30
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
-
63
- export type PasswordMethod = 'ARGON2' | 'PLAIN_TEXT' ;
64
-
65
- /**
66
- * Used to determine the password method.
67
- *
68
- * There are three options for the return value:
69
- * 1. "SHA256" -> the legacy hashing algorithm
70
- * 2. "ARGON2" -> the newest hashing algorithm
71
- * 3. "PLAIN_TEXT" -> regular ol' password with no hashing
72
- *
73
- * @returns "ARGON2" | "PLAIN_TEXT"
74
- */
75
- export function getPasswordMethod ( hashedPassword : string | undefined ) : PasswordMethod {
76
- if ( ! hashedPassword ) {
77
- return 'PLAIN_TEXT' ;
78
- }
79
- return 'ARGON2' ;
80
- }
81
-
82
31
type PasswordValidation = {
83
32
isPasswordValid : boolean
84
33
hashedPassword : string
85
34
} ;
86
35
87
36
type HandlePasswordValidationArgs = {
88
- /** The PasswordMethod */
89
- passwordMethod : PasswordMethod
90
37
/** The password provided by the user */
91
- passwordFromRequestBody : string
38
+ passwordFromRequestBody : string | undefined
92
39
/** The password set in PASSWORD or config */
93
40
passwordFromArgs : string | undefined
94
- /** The hashed-password set in HASHED_PASSWORD or config */
95
- hashedPasswordFromArgs : string | undefined
96
41
} ;
97
42
98
43
function safeCompare ( a : string , b : string ) : boolean {
99
44
if ( b . length > a . length ) {
100
- a = a . padEnd ( b . length ) ;
45
+ a = a . padEnd ( b . length , '0' ) ;
101
46
}
102
47
if ( a . length > b . length ) {
103
- b = b . padEnd ( a . length ) ;
48
+ b = b . padEnd ( a . length , '0' ) ;
104
49
}
105
50
return crypto . timingSafeEqual ( Buffer . from ( a ) , Buffer . from ( b ) ) ;
106
51
}
@@ -113,114 +58,44 @@ export const generatePassword = async (length = 24): Promise<string> => {
113
58
return buffer . toString ( 'hex' ) . substring ( 0 , length ) ;
114
59
} ;
115
60
116
- /**
117
- * Used to hash the password.
118
- */
119
- export const hash = async ( password : string ) : Promise < string > => {
120
- try {
121
- return await argon2 . hash ( password ) ;
122
- } catch ( error ) {
123
- console . error ( error ) ;
124
- return '' ;
125
- }
126
- } ;
127
-
128
- /**
129
- * Used to verify if the password matches the hash
130
- */
131
- export const isHashMatch = async ( password : string , hash : string ) => {
132
- if ( password === '' || hash === '' || ! hash . startsWith ( '$' ) ) {
133
- return false ;
134
- }
135
- try {
136
- return await argon2 . verify ( hash , password ) ;
137
- } catch ( error ) {
138
- throw new Error ( error ) ;
139
- }
140
- } ;
141
-
142
- /**
143
- * Used to hash the password using the sha256
144
- * algorithm. We only use this to for checking
145
- * the hashed-password set in the config.
146
- *
147
- * Kept for legacy reasons.
148
- */
149
- export const hashLegacy = ( str : string ) : string => {
61
+ export const hash = ( str : string ) : string => {
150
62
return crypto . createHash ( 'sha256' ) . update ( str ) . digest ( 'hex' ) ;
151
63
} ;
152
64
153
- /**
154
- * Used to check if the password matches the hash using
155
- * the hashLegacy function
156
- */
157
- export const isHashLegacyMatch = ( password : string , hashPassword : string ) => {
158
- const hashedWithLegacy = hashLegacy ( password ) ;
65
+ export const isHashMatch = ( password : string , hashPassword : string ) => {
66
+ const hashedWithLegacy = hash ( password ) ;
159
67
return safeCompare ( hashedWithLegacy , hashPassword ) ;
160
68
} ;
161
69
162
- /**
163
- * Checks if a password is valid and also returns the hash
164
- * using the PasswordMethod
165
- */
166
70
export async function handlePasswordValidation ( {
167
- passwordMethod,
168
71
passwordFromArgs,
169
- passwordFromRequestBody,
170
- hashedPasswordFromArgs,
72
+ passwordFromRequestBody
171
73
} : HandlePasswordValidationArgs ) : Promise < PasswordValidation > {
172
74
const passwordValidation : PasswordValidation = {
173
75
isPasswordValid : false ,
174
76
hashedPassword : '' ,
175
77
} ;
176
78
177
- switch ( passwordMethod ) {
178
- case 'PLAIN_TEXT' : {
179
- const isValid = passwordFromArgs ? safeCompare ( passwordFromRequestBody , passwordFromArgs ) : false ;
180
- passwordValidation . isPasswordValid = isValid ;
79
+ if ( passwordFromRequestBody ) {
80
+ const isValid = passwordFromArgs ? safeCompare ( passwordFromRequestBody , passwordFromArgs ) : false ;
81
+ passwordValidation . isPasswordValid = isValid ;
181
82
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 ;
192
- }
193
- default :
194
- break ;
83
+ const hashedPassword = hash ( passwordFromRequestBody ) ;
84
+ passwordValidation . hashedPassword = hashedPassword ;
195
85
}
196
86
197
87
return passwordValidation ;
198
88
}
199
89
200
90
export type IsCookieValidArgs = {
201
- passwordMethod : PasswordMethod
202
91
cookieKey : string
203
- hashedPasswordFromArgs : string | undefined
204
92
passwordFromArgs : string | undefined
205
93
} ;
206
94
207
95
/** Checks if a req.cookies.key is valid using the PasswordMethod */
208
96
export async function isCookieValid ( {
209
97
passwordFromArgs = '' ,
210
- cookieKey,
211
- hashedPasswordFromArgs = '' ,
212
- passwordMethod,
98
+ cookieKey
213
99
} : IsCookieValidArgs ) : Promise < boolean > {
214
- let isValid = false ;
215
- switch ( passwordMethod ) {
216
- case 'PLAIN_TEXT' :
217
- isValid = await isHashMatch ( passwordFromArgs , cookieKey ) ;
218
- break ;
219
- case 'ARGON2' :
220
- isValid = safeCompare ( cookieKey , hashedPasswordFromArgs ) ;
221
- break ;
222
- default :
223
- break ;
224
- }
225
- return isValid ;
100
+ return isHashMatch ( passwordFromArgs , cookieKey ) ;
226
101
}
0 commit comments