auth_token.go 6.0 KB

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