The new Ripple frontend.
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.

pw.go 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package main
  2. import (
  3. "database/sql"
  4. "strings"
  5. "github.com/gin-gonic/gin"
  6. "golang.org/x/crypto/bcrypt"
  7. "gopkg.in/mailgun/mailgun-go.v1"
  8. "zxq.co/ripple/rippleapi/common"
  9. "zxq.co/x/rs"
  10. )
  11. func passwordReset(c *gin.Context) {
  12. ctx := getContext(c)
  13. if ctx.User.ID != 0 {
  14. simpleReply(c, errorMessage{T(c, "You're already logged in!")})
  15. return
  16. }
  17. field := "username"
  18. if strings.Contains(c.PostForm("username"), "@") {
  19. field = "email"
  20. }
  21. var (
  22. id int
  23. username string
  24. email string
  25. privileges uint64
  26. )
  27. err := db.QueryRow("SELECT id, username, email, privileges FROM users WHERE "+field+" = ?",
  28. c.PostForm("username")).
  29. Scan(&id, &username, &email, &privileges)
  30. switch err {
  31. case nil:
  32. // ignore
  33. case sql.ErrNoRows:
  34. simpleReply(c, errorMessage{T(c, "That user could not be found.")})
  35. return
  36. default:
  37. c.Error(err)
  38. resp500(c)
  39. return
  40. }
  41. if common.UserPrivileges(privileges)&
  42. (common.UserPrivilegeNormal|common.UserPrivilegePendingVerification) == 0 {
  43. simpleReply(c, errorMessage{T(c, "You look pretty banned/locked here.")})
  44. return
  45. }
  46. // generate key
  47. key := rs.String(50)
  48. // TODO: WHY THE FUCK DOES THIS USE USERNAME AND NOT ID PLEASE WRITE MIGRATION
  49. _, err = db.Exec("INSERT INTO password_recovery(k, u) VALUES (?, ?)", key, username)
  50. if err != nil {
  51. c.Error(err)
  52. resp500(c)
  53. return
  54. }
  55. content := T(c,
  56. "Hey %s! Someone, which we really hope was you, requested a password reset for your account. In case it was you, please <a href='%s'>click here</a> to reset your password on Ripple. Otherwise, silently ignore this email.",
  57. username,
  58. config.BaseURL+"/pwreset/continue?k="+key,
  59. )
  60. msg := mailgun.NewMessage(
  61. config.MailgunFrom,
  62. T(c, "Ripple password recovery instructions"),
  63. content,
  64. email,
  65. )
  66. msg.SetHtml(content)
  67. _, _, err = mg.Send(msg)
  68. if err != nil {
  69. c.Error(err)
  70. resp500(c)
  71. return
  72. }
  73. addMessage(c, successMessage{T(c, "Done! You should shortly receive an email from us at the email you used to sign up on Ripple.")})
  74. getSession(c).Save()
  75. c.Redirect(302, "/")
  76. }
  77. func passwordResetContinue(c *gin.Context) {
  78. k := c.Query("k")
  79. // todo: check logged in
  80. if k == "" {
  81. respEmpty(c, T(c, "Password reset"), errorMessage{T(c, "Nope.")})
  82. return
  83. }
  84. var username string
  85. switch err := db.QueryRow("SELECT u FROM password_recovery WHERE k = ? LIMIT 1", k).
  86. Scan(&username); err {
  87. case nil:
  88. // move on
  89. case sql.ErrNoRows:
  90. respEmpty(c, T(c, "Reset password"), errorMessage{T(c, "That key could not be found. Perhaps it expired?")})
  91. return
  92. default:
  93. c.Error(err)
  94. resp500(c)
  95. return
  96. }
  97. renderResetPassword(c, username, k)
  98. }
  99. func passwordResetContinueSubmit(c *gin.Context) {
  100. // todo: check logged in
  101. var username string
  102. switch err := db.QueryRow("SELECT u FROM password_recovery WHERE k = ? LIMIT 1", c.PostForm("k")).
  103. Scan(&username); err {
  104. case nil:
  105. // move on
  106. case sql.ErrNoRows:
  107. respEmpty(c, T(c, "Reset password"), errorMessage{T(c, "That key could not be found. Perhaps it expired?")})
  108. return
  109. default:
  110. c.Error(err)
  111. resp500(c)
  112. return
  113. }
  114. p := c.PostForm("password")
  115. if s := validatePassword(p); s != "" {
  116. renderResetPassword(c, username, c.PostForm("k"), errorMessage{T(c, s)})
  117. return
  118. }
  119. pass, err := generatePassword(p)
  120. if err != nil {
  121. c.Error(err)
  122. resp500(c)
  123. return
  124. }
  125. _, err = db.Exec("UPDATE users SET password_md5 = ?, salt = '', password_version = '2' WHERE username = ?",
  126. pass, username)
  127. if err != nil {
  128. c.Error(err)
  129. resp500(c)
  130. return
  131. }
  132. _, err = db.Exec("DELETE FROM password_recovery WHERE k = ? LIMIT 1", c.PostForm("k"))
  133. if err != nil {
  134. c.Error(err)
  135. resp500(c)
  136. return
  137. }
  138. addMessage(c, successMessage{T(c, "All right, we have changed your password and you should now be able to login! Have fun!")})
  139. getSession(c).Save()
  140. c.Redirect(302, "/login")
  141. }
  142. func renderResetPassword(c *gin.Context, username, k string, messages ...message) {
  143. simple(c, getSimpleByFilename("pwreset/continue.html"), messages, map[string]interface{}{
  144. "Username": username,
  145. "Key": k,
  146. })
  147. }
  148. func generatePassword(p string) (string, error) {
  149. s, err := bcrypt.GenerateFromPassword([]byte(cmd5(p)), bcrypt.DefaultCost)
  150. return string(s), err
  151. }
  152. func changePassword(c *gin.Context) {
  153. ctx := getContext(c)
  154. if ctx.User.ID == 0 {
  155. resp403(c)
  156. }
  157. s, err := qb.QueryRow("SELECT email FROM users WHERE id = ?", ctx.User.ID)
  158. if err != nil {
  159. c.Error(err)
  160. }
  161. simple(c, getSimpleByFilename("settings/password.html"), nil, map[string]interface{}{
  162. "email": s["email"],
  163. })
  164. }
  165. func changePasswordSubmit(c *gin.Context) {
  166. var messages []message
  167. ctx := getContext(c)
  168. if ctx.User.ID == 0 {
  169. resp403(c)
  170. }
  171. defer func() {
  172. s, err := qb.QueryRow("SELECT email FROM users WHERE id = ?", ctx.User.ID)
  173. if err != nil {
  174. c.Error(err)
  175. }
  176. simple(c, getSimpleByFilename("settings/password.html"), messages, map[string]interface{}{
  177. "email": s["email"],
  178. })
  179. }()
  180. if ok, _ := CSRF.Validate(ctx.User.ID, c.PostForm("csrf")); !ok {
  181. addMessage(c, errorMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
  182. return
  183. }
  184. var password string
  185. db.Get(&password, "SELECT password_md5 FROM users WHERE id = ? LIMIT 1", ctx.User.ID)
  186. if err := bcrypt.CompareHashAndPassword(
  187. []byte(password),
  188. []byte(cmd5(c.PostForm("currentpassword"))),
  189. ); err != nil {
  190. messages = append(messages, errorMessage{T(c, "Wrong password.")})
  191. return
  192. }
  193. uq := new(common.UpdateQuery)
  194. uq.Add("email", c.PostForm("email"))
  195. if c.PostForm("newpassword") != "" {
  196. if s := validatePassword(c.PostForm("newpassword")); s != "" {
  197. messages = append(messages, errorMessage{T(c, s)})
  198. return
  199. }
  200. pw, err := generatePassword(c.PostForm("newpassword"))
  201. if err == nil {
  202. uq.Add("password_md5", pw)
  203. }
  204. sess := getSession(c)
  205. sess.Set("pw", cmd5(pw))
  206. sess.Save()
  207. }
  208. _, err := db.Exec("UPDATE users SET "+uq.Fields()+" WHERE id = ? LIMIT 1", append(uq.Parameters, ctx.User.ID)...)
  209. if err != nil {
  210. c.Error(err)
  211. }
  212. db.Exec("UPDATE users SET flags = flags & ~3 WHERE id = ? LIMIT 1", ctx.User.ID)
  213. messages = append(messages, successMessage{T(c, "Your settings have been saved.")})
  214. }