Fork to maintain patches against the official gitea for https://code.ceondo.com https://github.com/go-gitea/gitea

setting.go 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package user
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "io/ioutil"
  10. "strings"
  11. "github.com/Unknwon/com"
  12. "github.com/pquerna/otp"
  13. "github.com/pquerna/otp/totp"
  14. "encoding/base64"
  15. "html/template"
  16. "image/png"
  17. "code.gitea.io/gitea/models"
  18. "code.gitea.io/gitea/modules/auth"
  19. "code.gitea.io/gitea/modules/base"
  20. "code.gitea.io/gitea/modules/context"
  21. "code.gitea.io/gitea/modules/log"
  22. "code.gitea.io/gitea/modules/setting"
  23. )
  24. const (
  25. tplSettingsProfile base.TplName = "user/settings/profile"
  26. tplSettingsAvatar base.TplName = "user/settings/avatar"
  27. tplSettingsEmails base.TplName = "user/settings/email"
  28. tplSettingsKeys base.TplName = "user/settings/keys"
  29. tplSettingsSocial base.TplName = "user/settings/social"
  30. tplSettingsApplications base.TplName = "user/settings/applications"
  31. tplSettingsTwofa base.TplName = "user/settings/twofa"
  32. tplSettingsTwofaEnroll base.TplName = "user/settings/twofa_enroll"
  33. tplSettingsAccountLink base.TplName = "user/settings/account_link"
  34. tplSettingsOrganization base.TplName = "user/settings/organization"
  35. tplSettingsRepositories base.TplName = "user/settings/repos"
  36. tplSettingsDelete base.TplName = "user/settings/delete"
  37. tplSettingsSecurity base.TplName = "user/settings/security"
  38. )
  39. // Settings render user's profile page
  40. func Settings(ctx *context.Context) {
  41. ctx.Data["Title"] = ctx.Tr("settings")
  42. ctx.Data["PageIsSettingsProfile"] = true
  43. ctx.HTML(200, tplSettingsProfile)
  44. }
  45. func handleUsernameChange(ctx *context.Context, newName string) {
  46. // Non-local users are not allowed to change their username.
  47. if len(newName) == 0 || !ctx.User.IsLocal() {
  48. return
  49. }
  50. // Check if user name has been changed
  51. if ctx.User.LowerName != strings.ToLower(newName) {
  52. if err := models.ChangeUserName(ctx.User, newName); err != nil {
  53. switch {
  54. case models.IsErrUserAlreadyExist(err):
  55. ctx.Flash.Error(ctx.Tr("newName_been_taken"))
  56. ctx.Redirect(setting.AppSubURL + "/user/settings")
  57. case models.IsErrEmailAlreadyUsed(err):
  58. ctx.Flash.Error(ctx.Tr("form.email_been_used"))
  59. ctx.Redirect(setting.AppSubURL + "/user/settings")
  60. case models.IsErrNameReserved(err):
  61. ctx.Flash.Error(ctx.Tr("user.newName_reserved"))
  62. ctx.Redirect(setting.AppSubURL + "/user/settings")
  63. case models.IsErrNamePatternNotAllowed(err):
  64. ctx.Flash.Error(ctx.Tr("user.newName_pattern_not_allowed"))
  65. ctx.Redirect(setting.AppSubURL + "/user/settings")
  66. default:
  67. ctx.Handle(500, "ChangeUserName", err)
  68. }
  69. return
  70. }
  71. log.Trace("User name changed: %s -> %s", ctx.User.Name, newName)
  72. }
  73. // In case it's just a case change
  74. ctx.User.Name = newName
  75. ctx.User.LowerName = strings.ToLower(newName)
  76. }
  77. // SettingsPost response for change user's profile
  78. func SettingsPost(ctx *context.Context, form auth.UpdateProfileForm) {
  79. ctx.Data["Title"] = ctx.Tr("settings")
  80. ctx.Data["PageIsSettingsProfile"] = true
  81. if ctx.HasError() {
  82. ctx.HTML(200, tplSettingsProfile)
  83. return
  84. }
  85. handleUsernameChange(ctx, form.Name)
  86. if ctx.Written() {
  87. return
  88. }
  89. ctx.User.FullName = form.FullName
  90. ctx.User.Email = form.Email
  91. ctx.User.KeepEmailPrivate = form.KeepEmailPrivate
  92. ctx.User.Website = form.Website
  93. ctx.User.Location = form.Location
  94. if err := models.UpdateUserSetting(ctx.User); err != nil {
  95. if _, ok := err.(models.ErrEmailAlreadyUsed); ok {
  96. ctx.Flash.Error(ctx.Tr("form.email_been_used"))
  97. ctx.Redirect(setting.AppSubURL + "/user/settings")
  98. return
  99. }
  100. ctx.Handle(500, "UpdateUser", err)
  101. return
  102. }
  103. log.Trace("User settings updated: %s", ctx.User.Name)
  104. ctx.Flash.Success(ctx.Tr("settings.update_profile_success"))
  105. ctx.Redirect(setting.AppSubURL + "/user/settings")
  106. }
  107. // UpdateAvatarSetting update user's avatar
  108. // FIXME: limit size.
  109. func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *models.User) error {
  110. ctxUser.UseCustomAvatar = form.Source == auth.AvatarLocal
  111. if len(form.Gravatar) > 0 {
  112. ctxUser.Avatar = base.EncodeMD5(form.Gravatar)
  113. ctxUser.AvatarEmail = form.Gravatar
  114. }
  115. if form.Avatar != nil {
  116. fr, err := form.Avatar.Open()
  117. if err != nil {
  118. return fmt.Errorf("Avatar.Open: %v", err)
  119. }
  120. defer fr.Close()
  121. data, err := ioutil.ReadAll(fr)
  122. if err != nil {
  123. return fmt.Errorf("ioutil.ReadAll: %v", err)
  124. }
  125. if !base.IsImageFile(data) {
  126. return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image"))
  127. }
  128. if err = ctxUser.UploadAvatar(data); err != nil {
  129. return fmt.Errorf("UploadAvatar: %v", err)
  130. }
  131. } else {
  132. // No avatar is uploaded but setting has been changed to enable,
  133. // generate a random one when needed.
  134. if ctxUser.UseCustomAvatar && !com.IsFile(ctxUser.CustomAvatarPath()) {
  135. if err := ctxUser.GenerateRandomAvatar(); err != nil {
  136. log.Error(4, "GenerateRandomAvatar[%d]: %v", ctxUser.ID, err)
  137. }
  138. }
  139. }
  140. if err := models.UpdateUserCols(ctxUser, "avatar", "avatar_email", "use_custom_avatar"); err != nil {
  141. return fmt.Errorf("UpdateUser: %v", err)
  142. }
  143. return nil
  144. }
  145. // SettingsAvatar render user avatar page
  146. func SettingsAvatar(ctx *context.Context) {
  147. ctx.Data["Title"] = ctx.Tr("settings")
  148. ctx.Data["PageIsSettingsAvatar"] = true
  149. ctx.HTML(200, tplSettingsAvatar)
  150. }
  151. // SettingsAvatarPost response for change user's avatar request
  152. func SettingsAvatarPost(ctx *context.Context, form auth.AvatarForm) {
  153. if err := UpdateAvatarSetting(ctx, form, ctx.User); err != nil {
  154. ctx.Flash.Error(err.Error())
  155. } else {
  156. ctx.Flash.Success(ctx.Tr("settings.update_avatar_success"))
  157. }
  158. ctx.Redirect(setting.AppSubURL + "/user/settings/avatar")
  159. }
  160. // SettingsDeleteAvatar render delete avatar page
  161. func SettingsDeleteAvatar(ctx *context.Context) {
  162. if err := ctx.User.DeleteAvatar(); err != nil {
  163. ctx.Flash.Error(err.Error())
  164. }
  165. ctx.Redirect(setting.AppSubURL + "/user/settings/avatar")
  166. }
  167. // SettingsSecurity render change user's password page and 2FA
  168. func SettingsSecurity(ctx *context.Context) {
  169. ctx.Data["Title"] = ctx.Tr("settings")
  170. ctx.Data["PageIsSettingsSecurity"] = true
  171. ctx.Data["Email"] = ctx.User.Email
  172. enrolled := true
  173. _, err := models.GetTwoFactorByUID(ctx.User.ID)
  174. if err != nil {
  175. if models.IsErrTwoFactorNotEnrolled(err) {
  176. enrolled = false
  177. } else {
  178. ctx.Handle(500, "SettingsTwoFactor", err)
  179. return
  180. }
  181. }
  182. ctx.Data["TwofaEnrolled"] = enrolled
  183. ctx.HTML(200, tplSettingsSecurity)
  184. }
  185. // SettingsSecurityPost response for change user's password
  186. func SettingsSecurityPost(ctx *context.Context, form auth.ChangePasswordForm) {
  187. ctx.Data["Title"] = ctx.Tr("settings")
  188. ctx.Data["PageIsSettingsSecurity"] = true
  189. ctx.Data["PageIsSettingsDelete"] = true
  190. if ctx.HasError() {
  191. ctx.HTML(200, tplSettingsSecurity)
  192. return
  193. }
  194. if len(form.Password) < setting.MinPasswordLength {
  195. ctx.Flash.Error(ctx.Tr("auth.password_too_short", setting.MinPasswordLength))
  196. } else if ctx.User.IsPasswordSet() && !ctx.User.ValidatePassword(form.OldPassword) {
  197. ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
  198. } else if form.Password != form.Retype {
  199. ctx.Flash.Error(ctx.Tr("form.password_not_match"))
  200. } else {
  201. ctx.User.Passwd = form.Password
  202. var err error
  203. if ctx.User.Salt, err = models.GetUserSalt(); err != nil {
  204. ctx.Handle(500, "UpdateUser", err)
  205. return
  206. }
  207. ctx.User.EncodePasswd()
  208. if err := models.UpdateUserCols(ctx.User, "salt", "passwd"); err != nil {
  209. ctx.Handle(500, "UpdateUser", err)
  210. return
  211. }
  212. log.Trace("User password updated: %s", ctx.User.Name)
  213. ctx.Flash.Success(ctx.Tr("settings.change_password_success"))
  214. }
  215. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  216. }
  217. // SettingsEmails render user's emails page
  218. func SettingsEmails(ctx *context.Context) {
  219. ctx.Data["Title"] = ctx.Tr("settings")
  220. ctx.Data["PageIsSettingsEmails"] = true
  221. emails, err := models.GetEmailAddresses(ctx.User.ID)
  222. if err != nil {
  223. ctx.Handle(500, "GetEmailAddresses", err)
  224. return
  225. }
  226. ctx.Data["Emails"] = emails
  227. ctx.HTML(200, tplSettingsEmails)
  228. }
  229. // SettingsEmailPost response for change user's email
  230. func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) {
  231. ctx.Data["Title"] = ctx.Tr("settings")
  232. ctx.Data["PageIsSettingsEmails"] = true
  233. // Make emailaddress primary.
  234. if ctx.Query("_method") == "PRIMARY" {
  235. if err := models.MakeEmailPrimary(&models.EmailAddress{ID: ctx.QueryInt64("id")}); err != nil {
  236. ctx.Handle(500, "MakeEmailPrimary", err)
  237. return
  238. }
  239. log.Trace("Email made primary: %s", ctx.User.Name)
  240. ctx.Redirect(setting.AppSubURL + "/user/settings/email")
  241. return
  242. }
  243. // Add Email address.
  244. emails, err := models.GetEmailAddresses(ctx.User.ID)
  245. if err != nil {
  246. ctx.Handle(500, "GetEmailAddresses", err)
  247. return
  248. }
  249. ctx.Data["Emails"] = emails
  250. if ctx.HasError() {
  251. ctx.HTML(200, tplSettingsEmails)
  252. return
  253. }
  254. email := &models.EmailAddress{
  255. UID: ctx.User.ID,
  256. Email: form.Email,
  257. IsActivated: !setting.Service.RegisterEmailConfirm,
  258. }
  259. if err := models.AddEmailAddress(email); err != nil {
  260. if models.IsErrEmailAlreadyUsed(err) {
  261. ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsEmails, &form)
  262. return
  263. }
  264. ctx.Handle(500, "AddEmailAddress", err)
  265. return
  266. }
  267. // Send confirmation email
  268. if setting.Service.RegisterEmailConfirm {
  269. models.SendActivateEmailMail(ctx.Context, ctx.User, email)
  270. if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
  271. log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
  272. }
  273. ctx.Flash.Info(ctx.Tr("settings.add_email_confirmation_sent", email.Email, base.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language())))
  274. } else {
  275. ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
  276. }
  277. log.Trace("Email address added: %s", email.Email)
  278. ctx.Redirect(setting.AppSubURL + "/user/settings/email")
  279. }
  280. // DeleteEmail response for delete user's email
  281. func DeleteEmail(ctx *context.Context) {
  282. if err := models.DeleteEmailAddress(&models.EmailAddress{ID: ctx.QueryInt64("id"), UID: ctx.User.ID}); err != nil {
  283. ctx.Handle(500, "DeleteEmail", err)
  284. return
  285. }
  286. log.Trace("Email address deleted: %s", ctx.User.Name)
  287. ctx.Flash.Success(ctx.Tr("settings.email_deletion_success"))
  288. ctx.JSON(200, map[string]interface{}{
  289. "redirect": setting.AppSubURL + "/user/settings/email",
  290. })
  291. }
  292. // SettingsKeys render user's SSH/GPG public keys page
  293. func SettingsKeys(ctx *context.Context) {
  294. ctx.Data["Title"] = ctx.Tr("settings")
  295. ctx.Data["PageIsSettingsKeys"] = true
  296. ctx.Data["DisableSSH"] = setting.SSH.Disabled
  297. keys, err := models.ListPublicKeys(ctx.User.ID)
  298. if err != nil {
  299. ctx.Handle(500, "ListPublicKeys", err)
  300. return
  301. }
  302. ctx.Data["Keys"] = keys
  303. gpgkeys, err := models.ListGPGKeys(ctx.User.ID)
  304. if err != nil {
  305. ctx.Handle(500, "ListGPGKeys", err)
  306. return
  307. }
  308. ctx.Data["GPGKeys"] = gpgkeys
  309. ctx.HTML(200, tplSettingsKeys)
  310. }
  311. // SettingsKeysPost response for change user's SSH/GPG keys
  312. func SettingsKeysPost(ctx *context.Context, form auth.AddKeyForm) {
  313. ctx.Data["Title"] = ctx.Tr("settings")
  314. ctx.Data["PageIsSettingsKeys"] = true
  315. keys, err := models.ListPublicKeys(ctx.User.ID)
  316. if err != nil {
  317. ctx.Handle(500, "ListPublicKeys", err)
  318. return
  319. }
  320. ctx.Data["Keys"] = keys
  321. gpgkeys, err := models.ListGPGKeys(ctx.User.ID)
  322. if err != nil {
  323. ctx.Handle(500, "ListGPGKeys", err)
  324. return
  325. }
  326. ctx.Data["GPGKeys"] = gpgkeys
  327. if ctx.HasError() {
  328. ctx.HTML(200, tplSettingsKeys)
  329. return
  330. }
  331. switch form.Type {
  332. case "gpg":
  333. key, err := models.AddGPGKey(ctx.User.ID, form.Content)
  334. if err != nil {
  335. ctx.Data["HasGPGError"] = true
  336. switch {
  337. case models.IsErrGPGKeyParsing(err):
  338. ctx.Flash.Error(ctx.Tr("form.invalid_gpg_key", err.Error()))
  339. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  340. case models.IsErrGPGKeyIDAlreadyUsed(err):
  341. ctx.Data["Err_Content"] = true
  342. ctx.RenderWithErr(ctx.Tr("settings.gpg_key_id_used"), tplSettingsKeys, &form)
  343. case models.IsErrGPGNoEmailFound(err):
  344. ctx.Data["Err_Content"] = true
  345. ctx.RenderWithErr(ctx.Tr("settings.gpg_no_key_email_found"), tplSettingsKeys, &form)
  346. default:
  347. ctx.Handle(500, "AddPublicKey", err)
  348. }
  349. return
  350. }
  351. ctx.Flash.Success(ctx.Tr("settings.add_gpg_key_success", key.KeyID))
  352. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  353. case "ssh":
  354. content, err := models.CheckPublicKeyString(form.Content)
  355. if err != nil {
  356. if models.IsErrSSHDisabled(err) {
  357. ctx.Flash.Info(ctx.Tr("settings.ssh_disabled"))
  358. } else if models.IsErrKeyUnableVerify(err) {
  359. ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
  360. } else {
  361. ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
  362. }
  363. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  364. return
  365. }
  366. if _, err = models.AddPublicKey(ctx.User.ID, form.Title, content); err != nil {
  367. ctx.Data["HasSSHError"] = true
  368. switch {
  369. case models.IsErrKeyAlreadyExist(err):
  370. ctx.Data["Err_Content"] = true
  371. ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplSettingsKeys, &form)
  372. case models.IsErrKeyNameAlreadyUsed(err):
  373. ctx.Data["Err_Title"] = true
  374. ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), tplSettingsKeys, &form)
  375. default:
  376. ctx.Handle(500, "AddPublicKey", err)
  377. }
  378. return
  379. }
  380. ctx.Flash.Success(ctx.Tr("settings.add_key_success", form.Title))
  381. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  382. default:
  383. ctx.Flash.Warning("Function not implemented")
  384. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  385. }
  386. }
  387. // DeleteKey response for delete user's SSH/GPG key
  388. func DeleteKey(ctx *context.Context) {
  389. switch ctx.Query("type") {
  390. case "gpg":
  391. if err := models.DeleteGPGKey(ctx.User, ctx.QueryInt64("id")); err != nil {
  392. ctx.Flash.Error("DeleteGPGKey: " + err.Error())
  393. } else {
  394. ctx.Flash.Success(ctx.Tr("settings.gpg_key_deletion_success"))
  395. }
  396. case "ssh":
  397. if err := models.DeletePublicKey(ctx.User, ctx.QueryInt64("id")); err != nil {
  398. ctx.Flash.Error("DeletePublicKey: " + err.Error())
  399. } else {
  400. ctx.Flash.Success(ctx.Tr("settings.ssh_key_deletion_success"))
  401. }
  402. default:
  403. ctx.Flash.Warning("Function not implemented")
  404. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  405. }
  406. ctx.JSON(200, map[string]interface{}{
  407. "redirect": setting.AppSubURL + "/user/settings/keys",
  408. })
  409. }
  410. // SettingsApplications render user's access tokens page
  411. func SettingsApplications(ctx *context.Context) {
  412. ctx.Data["Title"] = ctx.Tr("settings")
  413. ctx.Data["PageIsSettingsApplications"] = true
  414. tokens, err := models.ListAccessTokens(ctx.User.ID)
  415. if err != nil {
  416. ctx.Handle(500, "ListAccessTokens", err)
  417. return
  418. }
  419. ctx.Data["Tokens"] = tokens
  420. ctx.HTML(200, tplSettingsApplications)
  421. }
  422. // SettingsApplicationsPost response for add user's access token
  423. func SettingsApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) {
  424. ctx.Data["Title"] = ctx.Tr("settings")
  425. ctx.Data["PageIsSettingsApplications"] = true
  426. if ctx.HasError() {
  427. tokens, err := models.ListAccessTokens(ctx.User.ID)
  428. if err != nil {
  429. ctx.Handle(500, "ListAccessTokens", err)
  430. return
  431. }
  432. ctx.Data["Tokens"] = tokens
  433. ctx.HTML(200, tplSettingsApplications)
  434. return
  435. }
  436. t := &models.AccessToken{
  437. UID: ctx.User.ID,
  438. Name: form.Name,
  439. }
  440. if err := models.NewAccessToken(t); err != nil {
  441. ctx.Handle(500, "NewAccessToken", err)
  442. return
  443. }
  444. ctx.Flash.Success(ctx.Tr("settings.generate_token_success"))
  445. ctx.Flash.Info(t.Sha1)
  446. ctx.Redirect(setting.AppSubURL + "/user/settings/applications")
  447. }
  448. // SettingsDeleteApplication response for delete user access token
  449. func SettingsDeleteApplication(ctx *context.Context) {
  450. if err := models.DeleteAccessTokenByID(ctx.QueryInt64("id"), ctx.User.ID); err != nil {
  451. ctx.Flash.Error("DeleteAccessTokenByID: " + err.Error())
  452. } else {
  453. ctx.Flash.Success(ctx.Tr("settings.delete_token_success"))
  454. }
  455. ctx.JSON(200, map[string]interface{}{
  456. "redirect": setting.AppSubURL + "/user/settings/applications",
  457. })
  458. }
  459. // SettingsTwoFactorRegenerateScratch regenerates the user's 2FA scratch code.
  460. func SettingsTwoFactorRegenerateScratch(ctx *context.Context) {
  461. ctx.Data["Title"] = ctx.Tr("settings")
  462. ctx.Data["PageIsSettingsSecurity"] = true
  463. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  464. if err != nil {
  465. ctx.Handle(500, "SettingsTwoFactor", err)
  466. return
  467. }
  468. if err = t.GenerateScratchToken(); err != nil {
  469. ctx.Handle(500, "SettingsTwoFactor", err)
  470. return
  471. }
  472. if err = models.UpdateTwoFactor(t); err != nil {
  473. ctx.Handle(500, "SettingsTwoFactor", err)
  474. return
  475. }
  476. ctx.Flash.Success(ctx.Tr("settings.twofa_scratch_token_regenerated", t.ScratchToken))
  477. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  478. }
  479. // SettingsTwoFactorDisable deletes the user's 2FA settings.
  480. func SettingsTwoFactorDisable(ctx *context.Context) {
  481. ctx.Data["Title"] = ctx.Tr("settings")
  482. ctx.Data["PageIsSettingsSecurity"] = true
  483. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  484. if err != nil {
  485. ctx.Handle(500, "SettingsTwoFactor", err)
  486. return
  487. }
  488. if err = models.DeleteTwoFactorByID(t.ID, ctx.User.ID); err != nil {
  489. ctx.Handle(500, "SettingsTwoFactor", err)
  490. return
  491. }
  492. ctx.Flash.Success(ctx.Tr("settings.twofa_disabled"))
  493. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  494. }
  495. func twofaGenerateSecretAndQr(ctx *context.Context) bool {
  496. var otpKey *otp.Key
  497. var err error
  498. uri := ctx.Session.Get("twofaUri")
  499. if uri != nil {
  500. otpKey, err = otp.NewKeyFromURL(uri.(string))
  501. }
  502. if otpKey == nil {
  503. err = nil // clear the error, in case the URL was invalid
  504. otpKey, err = totp.Generate(totp.GenerateOpts{
  505. Issuer: setting.AppName + " (" + strings.TrimRight(setting.AppURL, "/") + ")",
  506. AccountName: ctx.User.Name,
  507. })
  508. if err != nil {
  509. ctx.Handle(500, "SettingsTwoFactor", err)
  510. return false
  511. }
  512. }
  513. ctx.Data["TwofaSecret"] = otpKey.Secret()
  514. img, err := otpKey.Image(320, 240)
  515. if err != nil {
  516. ctx.Handle(500, "SettingsTwoFactor", err)
  517. return false
  518. }
  519. var imgBytes bytes.Buffer
  520. if err = png.Encode(&imgBytes, img); err != nil {
  521. ctx.Handle(500, "SettingsTwoFactor", err)
  522. return false
  523. }
  524. ctx.Data["QrUri"] = template.URL("data:image/png;base64," + base64.StdEncoding.EncodeToString(imgBytes.Bytes()))
  525. ctx.Session.Set("twofaSecret", otpKey.Secret())
  526. ctx.Session.Set("twofaUri", otpKey.String())
  527. return true
  528. }
  529. // SettingsTwoFactorEnroll shows the page where the user can enroll into 2FA.
  530. func SettingsTwoFactorEnroll(ctx *context.Context) {
  531. ctx.Data["Title"] = ctx.Tr("settings")
  532. ctx.Data["PageIsSettingsSecurity"] = true
  533. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  534. if t != nil {
  535. // already enrolled
  536. ctx.Handle(500, "SettingsTwoFactor", err)
  537. return
  538. }
  539. if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
  540. ctx.Handle(500, "SettingsTwoFactor", err)
  541. return
  542. }
  543. if !twofaGenerateSecretAndQr(ctx) {
  544. return
  545. }
  546. ctx.HTML(200, tplSettingsTwofaEnroll)
  547. }
  548. // SettingsTwoFactorEnrollPost handles enrolling the user into 2FA.
  549. func SettingsTwoFactorEnrollPost(ctx *context.Context, form auth.TwoFactorAuthForm) {
  550. ctx.Data["Title"] = ctx.Tr("settings")
  551. ctx.Data["PageIsSettingsSecurity"] = true
  552. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  553. if t != nil {
  554. // already enrolled
  555. ctx.Handle(500, "SettingsTwoFactor", err)
  556. return
  557. }
  558. if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
  559. ctx.Handle(500, "SettingsTwoFactor", err)
  560. return
  561. }
  562. if ctx.HasError() {
  563. if !twofaGenerateSecretAndQr(ctx) {
  564. return
  565. }
  566. ctx.HTML(200, tplSettingsTwofaEnroll)
  567. return
  568. }
  569. secret := ctx.Session.Get("twofaSecret").(string)
  570. if !totp.Validate(form.Passcode, secret) {
  571. if !twofaGenerateSecretAndQr(ctx) {
  572. return
  573. }
  574. ctx.Flash.Error(ctx.Tr("settings.passcode_invalid"))
  575. ctx.HTML(200, tplSettingsTwofaEnroll)
  576. return
  577. }
  578. t = &models.TwoFactor{
  579. UID: ctx.User.ID,
  580. }
  581. err = t.SetSecret(secret)
  582. if err != nil {
  583. ctx.Handle(500, "SettingsTwoFactor", err)
  584. return
  585. }
  586. err = t.GenerateScratchToken()
  587. if err != nil {
  588. ctx.Handle(500, "SettingsTwoFactor", err)
  589. return
  590. }
  591. if err = models.NewTwoFactor(t); err != nil {
  592. ctx.Handle(500, "SettingsTwoFactor", err)
  593. return
  594. }
  595. ctx.Session.Delete("twofaSecret")
  596. ctx.Session.Delete("twofaUri")
  597. ctx.Flash.Success(ctx.Tr("settings.twofa_enrolled", t.ScratchToken))
  598. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  599. }
  600. // SettingsAccountLinks render the account links settings page
  601. func SettingsAccountLinks(ctx *context.Context) {
  602. ctx.Data["Title"] = ctx.Tr("settings")
  603. ctx.Data["PageIsSettingsAccountLink"] = true
  604. accountLinks, err := models.ListAccountLinks(ctx.User)
  605. if err != nil {
  606. ctx.Handle(500, "ListAccountLinks", err)
  607. return
  608. }
  609. // map the provider display name with the LoginSource
  610. sources := make(map[*models.LoginSource]string)
  611. for _, externalAccount := range accountLinks {
  612. if loginSource, err := models.GetLoginSourceByID(externalAccount.LoginSourceID); err == nil {
  613. var providerDisplayName string
  614. if loginSource.IsOAuth2() {
  615. providerTechnicalName := loginSource.OAuth2().Provider
  616. providerDisplayName = models.OAuth2Providers[providerTechnicalName].DisplayName
  617. } else {
  618. providerDisplayName = loginSource.Name
  619. }
  620. sources[loginSource] = providerDisplayName
  621. }
  622. }
  623. ctx.Data["AccountLinks"] = sources
  624. ctx.HTML(200, tplSettingsAccountLink)
  625. }
  626. // SettingsDeleteAccountLink delete a single account link
  627. func SettingsDeleteAccountLink(ctx *context.Context) {
  628. if _, err := models.RemoveAccountLink(ctx.User, ctx.QueryInt64("loginSourceID")); err != nil {
  629. ctx.Flash.Error("RemoveAccountLink: " + err.Error())
  630. } else {
  631. ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success"))
  632. }
  633. ctx.JSON(200, map[string]interface{}{
  634. "redirect": setting.AppSubURL + "/user/settings/account_link",
  635. })
  636. }
  637. // SettingsDelete render user suicide page and response for delete user himself
  638. func SettingsDelete(ctx *context.Context) {
  639. ctx.Data["Title"] = ctx.Tr("settings")
  640. ctx.Data["PageIsSettingsDelete"] = true
  641. ctx.Data["Email"] = ctx.User.Email
  642. if ctx.Req.Method == "POST" {
  643. if _, err := models.UserSignIn(ctx.User.Name, ctx.Query("password")); err != nil {
  644. if models.IsErrUserNotExist(err) {
  645. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsDelete, nil)
  646. } else {
  647. ctx.Handle(500, "UserSignIn", err)
  648. }
  649. return
  650. }
  651. if err := models.DeleteUser(ctx.User); err != nil {
  652. switch {
  653. case models.IsErrUserOwnRepos(err):
  654. ctx.Flash.Error(ctx.Tr("form.still_own_repo"))
  655. ctx.Redirect(setting.AppSubURL + "/user/settings/delete")
  656. case models.IsErrUserHasOrgs(err):
  657. ctx.Flash.Error(ctx.Tr("form.still_has_org"))
  658. ctx.Redirect(setting.AppSubURL + "/user/settings/delete")
  659. default:
  660. ctx.Handle(500, "DeleteUser", err)
  661. }
  662. } else {
  663. log.Trace("Account deleted: %s", ctx.User.Name)
  664. ctx.Redirect(setting.AppSubURL + "/")
  665. }
  666. return
  667. }
  668. ctx.HTML(200, tplSettingsDelete)
  669. }
  670. // SettingsOrganization render all the organization of the user
  671. func SettingsOrganization(ctx *context.Context) {
  672. ctx.Data["Title"] = ctx.Tr("settings")
  673. ctx.Data["PageIsSettingsOrganization"] = true
  674. orgs, err := models.GetOrgsByUserID(ctx.User.ID, ctx.IsSigned)
  675. if err != nil {
  676. ctx.Handle(500, "GetOrgsByUserID", err)
  677. return
  678. }
  679. ctx.Data["Orgs"] = orgs
  680. ctx.HTML(200, tplSettingsOrganization)
  681. }
  682. // SettingsRepos display a list of all repositories of the user
  683. func SettingsRepos(ctx *context.Context) {
  684. ctx.Data["Title"] = ctx.Tr("settings")
  685. ctx.Data["PageIsSettingsRepos"] = true
  686. ctxUser := ctx.User
  687. var err error
  688. if err = ctxUser.GetRepositories(1, setting.UI.User.RepoPagingNum); err != nil {
  689. ctx.Handle(500, "GetRepositories", err)
  690. return
  691. }
  692. repos := ctxUser.Repos
  693. for i := range repos {
  694. if repos[i].IsFork {
  695. err := repos[i].GetBaseRepo()
  696. if err != nil {
  697. ctx.Handle(500, "GetBaseRepo", err)
  698. return
  699. }
  700. err = repos[i].BaseRepo.GetOwner()
  701. if err != nil {
  702. ctx.Handle(500, "GetOwner", err)
  703. return
  704. }
  705. }
  706. }
  707. ctx.Data["Owner"] = ctxUser
  708. ctx.Data["Repos"] = repos
  709. ctx.HTML(200, tplSettingsRepositories)
  710. }