You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

account.go 11KB

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