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 }