diff --git a/pkg/api/common_test.go b/pkg/api/common_test.go index eb1f89e3f22..f6c6e53e91d 100644 --- a/pkg/api/common_test.go +++ b/pkg/api/common_test.go @@ -149,4 +149,4 @@ func (s *fakeUserAuthTokenService) UserAuthenticatedHook(user *m.User, c *m.ReqC return nil } -func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) {} +func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) error { return nil } diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index 11740574d0b..9bb45062e00 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -602,4 +602,4 @@ func (s *fakeUserAuthTokenService) UserAuthenticatedHook(user *m.User, c *m.ReqC return nil } -func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) {} +func (s *fakeUserAuthTokenService) UserSignedOutHook(c *m.ReqContext) error { return nil } diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go index 7e9433c2d70..d9c5e897f70 100644 --- a/pkg/services/auth/auth_token.go +++ b/pkg/services/auth/auth_token.go @@ -3,6 +3,7 @@ package auth import ( "crypto/sha256" "encoding/hex" + "errors" "net/http" "net/url" "time" @@ -31,7 +32,7 @@ var ( type UserAuthTokenService interface { InitContextWithToken(ctx *models.ReqContext, orgID int64) bool UserAuthenticatedHook(user *models.User, c *models.ReqContext) error - UserSignedOutHook(c *models.ReqContext) + UserSignedOutHook(c *models.ReqContext) error } type UserAuthTokenServiceImpl struct { @@ -111,8 +112,27 @@ func (s *UserAuthTokenServiceImpl) UserAuthenticatedHook(user *models.User, c *m return nil } -func (s *UserAuthTokenServiceImpl) UserSignedOutHook(c *models.ReqContext) { - s.writeSessionCookie(c, "", -1) +func (s *UserAuthTokenServiceImpl) UserSignedOutHook(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 = ?` + res, err := s.SQLStore.NewSession().Exec(sql, hashedToken) + if err != nil { + return err + } + + affected, _ := res.RowsAffected() + if affected > 0 { + s.writeSessionCookie(c, "", -1) + return nil + } + + return errors.New("failed to delete session") } func (s *UserAuthTokenServiceImpl) CreateToken(userId int64, clientIP, userAgent string) (*userAuthToken, error) { diff --git a/pkg/services/auth/auth_token_test.go b/pkg/services/auth/auth_token_test.go index 2f75c660d9d..0114939ea48 100644 --- a/pkg/services/auth/auth_token_test.go +++ b/pkg/services/auth/auth_token_test.go @@ -1,10 +1,13 @@ package auth import ( + "net/http" "testing" "time" + "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" + "gopkg.in/macaron.v1" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/services/sqlstore" @@ -46,6 +49,28 @@ func TestUserAuthToken(t *testing.T) { So(err, ShouldEqual, ErrAuthTokenNotFound) So(LookupToken, ShouldBeNil) }) + + Convey("signing out should delete token and cookie if present", func() { + token, err := userAuthTokenService.CreateToken(userID, "192.168.1.1:1234", "some user agent2") + So(err, ShouldBeNil) + So(token, ShouldNotBeNil) + + httpreq := &http.Request{Header: make(http.Header)} + httpreq.AddCookie(&http.Cookie{Name: userAuthTokenService.Cfg.LoginCookieName, Value: token.AuthToken}) + + ctx := &models.ReqContext{Context: &macaron.Context{Req: macaron.Request{Request: httpreq}}} + + err = userAuthTokenService.UserSignedOutHook(ctx) + So(err, ShouldBeNil) + + // makes sure we tell the browser to overwrite the cookie + So(ctx.Resp.Header().Get("Set-Cookie"), ShouldEqual, "") + + // lookedUp, err = userAuthTokenService.LookupToken(token.UnhashedToken) + // So(err, ShouldBeNil) + // So(lookedUp, ShouldNotBeNil) + + }) }) Convey("expires correctly", func() {