summaryrefslogtreecommitdiffstats
path: root/services/user/email.go
diff options
context:
space:
mode:
Diffstat (limited to 'services/user/email.go')
-rw-r--r--services/user/email.go232
1 files changed, 232 insertions, 0 deletions
diff --git a/services/user/email.go b/services/user/email.go
new file mode 100644
index 0000000..e872526
--- /dev/null
+++ b/services/user/email.go
@@ -0,0 +1,232 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package user
+
+import (
+ "context"
+ "errors"
+ "strings"
+
+ "code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/mailer"
+)
+
+// AdminAddOrSetPrimaryEmailAddress is used by admins to add or set a user's primary email address
+func AdminAddOrSetPrimaryEmailAddress(ctx context.Context, u *user_model.User, emailStr string) error {
+ if strings.EqualFold(u.Email, emailStr) {
+ return nil
+ }
+
+ if err := user_model.ValidateEmailForAdmin(emailStr); err != nil {
+ return err
+ }
+
+ // Check if address exists already
+ email, err := user_model.GetEmailAddressByEmail(ctx, emailStr)
+ if err != nil && !errors.Is(err, util.ErrNotExist) {
+ return err
+ }
+ if email != nil && email.UID != u.ID {
+ return user_model.ErrEmailAlreadyUsed{Email: emailStr}
+ }
+
+ // Update old primary address
+ primary, err := user_model.GetPrimaryEmailAddressOfUser(ctx, u.ID)
+ if err != nil {
+ return err
+ }
+
+ primary.IsPrimary = false
+ if err := user_model.UpdateEmailAddress(ctx, primary); err != nil {
+ return err
+ }
+
+ // Insert new or update existing address
+ if email != nil {
+ email.IsPrimary = true
+ email.IsActivated = true
+ if err := user_model.UpdateEmailAddress(ctx, email); err != nil {
+ return err
+ }
+ } else {
+ email = &user_model.EmailAddress{
+ UID: u.ID,
+ Email: emailStr,
+ IsActivated: true,
+ IsPrimary: true,
+ }
+ if _, err := user_model.InsertEmailAddress(ctx, email); err != nil {
+ return err
+ }
+ }
+
+ u.Email = emailStr
+
+ return user_model.UpdateUserCols(ctx, u, "email")
+}
+
+func ReplacePrimaryEmailAddress(ctx context.Context, u *user_model.User, emailStr string) error {
+ if strings.EqualFold(u.Email, emailStr) {
+ return nil
+ }
+
+ if err := user_model.ValidateEmail(emailStr); err != nil {
+ return err
+ }
+
+ if !u.IsOrganization() {
+ // Check if address exists already
+ email, err := user_model.GetEmailAddressByEmail(ctx, emailStr)
+ if err != nil && !errors.Is(err, util.ErrNotExist) {
+ return err
+ }
+ if email != nil {
+ if email.IsPrimary && email.UID == u.ID {
+ return nil
+ }
+ return user_model.ErrEmailAlreadyUsed{Email: emailStr}
+ }
+
+ // Remove old primary address
+ primary, err := user_model.GetPrimaryEmailAddressOfUser(ctx, u.ID)
+ if err != nil {
+ return err
+ }
+ if _, err := db.DeleteByID[user_model.EmailAddress](ctx, primary.ID); err != nil {
+ return err
+ }
+
+ // Insert new primary address
+ email = &user_model.EmailAddress{
+ UID: u.ID,
+ Email: emailStr,
+ IsActivated: true,
+ IsPrimary: true,
+ }
+ if _, err := user_model.InsertEmailAddress(ctx, email); err != nil {
+ return err
+ }
+ }
+
+ u.Email = emailStr
+
+ return user_model.UpdateUserCols(ctx, u, "email")
+}
+
+func AddEmailAddresses(ctx context.Context, u *user_model.User, emails []string) error {
+ for _, emailStr := range emails {
+ if err := user_model.ValidateEmail(emailStr); err != nil {
+ return err
+ }
+
+ // Check if address exists already
+ email, err := user_model.GetEmailAddressByEmail(ctx, emailStr)
+ if err != nil && !errors.Is(err, util.ErrNotExist) {
+ return err
+ }
+ if email != nil {
+ return user_model.ErrEmailAlreadyUsed{Email: emailStr}
+ }
+
+ // Insert new address
+ email = &user_model.EmailAddress{
+ UID: u.ID,
+ Email: emailStr,
+ IsActivated: !setting.Service.RegisterEmailConfirm,
+ IsPrimary: false,
+ }
+ if _, err := user_model.InsertEmailAddress(ctx, email); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// ReplaceInactivePrimaryEmail replaces the primary email of a given user, even if the primary is not yet activated.
+func ReplaceInactivePrimaryEmail(ctx context.Context, oldEmail string, email *user_model.EmailAddress) error {
+ user := &user_model.User{}
+ has, err := db.GetEngine(ctx).ID(email.UID).Get(user)
+ if err != nil {
+ return err
+ } else if !has {
+ return user_model.ErrUserNotExist{
+ UID: email.UID,
+ }
+ }
+
+ err = AddEmailAddresses(ctx, user, []string{email.Email})
+ if err != nil {
+ return err
+ }
+
+ err = MakeEmailAddressPrimary(ctx, user, email, false)
+ if err != nil {
+ return err
+ }
+
+ return DeleteEmailAddresses(ctx, user, []string{oldEmail})
+}
+
+func DeleteEmailAddresses(ctx context.Context, u *user_model.User, emails []string) error {
+ for _, emailStr := range emails {
+ // Check if address exists
+ email, err := user_model.GetEmailAddressOfUser(ctx, emailStr, u.ID)
+ if err != nil {
+ return err
+ }
+ if email.IsPrimary {
+ return user_model.ErrPrimaryEmailCannotDelete{Email: emailStr}
+ }
+
+ // Remove address
+ if _, err := db.DeleteByID[user_model.EmailAddress](ctx, email.ID); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func MakeEmailAddressPrimary(ctx context.Context, u *user_model.User, newPrimaryEmail *user_model.EmailAddress, notify bool) error {
+ ctx, committer, err := db.TxContext(ctx)
+ if err != nil {
+ return err
+ }
+ defer committer.Close()
+ sess := db.GetEngine(ctx)
+
+ oldPrimaryEmail := u.Email
+
+ // 1. Update user table
+ u.Email = newPrimaryEmail.Email
+ if _, err = sess.ID(u.ID).Cols("email").Update(u); err != nil {
+ return err
+ }
+
+ // 2. Update old primary email
+ if _, err = sess.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&user_model.EmailAddress{
+ IsPrimary: false,
+ }); err != nil {
+ return err
+ }
+
+ // 3. update new primary email
+ newPrimaryEmail.IsPrimary = true
+ if _, err = sess.ID(newPrimaryEmail.ID).Cols("is_primary").Update(newPrimaryEmail); err != nil {
+ return err
+ }
+
+ if err := committer.Commit(); err != nil {
+ return err
+ }
+
+ if notify {
+ return mailer.SendPrimaryMailChange(u, oldPrimaryEmail)
+ }
+ return nil
+}