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.

login.go 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package main
  2. import (
  3. "database/sql"
  4. "html/template"
  5. "net/http"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "net/url"
  10. "github.com/gin-gonic/gin"
  11. "golang.org/x/crypto/bcrypt"
  12. "zxq.co/ripple/rippleapi/common"
  13. "zxq.co/x/rs"
  14. )
  15. func loginSubmit(c *gin.Context) {
  16. if getContext(c).User.ID != 0 {
  17. simpleReply(c, errorMessage{T(c, "You're already logged in!")})
  18. return
  19. }
  20. if c.PostForm("username") == "" || c.PostForm("password") == "" {
  21. simpleReply(c, errorMessage{T(c, "Username or password not set.")})
  22. return
  23. }
  24. param := "username_safe"
  25. u := c.PostForm("username")
  26. if strings.Contains(u, "@") {
  27. param = "email"
  28. } else {
  29. u = safeUsername(u)
  30. }
  31. var data struct {
  32. ID int
  33. Username string
  34. Password string
  35. PasswordVersion int
  36. Country string
  37. pRaw int64
  38. Privileges common.UserPrivileges
  39. Flags uint
  40. }
  41. err := db.QueryRow(`
  42. SELECT
  43. u.id, u.password_md5,
  44. u.username, u.password_version,
  45. s.country, u.privileges, u.flags
  46. FROM users u
  47. LEFT JOIN users_stats s ON s.id = u.id
  48. WHERE u.`+param+` = ? LIMIT 1`, strings.TrimSpace(u)).Scan(
  49. &data.ID, &data.Password,
  50. &data.Username, &data.PasswordVersion,
  51. &data.Country, &data.pRaw, &data.Flags,
  52. )
  53. data.Privileges = common.UserPrivileges(data.pRaw)
  54. switch {
  55. case err == sql.ErrNoRows:
  56. if param == "username_safe" {
  57. param = "username"
  58. }
  59. simpleReply(c, errorMessage{T(c, "No user with such %s!", param)})
  60. return
  61. case err != nil:
  62. c.Error(err)
  63. resp500(c)
  64. return
  65. }
  66. if data.PasswordVersion == 1 {
  67. addMessage(c, warningMessage{T(c, "Your password is sooooooo old, that we don't even know how to deal with it anymore. Could you please change it?")})
  68. c.Redirect(302, "/pwreset")
  69. return
  70. }
  71. if err := bcrypt.CompareHashAndPassword(
  72. []byte(data.Password),
  73. []byte(cmd5(c.PostForm("password"))),
  74. ); err != nil {
  75. simpleReply(c, errorMessage{T(c, "Wrong password.")})
  76. return
  77. }
  78. // update password if cost is < bcrypt.DefaultCost
  79. if i, err := bcrypt.Cost([]byte(data.Password)); err == nil && i < bcrypt.DefaultCost {
  80. pass, err := bcrypt.GenerateFromPassword([]byte(cmd5(c.PostForm("password"))), bcrypt.DefaultCost)
  81. if err == nil {
  82. if _, err := db.Exec("UPDATE users SET password_md5 = ? WHERE id = ?", string(pass), data.ID); err == nil {
  83. data.Password = string(pass)
  84. }
  85. }
  86. }
  87. sess := getSession(c)
  88. if data.Privileges&common.UserPrivilegePendingVerification > 0 {
  89. setYCookie(data.ID, c)
  90. addMessage(c, warningMessage{T(c, "You will need to verify your account first.")})
  91. sess.Save()
  92. c.Redirect(302, "/register/verify?u="+strconv.Itoa(data.ID))
  93. return
  94. }
  95. if data.Privileges&common.UserPrivilegeNormal == 0 {
  96. simpleReply(c, errorMessage{T(c, "You are not allowed to login. This means your account is either banned or locked.")})
  97. return
  98. }
  99. setYCookie(data.ID, c)
  100. sess.Set("userid", data.ID)
  101. sess.Set("pw", cmd5(data.Password))
  102. sess.Set("logout", rs.String(15))
  103. tfaEnabled := is2faEnabled(data.ID)
  104. if tfaEnabled == 0 {
  105. afterLogin(c, data.ID, data.Country, data.Flags)
  106. } else {
  107. sess.Set("2fa_must_validate", true)
  108. }
  109. redir := c.PostForm("redir")
  110. if len(redir) > 0 && redir[0] != '/' {
  111. redir = ""
  112. }
  113. if tfaEnabled > 0 {
  114. sess.Save()
  115. c.Redirect(302, "/2fa_gateway?redir="+url.QueryEscape(redir))
  116. } else {
  117. addMessage(c, successMessage{T(c, "Hey %s! You are now logged in.", template.HTMLEscapeString(data.Username))})
  118. sess.Save()
  119. if redir == "" {
  120. redir = "/"
  121. }
  122. c.Redirect(302, redir)
  123. }
  124. return
  125. }
  126. func afterLogin(c *gin.Context, id int, country string, flags uint) {
  127. s, err := generateToken(id, c)
  128. if err != nil {
  129. resp500(c)
  130. c.Error(err)
  131. return
  132. }
  133. getSession(c).Set("token", s)
  134. if country == "XX" {
  135. setCountry(c, id)
  136. }
  137. logIP(c, id)
  138. }
  139. func safeUsername(u string) string {
  140. return strings.Replace(strings.TrimSpace(strings.ToLower(u)), " ", "_", -1)
  141. }
  142. func logout(c *gin.Context) {
  143. ctx := getContext(c)
  144. if ctx.User.ID == 0 {
  145. respEmpty(c, "Log out", warningMessage{T(c, "You're already logged out!")})
  146. return
  147. }
  148. sess := getSession(c)
  149. s, _ := sess.Get("logout").(string)
  150. if s != c.Query("k") {
  151. // todo: return "are you sure you want to log out?" page
  152. respEmpty(c, "Log out", warningMessage{T(c, "Your session has expired. Please try redoing what you were trying to do.")})
  153. return
  154. }
  155. sess.Clear()
  156. http.SetCookie(c.Writer, &http.Cookie{
  157. Name: "rt",
  158. Value: "",
  159. Expires: time.Now().Add(-time.Hour),
  160. })
  161. addMessage(c, successMessage{T(c, "Successfully logged out.")})
  162. sess.Save()
  163. c.Redirect(302, "/")
  164. }
  165. func generateToken(id int, c *gin.Context) (string, error) {
  166. tok := common.RandomString(32)
  167. _, err := db.Exec(
  168. `INSERT INTO tokens(user, privileges, description, token, private)
  169. VALUES ( ?, '0', ?, ?, '1');`,
  170. id, clientIP(c), cmd5(tok))
  171. if err != nil {
  172. return "", err
  173. }
  174. return tok, nil
  175. }
  176. func checkToken(s string, id int, c *gin.Context) (string, error) {
  177. if s == "" {
  178. return generateToken(id, c)
  179. }
  180. if err := db.QueryRow("SELECT 1 FROM tokens WHERE token = ?", cmd5(s)).Scan(new(int)); err == sql.ErrNoRows {
  181. return generateToken(id, c)
  182. } else if err != nil {
  183. return "", err
  184. }
  185. return s, nil
  186. }