@@ -2,6 +2,7 @@ package api
2
2
3
3
import (
4
4
"net/http"
5
+ "regexp"
5
6
"strings"
6
7
"time"
7
8
@@ -23,7 +24,6 @@ import (
23
24
24
25
var (
25
26
EmailRateLimitExceeded error = errors .New ("email rate limit exceeded" )
26
- AddressNotAuthorized error = errors .New ("Destination email address not authorized" )
27
27
)
28
28
29
29
type GenerateLinkParams struct {
@@ -320,6 +320,8 @@ func (a *API) sendConfirmation(r *http.Request, tx *storage.Connection, u *model
320
320
u .ConfirmationToken = oldToken
321
321
if errors .Is (err , EmailRateLimitExceeded ) {
322
322
return tooManyRequestsError (ErrorCodeOverEmailSendRateLimit , EmailRateLimitExceeded .Error ())
323
+ } else if herr , ok := err .(* HTTPError ); ok {
324
+ return herr
323
325
}
324
326
return internalServerError ("Error sending confirmation email" ).WithInternalError (err )
325
327
}
@@ -351,6 +353,8 @@ func (a *API) sendInvite(r *http.Request, tx *storage.Connection, u *models.User
351
353
u .ConfirmationToken = oldToken
352
354
if errors .Is (err , EmailRateLimitExceeded ) {
353
355
return tooManyRequestsError (ErrorCodeOverEmailSendRateLimit , EmailRateLimitExceeded .Error ())
356
+ } else if herr , ok := err .(* HTTPError ); ok {
357
+ return herr
354
358
}
355
359
return internalServerError ("Error sending invite email" ).WithInternalError (err )
356
360
}
@@ -390,6 +394,8 @@ func (a *API) sendPasswordRecovery(r *http.Request, tx *storage.Connection, u *m
390
394
u .RecoveryToken = oldToken
391
395
if errors .Is (err , EmailRateLimitExceeded ) {
392
396
return tooManyRequestsError (ErrorCodeOverEmailSendRateLimit , EmailRateLimitExceeded .Error ())
397
+ } else if herr , ok := err .(* HTTPError ); ok {
398
+ return herr
393
399
}
394
400
return internalServerError ("Error sending recovery email" ).WithInternalError (err )
395
401
}
@@ -428,6 +434,8 @@ func (a *API) sendReauthenticationOtp(r *http.Request, tx *storage.Connection, u
428
434
u .ReauthenticationToken = oldToken
429
435
if errors .Is (err , EmailRateLimitExceeded ) {
430
436
return tooManyRequestsError (ErrorCodeOverEmailSendRateLimit , EmailRateLimitExceeded .Error ())
437
+ } else if herr , ok := err .(* HTTPError ); ok {
438
+ return herr
431
439
}
432
440
return internalServerError ("Error sending reauthentication email" ).WithInternalError (err )
433
441
}
@@ -467,6 +475,8 @@ func (a *API) sendMagicLink(r *http.Request, tx *storage.Connection, u *models.U
467
475
u .RecoveryToken = oldToken
468
476
if errors .Is (err , EmailRateLimitExceeded ) {
469
477
return tooManyRequestsError (ErrorCodeOverEmailSendRateLimit , EmailRateLimitExceeded .Error ())
478
+ } else if herr , ok := err .(* HTTPError ); ok {
479
+ return herr
470
480
}
471
481
return internalServerError ("Error sending magic link email" ).WithInternalError (err )
472
482
}
@@ -517,6 +527,8 @@ func (a *API) sendEmailChange(r *http.Request, tx *storage.Connection, u *models
517
527
if err := a .sendEmail (r , tx , u , mail .EmailChangeVerification , otpCurrent , otpNew , u .EmailChangeTokenNew ); err != nil {
518
528
if errors .Is (err , EmailRateLimitExceeded ) {
519
529
return tooManyRequestsError (ErrorCodeOverEmailSendRateLimit , EmailRateLimitExceeded .Error ())
530
+ } else if herr , ok := err .(* HTTPError ); ok {
531
+ return herr
520
532
}
521
533
return internalServerError ("Error sending email change email" ).WithInternalError (err )
522
534
}
@@ -569,13 +581,52 @@ func validateSentWithinFrequencyLimit(sentAt *time.Time, frequency time.Duration
569
581
return nil
570
582
}
571
583
584
+ var emailLabelPattern = regexp .MustCompile ("[+][^@]+@" )
585
+
586
+ func (a * API ) checkEmailAddressAuthorization (email string ) bool {
587
+ if len (a .config .External .Email .AuthorizedAddresses ) > 0 {
588
+ // allow labelled emails when authorization rules are in place
589
+ normalized := emailLabelPattern .ReplaceAllString (email , "@" )
590
+
591
+ for _ , authorizedAddress := range a .config .External .Email .AuthorizedAddresses {
592
+ if strings .EqualFold (normalized , authorizedAddress ) {
593
+ return true
594
+ }
595
+ }
596
+
597
+ return false
598
+ }
599
+
600
+ return true
601
+ }
602
+
572
603
func (a * API ) sendEmail (r * http.Request , tx * storage.Connection , u * models.User , emailActionType , otp , otpNew , tokenHashWithPrefix string ) error {
573
604
mailer := a .Mailer ()
574
605
ctx := r .Context ()
575
606
config := a .config
576
607
referrerURL := utilities .GetReferrer (r , config )
577
608
externalURL := getExternalHost (ctx )
578
609
610
+ if emailActionType != mail .EmailChangeVerification {
611
+ if u .GetEmail () != "" && ! a .checkEmailAddressAuthorization (u .GetEmail ()) {
612
+ return badRequestError (ErrorCodeEmailAddressNotAuthorized , "Email address %q cannot be used as it is not authorized" , u .GetEmail ())
613
+ }
614
+ } else {
615
+ // first check that the user can update their address to the
616
+ // new one in u.EmailChange
617
+ if u .EmailChange != "" && ! a .checkEmailAddressAuthorization (u .EmailChange ) {
618
+ return badRequestError (ErrorCodeEmailAddressNotAuthorized , "Email address %q cannot be used as it is not authorized" , u .EmailChange )
619
+ }
620
+
621
+ // if secure email change is enabled, check that the user
622
+ // account (which could have been created before the authorized
623
+ // address authorization restriction was enabled) can even
624
+ // receive the confirmation message to the existing address
625
+ if config .Mailer .SecureEmailChangeEnabled && u .GetEmail () != "" && ! a .checkEmailAddressAuthorization (u .GetEmail ()) {
626
+ return badRequestError (ErrorCodeEmailAddressNotAuthorized , "Email address %q cannot be used as it is not authorized" , u .GetEmail ())
627
+ }
628
+ }
629
+
579
630
// apply rate limiting before the email is sent out
580
631
if ok := a .limiterOpts .Email .Allow (); ! ok {
581
632
emailRateLimitCounter .Add (
0 commit comments