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