@@ -288,6 +288,184 @@ describe('Email Verification Token Expiration: ', () => {
288288 } ) ;
289289 } ) ;
290290
291+ it ( 'can conditionally send emails' , async ( ) => {
292+ let sendEmailOptions ;
293+ const emailAdapter = {
294+ sendVerificationEmail : options => {
295+ sendEmailOptions = options ;
296+ } ,
297+ sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
298+ sendMail : ( ) => { } ,
299+ } ;
300+ const verifyUserEmails = {
301+ method ( req ) {
302+ expect ( Object . keys ( req ) ) . toEqual ( [ 'original' , 'object' , 'master' , 'ip' ] ) ;
303+ return false ;
304+ } ,
305+ } ;
306+ const verifySpy = spyOn ( verifyUserEmails , 'method' ) . and . callThrough ( ) ;
307+ await reconfigureServer ( {
308+ appName : 'emailVerifyToken' ,
309+ verifyUserEmails : verifyUserEmails . method ,
310+ emailAdapter : emailAdapter ,
311+ emailVerifyTokenValidityDuration : 5 , // 5 seconds
312+ publicServerURL : 'http://localhost:8378/1' ,
313+ } ) ;
314+ const beforeSave = {
315+ method ( req ) {
316+ req . object . set ( 'emailVerified' , true ) ;
317+ } ,
318+ } ;
319+ const saveSpy = spyOn ( beforeSave , 'method' ) . and . callThrough ( ) ;
320+ const emailSpy = spyOn ( emailAdapter , 'sendVerificationEmail' ) . and . callThrough ( ) ;
321+ Parse . Cloud . beforeSave ( Parse . User , beforeSave . method ) ;
322+ const user = new Parse . User ( ) ;
323+ user . setUsername ( 'sets_email_verify_token_expires_at' ) ;
324+ user . setPassword ( 'expiringToken' ) ;
325+ user . set ( 'email' , 'user@example.com' ) ;
326+ await user . signUp ( ) ;
327+
328+ const config = Config . get ( 'test' ) ;
329+ const results = await config . database . find (
330+ '_User' ,
331+ {
332+ username : 'sets_email_verify_token_expires_at' ,
333+ } ,
334+ { } ,
335+ Auth . maintenance ( config )
336+ ) ;
337+
338+ expect ( results . length ) . toBe ( 1 ) ;
339+ const user_data = results [ 0 ] ;
340+ expect ( typeof user_data ) . toBe ( 'object' ) ;
341+ expect ( user_data . emailVerified ) . toEqual ( true ) ;
342+ expect ( user_data . _email_verify_token ) . toBeUndefined ( ) ;
343+ expect ( user_data . _email_verify_token_expires_at ) . toBeUndefined ( ) ;
344+ expect ( emailSpy ) . not . toHaveBeenCalled ( ) ;
345+ expect ( saveSpy ) . toHaveBeenCalled ( ) ;
346+ expect ( sendEmailOptions ) . toBeUndefined ( ) ;
347+ expect ( verifySpy ) . toHaveBeenCalled ( ) ;
348+ } ) ;
349+
350+ it ( 'can conditionally send emails and allow conditional login' , async ( ) => {
351+ let sendEmailOptions ;
352+ const emailAdapter = {
353+ sendVerificationEmail : options => {
354+ sendEmailOptions = options ;
355+ } ,
356+ sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
357+ sendMail : ( ) => { } ,
358+ } ;
359+ const verifyUserEmails = {
360+ method ( req ) {
361+ expect ( Object . keys ( req ) ) . toEqual ( [ 'original' , 'object' , 'master' , 'ip' ] ) ;
362+ if ( req . object . get ( 'username' ) === 'no_email' ) {
363+ return false ;
364+ }
365+ return true ;
366+ } ,
367+ } ;
368+ const verifySpy = spyOn ( verifyUserEmails , 'method' ) . and . callThrough ( ) ;
369+ await reconfigureServer ( {
370+ appName : 'emailVerifyToken' ,
371+ verifyUserEmails : verifyUserEmails . method ,
372+ preventLoginWithUnverifiedEmail : verifyUserEmails . method ,
373+ emailAdapter : emailAdapter ,
374+ emailVerifyTokenValidityDuration : 5 , // 5 seconds
375+ publicServerURL : 'http://localhost:8378/1' ,
376+ } ) ;
377+ const user = new Parse . User ( ) ;
378+ user . setUsername ( 'no_email' ) ;
379+ user . setPassword ( 'expiringToken' ) ;
380+ user . set ( 'email' , 'user@example.com' ) ;
381+ await user . signUp ( ) ;
382+ expect ( sendEmailOptions ) . toBeUndefined ( ) ;
383+ expect ( user . getSessionToken ( ) ) . toBeDefined ( ) ;
384+ expect ( verifySpy ) . toHaveBeenCalledTimes ( 2 ) ;
385+ const user2 = new Parse . User ( ) ;
386+ user2 . setUsername ( 'email' ) ;
387+ user2 . setPassword ( 'expiringToken' ) ;
388+ user2 . set ( 'email' , 'user2@example.com' ) ;
389+ await user2 . signUp ( ) ;
390+ expect ( user2 . getSessionToken ( ) ) . toBeUndefined ( ) ;
391+ expect ( sendEmailOptions ) . toBeDefined ( ) ;
392+ expect ( verifySpy ) . toHaveBeenCalledTimes ( 4 ) ;
393+ } ) ;
394+
395+ it ( 'can conditionally send user email verification' , async ( ) => {
396+ const emailAdapter = {
397+ sendVerificationEmail : ( ) => { } ,
398+ sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
399+ sendMail : ( ) => { } ,
400+ } ;
401+ const sendVerificationEmail = {
402+ method ( req ) {
403+ expect ( req . user ) . toBeDefined ( ) ;
404+ expect ( req . master ) . toBeDefined ( ) ;
405+ return false ;
406+ } ,
407+ } ;
408+ const sendSpy = spyOn ( sendVerificationEmail , 'method' ) . and . callThrough ( ) ;
409+ await reconfigureServer ( {
410+ appName : 'emailVerifyToken' ,
411+ verifyUserEmails : true ,
412+ emailAdapter : emailAdapter ,
413+ emailVerifyTokenValidityDuration : 5 , // 5 seconds
414+ publicServerURL : 'http://localhost:8378/1' ,
415+ sendUserEmailVerification : sendVerificationEmail . method ,
416+ } ) ;
417+ const emailSpy = spyOn ( emailAdapter , 'sendVerificationEmail' ) . and . callThrough ( ) ;
418+ const newUser = new Parse . User ( ) ;
419+ newUser . setUsername ( 'unsets_email_verify_token_expires_at' ) ;
420+ newUser . setPassword ( 'expiringToken' ) ;
421+ newUser . set ( 'email' , 'user@example.com' ) ;
422+ await newUser . signUp ( ) ;
423+ await Parse . User . requestEmailVerification ( 'user@example.com' ) ;
424+ expect ( sendSpy ) . toHaveBeenCalledTimes ( 2 ) ;
425+ expect ( emailSpy ) . toHaveBeenCalledTimes ( 0 ) ;
426+ } ) ;
427+
428+ it ( 'beforeSave options do not change existing behaviour' , async ( ) => {
429+ let sendEmailOptions ;
430+ const emailAdapter = {
431+ sendVerificationEmail : options => {
432+ sendEmailOptions = options ;
433+ } ,
434+ sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
435+ sendMail : ( ) => { } ,
436+ } ;
437+ await reconfigureServer ( {
438+ appName : 'emailVerifyToken' ,
439+ verifyUserEmails : true ,
440+ emailAdapter : emailAdapter ,
441+ emailVerifyTokenValidityDuration : 5 , // 5 seconds
442+ publicServerURL : 'http://localhost:8378/1' ,
443+ } ) ;
444+ const emailSpy = spyOn ( emailAdapter , 'sendVerificationEmail' ) . and . callThrough ( ) ;
445+ const newUser = new Parse . User ( ) ;
446+ newUser . setUsername ( 'unsets_email_verify_token_expires_at' ) ;
447+ newUser . setPassword ( 'expiringToken' ) ;
448+ newUser . set ( 'email' , 'user@parse.com' ) ;
449+ await newUser . signUp ( ) ;
450+ const response = await request ( {
451+ url : sendEmailOptions . link ,
452+ followRedirects : false ,
453+ } ) ;
454+ expect ( response . status ) . toEqual ( 302 ) ;
455+ const config = Config . get ( 'test' ) ;
456+ const results = await config . database . find ( '_User' , {
457+ username : 'unsets_email_verify_token_expires_at' ,
458+ } ) ;
459+
460+ expect ( results . length ) . toBe ( 1 ) ;
461+ const user = results [ 0 ] ;
462+ expect ( typeof user ) . toBe ( 'object' ) ;
463+ expect ( user . emailVerified ) . toEqual ( true ) ;
464+ expect ( typeof user . _email_verify_token ) . toBe ( 'undefined' ) ;
465+ expect ( typeof user . _email_verify_token_expires_at ) . toBe ( 'undefined' ) ;
466+ expect ( emailSpy ) . toHaveBeenCalled ( ) ;
467+ } ) ;
468+
291469 it ( 'unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful' , done => {
292470 const user = new Parse . User ( ) ;
293471 let sendEmailOptions ;
0 commit comments