diff --git a/pkg/services/auth/auth.go b/pkg/services/auth/auth.go new file mode 100644 index 00000000000..31316f473f5 --- /dev/null +++ b/pkg/services/auth/auth.go @@ -0,0 +1,6 @@ +package auth + +type UserToken interface { + GetUserId() int64 + GetToken() string +} diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go deleted file mode 100644 index 13b9ef607f5..00000000000 --- a/pkg/services/auth/auth_token.go +++ /dev/null @@ -1,279 +0,0 @@ -package auth - -import ( - "crypto/sha256" - "encoding/hex" - "errors" - "net/http" - "net/url" - "time" - - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/infra/serverlock" - "github.com/grafana/grafana/pkg/log" - "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/registry" - "github.com/grafana/grafana/pkg/services/sqlstore" - "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/util" -) - -func init() { - registry.RegisterService(&UserAuthTokenServiceImpl{}) -} - -var ( - getTime = time.Now - UrgentRotateTime = 1 * time.Minute - oneYearInSeconds = 31557600 //used as default maxage for session cookies. We validate/rotate them more often. -) - -// UserAuthTokenService are used for generating and validating user auth tokens -type UserAuthTokenService interface { - InitContextWithToken(ctx *models.ReqContext, orgID int64) bool - UserAuthenticatedHook(user *models.User, c *models.ReqContext) error - SignOutUser(c *models.ReqContext) error -} - -type UserAuthTokenServiceImpl struct { - SQLStore *sqlstore.SqlStore `inject:""` - ServerLockService *serverlock.ServerLockService `inject:""` - Cfg *setting.Cfg `inject:""` - log log.Logger -} - -// Init this service -func (s *UserAuthTokenServiceImpl) Init() error { - s.log = log.New("auth") - return nil -} - -func (s *UserAuthTokenServiceImpl) InitContextWithToken(ctx *models.ReqContext, orgID int64) bool { - //auth User - unhashedToken := ctx.GetCookie(s.Cfg.LoginCookieName) - if unhashedToken == "" { - return false - } - - userToken, err := s.LookupToken(unhashedToken) - if err != nil { - ctx.Logger.Info("failed to look up user based on cookie", "error", err) - return false - } - - query := models.GetSignedInUserQuery{UserId: userToken.UserId, OrgId: orgID} - if err := bus.Dispatch(&query); err != nil { - ctx.Logger.Error("Failed to get user with id", "userId", userToken.UserId, "error", err) - return false - } - - ctx.SignedInUser = query.Result - ctx.IsSignedIn = true - - //rotate session token if needed. - rotated, err := s.RefreshToken(userToken, ctx.RemoteAddr(), ctx.Req.UserAgent()) - if err != nil { - ctx.Logger.Error("failed to rotate token", "error", err, "userId", userToken.UserId, "tokenId", userToken.Id) - return true - } - - if rotated { - s.writeSessionCookie(ctx, userToken.UnhashedToken, oneYearInSeconds) - } - - return true -} - -func (s *UserAuthTokenServiceImpl) writeSessionCookie(ctx *models.ReqContext, value string, maxAge int) { - if setting.Env == setting.DEV { - ctx.Logger.Debug("new token", "unhashed token", value) - } - - ctx.Resp.Header().Del("Set-Cookie") - cookie := http.Cookie{ - Name: s.Cfg.LoginCookieName, - Value: url.QueryEscape(value), - HttpOnly: true, - Path: setting.AppSubUrl + "/", - Secure: s.Cfg.SecurityHTTPSCookies, - MaxAge: maxAge, - SameSite: s.Cfg.LoginCookieSameSite, - } - - http.SetCookie(ctx.Resp, &cookie) -} - -func (s *UserAuthTokenServiceImpl) UserAuthenticatedHook(user *models.User, c *models.ReqContext) error { - userToken, err := s.CreateToken(user.Id, c.RemoteAddr(), c.Req.UserAgent()) - if err != nil { - return err - } - - s.writeSessionCookie(c, userToken.UnhashedToken, oneYearInSeconds) - return nil -} - -func (s *UserAuthTokenServiceImpl) SignOutUser(c *models.ReqContext) error { - unhashedToken := c.GetCookie(s.Cfg.LoginCookieName) - if unhashedToken == "" { - return errors.New("cannot logout without session token") - } - - hashedToken := hashToken(unhashedToken) - - sql := `DELETE FROM user_auth_token WHERE auth_token = ?` - _, err := s.SQLStore.NewSession().Exec(sql, hashedToken) - - s.writeSessionCookie(c, "", -1) - return err -} - -func (s *UserAuthTokenServiceImpl) CreateToken(userId int64, clientIP, userAgent string) (*userAuthToken, error) { - clientIP = util.ParseIPAddress(clientIP) - token, err := util.RandomHex(16) - if err != nil { - return nil, err - } - - hashedToken := hashToken(token) - - now := getTime().Unix() - - userToken := userAuthToken{ - UserId: userId, - AuthToken: hashedToken, - PrevAuthToken: hashedToken, - ClientIp: clientIP, - UserAgent: userAgent, - RotatedAt: now, - CreatedAt: now, - UpdatedAt: now, - SeenAt: 0, - AuthTokenSeen: false, - } - _, err = s.SQLStore.NewSession().Insert(&userToken) - if err != nil { - return nil, err - } - - userToken.UnhashedToken = token - - return &userToken, nil -} - -func (s *UserAuthTokenServiceImpl) LookupToken(unhashedToken string) (*userAuthToken, error) { - hashedToken := hashToken(unhashedToken) - if setting.Env == setting.DEV { - s.log.Debug("looking up token", "unhashed", unhashedToken, "hashed", hashedToken) - } - - expireBefore := getTime().Add(time.Duration(-86400*s.Cfg.LoginCookieMaxDays) * time.Second).Unix() - - var userToken userAuthToken - exists, err := s.SQLStore.NewSession().Where("(auth_token = ? OR prev_auth_token = ?) AND created_at > ?", hashedToken, hashedToken, expireBefore).Get(&userToken) - if err != nil { - return nil, err - } - - if !exists { - return nil, ErrAuthTokenNotFound - } - - if userToken.AuthToken != hashedToken && userToken.PrevAuthToken == hashedToken && userToken.AuthTokenSeen { - userTokenCopy := userToken - userTokenCopy.AuthTokenSeen = false - expireBefore := getTime().Add(-UrgentRotateTime).Unix() - affectedRows, err := s.SQLStore.NewSession().Where("id = ? AND prev_auth_token = ? AND rotated_at < ?", userTokenCopy.Id, userTokenCopy.PrevAuthToken, expireBefore).AllCols().Update(&userTokenCopy) - if err != nil { - return nil, err - } - - if affectedRows == 0 { - s.log.Debug("prev seen token unchanged", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent) - } else { - s.log.Debug("prev seen token", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent) - } - } - - if !userToken.AuthTokenSeen && userToken.AuthToken == hashedToken { - userTokenCopy := userToken - userTokenCopy.AuthTokenSeen = true - userTokenCopy.SeenAt = getTime().Unix() - affectedRows, err := s.SQLStore.NewSession().Where("id = ? AND auth_token = ?", userTokenCopy.Id, userTokenCopy.AuthToken).AllCols().Update(&userTokenCopy) - if err != nil { - return nil, err - } - - if affectedRows == 1 { - userToken = userTokenCopy - } - - if affectedRows == 0 { - s.log.Debug("seen wrong token", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent) - } else { - s.log.Debug("seen token", "userTokenId", userToken.Id, "userId", userToken.UserId, "authToken", userToken.AuthToken, "clientIP", userToken.ClientIp, "userAgent", userToken.UserAgent) - } - } - - userToken.UnhashedToken = unhashedToken - - return &userToken, nil -} - -func (s *UserAuthTokenServiceImpl) RefreshToken(token *userAuthToken, clientIP, userAgent string) (bool, error) { - if token == nil { - return false, nil - } - - now := getTime() - - needsRotation := false - rotatedAt := time.Unix(token.RotatedAt, 0) - if token.AuthTokenSeen { - needsRotation = rotatedAt.Before(now.Add(-time.Duration(s.Cfg.LoginCookieRotation) * time.Minute)) - } else { - needsRotation = rotatedAt.Before(now.Add(-UrgentRotateTime)) - } - - if !needsRotation { - return false, nil - } - - s.log.Debug("refresh token needs rotation?", "auth_token_seen", token.AuthTokenSeen, "rotated_at", rotatedAt, "token.Id", token.Id) - - clientIP = util.ParseIPAddress(clientIP) - newToken, _ := util.RandomHex(16) - hashedToken := hashToken(newToken) - - // very important that auth_token_seen is set after the prev_auth_token = case when ... for mysql to function correctly - sql := ` - UPDATE user_auth_token - SET - seen_at = 0, - user_agent = ?, - client_ip = ?, - prev_auth_token = case when auth_token_seen = ? then auth_token else prev_auth_token end, - auth_token = ?, - auth_token_seen = ?, - rotated_at = ? - WHERE id = ? AND (auth_token_seen = ? OR rotated_at < ?)` - - res, err := s.SQLStore.NewSession().Exec(sql, userAgent, clientIP, s.SQLStore.Dialect.BooleanStr(true), hashedToken, s.SQLStore.Dialect.BooleanStr(false), now.Unix(), token.Id, s.SQLStore.Dialect.BooleanStr(true), now.Add(-30*time.Second).Unix()) - if err != nil { - return false, err - } - - affected, _ := res.RowsAffected() - s.log.Debug("rotated", "affected", affected, "auth_token_id", token.Id, "userId", token.UserId) - if affected > 0 { - token.UnhashedToken = newToken - return true, nil - } - - return false, nil -} - -func hashToken(token string) string { - hashBytes := sha256.Sum256([]byte(token + setting.SecretKey)) - return hex.EncodeToString(hashBytes[:]) -} diff --git a/pkg/services/auth/auth_token_test.go b/pkg/services/auth/auth_token_test.go deleted file mode 100644 index 312e53a3970..00000000000 --- a/pkg/services/auth/auth_token_test.go +++ /dev/null @@ -1,378 +0,0 @@ -package auth - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" - macaron "gopkg.in/macaron.v1" - - "github.com/grafana/grafana/pkg/log" - "github.com/grafana/grafana/pkg/services/sqlstore" - . "github.com/smartystreets/goconvey/convey" -) - -func TestUserAuthToken(t *testing.T) { - Convey("Test user auth token", t, func() { - ctx := createTestContext(t) - userAuthTokenService := ctx.tokenService - userID := int64(10) - - t := time.Date(2018, 12, 13, 13, 45, 0, 0, time.UTC) - getTime = func() time.Time { - return t - } - - Convey("When creating token", func() { - token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") - So(err, ShouldBeNil) - So(token, ShouldNotBeNil) - So(token.AuthTokenSeen, ShouldBeFalse) - - Convey("When lookup unhashed token should return user auth token", func() { - LookupToken, err := userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldBeNil) - So(LookupToken, ShouldNotBeNil) - So(LookupToken.UserId, ShouldEqual, userID) - So(LookupToken.AuthTokenSeen, ShouldBeTrue) - - storedAuthToken, err := ctx.getAuthTokenByID(LookupToken.Id) - So(err, ShouldBeNil) - So(storedAuthToken, ShouldNotBeNil) - So(storedAuthToken.AuthTokenSeen, ShouldBeTrue) - }) - - Convey("When lookup hashed token should return user auth token not found error", func() { - LookupToken, err := userAuthTokenService.LookupToken(token.AuthToken) - So(err, ShouldEqual, ErrAuthTokenNotFound) - So(LookupToken, ShouldBeNil) - }) - - Convey("signing out should delete token and cookie if present", func() { - httpreq := &http.Request{Header: make(http.Header)} - httpreq.AddCookie(&http.Cookie{Name: userAuthTokenService.Cfg.LoginCookieName, Value: token.UnhashedToken}) - - ctx := &models.ReqContext{Context: &macaron.Context{ - Req: macaron.Request{Request: httpreq}, - Resp: macaron.NewResponseWriter("POST", httptest.NewRecorder()), - }, - Logger: log.New("fakelogger"), - } - - err = userAuthTokenService.SignOutUser(ctx) - So(err, ShouldBeNil) - - // makes sure we tell the browser to overwrite the cookie - cookieHeader := fmt.Sprintf("%s=; Path=/; Max-Age=0; HttpOnly", userAuthTokenService.Cfg.LoginCookieName) - So(ctx.Resp.Header().Get("Set-Cookie"), ShouldEqual, cookieHeader) - }) - - Convey("signing out an none existing session should return an error", func() { - httpreq := &http.Request{Header: make(http.Header)} - httpreq.AddCookie(&http.Cookie{Name: userAuthTokenService.Cfg.LoginCookieName, Value: ""}) - - ctx := &models.ReqContext{Context: &macaron.Context{ - Req: macaron.Request{Request: httpreq}, - Resp: macaron.NewResponseWriter("POST", httptest.NewRecorder()), - }, - Logger: log.New("fakelogger"), - } - - err = userAuthTokenService.SignOutUser(ctx) - So(err, ShouldNotBeNil) - }) - }) - - Convey("expires correctly", func() { - token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") - So(err, ShouldBeNil) - So(token, ShouldNotBeNil) - - _, err = userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldBeNil) - - token, err = ctx.getAuthTokenByID(token.Id) - So(err, ShouldBeNil) - - getTime = func() time.Time { - return t.Add(time.Hour) - } - - refreshed, err := userAuthTokenService.RefreshToken(token, "192.168.10.11:1234", "some user agent") - So(err, ShouldBeNil) - So(refreshed, ShouldBeTrue) - - _, err = userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldBeNil) - - stillGood, err := userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldBeNil) - So(stillGood, ShouldNotBeNil) - - getTime = func() time.Time { - return t.Add(24 * 7 * time.Hour) - } - notGood, err := userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldEqual, ErrAuthTokenNotFound) - So(notGood, ShouldBeNil) - }) - - Convey("can properly rotate tokens", func() { - token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") - So(err, ShouldBeNil) - So(token, ShouldNotBeNil) - - prevToken := token.AuthToken - unhashedPrev := token.UnhashedToken - - refreshed, err := userAuthTokenService.RefreshToken(token, "192.168.10.12:1234", "a new user agent") - So(err, ShouldBeNil) - So(refreshed, ShouldBeFalse) - - updated, err := ctx.markAuthTokenAsSeen(token.Id) - So(err, ShouldBeNil) - So(updated, ShouldBeTrue) - - token, err = ctx.getAuthTokenByID(token.Id) - So(err, ShouldBeNil) - - getTime = func() time.Time { - return t.Add(time.Hour) - } - - refreshed, err = userAuthTokenService.RefreshToken(token, "192.168.10.12:1234", "a new user agent") - So(err, ShouldBeNil) - So(refreshed, ShouldBeTrue) - - unhashedToken := token.UnhashedToken - - token, err = ctx.getAuthTokenByID(token.Id) - So(err, ShouldBeNil) - token.UnhashedToken = unhashedToken - - So(token.RotatedAt, ShouldEqual, getTime().Unix()) - So(token.ClientIp, ShouldEqual, "192.168.10.12") - So(token.UserAgent, ShouldEqual, "a new user agent") - So(token.AuthTokenSeen, ShouldBeFalse) - So(token.SeenAt, ShouldEqual, 0) - So(token.PrevAuthToken, ShouldEqual, prevToken) - - // ability to auth using an old token - - lookedUp, err := userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldBeNil) - So(lookedUp, ShouldNotBeNil) - So(lookedUp.AuthTokenSeen, ShouldBeTrue) - So(lookedUp.SeenAt, ShouldEqual, getTime().Unix()) - - lookedUp, err = userAuthTokenService.LookupToken(unhashedPrev) - So(err, ShouldBeNil) - So(lookedUp, ShouldNotBeNil) - So(lookedUp.Id, ShouldEqual, token.Id) - So(lookedUp.AuthTokenSeen, ShouldBeTrue) - - getTime = func() time.Time { - return t.Add(time.Hour + (2 * time.Minute)) - } - - lookedUp, err = userAuthTokenService.LookupToken(unhashedPrev) - So(err, ShouldBeNil) - So(lookedUp, ShouldNotBeNil) - So(lookedUp.AuthTokenSeen, ShouldBeTrue) - - lookedUp, err = ctx.getAuthTokenByID(lookedUp.Id) - So(err, ShouldBeNil) - So(lookedUp, ShouldNotBeNil) - So(lookedUp.AuthTokenSeen, ShouldBeFalse) - - refreshed, err = userAuthTokenService.RefreshToken(token, "192.168.10.12:1234", "a new user agent") - So(err, ShouldBeNil) - So(refreshed, ShouldBeTrue) - - token, err = ctx.getAuthTokenByID(token.Id) - So(err, ShouldBeNil) - So(token, ShouldNotBeNil) - So(token.SeenAt, ShouldEqual, 0) - }) - - Convey("keeps prev token valid for 1 minute after it is confirmed", func() { - token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") - So(err, ShouldBeNil) - So(token, ShouldNotBeNil) - - lookedUp, err := userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldBeNil) - So(lookedUp, ShouldNotBeNil) - - getTime = func() time.Time { - return t.Add(10 * time.Minute) - } - - prevToken := token.UnhashedToken - refreshed, err := userAuthTokenService.RefreshToken(token, "1.1.1.1", "firefox") - So(err, ShouldBeNil) - So(refreshed, ShouldBeTrue) - - getTime = func() time.Time { - return t.Add(20 * time.Minute) - } - - current, err := userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldBeNil) - So(current, ShouldNotBeNil) - - prev, err := userAuthTokenService.LookupToken(prevToken) - So(err, ShouldBeNil) - So(prev, ShouldNotBeNil) - }) - - Convey("will not mark token unseen when prev and current are the same", func() { - token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") - So(err, ShouldBeNil) - So(token, ShouldNotBeNil) - - lookedUp, err := userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldBeNil) - So(lookedUp, ShouldNotBeNil) - - lookedUp, err = userAuthTokenService.LookupToken(token.UnhashedToken) - So(err, ShouldBeNil) - So(lookedUp, ShouldNotBeNil) - - lookedUp, err = ctx.getAuthTokenByID(lookedUp.Id) - So(err, ShouldBeNil) - So(lookedUp, ShouldNotBeNil) - So(lookedUp.AuthTokenSeen, ShouldBeTrue) - }) - - Convey("Rotate token", func() { - token, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") - So(err, ShouldBeNil) - So(token, ShouldNotBeNil) - - prevToken := token.AuthToken - - Convey("Should rotate current token and previous token when auth token seen", func() { - updated, err := ctx.markAuthTokenAsSeen(token.Id) - So(err, ShouldBeNil) - So(updated, ShouldBeTrue) - - getTime = func() time.Time { - return t.Add(10 * time.Minute) - } - - refreshed, err := userAuthTokenService.RefreshToken(token, "1.1.1.1", "firefox") - So(err, ShouldBeNil) - So(refreshed, ShouldBeTrue) - - storedToken, err := ctx.getAuthTokenByID(token.Id) - So(err, ShouldBeNil) - So(storedToken, ShouldNotBeNil) - So(storedToken.AuthTokenSeen, ShouldBeFalse) - So(storedToken.PrevAuthToken, ShouldEqual, prevToken) - So(storedToken.AuthToken, ShouldNotEqual, prevToken) - - prevToken = storedToken.AuthToken - - updated, err = ctx.markAuthTokenAsSeen(token.Id) - So(err, ShouldBeNil) - So(updated, ShouldBeTrue) - - getTime = func() time.Time { - return t.Add(20 * time.Minute) - } - - refreshed, err = userAuthTokenService.RefreshToken(token, "1.1.1.1", "firefox") - So(err, ShouldBeNil) - So(refreshed, ShouldBeTrue) - - storedToken, err = ctx.getAuthTokenByID(token.Id) - So(err, ShouldBeNil) - So(storedToken, ShouldNotBeNil) - So(storedToken.AuthTokenSeen, ShouldBeFalse) - So(storedToken.PrevAuthToken, ShouldEqual, prevToken) - So(storedToken.AuthToken, ShouldNotEqual, prevToken) - }) - - Convey("Should rotate current token, but keep previous token when auth token not seen", func() { - token.RotatedAt = getTime().Add(-2 * time.Minute).Unix() - - getTime = func() time.Time { - return t.Add(2 * time.Minute) - } - - refreshed, err := userAuthTokenService.RefreshToken(token, "1.1.1.1", "firefox") - So(err, ShouldBeNil) - So(refreshed, ShouldBeTrue) - - storedToken, err := ctx.getAuthTokenByID(token.Id) - So(err, ShouldBeNil) - So(storedToken, ShouldNotBeNil) - So(storedToken.AuthTokenSeen, ShouldBeFalse) - So(storedToken.PrevAuthToken, ShouldEqual, prevToken) - So(storedToken.AuthToken, ShouldNotEqual, prevToken) - }) - }) - - Reset(func() { - getTime = time.Now - }) - }) -} - -func createTestContext(t *testing.T) *testContext { - t.Helper() - - sqlstore := sqlstore.InitTestDB(t) - tokenService := &UserAuthTokenServiceImpl{ - SQLStore: sqlstore, - Cfg: &setting.Cfg{ - LoginCookieName: "grafana_session", - LoginCookieMaxDays: 7, - LoginDeleteExpiredTokensAfterDays: 30, - LoginCookieRotation: 10, - }, - log: log.New("test-logger"), - } - - UrgentRotateTime = time.Minute - - return &testContext{ - sqlstore: sqlstore, - tokenService: tokenService, - } -} - -type testContext struct { - sqlstore *sqlstore.SqlStore - tokenService *UserAuthTokenServiceImpl -} - -func (c *testContext) getAuthTokenByID(id int64) (*userAuthToken, error) { - sess := c.sqlstore.NewSession() - var t userAuthToken - found, err := sess.ID(id).Get(&t) - if err != nil || !found { - return nil, err - } - - return &t, nil -} - -func (c *testContext) markAuthTokenAsSeen(id int64) (bool, error) { - sess := c.sqlstore.NewSession() - res, err := sess.Exec("UPDATE user_auth_token SET auth_token_seen = ? WHERE id = ?", c.sqlstore.Dialect.BooleanStr(true), id) - if err != nil { - return false, err - } - - rowsAffected, err := res.RowsAffected() - if err != nil { - return false, err - } - return rowsAffected == 1, nil -} diff --git a/pkg/services/auth/authtoken/auth_token.go b/pkg/services/auth/authtoken/auth_token.go new file mode 100644 index 00000000000..4e4bd375501 --- /dev/null +++ b/pkg/services/auth/authtoken/auth_token.go @@ -0,0 +1,225 @@ +package authtoken + +import ( + "crypto/sha256" + "encoding/hex" + "time" + + "github.com/grafana/grafana/pkg/services/auth" + + "github.com/grafana/grafana/pkg/infra/serverlock" + + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/registry" + "github.com/grafana/grafana/pkg/services/sqlstore" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util" +) + +func init() { + registry.Register(®istry.Descriptor{ + Name: "AuthTokenService", + Instance: &UserAuthTokenServiceImpl{}, + InitPriority: registry.Low, + }) +} + +var getTime = time.Now + +const urgentRotateTime = 1 * time.Minute + +type UserAuthTokenServiceImpl struct { + SQLStore *sqlstore.SqlStore `inject:""` + ServerLockService *serverlock.ServerLockService `inject:""` + Cfg *setting.Cfg `inject:""` + log log.Logger +} + +func (s *UserAuthTokenServiceImpl) Init() error { + s.log = log.New("auth") + return nil +} + +func (s *UserAuthTokenServiceImpl) CreateToken(userId int64, clientIP, userAgent string) (auth.UserToken, error) { + clientIP = util.ParseIPAddress(clientIP) + token, err := util.RandomHex(16) + if err != nil { + return nil, err + } + + hashedToken := hashToken(token) + + now := getTime().Unix() + + userAuthToken := userAuthToken{ + UserId: userId, + AuthToken: hashedToken, + PrevAuthToken: hashedToken, + ClientIp: clientIP, + UserAgent: userAgent, + RotatedAt: now, + CreatedAt: now, + UpdatedAt: now, + SeenAt: 0, + AuthTokenSeen: false, + } + _, err = s.SQLStore.NewSession().Insert(&userAuthToken) + if err != nil { + return nil, err + } + + userAuthToken.UnhashedToken = token + + s.log.Debug("user auth token created", "tokenId", userAuthToken.Id, "userId", userAuthToken.UserId, "clientIP", userAuthToken.ClientIp, "userAgent", userAuthToken.UserAgent, "authToken", userAuthToken.AuthToken) + + return userAuthToken.toUserToken() +} + +func (s *UserAuthTokenServiceImpl) LookupToken(unhashedToken string) (auth.UserToken, error) { + hashedToken := hashToken(unhashedToken) + if setting.Env == setting.DEV { + s.log.Debug("looking up token", "unhashed", unhashedToken, "hashed", hashedToken) + } + + expireBefore := getTime().Add(time.Duration(-86400*s.Cfg.LoginCookieMaxDays) * time.Second).Unix() + + var model userAuthToken + exists, err := s.SQLStore.NewSession().Where("(auth_token = ? OR prev_auth_token = ?) AND created_at > ?", hashedToken, hashedToken, expireBefore).Get(&model) + if err != nil { + return nil, err + } + + if !exists { + return nil, ErrAuthTokenNotFound + } + + if model.AuthToken != hashedToken && model.PrevAuthToken == hashedToken && model.AuthTokenSeen { + modelCopy := model + modelCopy.AuthTokenSeen = false + expireBefore := getTime().Add(-urgentRotateTime).Unix() + affectedRows, err := s.SQLStore.NewSession().Where("id = ? AND prev_auth_token = ? AND rotated_at < ?", modelCopy.Id, modelCopy.PrevAuthToken, expireBefore).AllCols().Update(&modelCopy) + if err != nil { + return nil, err + } + + if affectedRows == 0 { + s.log.Debug("prev seen token unchanged", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent, "authToken", model.AuthToken) + } else { + s.log.Debug("prev seen token", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent, "authToken", model.AuthToken) + } + } + + if !model.AuthTokenSeen && model.AuthToken == hashedToken { + modelCopy := model + modelCopy.AuthTokenSeen = true + modelCopy.SeenAt = getTime().Unix() + affectedRows, err := s.SQLStore.NewSession().Where("id = ? AND auth_token = ?", modelCopy.Id, modelCopy.AuthToken).AllCols().Update(&modelCopy) + if err != nil { + return nil, err + } + + if affectedRows == 1 { + model = modelCopy + } + + if affectedRows == 0 { + s.log.Debug("seen wrong token", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent, "authToken", model.AuthToken) + } else { + s.log.Debug("seen token", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent, "authToken", model.AuthToken) + } + } + + model.UnhashedToken = unhashedToken + return model.toUserToken() +} + +func (s *UserAuthTokenServiceImpl) TryRotateToken(token auth.UserToken, clientIP, userAgent string) (bool, error) { + if token == nil { + return false, nil + } + + model, err := extractModelFromToken(token) + if err != nil { + return false, err + } + + now := getTime() + + needsRotation := false + rotatedAt := time.Unix(model.RotatedAt, 0) + if model.AuthTokenSeen { + needsRotation = rotatedAt.Before(now.Add(-time.Duration(s.Cfg.LoginCookieRotation) * time.Minute)) + } else { + needsRotation = rotatedAt.Before(now.Add(-urgentRotateTime)) + } + + if !needsRotation { + return false, nil + } + + s.log.Debug("token needs rotation", "tokenId", model.Id, "authTokenSeen", model.AuthTokenSeen, "rotatedAt", rotatedAt) + + clientIP = util.ParseIPAddress(clientIP) + newToken, err := util.RandomHex(16) + if err != nil { + return false, err + } + hashedToken := hashToken(newToken) + + // very important that auth_token_seen is set after the prev_auth_token = case when ... for mysql to function correctly + sql := ` + UPDATE user_auth_token + SET + seen_at = 0, + user_agent = ?, + client_ip = ?, + prev_auth_token = case when auth_token_seen = ? then auth_token else prev_auth_token end, + auth_token = ?, + auth_token_seen = ?, + rotated_at = ? + WHERE id = ? AND (auth_token_seen = ? OR rotated_at < ?)` + + res, err := s.SQLStore.NewSession().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()) + if err != nil { + return false, err + } + + affected, _ := res.RowsAffected() + s.log.Debug("auth token rotated", "affected", affected, "auth_token_id", model.Id, "userId", model.UserId) + if affected > 0 { + model.UnhashedToken = newToken + return true, nil + } + + return false, nil +} + +func (s *UserAuthTokenServiceImpl) RevokeToken(token auth.UserToken) error { + if token == nil { + return ErrAuthTokenNotFound + } + + model, err := extractModelFromToken(token) + if err != nil { + return err + } + + rowsAffected, err := s.SQLStore.NewSession().Delete(model) + if err != nil { + return err + } + + if rowsAffected == 0 { + s.log.Debug("user auth token not found/revoked", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent) + return ErrAuthTokenNotFound + } + + s.log.Debug("user auth token revoked", "tokenId", model.Id, "userId", model.UserId, "clientIP", model.ClientIp, "userAgent", model.UserAgent) + + return nil +} + +func hashToken(token string) string { + hashBytes := sha256.Sum256([]byte(token + setting.SecretKey)) + return hex.EncodeToString(hashBytes[:]) +} diff --git a/pkg/services/auth/authtoken/auth_token_test.go b/pkg/services/auth/authtoken/auth_token_test.go new file mode 100644 index 00000000000..7809e235f5c --- /dev/null +++ b/pkg/services/auth/authtoken/auth_token_test.go @@ -0,0 +1,386 @@ +package authtoken + +import ( + "testing" + "time" + + "github.com/grafana/grafana/pkg/setting" + + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/services/sqlstore" + . "github.com/smartystreets/goconvey/convey" +) + +func TestUserAuthToken(t *testing.T) { + Convey("Test user auth token", t, func() { + ctx := createTestContext(t) + userAuthTokenService := ctx.tokenService + userID := int64(10) + + t := time.Date(2018, 12, 13, 13, 45, 0, 0, time.UTC) + getTime = func() time.Time { + return t + } + + Convey("When creating token", func() { + userToken, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") + So(err, ShouldBeNil) + model, err := extractModelFromToken(userToken) + So(err, ShouldBeNil) + So(model, ShouldNotBeNil) + So(model.AuthTokenSeen, ShouldBeFalse) + + Convey("When lookup unhashed token should return user auth token", func() { + userToken, err := userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldBeNil) + lookedUpModel, err := extractModelFromToken(userToken) + So(err, ShouldBeNil) + So(lookedUpModel, ShouldNotBeNil) + So(lookedUpModel.UserId, ShouldEqual, userID) + So(lookedUpModel.AuthTokenSeen, ShouldBeTrue) + + storedAuthToken, err := ctx.getAuthTokenByID(lookedUpModel.Id) + So(err, ShouldBeNil) + So(storedAuthToken, ShouldNotBeNil) + So(storedAuthToken.AuthTokenSeen, ShouldBeTrue) + }) + + Convey("When lookup hashed token should return user auth token not found error", func() { + userToken, err := userAuthTokenService.LookupToken(model.AuthToken) + So(err, ShouldEqual, ErrAuthTokenNotFound) + So(userToken, ShouldBeNil) + }) + + Convey("revoking existing token should delete token", func() { + err = userAuthTokenService.RevokeToken(userToken) + So(err, ShouldBeNil) + + model, err := ctx.getAuthTokenByID(model.Id) + So(err, ShouldBeNil) + So(model, ShouldBeNil) + }) + + Convey("revoking nil token should return error", func() { + err = userAuthTokenService.RevokeToken(nil) + So(err, ShouldEqual, ErrAuthTokenNotFound) + }) + + Convey("revoking non-existing token should return error", func() { + model.Id = 1000 + nonExistingToken, err := model.toUserToken() + So(err, ShouldBeNil) + err = userAuthTokenService.RevokeToken(nonExistingToken) + So(err, ShouldEqual, ErrAuthTokenNotFound) + }) + }) + + Convey("expires correctly", func() { + userToken, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") + So(err, ShouldBeNil) + model, err := extractModelFromToken(userToken) + So(err, ShouldBeNil) + So(model, ShouldNotBeNil) + + _, err = userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldBeNil) + + model, err = ctx.getAuthTokenByID(model.Id) + So(err, ShouldBeNil) + + userToken, err = model.toUserToken() + So(err, ShouldBeNil) + + getTime = func() time.Time { + return t.Add(time.Hour) + } + + rotated, err := userAuthTokenService.TryRotateToken(userToken, "192.168.10.11:1234", "some user agent") + So(err, ShouldBeNil) + So(rotated, ShouldBeTrue) + + _, err = userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldBeNil) + + stillGood, err := userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldBeNil) + So(stillGood, ShouldNotBeNil) + + getTime = func() time.Time { + return t.Add(24 * 7 * time.Hour) + } + notGood, err := userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldEqual, ErrAuthTokenNotFound) + So(notGood, ShouldBeNil) + }) + + Convey("can properly rotate tokens", func() { + userToken, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") + So(err, ShouldBeNil) + model, err := extractModelFromToken(userToken) + So(err, ShouldBeNil) + So(model, ShouldNotBeNil) + + prevToken := model.AuthToken + unhashedPrev := model.UnhashedToken + + rotated, err := userAuthTokenService.TryRotateToken(userToken, "192.168.10.12:1234", "a new user agent") + So(err, ShouldBeNil) + So(rotated, ShouldBeFalse) + + updated, err := ctx.markAuthTokenAsSeen(model.Id) + So(err, ShouldBeNil) + So(updated, ShouldBeTrue) + + model, err = ctx.getAuthTokenByID(model.Id) + So(err, ShouldBeNil) + tok, err := model.toUserToken() + So(err, ShouldBeNil) + + getTime = func() time.Time { + return t.Add(time.Hour) + } + + rotated, err = userAuthTokenService.TryRotateToken(tok, "192.168.10.12:1234", "a new user agent") + So(err, ShouldBeNil) + So(rotated, ShouldBeTrue) + + unhashedToken := model.UnhashedToken + + model, err = ctx.getAuthTokenByID(model.Id) + So(err, ShouldBeNil) + model.UnhashedToken = unhashedToken + + So(model.RotatedAt, ShouldEqual, getTime().Unix()) + So(model.ClientIp, ShouldEqual, "192.168.10.12") + So(model.UserAgent, ShouldEqual, "a new user agent") + So(model.AuthTokenSeen, ShouldBeFalse) + So(model.SeenAt, ShouldEqual, 0) + So(model.PrevAuthToken, ShouldEqual, prevToken) + + // ability to auth using an old token + + lookedUpUserToken, err := userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldBeNil) + lookedUpModel, err := extractModelFromToken(lookedUpUserToken) + So(err, ShouldBeNil) + So(lookedUpModel, ShouldNotBeNil) + So(lookedUpModel.AuthTokenSeen, ShouldBeTrue) + So(lookedUpModel.SeenAt, ShouldEqual, getTime().Unix()) + + lookedUpUserToken, err = userAuthTokenService.LookupToken(unhashedPrev) + So(err, ShouldBeNil) + So(lookedUpModel, ShouldNotBeNil) + So(lookedUpModel.Id, ShouldEqual, model.Id) + So(lookedUpModel.AuthTokenSeen, ShouldBeTrue) + + getTime = func() time.Time { + return t.Add(time.Hour + (2 * time.Minute)) + } + + lookedUpUserToken, err = userAuthTokenService.LookupToken(unhashedPrev) + So(err, ShouldBeNil) + lookedUpModel, err = extractModelFromToken(lookedUpUserToken) + So(err, ShouldBeNil) + So(lookedUpModel, ShouldNotBeNil) + So(lookedUpModel.AuthTokenSeen, ShouldBeTrue) + + lookedUpModel, err = ctx.getAuthTokenByID(lookedUpModel.Id) + So(err, ShouldBeNil) + So(lookedUpModel, ShouldNotBeNil) + So(lookedUpModel.AuthTokenSeen, ShouldBeFalse) + + rotated, err = userAuthTokenService.TryRotateToken(userToken, "192.168.10.12:1234", "a new user agent") + So(err, ShouldBeNil) + So(rotated, ShouldBeTrue) + + model, err = ctx.getAuthTokenByID(model.Id) + So(err, ShouldBeNil) + So(model, ShouldNotBeNil) + So(model.SeenAt, ShouldEqual, 0) + }) + + Convey("keeps prev token valid for 1 minute after it is confirmed", func() { + userToken, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") + So(err, ShouldBeNil) + model, err := extractModelFromToken(userToken) + So(err, ShouldBeNil) + So(model, ShouldNotBeNil) + + lookedUpUserToken, err := userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldBeNil) + So(lookedUpUserToken, ShouldNotBeNil) + + getTime = func() time.Time { + return t.Add(10 * time.Minute) + } + + prevToken := model.UnhashedToken + rotated, err := userAuthTokenService.TryRotateToken(userToken, "1.1.1.1", "firefox") + So(err, ShouldBeNil) + So(rotated, ShouldBeTrue) + + getTime = func() time.Time { + return t.Add(20 * time.Minute) + } + + currentUserToken, err := userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldBeNil) + So(currentUserToken, ShouldNotBeNil) + + prevUserToken, err := userAuthTokenService.LookupToken(prevToken) + So(err, ShouldBeNil) + So(prevUserToken, ShouldNotBeNil) + }) + + Convey("will not mark token unseen when prev and current are the same", func() { + userToken, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") + So(err, ShouldBeNil) + model, err := extractModelFromToken(userToken) + So(err, ShouldBeNil) + So(model, ShouldNotBeNil) + + lookedUpUserToken, err := userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldBeNil) + lookedUpModel, err := extractModelFromToken(lookedUpUserToken) + So(err, ShouldBeNil) + So(lookedUpModel, ShouldNotBeNil) + + lookedUpUserToken, err = userAuthTokenService.LookupToken(model.UnhashedToken) + So(err, ShouldBeNil) + lookedUpModel, err = extractModelFromToken(lookedUpUserToken) + So(err, ShouldBeNil) + So(lookedUpModel, ShouldNotBeNil) + + lookedUpModel, err = ctx.getAuthTokenByID(lookedUpModel.Id) + So(err, ShouldBeNil) + So(lookedUpModel, ShouldNotBeNil) + So(lookedUpModel.AuthTokenSeen, ShouldBeTrue) + }) + + Convey("Rotate token", func() { + userToken, err := userAuthTokenService.CreateToken(userID, "192.168.10.11:1234", "some user agent") + So(err, ShouldBeNil) + model, err := extractModelFromToken(userToken) + So(err, ShouldBeNil) + So(model, ShouldNotBeNil) + + prevToken := model.AuthToken + + Convey("Should rotate current token and previous token when auth token seen", func() { + updated, err := ctx.markAuthTokenAsSeen(model.Id) + So(err, ShouldBeNil) + So(updated, ShouldBeTrue) + + getTime = func() time.Time { + return t.Add(10 * time.Minute) + } + + rotated, err := userAuthTokenService.TryRotateToken(userToken, "1.1.1.1", "firefox") + So(err, ShouldBeNil) + So(rotated, ShouldBeTrue) + + storedToken, err := ctx.getAuthTokenByID(model.Id) + So(err, ShouldBeNil) + So(storedToken, ShouldNotBeNil) + So(storedToken.AuthTokenSeen, ShouldBeFalse) + So(storedToken.PrevAuthToken, ShouldEqual, prevToken) + So(storedToken.AuthToken, ShouldNotEqual, prevToken) + + prevToken = storedToken.AuthToken + + updated, err = ctx.markAuthTokenAsSeen(model.Id) + So(err, ShouldBeNil) + So(updated, ShouldBeTrue) + + getTime = func() time.Time { + return t.Add(20 * time.Minute) + } + + rotated, err = userAuthTokenService.TryRotateToken(userToken, "1.1.1.1", "firefox") + So(err, ShouldBeNil) + So(rotated, ShouldBeTrue) + + storedToken, err = ctx.getAuthTokenByID(model.Id) + So(err, ShouldBeNil) + So(storedToken, ShouldNotBeNil) + So(storedToken.AuthTokenSeen, ShouldBeFalse) + So(storedToken.PrevAuthToken, ShouldEqual, prevToken) + So(storedToken.AuthToken, ShouldNotEqual, prevToken) + }) + + Convey("Should rotate current token, but keep previous token when auth token not seen", func() { + model.RotatedAt = getTime().Add(-2 * time.Minute).Unix() + + getTime = func() time.Time { + return t.Add(2 * time.Minute) + } + + rotated, err := userAuthTokenService.TryRotateToken(userToken, "1.1.1.1", "firefox") + So(err, ShouldBeNil) + So(rotated, ShouldBeTrue) + + storedToken, err := ctx.getAuthTokenByID(model.Id) + So(err, ShouldBeNil) + So(storedToken, ShouldNotBeNil) + So(storedToken.AuthTokenSeen, ShouldBeFalse) + So(storedToken.PrevAuthToken, ShouldEqual, prevToken) + So(storedToken.AuthToken, ShouldNotEqual, prevToken) + }) + }) + + Reset(func() { + getTime = time.Now + }) + }) +} + +func createTestContext(t *testing.T) *testContext { + t.Helper() + + sqlstore := sqlstore.InitTestDB(t) + tokenService := &UserAuthTokenServiceImpl{ + SQLStore: sqlstore, + Cfg: &setting.Cfg{ + LoginCookieName: "grafana_session", + LoginCookieMaxDays: 7, + LoginDeleteExpiredTokensAfterDays: 30, + LoginCookieRotation: 10, + }, + log: log.New("test-logger"), + } + + return &testContext{ + sqlstore: sqlstore, + tokenService: tokenService, + } +} + +type testContext struct { + sqlstore *sqlstore.SqlStore + tokenService *UserAuthTokenServiceImpl +} + +func (c *testContext) getAuthTokenByID(id int64) (*userAuthToken, error) { + sess := c.sqlstore.NewSession() + var t userAuthToken + found, err := sess.ID(id).Get(&t) + if err != nil || !found { + return nil, err + } + + return &t, nil +} + +func (c *testContext) markAuthTokenAsSeen(id int64) (bool, error) { + sess := c.sqlstore.NewSession() + res, err := sess.Exec("UPDATE user_auth_token SET auth_token_seen = ? WHERE id = ?", c.sqlstore.Dialect.BooleanStr(true), id) + if err != nil { + return false, err + } + + rowsAffected, err := res.RowsAffected() + if err != nil { + return false, err + } + return rowsAffected == 1, nil +} diff --git a/pkg/services/auth/authtoken/model.go b/pkg/services/auth/authtoken/model.go new file mode 100644 index 00000000000..8bd89c68b04 --- /dev/null +++ b/pkg/services/auth/authtoken/model.go @@ -0,0 +1,76 @@ +package authtoken + +import ( + "errors" + "fmt" + + "github.com/grafana/grafana/pkg/services/auth" +) + +// Typed errors +var ( + ErrAuthTokenNotFound = errors.New("user auth token not found") +) + +type userAuthToken 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 `xorm:"-"` +} + +func (uat *userAuthToken) toUserToken() (auth.UserToken, error) { + if uat == nil { + return nil, fmt.Errorf("needs pointer to userAuthToken struct") + } + + return &userTokenImpl{ + userAuthToken: uat, + }, nil +} + +type userToken interface { + auth.UserToken + GetModel() *userAuthToken +} + +type userTokenImpl struct { + *userAuthToken +} + +func (ut *userTokenImpl) GetUserId() int64 { + return ut.UserId +} + +func (ut *userTokenImpl) GetToken() string { + return ut.UnhashedToken +} + +func (ut *userTokenImpl) GetModel() *userAuthToken { + return ut.userAuthToken +} + +func extractModelFromToken(token auth.UserToken) (*userAuthToken, error) { + ut, ok := token.(userToken) + if !ok { + return nil, fmt.Errorf("failed to cast token") + } + + return ut.GetModel(), nil +} + +// UserAuthTokenService are used for generating and validating user auth tokens +type UserAuthTokenService interface { + CreateToken(userId int64, clientIP, userAgent string) (auth.UserToken, error) + LookupToken(unhashedToken string) (auth.UserToken, error) + TryRotateToken(token auth.UserToken, clientIP, userAgent string) (bool, error) + RevokeToken(token auth.UserToken) error +} diff --git a/pkg/services/auth/session_cleanup.go b/pkg/services/auth/authtoken/session_cleanup.go similarity index 98% rename from pkg/services/auth/session_cleanup.go rename to pkg/services/auth/authtoken/session_cleanup.go index 7e523181a7b..cd2b766d6c0 100644 --- a/pkg/services/auth/session_cleanup.go +++ b/pkg/services/auth/authtoken/session_cleanup.go @@ -1,4 +1,4 @@ -package auth +package authtoken import ( "context" diff --git a/pkg/services/auth/session_cleanup_test.go b/pkg/services/auth/authtoken/session_cleanup_test.go similarity index 98% rename from pkg/services/auth/session_cleanup_test.go rename to pkg/services/auth/authtoken/session_cleanup_test.go index eef2cd74d04..101a279c374 100644 --- a/pkg/services/auth/session_cleanup_test.go +++ b/pkg/services/auth/authtoken/session_cleanup_test.go @@ -1,4 +1,4 @@ -package auth +package authtoken import ( "fmt" diff --git a/pkg/services/auth/model.go b/pkg/services/auth/model.go deleted file mode 100644 index 7a0f49539f2..00000000000 --- a/pkg/services/auth/model.go +++ /dev/null @@ -1,25 +0,0 @@ -package auth - -import ( - "errors" -) - -// Typed errors -var ( - ErrAuthTokenNotFound = errors.New("User auth token not found") -) - -type userAuthToken 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 `xorm:"-"` -}