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.

189 lines
4.7 KiB

  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package setting
  6. import (
  7. "bytes"
  8. "encoding/base64"
  9. "html/template"
  10. "image/png"
  11. "strings"
  12. "code.gitea.io/gitea/models"
  13. "code.gitea.io/gitea/modules/auth"
  14. "code.gitea.io/gitea/modules/context"
  15. "code.gitea.io/gitea/modules/setting"
  16. "github.com/pquerna/otp"
  17. "github.com/pquerna/otp/totp"
  18. )
  19. // RegenerateScratchTwoFactor regenerates the user's 2FA scratch code.
  20. func RegenerateScratchTwoFactor(ctx *context.Context) {
  21. ctx.Data["Title"] = ctx.Tr("settings")
  22. ctx.Data["PageIsSettingsSecurity"] = true
  23. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  24. if err != nil {
  25. ctx.ServerError("SettingsTwoFactor", err)
  26. return
  27. }
  28. token, err := t.GenerateScratchToken()
  29. if err != nil {
  30. ctx.ServerError("SettingsTwoFactor", err)
  31. return
  32. }
  33. if err = models.UpdateTwoFactor(t); err != nil {
  34. ctx.ServerError("SettingsTwoFactor", err)
  35. return
  36. }
  37. ctx.Flash.Success(ctx.Tr("settings.twofa_scratch_token_regenerated", token))
  38. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  39. }
  40. // DisableTwoFactor deletes the user's 2FA settings.
  41. func DisableTwoFactor(ctx *context.Context) {
  42. ctx.Data["Title"] = ctx.Tr("settings")
  43. ctx.Data["PageIsSettingsSecurity"] = true
  44. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  45. if err != nil {
  46. ctx.ServerError("SettingsTwoFactor", err)
  47. return
  48. }
  49. if err = models.DeleteTwoFactorByID(t.ID, ctx.User.ID); err != nil {
  50. ctx.ServerError("SettingsTwoFactor", err)
  51. return
  52. }
  53. ctx.Flash.Success(ctx.Tr("settings.twofa_disabled"))
  54. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  55. }
  56. func twofaGenerateSecretAndQr(ctx *context.Context) bool {
  57. var otpKey *otp.Key
  58. var err error
  59. uri := ctx.Session.Get("twofaUri")
  60. if uri != nil {
  61. otpKey, err = otp.NewKeyFromURL(uri.(string))
  62. }
  63. if otpKey == nil {
  64. err = nil // clear the error, in case the URL was invalid
  65. otpKey, err = totp.Generate(totp.GenerateOpts{
  66. SecretSize: 40,
  67. Issuer: setting.AppName + " (" + strings.TrimRight(setting.AppURL, "/") + ")",
  68. AccountName: ctx.User.Name,
  69. })
  70. if err != nil {
  71. ctx.ServerError("SettingsTwoFactor", err)
  72. return false
  73. }
  74. }
  75. ctx.Data["TwofaSecret"] = otpKey.Secret()
  76. img, err := otpKey.Image(320, 240)
  77. if err != nil {
  78. ctx.ServerError("SettingsTwoFactor", err)
  79. return false
  80. }
  81. var imgBytes bytes.Buffer
  82. if err = png.Encode(&imgBytes, img); err != nil {
  83. ctx.ServerError("SettingsTwoFactor", err)
  84. return false
  85. }
  86. ctx.Data["QrUri"] = template.URL("data:image/png;base64," + base64.StdEncoding.EncodeToString(imgBytes.Bytes()))
  87. ctx.Session.Set("twofaSecret", otpKey.Secret())
  88. ctx.Session.Set("twofaUri", otpKey.String())
  89. return true
  90. }
  91. // EnrollTwoFactor shows the page where the user can enroll into 2FA.
  92. func EnrollTwoFactor(ctx *context.Context) {
  93. ctx.Data["Title"] = ctx.Tr("settings")
  94. ctx.Data["PageIsSettingsSecurity"] = true
  95. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  96. if t != nil {
  97. // already enrolled
  98. ctx.ServerError("SettingsTwoFactor", err)
  99. return
  100. }
  101. if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
  102. ctx.ServerError("SettingsTwoFactor", err)
  103. return
  104. }
  105. if !twofaGenerateSecretAndQr(ctx) {
  106. return
  107. }
  108. ctx.HTML(200, tplSettingsTwofaEnroll)
  109. }
  110. // EnrollTwoFactorPost handles enrolling the user into 2FA.
  111. func EnrollTwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) {
  112. ctx.Data["Title"] = ctx.Tr("settings")
  113. ctx.Data["PageIsSettingsSecurity"] = true
  114. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  115. if t != nil {
  116. // already enrolled
  117. ctx.ServerError("SettingsTwoFactor", err)
  118. return
  119. }
  120. if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
  121. ctx.ServerError("SettingsTwoFactor", err)
  122. return
  123. }
  124. if ctx.HasError() {
  125. if !twofaGenerateSecretAndQr(ctx) {
  126. return
  127. }
  128. ctx.HTML(200, tplSettingsTwofaEnroll)
  129. return
  130. }
  131. secret := ctx.Session.Get("twofaSecret").(string)
  132. if !totp.Validate(form.Passcode, secret) {
  133. if !twofaGenerateSecretAndQr(ctx) {
  134. return
  135. }
  136. ctx.Flash.Error(ctx.Tr("settings.passcode_invalid"))
  137. ctx.HTML(200, tplSettingsTwofaEnroll)
  138. return
  139. }
  140. t = &models.TwoFactor{
  141. UID: ctx.User.ID,
  142. }
  143. err = t.SetSecret(secret)
  144. if err != nil {
  145. ctx.ServerError("SettingsTwoFactor", err)
  146. return
  147. }
  148. token, err := t.GenerateScratchToken()
  149. if err != nil {
  150. ctx.ServerError("SettingsTwoFactor", err)
  151. return
  152. }
  153. if err = models.NewTwoFactor(t); err != nil {
  154. ctx.ServerError("SettingsTwoFactor", err)
  155. return
  156. }
  157. ctx.Session.Delete("twofaSecret")
  158. ctx.Session.Delete("twofaUri")
  159. ctx.Flash.Success(ctx.Tr("settings.twofa_enrolled", token))
  160. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  161. }