diff options
author | 2024-06-06 15:43:25 +0200 | |
---|---|---|
committer | 2024-06-06 14:43:25 +0100 | |
commit | bcda048eab799284fc46d74706334bf9ef76dc83 (patch) | |
tree | c4595fe5e6e6fd570d59cee7095a336f2e884344 /internal/processing/user/email.go | |
parent | drop date (#2969) (diff) | |
download | gotosocial-bcda048eab799284fc46d74706334bf9ef76dc83.tar.xz |
[feature] Self-serve email change for users (#2957)
* [feature] Email change
* frontend stuff for changing email
* docs
* tests etc
* differentiate more clearly between local user+account and account
* populate user
Diffstat (limited to 'internal/processing/user/email.go')
-rw-r--r-- | internal/processing/user/email.go | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/internal/processing/user/email.go b/internal/processing/user/email.go index 2b27c6c92..ea9dbb64c 100644 --- a/internal/processing/user/email.go +++ b/internal/processing/user/email.go @@ -23,11 +23,92 @@ import ( "fmt" "time" + "github.com/superseriousbusiness/gotosocial/internal/ap" + apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/messages" + "github.com/superseriousbusiness/gotosocial/internal/validate" + "golang.org/x/crypto/bcrypt" ) +// EmailChange processes an email address change request for the given user. +func (p *Processor) EmailChange( + ctx context.Context, + user *gtsmodel.User, + password string, + newEmail string, +) (*apimodel.User, gtserror.WithCode) { + // Ensure provided password is correct. + if err := bcrypt.CompareHashAndPassword([]byte(user.EncryptedPassword), []byte(password)); err != nil { + err := gtserror.Newf("%w", err) + return nil, gtserror.NewErrorUnauthorized(err, "password was incorrect") + } + + // Ensure new email address is valid. + if err := validate.Email(newEmail); err != nil { + return nil, gtserror.NewErrorBadRequest(err, err.Error()) + } + + // Ensure new email address is different + // from current email address. + if newEmail == user.Email { + const help = "new email address cannot be the same as current email address" + err := gtserror.New(help) + return nil, gtserror.NewErrorBadRequest(err, help) + } + + if newEmail == user.UnconfirmedEmail { + const help = "you already have an email change request pending for given email address" + err := gtserror.New(help) + return nil, gtserror.NewErrorBadRequest(err, help) + } + + // Ensure this address isn't already used by another account. + emailAvailable, err := p.state.DB.IsEmailAvailable(ctx, newEmail) + if err != nil { + err := gtserror.Newf("db error checking email availability: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + if !emailAvailable { + const help = "new email address is already in use on this instance" + err := gtserror.New(help) + return nil, gtserror.NewErrorConflict(err, help) + } + + // Set new email address on user. + user.UnconfirmedEmail = newEmail + if err := p.state.DB.UpdateUser( + ctx, user, + "unconfirmed_email", + ); err != nil { + err := gtserror.Newf("db error updating user: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + // Ensure user populated (we need account). + if err := p.state.DB.PopulateUser(ctx, user); err != nil { + err := gtserror.Newf("db error populating user: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + // Add email sending job to the queue. + p.state.Workers.Client.Queue.Push(&messages.FromClientAPI{ + // Use ap.ObjectProfile here to + // distinguish this message (user model) + // from ap.ActorPerson (account model). + APObjectType: ap.ObjectProfile, + APActivityType: ap.ActivityUpdate, + GTSModel: user, + Origin: user.Account, + Target: user.Account, + }) + + return p.converter.UserToAPIUser(ctx, user), nil +} + // EmailGetUserForConfirmToken retrieves the user (with account) from // the database for the given "confirm your email" token string. func (p *Processor) EmailGetUserForConfirmToken(ctx context.Context, token string) (*gtsmodel.User, gtserror.WithCode) { |