User: email verification completion (#85259)
* TempUser: Include InvitedById in TempUserDTO * Extract email verfication completion flow to service
This commit is contained in:
@@ -4,26 +4,36 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidCode = errutil.BadRequest("user.code.invalid", errutil.WithPublicMessage("Invalid verification code"))
|
||||
errExpiredCode = errutil.BadRequest("user.code.expired", errutil.WithPublicMessage("Verification code has expired"))
|
||||
)
|
||||
|
||||
var _ user.Verifier = (*Verifier)(nil)
|
||||
|
||||
func ProvideVerifier(us user.Service, ts tempuser.Service, ns notifications.Service) *Verifier {
|
||||
return &Verifier{us, ts, ns}
|
||||
func ProvideVerifier(cfg *setting.Cfg, us user.Service, ts tempuser.Service, ns notifications.Service) *Verifier {
|
||||
return &Verifier{cfg, us, ts, ns}
|
||||
}
|
||||
|
||||
type Verifier struct {
|
||||
us user.Service
|
||||
ts tempuser.Service
|
||||
ns notifications.Service
|
||||
cfg *setting.Cfg
|
||||
us user.Service
|
||||
ts tempuser.Service
|
||||
ns notifications.Service
|
||||
}
|
||||
|
||||
func (s *Verifier) VerifyEmail(ctx context.Context, cmd user.VerifyEmailCommand) error {
|
||||
func (s *Verifier) Start(ctx context.Context, cmd user.StartVerifyEmailCommand) error {
|
||||
usr, err := s.us.GetByLogin(ctx, &user.GetUserByLoginQuery{
|
||||
LoginOrEmail: cmd.Email,
|
||||
})
|
||||
@@ -80,3 +90,60 @@ func (s *Verifier) VerifyEmail(ctx context.Context, cmd user.VerifyEmailCommand)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Verifier) Complete(ctx context.Context, cmd user.CompleteEmailVerifyCommand) error {
|
||||
tmpUsr, err := s.ts.GetTempUserByCode(ctx, &tempuser.GetTempUserByCodeQuery{Code: cmd.Code})
|
||||
if err != nil {
|
||||
return errInvalidCode.Errorf("failed to verify code: %w", err)
|
||||
}
|
||||
|
||||
if tmpUsr.Status != tempuser.TmpUserEmailUpdateStarted {
|
||||
return errInvalidCode.Errorf("wrong status for verification code: %s", tmpUsr.Status)
|
||||
}
|
||||
|
||||
if !tmpUsr.EmailSent {
|
||||
return errInvalidCode.Errorf("email was not marked as sent")
|
||||
}
|
||||
|
||||
if tmpUsr.EmailSentOn.Add(s.cfg.VerificationEmailMaxLifetime).Before(time.Now()) {
|
||||
return errExpiredCode.Errorf("verification code has expired")
|
||||
}
|
||||
|
||||
usr, err := s.us.GetByID(ctx, &user.GetUserByIDQuery{ID: tmpUsr.InvitedByID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
verified := true
|
||||
update := &user.UpdateUserCommand{
|
||||
Email: tmpUsr.Email,
|
||||
UserID: tmpUsr.InvitedByID,
|
||||
EmailVerified: &verified,
|
||||
}
|
||||
switch tmpUsr.Name {
|
||||
case string(user.EmailUpdateAction):
|
||||
// User updated the email field
|
||||
if _, err := mail.ParseAddress(usr.Login); err == nil {
|
||||
// If username was also an email, we update it to keep it in sync with the email field
|
||||
update.Login = tmpUsr.Email
|
||||
}
|
||||
case string(user.LoginUpdateAction):
|
||||
// User updated the username field with a new email
|
||||
update.Login = tmpUsr.Email
|
||||
default:
|
||||
return errors.New("trying to update email on unknown field")
|
||||
}
|
||||
|
||||
if err := s.us.Update(ctx, update); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.ts.UpdateTempUserStatus(
|
||||
ctx,
|
||||
&tempuser.UpdateTempUserStatusCommand{Code: cmd.Code, Status: tempuser.TmpUserEmailUpdateCompleted},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user