auth_token.go 6.0 KB

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