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.

security_twofa.go 5.2KB

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