code.gitea.io/gitea@v1.21.7/routers/web/user/setting/account.go (about)

     1  // Copyright 2014 The Gogs Authors. All rights reserved.
     2  // Copyright 2018 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package setting
     6  
     7  import (
     8  	"errors"
     9  	"net/http"
    10  	"time"
    11  
    12  	"code.gitea.io/gitea/models"
    13  	user_model "code.gitea.io/gitea/models/user"
    14  	"code.gitea.io/gitea/modules/auth/password"
    15  	"code.gitea.io/gitea/modules/base"
    16  	"code.gitea.io/gitea/modules/context"
    17  	"code.gitea.io/gitea/modules/log"
    18  	"code.gitea.io/gitea/modules/setting"
    19  	"code.gitea.io/gitea/modules/timeutil"
    20  	"code.gitea.io/gitea/modules/web"
    21  	"code.gitea.io/gitea/services/auth"
    22  	"code.gitea.io/gitea/services/forms"
    23  	"code.gitea.io/gitea/services/mailer"
    24  	"code.gitea.io/gitea/services/user"
    25  )
    26  
    27  const (
    28  	tplSettingsAccount base.TplName = "user/settings/account"
    29  )
    30  
    31  // Account renders change user's password, user's email and user suicide page
    32  func Account(ctx *context.Context) {
    33  	ctx.Data["Title"] = ctx.Tr("settings.account")
    34  	ctx.Data["PageIsSettingsAccount"] = true
    35  	ctx.Data["Email"] = ctx.Doer.Email
    36  	ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail
    37  
    38  	loadAccountData(ctx)
    39  
    40  	ctx.HTML(http.StatusOK, tplSettingsAccount)
    41  }
    42  
    43  // AccountPost response for change user's password
    44  func AccountPost(ctx *context.Context) {
    45  	form := web.GetForm(ctx).(*forms.ChangePasswordForm)
    46  	ctx.Data["Title"] = ctx.Tr("settings")
    47  	ctx.Data["PageIsSettingsAccount"] = true
    48  
    49  	if ctx.HasError() {
    50  		loadAccountData(ctx)
    51  
    52  		ctx.HTML(http.StatusOK, tplSettingsAccount)
    53  		return
    54  	}
    55  
    56  	if len(form.Password) < setting.MinPasswordLength {
    57  		ctx.Flash.Error(ctx.Tr("auth.password_too_short", setting.MinPasswordLength))
    58  	} else if ctx.Doer.IsPasswordSet() && !ctx.Doer.ValidatePassword(form.OldPassword) {
    59  		ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
    60  	} else if form.Password != form.Retype {
    61  		ctx.Flash.Error(ctx.Tr("form.password_not_match"))
    62  	} else if !password.IsComplexEnough(form.Password) {
    63  		ctx.Flash.Error(password.BuildComplexityError(ctx.Locale))
    64  	} else if pwned, err := password.IsPwned(ctx, form.Password); pwned || err != nil {
    65  		errMsg := ctx.Tr("auth.password_pwned")
    66  		if err != nil {
    67  			log.Error(err.Error())
    68  			errMsg = ctx.Tr("auth.password_pwned_err")
    69  		}
    70  		ctx.Flash.Error(errMsg)
    71  	} else {
    72  		var err error
    73  		if err = ctx.Doer.SetPassword(form.Password); err != nil {
    74  			ctx.ServerError("UpdateUser", err)
    75  			return
    76  		}
    77  		if err := user_model.UpdateUserCols(ctx, ctx.Doer, "salt", "passwd_hash_algo", "passwd"); err != nil {
    78  			ctx.ServerError("UpdateUser", err)
    79  			return
    80  		}
    81  		log.Trace("User password updated: %s", ctx.Doer.Name)
    82  		ctx.Flash.Success(ctx.Tr("settings.change_password_success"))
    83  	}
    84  
    85  	ctx.Redirect(setting.AppSubURL + "/user/settings/account")
    86  }
    87  
    88  // EmailPost response for change user's email
    89  func EmailPost(ctx *context.Context) {
    90  	form := web.GetForm(ctx).(*forms.AddEmailForm)
    91  	ctx.Data["Title"] = ctx.Tr("settings")
    92  	ctx.Data["PageIsSettingsAccount"] = true
    93  
    94  	// Make emailaddress primary.
    95  	if ctx.FormString("_method") == "PRIMARY" {
    96  		if err := user_model.MakeEmailPrimary(ctx, &user_model.EmailAddress{ID: ctx.FormInt64("id")}); err != nil {
    97  			ctx.ServerError("MakeEmailPrimary", err)
    98  			return
    99  		}
   100  
   101  		log.Trace("Email made primary: %s", ctx.Doer.Name)
   102  		ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   103  		return
   104  	}
   105  	// Send activation Email
   106  	if ctx.FormString("_method") == "SENDACTIVATION" {
   107  		var address string
   108  		if setting.CacheService.Enabled && ctx.Cache.IsExist("MailResendLimit_"+ctx.Doer.LowerName) {
   109  			log.Error("Send activation: activation still pending")
   110  			ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   111  			return
   112  		}
   113  
   114  		id := ctx.FormInt64("id")
   115  		email, err := user_model.GetEmailAddressByID(ctx, ctx.Doer.ID, id)
   116  		if err != nil {
   117  			log.Error("GetEmailAddressByID(%d,%d) error: %v", ctx.Doer.ID, id, err)
   118  			ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   119  			return
   120  		}
   121  		if email == nil {
   122  			log.Warn("Send activation failed: EmailAddress[%d] not found for user: %-v", id, ctx.Doer)
   123  			ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   124  			return
   125  		}
   126  		if email.IsActivated {
   127  			log.Debug("Send activation failed: email %s is already activated for user: %-v", email.Email, ctx.Doer)
   128  			ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   129  			return
   130  		}
   131  		if email.IsPrimary {
   132  			if ctx.Doer.IsActive && !setting.Service.RegisterEmailConfirm {
   133  				log.Debug("Send activation failed: email %s is already activated for user: %-v", email.Email, ctx.Doer)
   134  				ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   135  				return
   136  			}
   137  			// Only fired when the primary email is inactive (Wrong state)
   138  			mailer.SendActivateAccountMail(ctx.Locale, ctx.Doer)
   139  		} else {
   140  			mailer.SendActivateEmailMail(ctx.Doer, email)
   141  		}
   142  		address = email.Email
   143  
   144  		if setting.CacheService.Enabled {
   145  			if err := ctx.Cache.Put("MailResendLimit_"+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil {
   146  				log.Error("Set cache(MailResendLimit) fail: %v", err)
   147  			}
   148  		}
   149  		ctx.Flash.Info(ctx.Tr("settings.add_email_confirmation_sent", address, timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)))
   150  		ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   151  		return
   152  	}
   153  	// Set Email Notification Preference
   154  	if ctx.FormString("_method") == "NOTIFICATION" {
   155  		preference := ctx.FormString("preference")
   156  		if !(preference == user_model.EmailNotificationsEnabled ||
   157  			preference == user_model.EmailNotificationsOnMention ||
   158  			preference == user_model.EmailNotificationsDisabled ||
   159  			preference == user_model.EmailNotificationsAndYourOwn) {
   160  			log.Error("Email notifications preference change returned unrecognized option %s: %s", preference, ctx.Doer.Name)
   161  			ctx.ServerError("SetEmailPreference", errors.New("option unrecognized"))
   162  			return
   163  		}
   164  		if err := user_model.SetEmailNotifications(ctx, ctx.Doer, preference); err != nil {
   165  			log.Error("Set Email Notifications failed: %v", err)
   166  			ctx.ServerError("SetEmailNotifications", err)
   167  			return
   168  		}
   169  		log.Trace("Email notifications preference made %s: %s", preference, ctx.Doer.Name)
   170  		ctx.Flash.Success(ctx.Tr("settings.email_preference_set_success"))
   171  		ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   172  		return
   173  	}
   174  
   175  	if ctx.HasError() {
   176  		loadAccountData(ctx)
   177  
   178  		ctx.HTML(http.StatusOK, tplSettingsAccount)
   179  		return
   180  	}
   181  
   182  	email := &user_model.EmailAddress{
   183  		UID:         ctx.Doer.ID,
   184  		Email:       form.Email,
   185  		IsActivated: !setting.Service.RegisterEmailConfirm,
   186  	}
   187  	if err := user_model.AddEmailAddress(ctx, email); err != nil {
   188  		if user_model.IsErrEmailAlreadyUsed(err) {
   189  			loadAccountData(ctx)
   190  
   191  			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form)
   192  			return
   193  		} else if user_model.IsErrEmailCharIsNotSupported(err) ||
   194  			user_model.IsErrEmailInvalid(err) {
   195  			loadAccountData(ctx)
   196  
   197  			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplSettingsAccount, &form)
   198  			return
   199  		}
   200  		ctx.ServerError("AddEmailAddress", err)
   201  		return
   202  	}
   203  
   204  	// Send confirmation email
   205  	if setting.Service.RegisterEmailConfirm {
   206  		mailer.SendActivateEmailMail(ctx.Doer, email)
   207  		if setting.CacheService.Enabled {
   208  			if err := ctx.Cache.Put("MailResendLimit_"+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil {
   209  				log.Error("Set cache(MailResendLimit) fail: %v", err)
   210  			}
   211  		}
   212  		ctx.Flash.Info(ctx.Tr("settings.add_email_confirmation_sent", email.Email, timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)))
   213  	} else {
   214  		ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
   215  	}
   216  
   217  	log.Trace("Email address added: %s", email.Email)
   218  	ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   219  }
   220  
   221  // DeleteEmail response for delete user's email
   222  func DeleteEmail(ctx *context.Context) {
   223  	if err := user_model.DeleteEmailAddress(ctx, &user_model.EmailAddress{ID: ctx.FormInt64("id"), UID: ctx.Doer.ID}); err != nil {
   224  		ctx.ServerError("DeleteEmail", err)
   225  		return
   226  	}
   227  	log.Trace("Email address deleted: %s", ctx.Doer.Name)
   228  
   229  	ctx.Flash.Success(ctx.Tr("settings.email_deletion_success"))
   230  	ctx.JSONRedirect(setting.AppSubURL + "/user/settings/account")
   231  }
   232  
   233  // DeleteAccount render user suicide page and response for delete user himself
   234  func DeleteAccount(ctx *context.Context) {
   235  	ctx.Data["Title"] = ctx.Tr("settings")
   236  	ctx.Data["PageIsSettingsAccount"] = true
   237  
   238  	if _, _, err := auth.UserSignIn(ctx, ctx.Doer.Name, ctx.FormString("password")); err != nil {
   239  		if user_model.IsErrUserNotExist(err) {
   240  			loadAccountData(ctx)
   241  
   242  			ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsAccount, nil)
   243  		} else {
   244  			ctx.ServerError("UserSignIn", err)
   245  		}
   246  		return
   247  	}
   248  
   249  	// admin should not delete themself
   250  	if ctx.Doer.IsAdmin {
   251  		ctx.Flash.Error(ctx.Tr("form.admin_cannot_delete_self"))
   252  		ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   253  		return
   254  	}
   255  
   256  	if err := user.DeleteUser(ctx, ctx.Doer, false); err != nil {
   257  		switch {
   258  		case models.IsErrUserOwnRepos(err):
   259  			ctx.Flash.Error(ctx.Tr("form.still_own_repo"))
   260  			ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   261  		case models.IsErrUserHasOrgs(err):
   262  			ctx.Flash.Error(ctx.Tr("form.still_has_org"))
   263  			ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   264  		case models.IsErrUserOwnPackages(err):
   265  			ctx.Flash.Error(ctx.Tr("form.still_own_packages"))
   266  			ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   267  		case models.IsErrDeleteLastAdminUser(err):
   268  			ctx.Flash.Error(ctx.Tr("auth.last_admin"))
   269  			ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   270  		default:
   271  			ctx.ServerError("DeleteUser", err)
   272  		}
   273  	} else {
   274  		log.Trace("Account deleted: %s", ctx.Doer.Name)
   275  		ctx.Redirect(setting.AppSubURL + "/")
   276  	}
   277  }
   278  
   279  func loadAccountData(ctx *context.Context) {
   280  	emlist, err := user_model.GetEmailAddresses(ctx, ctx.Doer.ID)
   281  	if err != nil {
   282  		ctx.ServerError("GetEmailAddresses", err)
   283  		return
   284  	}
   285  	type UserEmail struct {
   286  		user_model.EmailAddress
   287  		CanBePrimary bool
   288  	}
   289  	pendingActivation := setting.CacheService.Enabled && ctx.Cache.IsExist("MailResendLimit_"+ctx.Doer.LowerName)
   290  	emails := make([]*UserEmail, len(emlist))
   291  	for i, em := range emlist {
   292  		var email UserEmail
   293  		email.EmailAddress = *em
   294  		email.CanBePrimary = em.IsActivated
   295  		emails[i] = &email
   296  	}
   297  	ctx.Data["Emails"] = emails
   298  	ctx.Data["EmailNotificationsPreference"] = ctx.Doer.EmailNotifications()
   299  	ctx.Data["ActivationsPending"] = pendingActivation
   300  	ctx.Data["CanAddEmails"] = !pendingActivation || !setting.Service.RegisterEmailConfirm
   301  
   302  	if setting.Service.UserDeleteWithCommentsMaxTime != 0 {
   303  		ctx.Data["UserDeleteWithCommentsMaxTime"] = setting.Service.UserDeleteWithCommentsMaxTime.String()
   304  		ctx.Data["UserDeleteWithComments"] = ctx.Doer.CreatedUnix.AsTime().Add(setting.Service.UserDeleteWithCommentsMaxTime).After(time.Now())
   305  	}
   306  }