|
|
@@ -10,6 +10,8 @@ import (
|
|
|
msession "github.com/go-macaron/session"
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
|
|
m "github.com/grafana/grafana/pkg/models"
|
|
|
+ "github.com/grafana/grafana/pkg/services/auth"
|
|
|
+ "github.com/grafana/grafana/pkg/services/auth/authtoken"
|
|
|
"github.com/grafana/grafana/pkg/services/session"
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
|
"github.com/grafana/grafana/pkg/util"
|
|
|
@@ -146,17 +148,91 @@ func TestMiddlewareContext(t *testing.T) {
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- middlewareScenario("Auth token service", func(sc *scenarioContext) {
|
|
|
- var wasCalled bool
|
|
|
- sc.userAuthTokenService.initContextWithTokenProvider = func(ctx *m.ReqContext, orgId int64) bool {
|
|
|
- wasCalled = true
|
|
|
- return false
|
|
|
+ middlewareScenario("Non-expired auth token in cookie which not are being rotated", func(sc *scenarioContext) {
|
|
|
+ sc.withTokenSessionCookie("token")
|
|
|
+
|
|
|
+ bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
|
|
+ query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+
|
|
|
+ sc.userAuthTokenService.lookupTokenProvider = func(unhashedToken string) (auth.UserToken, error) {
|
|
|
+ return &userTokenImpl{
|
|
|
+ userId: 12,
|
|
|
+ token: unhashedToken,
|
|
|
+ }, nil
|
|
|
}
|
|
|
|
|
|
sc.fakeReq("GET", "/").exec()
|
|
|
|
|
|
- Convey("should call middleware", func() {
|
|
|
- So(wasCalled, ShouldBeTrue)
|
|
|
+ Convey("should init context with user info", func() {
|
|
|
+ So(sc.context.IsSignedIn, ShouldBeTrue)
|
|
|
+ So(sc.context.UserId, ShouldEqual, 12)
|
|
|
+ So(sc.context.UserToken.GetUserId(), ShouldEqual, 12)
|
|
|
+ So(sc.context.UserToken.GetToken(), ShouldEqual, "token")
|
|
|
+ })
|
|
|
+
|
|
|
+ Convey("should not set cookie", func() {
|
|
|
+ So(sc.resp.Header().Get("Set-Cookie"), ShouldEqual, "")
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ middlewareScenario("Non-expired auth token in cookie which are being rotated", func(sc *scenarioContext) {
|
|
|
+ sc.withTokenSessionCookie("token")
|
|
|
+
|
|
|
+ bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
|
|
+ query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+
|
|
|
+ sc.userAuthTokenService.lookupTokenProvider = func(unhashedToken string) (auth.UserToken, error) {
|
|
|
+ return &userTokenImpl{
|
|
|
+ userId: 12,
|
|
|
+ token: unhashedToken,
|
|
|
+ }, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ sc.userAuthTokenService.tryRotateTokenProvider = func(userToken auth.UserToken, clientIP, userAgent string) (bool, error) {
|
|
|
+ userToken.(fakeUserToken).SetToken("rotated")
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ expectedCookie := &http.Cookie{
|
|
|
+ Name: cookieName,
|
|
|
+ Value: "rotated",
|
|
|
+ Path: setting.AppSubUrl + "/",
|
|
|
+ HttpOnly: true,
|
|
|
+ MaxAge: OneYearInSeconds,
|
|
|
+ SameSite: http.SameSiteLaxMode,
|
|
|
+ }
|
|
|
+
|
|
|
+ sc.fakeReq("GET", "/").exec()
|
|
|
+
|
|
|
+ Convey("should init context with user info", func() {
|
|
|
+ So(sc.context.IsSignedIn, ShouldBeTrue)
|
|
|
+ So(sc.context.UserId, ShouldEqual, 12)
|
|
|
+ So(sc.context.UserToken.GetUserId(), ShouldEqual, 12)
|
|
|
+ So(sc.context.UserToken.GetToken(), ShouldEqual, "rotated")
|
|
|
+ })
|
|
|
+
|
|
|
+ Convey("should set cookie", func() {
|
|
|
+ So(sc.resp.Header().Get("Set-Cookie"), ShouldEqual, expectedCookie.String())
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ middlewareScenario("Invalid/expired auth token in cookie", func(sc *scenarioContext) {
|
|
|
+ sc.withTokenSessionCookie("token")
|
|
|
+
|
|
|
+ sc.userAuthTokenService.lookupTokenProvider = func(unhashedToken string) (auth.UserToken, error) {
|
|
|
+ return nil, authtoken.ErrAuthTokenNotFound
|
|
|
+ }
|
|
|
+
|
|
|
+ sc.fakeReq("GET", "/").exec()
|
|
|
+
|
|
|
+ Convey("should not init context with user info", func() {
|
|
|
+ So(sc.context.IsSignedIn, ShouldBeFalse)
|
|
|
+ So(sc.context.UserId, ShouldEqual, 0)
|
|
|
+ So(sc.context.UserToken, ShouldBeNil)
|
|
|
})
|
|
|
})
|
|
|
|
|
|
@@ -508,6 +584,7 @@ type scenarioContext struct {
|
|
|
resp *httptest.ResponseRecorder
|
|
|
apiKey string
|
|
|
authHeader string
|
|
|
+ tokenSessionCookie string
|
|
|
respJson map[string]interface{}
|
|
|
handlerFunc handlerFunc
|
|
|
defaultHandler macaron.Handler
|
|
|
@@ -522,6 +599,11 @@ func (sc *scenarioContext) withValidApiKey() *scenarioContext {
|
|
|
return sc
|
|
|
}
|
|
|
|
|
|
+func (sc *scenarioContext) withTokenSessionCookie(unhashedToken string) *scenarioContext {
|
|
|
+ sc.tokenSessionCookie = unhashedToken
|
|
|
+ return sc
|
|
|
+}
|
|
|
+
|
|
|
func (sc *scenarioContext) withAuthorizationHeader(authHeader string) *scenarioContext {
|
|
|
sc.authHeader = authHeader
|
|
|
return sc
|
|
|
@@ -571,6 +653,13 @@ func (sc *scenarioContext) exec() {
|
|
|
sc.req.Header.Add("Authorization", sc.authHeader)
|
|
|
}
|
|
|
|
|
|
+ if sc.tokenSessionCookie != "" {
|
|
|
+ sc.req.AddCookie(&http.Cookie{
|
|
|
+ Name: cookieName,
|
|
|
+ Value: sc.tokenSessionCookie,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
sc.m.ServeHTTP(sc.resp, sc.req)
|
|
|
|
|
|
if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" {
|
|
|
@@ -582,24 +671,70 @@ func (sc *scenarioContext) exec() {
|
|
|
type scenarioFunc func(c *scenarioContext)
|
|
|
type handlerFunc func(c *m.ReqContext)
|
|
|
|
|
|
+type fakeUserToken interface {
|
|
|
+ auth.UserToken
|
|
|
+ SetToken(token string)
|
|
|
+}
|
|
|
+
|
|
|
+type userTokenImpl struct {
|
|
|
+ userId int64
|
|
|
+ token string
|
|
|
+}
|
|
|
+
|
|
|
+func (ut *userTokenImpl) GetUserId() int64 {
|
|
|
+ return ut.userId
|
|
|
+}
|
|
|
+
|
|
|
+func (ut *userTokenImpl) GetToken() string {
|
|
|
+ return ut.token
|
|
|
+}
|
|
|
+
|
|
|
+func (ut *userTokenImpl) SetToken(token string) {
|
|
|
+ ut.token = token
|
|
|
+}
|
|
|
+
|
|
|
type fakeUserAuthTokenService struct {
|
|
|
- initContextWithTokenProvider func(ctx *m.ReqContext, orgID int64) bool
|
|
|
+ createTokenProvider func(userId int64, clientIP, userAgent string) (auth.UserToken, error)
|
|
|
+ tryRotateTokenProvider func(token auth.UserToken, clientIP, userAgent string) (bool, error)
|
|
|
+ lookupTokenProvider func(unhashedToken string) (auth.UserToken, error)
|
|
|
+ revokeTokenProvider func(token auth.UserToken) error
|
|
|
}
|
|
|
|
|
|
func newFakeUserAuthTokenService() *fakeUserAuthTokenService {
|
|
|
return &fakeUserAuthTokenService{
|
|
|
- initContextWithTokenProvider: func(ctx *m.ReqContext, orgID int64) bool {
|
|
|
- return false
|
|
|
+ createTokenProvider: func(userId int64, clientIP, userAgent string) (auth.UserToken, error) {
|
|
|
+ return &userTokenImpl{
|
|
|
+ userId: 0,
|
|
|
+ token: "",
|
|
|
+ }, nil
|
|
|
+ },
|
|
|
+ tryRotateTokenProvider: func(token auth.UserToken, clientIP, userAgent string) (bool, error) {
|
|
|
+ return false, nil
|
|
|
+ },
|
|
|
+ lookupTokenProvider: func(unhashedToken string) (auth.UserToken, error) {
|
|
|
+ return &userTokenImpl{
|
|
|
+ userId: 0,
|
|
|
+ token: "",
|
|
|
+ }, nil
|
|
|
+ },
|
|
|
+ revokeTokenProvider: func(token auth.UserToken) error {
|
|
|
+ return nil
|
|
|
},
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (s *fakeUserAuthTokenService) InitContextWithToken(ctx *m.ReqContext, orgID int64) bool {
|
|
|
- return s.initContextWithTokenProvider(ctx, orgID)
|
|
|
+func (s *fakeUserAuthTokenService) CreateToken(userId int64, clientIP, userAgent string) (auth.UserToken, error) {
|
|
|
+ return s.createTokenProvider(userId, clientIP, userAgent)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *fakeUserAuthTokenService) LookupToken(unhashedToken string) (auth.UserToken, error) {
|
|
|
+ return s.lookupTokenProvider(unhashedToken)
|
|
|
}
|
|
|
|
|
|
-func (s *fakeUserAuthTokenService) UserAuthenticatedHook(user *m.User, c *m.ReqContext) error {
|
|
|
- return nil
|
|
|
+func (s *fakeUserAuthTokenService) TryRotateToken(token auth.UserToken, clientIP, userAgent string) (bool, error) {
|
|
|
+ return s.tryRotateTokenProvider(token, clientIP, userAgent)
|
|
|
}
|
|
|
|
|
|
-func (s *fakeUserAuthTokenService) SignOutUser(c *m.ReqContext) error { return nil }
|
|
|
+func (s *fakeUserAuthTokenService) RevokeToken(token auth.UserToken) error {
|
|
|
+ return s.revokeTokenProvider(token)
|
|
|
+}
|