User: email verification completion (#85259)

* TempUser: Include InvitedById in TempUserDTO

* Extract email verfication completion flow to service
This commit is contained in:
Karl Persson
2024-03-28 16:05:33 +01:00
committed by GitHub
parent 4a3140a0aa
commit 73e426b081
11 changed files with 275 additions and 108 deletions
+73 -6
View File
@@ -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
}