auth_token.go 7.5 KB

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