auth_token.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. package auth
  2. import (
  3. "crypto/sha256"
  4. "encoding/hex"
  5. "net/http"
  6. "net/url"
  7. "time"
  8. "github.com/grafana/grafana/pkg/log"
  9. "github.com/grafana/grafana/pkg/models"
  10. "github.com/grafana/grafana/pkg/registry"
  11. "github.com/grafana/grafana/pkg/services/sqlstore"
  12. "github.com/grafana/grafana/pkg/setting"
  13. "github.com/grafana/grafana/pkg/util"
  14. )
  15. func init() {
  16. registry.RegisterService(&UserAuthTokenService{})
  17. }
  18. var now = time.Now
  19. // UserAuthTokenService are used for generating and validating user auth tokens
  20. type UserAuthTokenService struct {
  21. SQLStore *sqlstore.SqlStore `inject:""`
  22. log log.Logger
  23. }
  24. // Init this service
  25. func (s *UserAuthTokenService) Init() error {
  26. s.log = log.New("auth")
  27. return nil
  28. }
  29. const sessionCookieKey = "grafana_session"
  30. func (s *UserAuthTokenService) UserAuthenticatedHook(user *models.User, c *models.ReqContext) error {
  31. userToken, err := s.CreateToken(user.Id, c.RemoteAddr(), c.Req.UserAgent())
  32. if err != nil {
  33. return err
  34. }
  35. c.Resp.Header().Del("Set-Cookie")
  36. cookie := http.Cookie{
  37. Name: sessionCookieKey,
  38. Value: url.QueryEscape(userToken.UnhashedToken),
  39. HttpOnly: true,
  40. MaxAge: int(time.Minute * 10),
  41. Domain: setting.Domain,
  42. Path: setting.AppSubUrl + "/",
  43. }
  44. c.Resp.Header().Add("Set-Cookie", cookie.String())
  45. return nil
  46. }
  47. func (s *UserAuthTokenService) UserSignedOutHook(c *models.ReqContext) {
  48. //c.SetCookie(sessionCookieKey, "", -1, setting.AppSubUrl+"/", setting.Domain, false, true)
  49. c.Resp.Header().Del("Set-Cookie")
  50. cookie := http.Cookie{
  51. Name: sessionCookieKey,
  52. Value: "",
  53. HttpOnly: true,
  54. MaxAge: -1,
  55. Domain: setting.Domain,
  56. Path: setting.AppSubUrl + "/",
  57. }
  58. c.Resp.Header().Add("Set-Cookie", cookie.String())
  59. }
  60. // func (s *UserAuthTokenService) RequestMiddleware() macaron.Handler {
  61. // return func(ctx *models.ReqContext) {
  62. // authToken := ctx.GetCookie(sessionCookieKey)
  63. // userToken, err := s.LookupToken(authToken)
  64. // if err != nil {
  65. // }
  66. // ctx.Next()
  67. // refreshed, err := s.RefreshToken(userToken, ctx.RemoteAddr(), ctx.Req.UserAgent())
  68. // if err != nil {
  69. // }
  70. // if refreshed {
  71. // ctx.Resp.Header().Del("Set-Cookie")
  72. // ctx.SetCookie(sessionCookieKey, userToken.unhashedToken, setting.AppSubUrl+"/", setting.Domain, false, true)
  73. // }
  74. // }
  75. // }
  76. func (s *UserAuthTokenService) CreateToken(userId int64, clientIP, userAgent string) (*models.UserAuthToken, error) {
  77. clientIP = util.ParseIPAddress(clientIP)
  78. token, err := util.RandomHex(16)
  79. if err != nil {
  80. return nil, err
  81. }
  82. hashedToken := hashToken(token)
  83. userToken := models.UserAuthToken{
  84. UserId: userId,
  85. AuthToken: hashedToken,
  86. PrevAuthToken: hashedToken,
  87. ClientIp: clientIP,
  88. UserAgent: userAgent,
  89. RotatedAt: now().Unix(),
  90. CreatedAt: now().Unix(),
  91. UpdatedAt: now().Unix(),
  92. SeenAt: 0,
  93. AuthTokenSeen: false,
  94. }
  95. _, err = s.SQLStore.NewSession().Insert(&userToken)
  96. if err != nil {
  97. return nil, err
  98. }
  99. userToken.UnhashedToken = token
  100. return &userToken, nil
  101. }
  102. func (s *UserAuthTokenService) LookupToken(unhashedToken string) (*models.UserAuthToken, error) {
  103. hashedToken := hashToken(unhashedToken)
  104. var userToken models.UserAuthToken
  105. exists, err := s.SQLStore.NewSession().Where("auth_token = ? OR prev_auth_token = ?", hashedToken, hashedToken).Get(&userToken)
  106. if err != nil {
  107. return nil, err
  108. }
  109. if !exists {
  110. return nil, ErrAuthTokenNotFound
  111. }
  112. if userToken.AuthToken != hashedToken && userToken.PrevAuthToken == hashedToken && userToken.AuthTokenSeen {
  113. userToken.AuthTokenSeen = false
  114. expireBefore := now().Add(-1 * time.Minute).Unix()
  115. affectedRows, err := s.SQLStore.NewSession().Where("id = ? AND prev_auth_token = ? AND rotated_at < ?", userToken.Id, userToken.PrevAuthToken, expireBefore).AllCols().Update(&userToken)
  116. if err != nil {
  117. return nil, err
  118. }
  119. if affectedRows == 0 {
  120. s.log.Debug("prev seen token unchanged", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent)
  121. } else {
  122. s.log.Debug("prev seen token", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent)
  123. }
  124. }
  125. if !userToken.AuthTokenSeen && userToken.AuthToken == hashedToken {
  126. userTokenCopy := userToken
  127. userTokenCopy.AuthTokenSeen = true
  128. userTokenCopy.SeenAt = now().Unix()
  129. affectedRows, err := s.SQLStore.NewSession().Where("id = ? AND auth_token = ?", userTokenCopy.Id, userTokenCopy.AuthToken).AllCols().Update(&userTokenCopy)
  130. if err != nil {
  131. return nil, err
  132. }
  133. if affectedRows == 1 {
  134. userToken = userTokenCopy
  135. }
  136. if affectedRows == 0 {
  137. s.log.Debug("seen wrong token", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent)
  138. } else {
  139. s.log.Debug("seen token", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent)
  140. }
  141. }
  142. userToken.UnhashedToken = unhashedToken
  143. return &userToken, nil
  144. }
  145. func (s *UserAuthTokenService) RefreshToken(token *models.UserAuthToken, clientIP, userAgent string) (bool, error) {
  146. if token == nil {
  147. return false, nil
  148. }
  149. var needsRotation = false
  150. rotatedAt := time.Unix(token.RotatedAt, 0)
  151. if token.AuthTokenSeen {
  152. needsRotation = rotatedAt.Before(now().Add(time.Duration(-1) * time.Minute))
  153. } else {
  154. needsRotation = rotatedAt.Before(now().Add(time.Duration(-30) * time.Second))
  155. }
  156. s.log.Info("refresh token", "needs rotation?", needsRotation, "auth_token_seen", token.AuthTokenSeen, "rotated_at", rotatedAt, "token.Id", token.Id)
  157. if !needsRotation {
  158. return false, nil
  159. }
  160. newToken, _ := util.RandomHex(16)
  161. hashedToken := hashToken(newToken)
  162. sql := `
  163. UPDATE user_auth_token
  164. SET
  165. auth_token_seen = false,
  166. seen_at = null,
  167. user_agent = ?,
  168. client_ip = ?,
  169. prev_auth_token = case when auth_token_seen then auth_token else prev_auth_token end,
  170. auth_token = ?,
  171. rotated_at = ?
  172. WHERE id = ? AND (auth_token_seen or rotated_at < ?)`
  173. res, err := s.SQLStore.NewSession().Exec(sql, userAgent, clientIP, hashedToken, now().Unix(), token.Id, now().Add(time.Duration(-30)*time.Second))
  174. if err != nil {
  175. return false, err
  176. }
  177. affected, _ := res.RowsAffected()
  178. s.log.Info("rotated", "affected", affected, "auth_token_id", token.Id, "userId", token.UserId, "user_agent", userAgent, "client_ip", clientIP)
  179. if affected > 0 {
  180. token.UnhashedToken = newToken
  181. return true, nil
  182. }
  183. return false, nil
  184. }
  185. func hashToken(token string) string {
  186. hashBytes := sha256.Sum256([]byte(token + setting.SecretKey))
  187. return hex.EncodeToString(hashBytes[:])
  188. }