mirror of https://github.com/grafana/grafana
Merge pull request #15239 from grafana/auth_token_middleware_refactor
Auth token package and middleware refactoringpull/15198/head^2
commit
c71904e326
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,32 @@ |
|||||||
|
package models |
||||||
|
|
||||||
|
import "errors" |
||||||
|
|
||||||
|
// Typed errors
|
||||||
|
var ( |
||||||
|
ErrUserTokenNotFound = errors.New("user token not found") |
||||||
|
) |
||||||
|
|
||||||
|
// UserToken represents a user token
|
||||||
|
type UserToken struct { |
||||||
|
Id int64 |
||||||
|
UserId int64 |
||||||
|
AuthToken string |
||||||
|
PrevAuthToken string |
||||||
|
UserAgent string |
||||||
|
ClientIp string |
||||||
|
AuthTokenSeen bool |
||||||
|
SeenAt int64 |
||||||
|
RotatedAt int64 |
||||||
|
CreatedAt int64 |
||||||
|
UpdatedAt int64 |
||||||
|
UnhashedToken string |
||||||
|
} |
||||||
|
|
||||||
|
// UserTokenService are used for generating and validating user tokens
|
||||||
|
type UserTokenService interface { |
||||||
|
CreateToken(userId int64, clientIP, userAgent string) (*UserToken, error) |
||||||
|
LookupToken(unhashedToken string) (*UserToken, error) |
||||||
|
TryRotateToken(token *UserToken, clientIP, userAgent string) (bool, error) |
||||||
|
RevokeToken(token *UserToken) error |
||||||
|
} |
@ -1,38 +0,0 @@ |
|||||||
package auth |
|
||||||
|
|
||||||
import ( |
|
||||||
"context" |
|
||||||
"time" |
|
||||||
) |
|
||||||
|
|
||||||
func (srv *UserAuthTokenServiceImpl) Run(ctx context.Context) error { |
|
||||||
ticker := time.NewTicker(time.Hour * 12) |
|
||||||
deleteSessionAfter := time.Hour * 24 * time.Duration(srv.Cfg.LoginDeleteExpiredTokensAfterDays) |
|
||||||
|
|
||||||
for { |
|
||||||
select { |
|
||||||
case <-ticker.C: |
|
||||||
srv.ServerLockService.LockAndExecute(ctx, "delete old sessions", time.Hour*12, func() { |
|
||||||
srv.deleteOldSession(deleteSessionAfter) |
|
||||||
}) |
|
||||||
|
|
||||||
case <-ctx.Done(): |
|
||||||
return ctx.Err() |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (srv *UserAuthTokenServiceImpl) deleteOldSession(deleteSessionAfter time.Duration) (int64, error) { |
|
||||||
sql := `DELETE from user_auth_token WHERE rotated_at < ?` |
|
||||||
|
|
||||||
deleteBefore := getTime().Add(-deleteSessionAfter) |
|
||||||
res, err := srv.SQLStore.NewSession().Exec(sql, deleteBefore.Unix()) |
|
||||||
if err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
|
|
||||||
affected, err := res.RowsAffected() |
|
||||||
srv.log.Info("deleted old sessions", "count", affected) |
|
||||||
|
|
||||||
return affected, err |
|
||||||
} |
|
@ -1,36 +0,0 @@ |
|||||||
package auth |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"testing" |
|
||||||
"time" |
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey" |
|
||||||
) |
|
||||||
|
|
||||||
func TestUserAuthTokenCleanup(t *testing.T) { |
|
||||||
|
|
||||||
Convey("Test user auth token cleanup", t, func() { |
|
||||||
ctx := createTestContext(t) |
|
||||||
|
|
||||||
insertToken := func(token string, prev string, rotatedAt int64) { |
|
||||||
ut := userAuthToken{AuthToken: token, PrevAuthToken: prev, RotatedAt: rotatedAt, UserAgent: "", ClientIp: ""} |
|
||||||
_, err := ctx.sqlstore.NewSession().Insert(&ut) |
|
||||||
So(err, ShouldBeNil) |
|
||||||
} |
|
||||||
|
|
||||||
// insert three old tokens that should be deleted
|
|
||||||
for i := 0; i < 3; i++ { |
|
||||||
insertToken(fmt.Sprintf("oldA%d", i), fmt.Sprintf("oldB%d", i), int64(i)) |
|
||||||
} |
|
||||||
|
|
||||||
// insert three active tokens that should not be deleted
|
|
||||||
for i := 0; i < 3; i++ { |
|
||||||
insertToken(fmt.Sprintf("newA%d", i), fmt.Sprintf("newB%d", i), getTime().Unix()) |
|
||||||
} |
|
||||||
|
|
||||||
affected, err := ctx.tokenService.deleteOldSession(time.Hour) |
|
||||||
So(err, ShouldBeNil) |
|
||||||
So(affected, ShouldEqual, 3) |
|
||||||
}) |
|
||||||
} |
|
@ -0,0 +1,57 @@ |
|||||||
|
package auth |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
func (srv *UserAuthTokenService) Run(ctx context.Context) error { |
||||||
|
ticker := time.NewTicker(time.Hour) |
||||||
|
maxInactiveLifetime := time.Duration(srv.Cfg.LoginMaxInactiveLifetimeDays) * 24 * time.Hour |
||||||
|
maxLifetime := time.Duration(srv.Cfg.LoginMaxLifetimeDays) * 24 * time.Hour |
||||||
|
|
||||||
|
err := srv.ServerLockService.LockAndExecute(ctx, "cleanup expired auth tokens", time.Hour*12, func() { |
||||||
|
srv.deleteExpiredTokens(maxInactiveLifetime, maxLifetime) |
||||||
|
}) |
||||||
|
if err != nil { |
||||||
|
srv.log.Error("failed to lock and execite cleanup of expired auth token", "erro", err) |
||||||
|
} |
||||||
|
|
||||||
|
for { |
||||||
|
select { |
||||||
|
case <-ticker.C: |
||||||
|
err := srv.ServerLockService.LockAndExecute(ctx, "cleanup expired auth tokens", time.Hour*12, func() { |
||||||
|
srv.deleteExpiredTokens(maxInactiveLifetime, maxLifetime) |
||||||
|
}) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
srv.log.Error("failed to lock and execite cleanup of expired auth token", "erro", err) |
||||||
|
} |
||||||
|
|
||||||
|
case <-ctx.Done(): |
||||||
|
return ctx.Err() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (srv *UserAuthTokenService) deleteExpiredTokens(maxInactiveLifetime, maxLifetime time.Duration) (int64, error) { |
||||||
|
createdBefore := getTime().Add(-maxLifetime) |
||||||
|
rotatedBefore := getTime().Add(-maxInactiveLifetime) |
||||||
|
|
||||||
|
srv.log.Debug("starting cleanup of expired auth tokens", "createdBefore", createdBefore, "rotatedBefore", rotatedBefore) |
||||||
|
|
||||||
|
sql := `DELETE from user_auth_token WHERE created_at <= ? OR rotated_at <= ?` |
||||||
|
res, err := srv.SQLStore.NewSession().Exec(sql, createdBefore.Unix(), rotatedBefore.Unix()) |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
|
||||||
|
affected, err := res.RowsAffected() |
||||||
|
if err != nil { |
||||||
|
srv.log.Error("failed to cleanup expired auth tokens", "error", err) |
||||||
|
return 0, nil |
||||||
|
} |
||||||
|
|
||||||
|
srv.log.Info("cleanup of expired auth tokens done", "count", affected) |
||||||
|
return affected, err |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
package auth |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
|
||||||
|
. "github.com/smartystreets/goconvey/convey" |
||||||
|
) |
||||||
|
|
||||||
|
func TestUserAuthTokenCleanup(t *testing.T) { |
||||||
|
|
||||||
|
Convey("Test user auth token cleanup", t, func() { |
||||||
|
ctx := createTestContext(t) |
||||||
|
ctx.tokenService.Cfg.LoginMaxInactiveLifetimeDays = 7 |
||||||
|
ctx.tokenService.Cfg.LoginMaxLifetimeDays = 30 |
||||||
|
|
||||||
|
insertToken := func(token string, prev string, createdAt, rotatedAt int64) { |
||||||
|
ut := userAuthToken{AuthToken: token, PrevAuthToken: prev, CreatedAt: createdAt, RotatedAt: rotatedAt, UserAgent: "", ClientIp: ""} |
||||||
|
_, err := ctx.sqlstore.NewSession().Insert(&ut) |
||||||
|
So(err, ShouldBeNil) |
||||||
|
} |
||||||
|
|
||||||
|
t := time.Date(2018, 12, 13, 13, 45, 0, 0, time.UTC) |
||||||
|
getTime = func() time.Time { |
||||||
|
return t |
||||||
|
} |
||||||
|
|
||||||
|
Convey("should delete tokens where token rotation age is older than or equal 7 days", func() { |
||||||
|
from := t.Add(-7 * 24 * time.Hour) |
||||||
|
|
||||||
|
// insert three old tokens that should be deleted
|
||||||
|
for i := 0; i < 3; i++ { |
||||||
|
insertToken(fmt.Sprintf("oldA%d", i), fmt.Sprintf("oldB%d", i), from.Unix(), from.Unix()) |
||||||
|
} |
||||||
|
|
||||||
|
// insert three active tokens that should not be deleted
|
||||||
|
for i := 0; i < 3; i++ { |
||||||
|
from = from.Add(time.Second) |
||||||
|
insertToken(fmt.Sprintf("newA%d", i), fmt.Sprintf("newB%d", i), from.Unix(), from.Unix()) |
||||||
|
} |
||||||
|
|
||||||
|
affected, err := ctx.tokenService.deleteExpiredTokens(7*24*time.Hour, 30*24*time.Hour) |
||||||
|
So(err, ShouldBeNil) |
||||||
|
So(affected, ShouldEqual, 3) |
||||||
|
}) |
||||||
|
|
||||||
|
Convey("should delete tokens where token age is older than or equal 30 days", func() { |
||||||
|
from := t.Add(-30 * 24 * time.Hour) |
||||||
|
fromRotate := t.Add(-time.Second) |
||||||
|
|
||||||
|
// insert three old tokens that should be deleted
|
||||||
|
for i := 0; i < 3; i++ { |
||||||
|
insertToken(fmt.Sprintf("oldA%d", i), fmt.Sprintf("oldB%d", i), from.Unix(), fromRotate.Unix()) |
||||||
|
} |
||||||
|
|
||||||
|
// insert three active tokens that should not be deleted
|
||||||
|
for i := 0; i < 3; i++ { |
||||||
|
from = from.Add(time.Second) |
||||||
|
insertToken(fmt.Sprintf("newA%d", i), fmt.Sprintf("newB%d", i), from.Unix(), fromRotate.Unix()) |
||||||
|
} |
||||||
|
|
||||||
|
affected, err := ctx.tokenService.deleteExpiredTokens(7*24*time.Hour, 30*24*time.Hour) |
||||||
|
So(err, ShouldBeNil) |
||||||
|
So(affected, ShouldEqual, 3) |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
Loading…
Reference in new issue