@@ -293,68 +293,67 @@ module.exports = {
293293 } ,
294294 ...self . isPasswordResetEnabled ( ) && {
295295 async resetRequest ( req ) {
296- const wait = ( t = 2000 ) => Promise . delay ( t ) ;
296+ const MIN_RESPONSE_TIME = 2000 ;
297+ const startTime = Date . now ( ) ;
297298 const site = ( req . headers . host || '' ) . replace ( / : \d + $ / , '' ) ;
298299 const email = self . apos . launder . string ( req . body . email ) ;
299300 if ( ! email . length ) {
300301 throw self . apos . error ( 'invalid' , req . t ( 'apostrophe:loginResetEmailRequired' ) ) ;
301302 }
302303 let user ;
303- // error not reported to browser for security reasons
304304 try {
305305 user = await self . getPasswordResetUser ( req . body . email ) ;
306306 } catch ( e ) {
307307 self . apos . util . error ( e ) ;
308308 }
309309 if ( ! user ) {
310- await wait ( ) ;
311310 self . apos . util . error (
312311 `Reset password request error - the user ${ email } doesn\`t exist.`
313312 ) ;
314- return ;
315- }
316- if ( ! user . email ) {
317- await wait ( ) ;
313+ } else if ( ! user . email ) {
318314 self . apos . util . error (
319315 `Reset password request error - the user ${ user . username } doesn\`t have an email.`
320316 ) ;
321- return ;
322- }
323- const reset = self . apos . util . generateId ( ) ;
324- user . passwordReset = reset ;
325- user . passwordResetAt = new Date ( ) ;
326- await self . apos . user . update ( req , user , { permissions : false } ) ;
327- // Fix - missing host in the absoluteUrl results in a panic.
328- let port = ( req . headers . host || '' ) . split ( ':' ) [ 1 ] ;
329- if ( ! port || [ '80' , '443' ] . includes ( port ) ) {
330- port = '' ;
331317 } else {
332- port = `:${ port } ` ;
318+ const reset = self . apos . util . generateId ( ) ;
319+ user . passwordReset = reset ;
320+ user . passwordResetAt = new Date ( ) ;
321+ await self . apos . user . update ( req , user , { permissions : false } ) ;
322+ let port = ( req . headers . host || '' ) . split ( ':' ) [ 1 ] ;
323+ if ( ! port || [ '80' , '443' ] . includes ( port ) ) {
324+ port = '' ;
325+ } else {
326+ port = `:${ port } ` ;
327+ }
328+ const parsed = new URL (
329+ req . absoluteUrl ,
330+ self . apos . baseUrl
331+ ? undefined
332+ : `${ req . protocol } ://${ req . hostname } ${ port } `
333+ ) ;
334+ parsed . pathname = self . login ( ) ;
335+ parsed . search = '?' ;
336+ parsed . searchParams . append ( 'reset' , reset ) ;
337+ parsed . searchParams . append ( 'email' , user . email ) ;
338+ try {
339+ await self . email ( req , 'passwordResetEmail' , {
340+ user,
341+ url : parsed . toString ( ) ,
342+ site
343+ } , {
344+ to : user . email ,
345+ subject : req . t ( 'apostrophe:passwordResetRequest' , { site } )
346+ } ) ;
347+ } catch ( err ) {
348+ self . apos . util . error ( `Error while sending email to ${ user . email } ` , err ) ;
349+ }
333350 }
334- const parsed = new URL (
335- req . absoluteUrl ,
336- self . apos . baseUrl
337- ? undefined
338- : `${ req . protocol } ://${ req . hostname } ${ port } `
339- ) ;
340- parsed . pathname = self . login ( ) ;
341- parsed . search = '?' ;
342- parsed . searchParams . append ( 'reset' , reset ) ;
343- parsed . searchParams . append ( 'email' , user . email ) ;
344- try {
345- await self . email ( req , 'passwordResetEmail' , {
346- user,
347- url : parsed . toString ( ) ,
348- site
349- } , {
350- to : user . email ,
351- subject : req . t ( 'apostrophe:passwordResetRequest' , { site } )
352- } ) ;
353- } catch ( err ) {
354- self . apos . util . error ( `Error while sending email to ${ user . email } ` , err ) ;
351+ // Pad all paths to a constant minimum duration
352+ const elapsed = Date . now ( ) - startTime ;
353+ if ( elapsed < MIN_RESPONSE_TIME ) {
354+ await Promise . delay ( MIN_RESPONSE_TIME - elapsed ) ;
355355 }
356356 } ,
357-
358357 async reset ( req ) {
359358 const password = self . apos . launder . string ( req . body . password ) ;
360359 if ( ! password . length ) {
0 commit comments