auth_token.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. package auth
  2. import (
  3. "context"
  4. "crypto/sha256"
  5. "encoding/hex"
  6. "strings"
  7. "time"
  8. "github.com/grafana/grafana/pkg/infra/serverlock"
  9. "github.com/grafana/grafana/pkg/infra/log"
  10. "github.com/grafana/grafana/pkg/models"
  11. "github.com/grafana/grafana/pkg/registry"
  12. "github.com/grafana/grafana/pkg/services/sqlstore"
  13. "github.com/grafana/grafana/pkg/setting"
  14. "github.com/grafana/grafana/pkg/util"
  15. )
  16. func init() {
  17. registry.RegisterService(&UserAuthTokenService{})
  18. }
  19. var getTime = time.Now
  20. const urgentRotateTime = 1 * time.Minute
  21. type UserAuthTokenService struct {
  22. SQLStore *sqlstore.SqlStore `inject:""`
  23. ServerLockService *serverlock.ServerLockService `inject:""`
  24. Cfg *setting.Cfg `inject:""`
  25. log log.Logger
  26. }
  27. func (s *UserAuthTokenService) Init() error {
  28. s.log = log.New("auth")
  29. return nil
  30. }
  31. func (s *UserAuthTokenService) ActiveTokenCount(ctx context.Context) (int64, error) {
  32. var count int64
  33. var err error
  34. err = s.SQLStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  35. var model userAuthToken
  36. count, err = dbSession.Where(`created_at > ? AND rotated_at > ?`,
  37. s.createdAfterParam(),
  38. s.rotatedAfterParam()).
  39. Count(&model)
  40. return err
  41. })
  42. return count, err
  43. }
  44. func (s *UserAuthTokenService) CreateToken(ctx context.Context, userId int64, clientIP, userAgent string) (*models.UserToken, error) {
  45. clientIP = util.ParseIPAddress(clientIP)
  46. token, err := util.RandomHex(16)
  47. if err != nil {
  48. return nil, err
  49. }
  50. hashedToken := hashToken(token)
  51. now := getTime().Unix()
  52. userAuthToken := userAuthToken{
  53. UserId: userId,
  54. AuthToken: hashedToken,
  55. PrevAuthToken: hashedToken,
  56. ClientIp: clientIP,
  57. UserAgent: userAgent,
  58. RotatedAt: now,
  59. CreatedAt: now,
  60. UpdatedAt: now,
  61. SeenAt: 0,
  62. AuthTokenSeen: false,
  63. }
  64. err = s.SQLStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  65. _, err = dbSession.Insert(&userAuthToken)
  66. return err
  67. })
  68. if err != nil {
  69. return nil, err
  70. }
  71. userAuthToken.UnhashedToken = token
  72. s.log.Debug("user auth token created", "tokenId", userAuthToken.Id, "userId", userAuthToken.UserId, "clientIP", userAuthToken.ClientIp, "userAgent", userAuthToken.UserAgent, "authToken", userAuthToken.AuthToken)
  73. var userToken models.UserToken
  74. err = userAuthToken.toUserToken(&userToken)
  75. return &userToken, err
  76. }
  77. func (s *UserAuthTokenService) LookupToken(ctx context.Context, unhashedToken string) (*models.UserToken, error) {
  78. hashedToken := hashToken(unhashedToken)
  79. if setting.Env == setting.DEV {
  80. s.log.Debug("looking up token", "unhashed", unhashedToken, "hashed", hashedToken)
  81. }
  82. var model userAuthToken
  83. var exists bool
  84. var err error
  85. err = s.SQLStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  86. exists, err = dbSession.Where("(auth_token = ? OR prev_auth_token = ?) AND created_at > ? AND rotated_at > ?",
  87. hashedToken,
  88. hashedToken,
  89. s.createdAfterParam(),
  90. s.rotatedAfterParam()).
  91. Get(&model)
  92. return err
  93. })
  94. if err != nil {
  95. return nil, err
  96. }
  97. if !exists {
  98. return nil, models.ErrUserTokenNotFound
  99. }
  100. if model.AuthToken != hashedToken && model.PrevAuthToken == hashedToken && model.AuthTokenSeen {
  101. modelCopy := model
  102. modelCopy.AuthTokenSeen = false
  103. expireBefore := getTime().Add(-urgentRotateTime).Unix()
  104. var affectedRows int64
  105. err = s.SQLStore.WithTransactionalDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  106. affectedRows, err = dbSession.Where("id = ? AND prev_auth_token = ? AND rotated_at < ?",
  107. modelCopy.Id,
  108. modelCopy.PrevAuthToken,
  109. expireBefore).
  110. AllCols().Update(&modelCopy)
  111. return err
  112. })
  113. if err != nil {
  114. return nil, err
  115. }
  116. if affectedRows == 0 {
  117. s.log.Debug("prev seen token unchanged", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent, "authToken", model.AuthToken)
  118. } else {
  119. s.log.Debug("prev seen token", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent, "authToken", model.AuthToken)
  120. }
  121. }
  122. if !model.AuthTokenSeen && model.AuthToken == hashedToken {
  123. modelCopy := model
  124. modelCopy.AuthTokenSeen = true
  125. modelCopy.SeenAt = getTime().Unix()
  126. var affectedRows int64
  127. err = s.SQLStore.WithTransactionalDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  128. affectedRows, err = dbSession.Where("id = ? AND auth_token = ?",
  129. modelCopy.Id,
  130. modelCopy.AuthToken).
  131. AllCols().Update(&modelCopy)
  132. return err
  133. })
  134. if err != nil {
  135. return nil, err
  136. }
  137. if affectedRows == 1 {
  138. model = modelCopy
  139. }
  140. if affectedRows == 0 {
  141. s.log.Debug("seen wrong token", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent, "authToken", model.AuthToken)
  142. } else {
  143. s.log.Debug("seen token", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent, "authToken", model.AuthToken)
  144. }
  145. }
  146. model.UnhashedToken = unhashedToken
  147. var userToken models.UserToken
  148. err = model.toUserToken(&userToken)
  149. return &userToken, err
  150. }
  151. func (s *UserAuthTokenService) TryRotateToken(ctx context.Context, token *models.UserToken, clientIP, userAgent string) (bool, error) {
  152. if token == nil {
  153. return false, nil
  154. }
  155. model := userAuthTokenFromUserToken(token)
  156. now := getTime()
  157. var needsRotation bool
  158. rotatedAt := time.Unix(model.RotatedAt, 0)
  159. if model.AuthTokenSeen {
  160. needsRotation = rotatedAt.Before(now.Add(-time.Duration(s.Cfg.TokenRotationIntervalMinutes) * time.Minute))
  161. } else {
  162. needsRotation = rotatedAt.Before(now.Add(-urgentRotateTime))
  163. }
  164. if !needsRotation {
  165. return false, nil
  166. }
  167. s.log.Debug("token needs rotation", "tokenId", model.Id, "authTokenSeen", model.AuthTokenSeen, "rotatedAt", rotatedAt)
  168. clientIP = util.ParseIPAddress(clientIP)
  169. newToken, err := util.RandomHex(16)
  170. if err != nil {
  171. return false, err
  172. }
  173. hashedToken := hashToken(newToken)
  174. // very important that auth_token_seen is set after the prev_auth_token = case when ... for mysql to function correctly
  175. sql := `
  176. UPDATE user_auth_token
  177. SET
  178. seen_at = 0,
  179. user_agent = ?,
  180. client_ip = ?,
  181. prev_auth_token = case when auth_token_seen = ? then auth_token else prev_auth_token end,
  182. auth_token = ?,
  183. auth_token_seen = ?,
  184. rotated_at = ?
  185. WHERE id = ? AND (auth_token_seen = ? OR rotated_at < ?)`
  186. var affected int64
  187. err = s.SQLStore.WithTransactionalDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  188. res, err := dbSession.Exec(sql, userAgent, clientIP, s.SQLStore.Dialect.BooleanStr(true), hashedToken, s.SQLStore.Dialect.BooleanStr(false), now.Unix(), model.Id, s.SQLStore.Dialect.BooleanStr(true), now.Add(-30*time.Second).Unix())
  189. if err != nil {
  190. return err
  191. }
  192. affected, err = res.RowsAffected()
  193. return err
  194. })
  195. if err != nil {
  196. return false, err
  197. }
  198. s.log.Debug("auth token rotated", "affected", affected, "auth_token_id", model.Id, "userId", model.UserId)
  199. if affected > 0 {
  200. model.UnhashedToken = newToken
  201. model.toUserToken(token)
  202. return true, nil
  203. }
  204. return false, nil
  205. }
  206. func (s *UserAuthTokenService) RevokeToken(ctx context.Context, token *models.UserToken) error {
  207. if token == nil {
  208. return models.ErrUserTokenNotFound
  209. }
  210. model := userAuthTokenFromUserToken(token)
  211. var rowsAffected int64
  212. var err error
  213. err = s.SQLStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  214. rowsAffected, err = dbSession.Delete(model)
  215. return err
  216. })
  217. if err != nil {
  218. return err
  219. }
  220. if rowsAffected == 0 {
  221. s.log.Debug("user auth token not found/revoked", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent)
  222. return models.ErrUserTokenNotFound
  223. }
  224. s.log.Debug("user auth token revoked", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent)
  225. return nil
  226. }
  227. func (s *UserAuthTokenService) RevokeAllUserTokens(ctx context.Context, userId int64) error {
  228. return s.SQLStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  229. sql := `DELETE from user_auth_token WHERE user_id = ?`
  230. res, err := dbSession.Exec(sql, userId)
  231. if err != nil {
  232. return err
  233. }
  234. affected, err := res.RowsAffected()
  235. if err != nil {
  236. return err
  237. }
  238. s.log.Debug("all user tokens for user revoked", "userId", userId, "count", affected)
  239. return err
  240. })
  241. }
  242. func (s *UserAuthTokenService) BatchRevokeAllUserTokens(ctx context.Context, userIds []int64) error {
  243. return s.SQLStore.WithTransactionalDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  244. if len(userIds) == 0 {
  245. return nil
  246. }
  247. user_id_params := strings.Repeat(",?", len(userIds)-1)
  248. sql := "DELETE from user_auth_token WHERE user_id IN (?" + user_id_params + ")"
  249. params := []interface{}{sql}
  250. for _, v := range userIds {
  251. params = append(params, v)
  252. }
  253. res, err := dbSession.Exec(params...)
  254. if err != nil {
  255. return err
  256. }
  257. affected, err := res.RowsAffected()
  258. if err != nil {
  259. return err
  260. }
  261. s.log.Debug("all user tokens for given users revoked", "usersCount", len(userIds), "count", affected)
  262. return err
  263. })
  264. }
  265. func (s *UserAuthTokenService) GetUserToken(ctx context.Context, userId, userTokenId int64) (*models.UserToken, error) {
  266. var result models.UserToken
  267. err := s.SQLStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  268. var token userAuthToken
  269. exists, err := dbSession.Where("id = ? AND user_id = ?", userTokenId, userId).Get(&token)
  270. if err != nil {
  271. return err
  272. }
  273. if !exists {
  274. return models.ErrUserTokenNotFound
  275. }
  276. token.toUserToken(&result)
  277. return nil
  278. })
  279. return &result, err
  280. }
  281. func (s *UserAuthTokenService) GetUserTokens(ctx context.Context, userId int64) ([]*models.UserToken, error) {
  282. result := []*models.UserToken{}
  283. err := s.SQLStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
  284. var tokens []*userAuthToken
  285. err := dbSession.Where("user_id = ? AND created_at > ? AND rotated_at > ?",
  286. userId,
  287. s.createdAfterParam(),
  288. s.rotatedAfterParam()).
  289. Find(&tokens)
  290. if err != nil {
  291. return err
  292. }
  293. for _, token := range tokens {
  294. var userToken models.UserToken
  295. token.toUserToken(&userToken)
  296. result = append(result, &userToken)
  297. }
  298. return nil
  299. })
  300. return result, err
  301. }
  302. func (s *UserAuthTokenService) createdAfterParam() int64 {
  303. tokenMaxLifetime := time.Duration(s.Cfg.LoginMaxLifetimeDays) * 24 * time.Hour
  304. return getTime().Add(-tokenMaxLifetime).Unix()
  305. }
  306. func (s *UserAuthTokenService) rotatedAfterParam() int64 {
  307. tokenMaxInactiveLifetime := time.Duration(s.Cfg.LoginMaxInactiveLifetimeDays) * 24 * time.Hour
  308. return getTime().Add(-tokenMaxInactiveLifetime).Unix()
  309. }
  310. func hashToken(token string) string {
  311. hashBytes := sha256.Sum256([]byte(token + setting.SecretKey))
  312. return hex.EncodeToString(hashBytes[:])
  313. }