diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 54f0601e577..c85bdb6f2e7 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -47,14 +47,14 @@ type HTTPServer struct { streamManager *live.StreamManager httpSrv *http.Server - RouteRegister routing.RouteRegister `inject:""` - Bus bus.Bus `inject:""` - RenderService rendering.Service `inject:""` - Cfg *setting.Cfg `inject:""` - HooksService *hooks.HooksService `inject:""` - CacheService *cache.CacheService `inject:""` - DatasourceCache datasources.CacheService `inject:""` - AuthTokenService *auth.UserAuthTokenService `inject:""` + RouteRegister routing.RouteRegister `inject:""` + Bus bus.Bus `inject:""` + RenderService rendering.Service `inject:""` + Cfg *setting.Cfg `inject:""` + HooksService *hooks.HooksService `inject:""` + CacheService *cache.CacheService `inject:""` + DatasourceCache datasources.CacheService `inject:""` + AuthTokenService auth.UserAuthTokenService `inject:""` } func (hs *HTTPServer) Init() error { diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index 6c4ce1c20ae..705d28db3eb 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -21,7 +21,7 @@ var ( ReqOrgAdmin = RoleAuth(m.ROLE_ADMIN) ) -func GetContextHandler(ats *auth.UserAuthTokenService) macaron.Handler { +func GetContextHandler(ats auth.UserAuthTokenService) macaron.Handler { return func(c *macaron.Context) { ctx := &m.ReqContext{ Context: c, diff --git a/pkg/models/context.go b/pkg/models/context.go index f6df8b1c2f0..1a78c021f45 100644 --- a/pkg/models/context.go +++ b/pkg/models/context.go @@ -10,25 +10,9 @@ import ( "gopkg.in/macaron.v1" ) -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:"-"` -} - type ReqContext struct { *macaron.Context *SignedInUser - UserToken *UserAuthToken Session session.SessionStore diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go index 181cc4315c9..0b5af181b8b 100644 --- a/pkg/services/auth/auth_token.go +++ b/pkg/services/auth/auth_token.go @@ -18,67 +18,72 @@ import ( ) func init() { - registry.RegisterService(&UserAuthTokenService{}) + registry.RegisterService(&UserAuthTokenServiceImpl{}) } var ( getTime = time.Now - RotateTime = 30 * time.Second - UrgentRotateTime = 10 * time.Second + RotateTime = 2 * time.Minute + UrgentRotateTime = 20 * time.Second 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 struct { +type UserAuthTokenService interface { + InitContextWithToken(ctx *models.ReqContext, orgID int64) bool + UserAuthenticatedHook(user *models.User, c *models.ReqContext) error + UserSignedOutHook(c *models.ReqContext) +} + +type UserAuthTokenServiceImpl struct { SQLStore *sqlstore.SqlStore `inject:""` ServerLockService *serverlock.ServerLockService `inject:""` log log.Logger } // Init this service -func (s *UserAuthTokenService) Init() error { +func (s *UserAuthTokenServiceImpl) Init() error { s.log = log.New("auth") return nil } -func (s *UserAuthTokenService) InitContextWithToken(ctx *models.ReqContext, orgID int64) bool { +func (s *UserAuthTokenServiceImpl) InitContextWithToken(ctx *models.ReqContext, orgID int64) bool { //auth User unhashedToken := ctx.GetCookie(setting.SessionOptions.CookieName) if unhashedToken == "" { return false } - user, err := s.LookupToken(unhashedToken) + 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: user.UserId, OrgId: orgID} + 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", user.UserId, "error", err) + ctx.Logger.Error("Failed to get user with id", "userId", userToken.UserId, "error", err) return false } ctx.SignedInUser = query.Result ctx.IsSignedIn = true - ctx.UserToken = user //rotate session token if needed. - rotated, err := s.RefreshToken(ctx.UserToken, ctx.RemoteAddr(), ctx.Req.UserAgent()) + rotated, err := s.RefreshToken(userToken, ctx.RemoteAddr(), ctx.Req.UserAgent()) if err != nil { - ctx.Logger.Error("failed to rotate token", "error", err, "user.id", user.UserId, "user_token.id", user.Id) + ctx.Logger.Error("failed to rotate token", "error", err, "userId", userToken.UserId, "tokenId", userToken.Id) return true } if rotated { - s.writeSessionCookie(ctx, ctx.UserToken.UnhashedToken, oneYearInSeconds) + s.writeSessionCookie(ctx, userToken.UnhashedToken, oneYearInSeconds) } return true } -func (s *UserAuthTokenService) writeSessionCookie(ctx *models.ReqContext, value string, maxAge int) { +func (s *UserAuthTokenServiceImpl) writeSessionCookie(ctx *models.ReqContext, value string, maxAge int) { ctx.Logger.Info("new token", "unhashed token", value) ctx.Resp.Header().Del("Set-Cookie") @@ -94,23 +99,21 @@ func (s *UserAuthTokenService) writeSessionCookie(ctx *models.ReqContext, value http.SetCookie(ctx.Resp, &cookie) } -func (s *UserAuthTokenService) UserAuthenticatedHook(user *models.User, c *models.ReqContext) error { +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 } - c.UserToken = userToken - s.writeSessionCookie(c, userToken.UnhashedToken, oneYearInSeconds) return nil } -func (s *UserAuthTokenService) UserSignedOutHook(c *models.ReqContext) { +func (s *UserAuthTokenServiceImpl) UserSignedOutHook(c *models.ReqContext) { s.writeSessionCookie(c, "", -1) } -func (s *UserAuthTokenService) CreateToken(userId int64, clientIP, userAgent string) (*models.UserAuthToken, error) { +func (s *UserAuthTokenServiceImpl) CreateToken(userId int64, clientIP, userAgent string) (*userAuthToken, error) { clientIP = util.ParseIPAddress(clientIP) token, err := util.RandomHex(16) if err != nil { @@ -121,7 +124,7 @@ func (s *UserAuthTokenService) CreateToken(userId int64, clientIP, userAgent str now := getTime().Unix() - userToken := models.UserAuthToken{ + userToken := userAuthToken{ UserId: userId, AuthToken: hashedToken, PrevAuthToken: hashedToken, @@ -143,11 +146,11 @@ func (s *UserAuthTokenService) CreateToken(userId int64, clientIP, userAgent str return &userToken, nil } -func (s *UserAuthTokenService) LookupToken(unhashedToken string) (*models.UserAuthToken, error) { +func (s *UserAuthTokenServiceImpl) LookupToken(unhashedToken string) (*userAuthToken, error) { hashedToken := hashToken(unhashedToken) expireBefore := getTime().Add(time.Duration(-86400*setting.LogInRememberDays) * time.Second).Unix() - var userToken models.UserAuthToken + 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 @@ -198,7 +201,7 @@ func (s *UserAuthTokenService) LookupToken(unhashedToken string) (*models.UserAu return &userToken, nil } -func (s *UserAuthTokenService) RefreshToken(token *models.UserAuthToken, clientIP, userAgent string) (bool, error) { +func (s *UserAuthTokenServiceImpl) RefreshToken(token *userAuthToken, clientIP, userAgent string) (bool, error) { if token == nil { return false, nil } diff --git a/pkg/services/auth/auth_token_test.go b/pkg/services/auth/auth_token_test.go index 27405059e26..ee9bdf60bb9 100644 --- a/pkg/services/auth/auth_token_test.go +++ b/pkg/services/auth/auth_token_test.go @@ -7,7 +7,6 @@ import ( "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/log" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/sqlstore" . "github.com/smartystreets/goconvey/convey" ) @@ -290,7 +289,7 @@ func createTestContext(t *testing.T) *testContext { t.Helper() sqlstore := sqlstore.InitTestDB(t) - tokenService := &UserAuthTokenService{ + tokenService := &UserAuthTokenServiceImpl{ SQLStore: sqlstore, log: log.New("test-logger"), } @@ -307,12 +306,12 @@ func createTestContext(t *testing.T) *testContext { type testContext struct { sqlstore *sqlstore.SqlStore - tokenService *UserAuthTokenService + tokenService *UserAuthTokenServiceImpl } -func (c *testContext) getAuthTokenByID(id int64) (*models.UserAuthToken, error) { +func (c *testContext) getAuthTokenByID(id int64) (*userAuthToken, error) { sess := c.sqlstore.NewSession() - var t models.UserAuthToken + var t userAuthToken found, err := sess.ID(id).Get(&t) if err != nil || !found { return nil, err diff --git a/pkg/services/auth/model.go b/pkg/services/auth/model.go index 4347f6d2d6a..7a0f49539f2 100644 --- a/pkg/services/auth/model.go +++ b/pkg/services/auth/model.go @@ -9,17 +9,17 @@ 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:"-"` -// } +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:"-"` +} diff --git a/pkg/services/auth/session_cleanup.go b/pkg/services/auth/session_cleanup.go index 16f6deb9005..64dee43d677 100644 --- a/pkg/services/auth/session_cleanup.go +++ b/pkg/services/auth/session_cleanup.go @@ -5,7 +5,7 @@ import ( "time" ) -func (srv *UserAuthTokenService) Run(ctx context.Context) error { +func (srv *UserAuthTokenServiceImpl) Run(ctx context.Context) error { ticker := time.NewTicker(time.Hour * 12) deleteSessionAfter := time.Hour * 24 * 7 * 30 @@ -22,7 +22,7 @@ func (srv *UserAuthTokenService) Run(ctx context.Context) error { } } -func (srv *UserAuthTokenService) deleteOldSession(deleteSessionAfter time.Duration) (int64, error) { +func (srv *UserAuthTokenServiceImpl) deleteOldSession(deleteSessionAfter time.Duration) (int64, error) { sql := `DELETE from user_auth_token WHERE rotated_at < ?` deleteBefore := getTime().Add(-deleteSessionAfter) diff --git a/pkg/services/auth/session_cleanup_test.go b/pkg/services/auth/session_cleanup_test.go index 1f3b7ad0c7d..eef2cd74d04 100644 --- a/pkg/services/auth/session_cleanup_test.go +++ b/pkg/services/auth/session_cleanup_test.go @@ -5,7 +5,6 @@ import ( "testing" "time" - "github.com/grafana/grafana/pkg/models" . "github.com/smartystreets/goconvey/convey" ) @@ -15,7 +14,7 @@ func TestUserAuthTokenCleanup(t *testing.T) { ctx := createTestContext(t) insertToken := func(token string, prev string, rotatedAt int64) { - ut := models.UserAuthToken{AuthToken: token, PrevAuthToken: prev, RotatedAt: rotatedAt, UserAgent: "", ClientIp: ""} + ut := userAuthToken{AuthToken: token, PrevAuthToken: prev, RotatedAt: rotatedAt, UserAgent: "", ClientIp: ""} _, err := ctx.sqlstore.NewSession().Insert(&ut) So(err, ShouldBeNil) }