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.

setting.go 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  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. tplSettingsPassword base.TplName = "user/settings/password"
  28. tplSettingsEmails base.TplName = "user/settings/email"
  29. tplSettingsKeys base.TplName = "user/settings/keys"
  30. tplSettingsSocial base.TplName = "user/settings/social"
  31. tplSettingsApplications base.TplName = "user/settings/applications"
  32. tplSettingsTwofa base.TplName = "user/settings/twofa"
  33. tplSettingsTwofaEnroll base.TplName = "user/settings/twofa_enroll"
  34. tplSettingsAccountLink base.TplName = "user/settings/account_link"
  35. tplSettingsDelete base.TplName = "user/settings/delete"
  36. tplSecurity base.TplName = "user/security"
  37. )
  38. // Settings render user's profile page
  39. func Settings(ctx *context.Context) {
  40. ctx.Data["Title"] = ctx.Tr("settings")
  41. ctx.Data["PageIsSettingsProfile"] = true
  42. ctx.HTML(200, tplSettingsProfile)
  43. }
  44. func handleUsernameChange(ctx *context.Context, newName string) {
  45. // Non-local users are not allowed to change their username.
  46. if len(newName) == 0 || !ctx.User.IsLocal() {
  47. return
  48. }
  49. // Check if user name has been changed
  50. if ctx.User.LowerName != strings.ToLower(newName) {
  51. if err := models.ChangeUserName(ctx.User, newName); err != nil {
  52. switch {
  53. case models.IsErrUserAlreadyExist(err):
  54. ctx.Flash.Error(ctx.Tr("newName_been_taken"))
  55. ctx.Redirect(setting.AppSubURL + "/user/settings")
  56. case models.IsErrEmailAlreadyUsed(err):
  57. ctx.Flash.Error(ctx.Tr("form.email_been_used"))
  58. ctx.Redirect(setting.AppSubURL + "/user/settings")
  59. case models.IsErrNameReserved(err):
  60. ctx.Flash.Error(ctx.Tr("user.newName_reserved"))
  61. ctx.Redirect(setting.AppSubURL + "/user/settings")
  62. case models.IsErrNamePatternNotAllowed(err):
  63. ctx.Flash.Error(ctx.Tr("user.newName_pattern_not_allowed"))
  64. ctx.Redirect(setting.AppSubURL + "/user/settings")
  65. default:
  66. ctx.Handle(500, "ChangeUserName", err)
  67. }
  68. return
  69. }
  70. log.Trace("User name changed: %s -> %s", ctx.User.Name, newName)
  71. }
  72. // In case it's just a case change
  73. ctx.User.Name = newName
  74. ctx.User.LowerName = strings.ToLower(newName)
  75. }
  76. // SettingsPost response for change user's profile
  77. func SettingsPost(ctx *context.Context, form auth.UpdateProfileForm) {
  78. ctx.Data["Title"] = ctx.Tr("settings")
  79. ctx.Data["PageIsSettingsProfile"] = true
  80. if ctx.HasError() {
  81. ctx.HTML(200, tplSettingsProfile)
  82. return
  83. }
  84. handleUsernameChange(ctx, form.Name)
  85. if ctx.Written() {
  86. return
  87. }
  88. ctx.User.FullName = form.FullName
  89. ctx.User.Email = form.Email
  90. ctx.User.KeepEmailPrivate = form.KeepEmailPrivate
  91. ctx.User.Website = form.Website
  92. ctx.User.Location = form.Location
  93. if err := models.UpdateUserSetting(ctx.User); err != nil {
  94. if _, ok := err.(models.ErrEmailAlreadyUsed); ok {
  95. ctx.Flash.Error(ctx.Tr("form.email_been_used"))
  96. ctx.Redirect(setting.AppSubURL + "/user/settings")
  97. return
  98. }
  99. ctx.Handle(500, "UpdateUser", err)
  100. return
  101. }
  102. log.Trace("User settings updated: %s", ctx.User.Name)
  103. ctx.Flash.Success(ctx.Tr("settings.update_profile_success"))
  104. ctx.Redirect(setting.AppSubURL + "/user/settings")
  105. }
  106. // UpdateAvatarSetting update user's avatar
  107. // FIXME: limit size.
  108. func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *models.User) error {
  109. ctxUser.UseCustomAvatar = form.Source == auth.AvatarLocal
  110. if len(form.Gravatar) > 0 {
  111. ctxUser.Avatar = base.EncodeMD5(form.Gravatar)
  112. ctxUser.AvatarEmail = form.Gravatar
  113. }
  114. if form.Avatar != nil {
  115. fr, err := form.Avatar.Open()
  116. if err != nil {
  117. return fmt.Errorf("Avatar.Open: %v", err)
  118. }
  119. defer fr.Close()
  120. data, err := ioutil.ReadAll(fr)
  121. if err != nil {
  122. return fmt.Errorf("ioutil.ReadAll: %v", err)
  123. }
  124. if !base.IsImageFile(data) {
  125. return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image"))
  126. }
  127. if err = ctxUser.UploadAvatar(data); err != nil {
  128. return fmt.Errorf("UploadAvatar: %v", err)
  129. }
  130. } else {
  131. // No avatar is uploaded but setting has been changed to enable,
  132. // generate a random one when needed.
  133. if ctxUser.UseCustomAvatar && !com.IsFile(ctxUser.CustomAvatarPath()) {
  134. if err := ctxUser.GenerateRandomAvatar(); err != nil {
  135. log.Error(4, "GenerateRandomAvatar[%d]: %v", ctxUser.ID, err)
  136. }
  137. }
  138. }
  139. if err := models.UpdateUser(ctxUser); err != nil {
  140. return fmt.Errorf("UpdateUser: %v", err)
  141. }
  142. return nil
  143. }
  144. // SettingsAvatar render user avatar page
  145. func SettingsAvatar(ctx *context.Context) {
  146. ctx.Data["Title"] = ctx.Tr("settings")
  147. ctx.Data["PageIsSettingsAvatar"] = true
  148. ctx.HTML(200, tplSettingsAvatar)
  149. }
  150. // SettingsAvatarPost response for change user's avatar request
  151. func SettingsAvatarPost(ctx *context.Context, form auth.AvatarForm) {
  152. if err := UpdateAvatarSetting(ctx, form, ctx.User); err != nil {
  153. ctx.Flash.Error(err.Error())
  154. } else {
  155. ctx.Flash.Success(ctx.Tr("settings.update_avatar_success"))
  156. }
  157. ctx.Redirect(setting.AppSubURL + "/user/settings/avatar")
  158. }
  159. // SettingsDeleteAvatar render delete avatar page
  160. func SettingsDeleteAvatar(ctx *context.Context) {
  161. if err := ctx.User.DeleteAvatar(); err != nil {
  162. ctx.Flash.Error(err.Error())
  163. }
  164. ctx.Redirect(setting.AppSubURL + "/user/settings/avatar")
  165. }
  166. // SettingsPassword render change user's password page
  167. func SettingsPassword(ctx *context.Context) {
  168. ctx.Data["Title"] = ctx.Tr("settings")
  169. ctx.Data["PageIsSettingsPassword"] = true
  170. ctx.Data["Email"] = ctx.User.Email
  171. ctx.HTML(200, tplSettingsPassword)
  172. }
  173. // SettingsPasswordPost response for change user's password
  174. func SettingsPasswordPost(ctx *context.Context, form auth.ChangePasswordForm) {
  175. ctx.Data["Title"] = ctx.Tr("settings")
  176. ctx.Data["PageIsSettingsPassword"] = true
  177. ctx.Data["PageIsSettingsDelete"] = true
  178. if ctx.HasError() {
  179. ctx.HTML(200, tplSettingsPassword)
  180. return
  181. }
  182. if ctx.User.IsPasswordSet() && !ctx.User.ValidatePassword(form.OldPassword) {
  183. ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
  184. } else if form.Password != form.Retype {
  185. ctx.Flash.Error(ctx.Tr("form.password_not_match"))
  186. } else {
  187. ctx.User.Passwd = form.Password
  188. var err error
  189. if ctx.User.Salt, err = models.GetUserSalt(); err != nil {
  190. ctx.Handle(500, "UpdateUser", err)
  191. return
  192. }
  193. ctx.User.EncodePasswd()
  194. if err := models.UpdateUser(ctx.User); err != nil {
  195. ctx.Handle(500, "UpdateUser", err)
  196. return
  197. }
  198. log.Trace("User password updated: %s", ctx.User.Name)
  199. ctx.Flash.Success(ctx.Tr("settings.change_password_success"))
  200. }
  201. ctx.Redirect(setting.AppSubURL + "/user/settings/password")
  202. }
  203. // SettingsEmails render user's emails page
  204. func SettingsEmails(ctx *context.Context) {
  205. ctx.Data["Title"] = ctx.Tr("settings")
  206. ctx.Data["PageIsSettingsEmails"] = true
  207. emails, err := models.GetEmailAddresses(ctx.User.ID)
  208. if err != nil {
  209. ctx.Handle(500, "GetEmailAddresses", err)
  210. return
  211. }
  212. ctx.Data["Emails"] = emails
  213. ctx.HTML(200, tplSettingsEmails)
  214. }
  215. // SettingsEmailPost response for change user's email
  216. func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) {
  217. ctx.Data["Title"] = ctx.Tr("settings")
  218. ctx.Data["PageIsSettingsEmails"] = true
  219. // Make emailaddress primary.
  220. if ctx.Query("_method") == "PRIMARY" {
  221. if err := models.MakeEmailPrimary(&models.EmailAddress{ID: ctx.QueryInt64("id")}); err != nil {
  222. ctx.Handle(500, "MakeEmailPrimary", err)
  223. return
  224. }
  225. log.Trace("Email made primary: %s", ctx.User.Name)
  226. ctx.Redirect(setting.AppSubURL + "/user/settings/email")
  227. return
  228. }
  229. // Add Email address.
  230. emails, err := models.GetEmailAddresses(ctx.User.ID)
  231. if err != nil {
  232. ctx.Handle(500, "GetEmailAddresses", err)
  233. return
  234. }
  235. ctx.Data["Emails"] = emails
  236. if ctx.HasError() {
  237. ctx.HTML(200, tplSettingsEmails)
  238. return
  239. }
  240. email := &models.EmailAddress{
  241. UID: ctx.User.ID,
  242. Email: form.Email,
  243. IsActivated: !setting.Service.RegisterEmailConfirm,
  244. }
  245. if err := models.AddEmailAddress(email); err != nil {
  246. if models.IsErrEmailAlreadyUsed(err) {
  247. ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsEmails, &form)
  248. return
  249. }
  250. ctx.Handle(500, "AddEmailAddress", err)
  251. return
  252. }
  253. // Send confirmation email
  254. if setting.Service.RegisterEmailConfirm {
  255. models.SendActivateEmailMail(ctx.Context, ctx.User, email)
  256. if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
  257. log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
  258. }
  259. ctx.Flash.Info(ctx.Tr("settings.add_email_confirmation_sent", email.Email, setting.Service.ActiveCodeLives/60))
  260. } else {
  261. ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
  262. }
  263. log.Trace("Email address added: %s", email.Email)
  264. ctx.Redirect(setting.AppSubURL + "/user/settings/email")
  265. }
  266. // DeleteEmail response for delete user's email
  267. func DeleteEmail(ctx *context.Context) {
  268. if err := models.DeleteEmailAddress(&models.EmailAddress{ID: ctx.QueryInt64("id"), UID: ctx.User.ID}); err != nil {
  269. ctx.Handle(500, "DeleteEmail", err)
  270. return
  271. }
  272. log.Trace("Email address deleted: %s", ctx.User.Name)
  273. ctx.Flash.Success(ctx.Tr("settings.email_deletion_success"))
  274. ctx.JSON(200, map[string]interface{}{
  275. "redirect": setting.AppSubURL + "/user/settings/email",
  276. })
  277. }
  278. // SettingsKeys render user's SSH/GPG public keys page
  279. func SettingsKeys(ctx *context.Context) {
  280. ctx.Data["Title"] = ctx.Tr("settings")
  281. ctx.Data["PageIsSettingsKeys"] = true
  282. keys, err := models.ListPublicKeys(ctx.User.ID)
  283. if err != nil {
  284. ctx.Handle(500, "ListPublicKeys", err)
  285. return
  286. }
  287. ctx.Data["Keys"] = keys
  288. gpgkeys, err := models.ListGPGKeys(ctx.User.ID)
  289. if err != nil {
  290. ctx.Handle(500, "ListGPGKeys", err)
  291. return
  292. }
  293. ctx.Data["GPGKeys"] = gpgkeys
  294. ctx.HTML(200, tplSettingsKeys)
  295. }
  296. // SettingsKeysPost response for change user's SSH/GPG keys
  297. func SettingsKeysPost(ctx *context.Context, form auth.AddKeyForm) {
  298. ctx.Data["Title"] = ctx.Tr("settings")
  299. ctx.Data["PageIsSettingsKeys"] = true
  300. keys, err := models.ListPublicKeys(ctx.User.ID)
  301. if err != nil {
  302. ctx.Handle(500, "ListPublicKeys", err)
  303. return
  304. }
  305. ctx.Data["Keys"] = keys
  306. gpgkeys, err := models.ListGPGKeys(ctx.User.ID)
  307. if err != nil {
  308. ctx.Handle(500, "ListGPGKeys", err)
  309. return
  310. }
  311. ctx.Data["GPGKeys"] = gpgkeys
  312. if ctx.HasError() {
  313. ctx.HTML(200, tplSettingsKeys)
  314. return
  315. }
  316. switch form.Type {
  317. case "gpg":
  318. key, err := models.AddGPGKey(ctx.User.ID, form.Content)
  319. if err != nil {
  320. ctx.Data["HasGPGError"] = true
  321. switch {
  322. case models.IsErrGPGKeyParsing(err):
  323. ctx.Flash.Error(ctx.Tr("form.invalid_gpg_key", err.Error()))
  324. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  325. case models.IsErrGPGKeyIDAlreadyUsed(err):
  326. ctx.Data["Err_Content"] = true
  327. ctx.RenderWithErr(ctx.Tr("settings.gpg_key_id_used"), tplSettingsKeys, &form)
  328. case models.IsErrGPGEmailNotFound(err):
  329. ctx.Data["Err_Content"] = true
  330. ctx.RenderWithErr(ctx.Tr("settings.gpg_key_email_not_found", err.(models.ErrGPGEmailNotFound).Email), tplSettingsKeys, &form)
  331. default:
  332. ctx.Handle(500, "AddPublicKey", err)
  333. }
  334. return
  335. }
  336. ctx.Flash.Success(ctx.Tr("settings.add_gpg_key_success", key.KeyID))
  337. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  338. case "ssh":
  339. content, err := models.CheckPublicKeyString(form.Content)
  340. if err != nil {
  341. if models.IsErrKeyUnableVerify(err) {
  342. ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
  343. } else {
  344. ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
  345. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  346. return
  347. }
  348. }
  349. if _, err = models.AddPublicKey(ctx.User.ID, form.Title, content); err != nil {
  350. ctx.Data["HasSSHError"] = true
  351. switch {
  352. case models.IsErrKeyAlreadyExist(err):
  353. ctx.Data["Err_Content"] = true
  354. ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplSettingsKeys, &form)
  355. case models.IsErrKeyNameAlreadyUsed(err):
  356. ctx.Data["Err_Title"] = true
  357. ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), tplSettingsKeys, &form)
  358. default:
  359. ctx.Handle(500, "AddPublicKey", err)
  360. }
  361. return
  362. }
  363. ctx.Flash.Success(ctx.Tr("settings.add_key_success", form.Title))
  364. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  365. default:
  366. ctx.Flash.Warning("Function not implemented")
  367. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  368. }
  369. }
  370. // DeleteKey response for delete user's SSH/GPG key
  371. func DeleteKey(ctx *context.Context) {
  372. switch ctx.Query("type") {
  373. case "gpg":
  374. if err := models.DeleteGPGKey(ctx.User, ctx.QueryInt64("id")); err != nil {
  375. ctx.Flash.Error("DeleteGPGKey: " + err.Error())
  376. } else {
  377. ctx.Flash.Success(ctx.Tr("settings.gpg_key_deletion_success"))
  378. }
  379. case "ssh":
  380. if err := models.DeletePublicKey(ctx.User, ctx.QueryInt64("id")); err != nil {
  381. ctx.Flash.Error("DeletePublicKey: " + err.Error())
  382. } else {
  383. ctx.Flash.Success(ctx.Tr("settings.ssh_key_deletion_success"))
  384. }
  385. default:
  386. ctx.Flash.Warning("Function not implemented")
  387. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  388. }
  389. ctx.JSON(200, map[string]interface{}{
  390. "redirect": setting.AppSubURL + "/user/settings/keys",
  391. })
  392. }
  393. // SettingsApplications render user's access tokens page
  394. func SettingsApplications(ctx *context.Context) {
  395. ctx.Data["Title"] = ctx.Tr("settings")
  396. ctx.Data["PageIsSettingsApplications"] = true
  397. tokens, err := models.ListAccessTokens(ctx.User.ID)
  398. if err != nil {
  399. ctx.Handle(500, "ListAccessTokens", err)
  400. return
  401. }
  402. ctx.Data["Tokens"] = tokens
  403. ctx.HTML(200, tplSettingsApplications)
  404. }
  405. // SettingsApplicationsPost response for add user's access token
  406. func SettingsApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) {
  407. ctx.Data["Title"] = ctx.Tr("settings")
  408. ctx.Data["PageIsSettingsApplications"] = true
  409. if ctx.HasError() {
  410. tokens, err := models.ListAccessTokens(ctx.User.ID)
  411. if err != nil {
  412. ctx.Handle(500, "ListAccessTokens", err)
  413. return
  414. }
  415. ctx.Data["Tokens"] = tokens
  416. ctx.HTML(200, tplSettingsApplications)
  417. return
  418. }
  419. t := &models.AccessToken{
  420. UID: ctx.User.ID,
  421. Name: form.Name,
  422. }
  423. if err := models.NewAccessToken(t); err != nil {
  424. ctx.Handle(500, "NewAccessToken", err)
  425. return
  426. }
  427. ctx.Flash.Success(ctx.Tr("settings.generate_token_succees"))
  428. ctx.Flash.Info(t.Sha1)
  429. ctx.Redirect(setting.AppSubURL + "/user/settings/applications")
  430. }
  431. // SettingsDeleteApplication response for delete user access token
  432. func SettingsDeleteApplication(ctx *context.Context) {
  433. if err := models.DeleteAccessTokenByID(ctx.QueryInt64("id"), ctx.User.ID); err != nil {
  434. ctx.Flash.Error("DeleteAccessTokenByID: " + err.Error())
  435. } else {
  436. ctx.Flash.Success(ctx.Tr("settings.delete_token_success"))
  437. }
  438. ctx.JSON(200, map[string]interface{}{
  439. "redirect": setting.AppSubURL + "/user/settings/applications",
  440. })
  441. }
  442. // SettingsTwoFactor renders the 2FA page.
  443. func SettingsTwoFactor(ctx *context.Context) {
  444. ctx.Data["Title"] = ctx.Tr("settings")
  445. ctx.Data["PageIsSettingsTwofa"] = true
  446. enrolled := true
  447. _, err := models.GetTwoFactorByUID(ctx.User.ID)
  448. if err != nil {
  449. if models.IsErrTwoFactorNotEnrolled(err) {
  450. enrolled = false
  451. } else {
  452. ctx.Handle(500, "SettingsTwoFactor", err)
  453. return
  454. }
  455. }
  456. ctx.Data["TwofaEnrolled"] = enrolled
  457. ctx.HTML(200, tplSettingsTwofa)
  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["PageIsSettingsTwofa"] = 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/two_factor")
  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["PageIsSettingsTwofa"] = 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/two_factor")
  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,
  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["PageIsSettingsTwofa"] = 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["PageIsSettingsTwofa"] = 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/two_factor")
  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. }