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

security_twofa.go 4.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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. // Filter unsafe character ':' in issuer
  64. issuer := strings.Replace(setting.AppName+" ("+setting.Domain+")", ":", "", -1)
  65. if otpKey == nil {
  66. err = nil // clear the error, in case the URL was invalid
  67. otpKey, err = totp.Generate(totp.GenerateOpts{
  68. SecretSize: 40,
  69. Issuer: issuer,
  70. AccountName: ctx.User.Name,
  71. })
  72. if err != nil {
  73. ctx.ServerError("SettingsTwoFactor", err)
  74. return false
  75. }
  76. }
  77. ctx.Data["TwofaSecret"] = otpKey.Secret()
  78. img, err := otpKey.Image(320, 240)
  79. if err != nil {
  80. ctx.ServerError("SettingsTwoFactor", err)
  81. return false
  82. }
  83. var imgBytes bytes.Buffer
  84. if err = png.Encode(&imgBytes, img); err != nil {
  85. ctx.ServerError("SettingsTwoFactor", err)
  86. return false
  87. }
  88. ctx.Data["QrUri"] = template.URL("data:image/png;base64," + base64.StdEncoding.EncodeToString(imgBytes.Bytes()))
  89. ctx.Session.Set("twofaSecret", otpKey.Secret())
  90. ctx.Session.Set("twofaUri", otpKey.String())
  91. return true
  92. }
  93. // EnrollTwoFactor shows the page where the user can enroll into 2FA.
  94. func EnrollTwoFactor(ctx *context.Context) {
  95. ctx.Data["Title"] = ctx.Tr("settings")
  96. ctx.Data["PageIsSettingsSecurity"] = true
  97. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  98. if t != nil {
  99. // already enrolled
  100. ctx.ServerError("SettingsTwoFactor", err)
  101. return
  102. }
  103. if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
  104. ctx.ServerError("SettingsTwoFactor", err)
  105. return
  106. }
  107. if !twofaGenerateSecretAndQr(ctx) {
  108. return
  109. }
  110. ctx.HTML(200, tplSettingsTwofaEnroll)
  111. }
  112. // EnrollTwoFactorPost handles enrolling the user into 2FA.
  113. func EnrollTwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) {
  114. ctx.Data["Title"] = ctx.Tr("settings")
  115. ctx.Data["PageIsSettingsSecurity"] = true
  116. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  117. if t != nil {
  118. // already enrolled
  119. ctx.ServerError("SettingsTwoFactor", err)
  120. return
  121. }
  122. if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
  123. ctx.ServerError("SettingsTwoFactor", err)
  124. return
  125. }
  126. if ctx.HasError() {
  127. if !twofaGenerateSecretAndQr(ctx) {
  128. return
  129. }
  130. ctx.HTML(200, tplSettingsTwofaEnroll)
  131. return
  132. }
  133. secret := ctx.Session.Get("twofaSecret").(string)
  134. if !totp.Validate(form.Passcode, secret) {
  135. if !twofaGenerateSecretAndQr(ctx) {
  136. return
  137. }
  138. ctx.Flash.Error(ctx.Tr("settings.passcode_invalid"))
  139. ctx.HTML(200, tplSettingsTwofaEnroll)
  140. return
  141. }
  142. t = &models.TwoFactor{
  143. UID: ctx.User.ID,
  144. }
  145. err = t.SetSecret(secret)
  146. if err != nil {
  147. ctx.ServerError("SettingsTwoFactor", err)
  148. return
  149. }
  150. token, err := t.GenerateScratchToken()
  151. if err != nil {
  152. ctx.ServerError("SettingsTwoFactor", err)
  153. return
  154. }
  155. if err = models.NewTwoFactor(t); err != nil {
  156. ctx.ServerError("SettingsTwoFactor", err)
  157. return
  158. }
  159. ctx.Session.Delete("twofaSecret")
  160. ctx.Session.Delete("twofaUri")
  161. ctx.Flash.Success(ctx.Tr("settings.twofa_enrolled", token))
  162. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  163. }