satellite/console: fix account creation race condition
This change fixes an issue where multiple unverified users with the same email address could be created if registration requests were sent sufficiently close to one another. Resolves #6156 Change-Id: If8b1a145bcab842ace718119183de59947430463
This commit is contained in:
parent
b2780b028d
commit
7f9317aa48
@ -313,7 +313,9 @@ func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
|
||||
secret,
|
||||
)
|
||||
if err != nil {
|
||||
a.serveJSONError(ctx, w, err)
|
||||
if !console.ErrEmailUsed.Has(err) {
|
||||
a.serveJSONError(ctx, w, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -766,18 +766,6 @@ func (s *Service) CreateUser(ctx context.Context, user CreateUser, tokenSecret R
|
||||
return nil, ErrRegToken.Wrap(err)
|
||||
}
|
||||
|
||||
verified, unverified, err := s.store.Users().GetByEmailWithUnverified(ctx, user.Email)
|
||||
if err != nil {
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
if verified != nil {
|
||||
mon.Counter("create_user_duplicate_verified").Inc(1) //mon:locked
|
||||
return nil, ErrEmailUsed.New(emailUsedErrMsg)
|
||||
} else if len(unverified) != 0 {
|
||||
mon.Counter("create_user_duplicate_unverified").Inc(1) //mon:locked
|
||||
return nil, ErrEmailUsed.New(emailUsedErrMsg)
|
||||
}
|
||||
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), s.config.PasswordCost)
|
||||
if err != nil {
|
||||
return nil, Error.Wrap(err)
|
||||
@ -787,7 +775,7 @@ func (s *Service) CreateUser(ctx context.Context, user CreateUser, tokenSecret R
|
||||
err = s.store.WithTx(ctx, func(ctx context.Context, tx DBTx) error {
|
||||
userID, err := uuid.New()
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
return err
|
||||
}
|
||||
|
||||
newUser := &User{
|
||||
@ -825,13 +813,40 @@ func (s *Service) CreateUser(ctx context.Context, user CreateUser, tokenSecret R
|
||||
newUser,
|
||||
)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
return err
|
||||
}
|
||||
|
||||
verified, unverified, err := tx.Users().GetByEmailWithUnverified(ctx, user.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if verified != nil {
|
||||
err = tx.Users().Delete(ctx, u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mon.Counter("create_user_duplicate_verified").Inc(1) //mon:locked
|
||||
return ErrEmailUsed.New(emailUsedErrMsg)
|
||||
}
|
||||
|
||||
for _, other := range unverified {
|
||||
// We compare IDs because a parallel user creation transaction for the same
|
||||
// email could have created a record at the same time as ours.
|
||||
if other.CreatedAt.Before(u.CreatedAt) || other.ID.Less(u.ID) {
|
||||
err = tx.Users().Delete(ctx, u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mon.Counter("create_user_duplicate_unverified").Inc(1) //mon:locked
|
||||
return ErrEmailUsed.New(emailUsedErrMsg)
|
||||
}
|
||||
}
|
||||
|
||||
if registrationToken != nil {
|
||||
err = tx.RegistrationTokens().UpdateOwner(ctx, registrationToken.Secret, u.ID)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user