mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 05:32:32 +08:00
move auth token middleware/hooks to middleware package
fix/adds auth token middleware tests
This commit is contained in:
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/middleware"
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"gopkg.in/macaron.v1"
|
"gopkg.in/macaron.v1"
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
@ -129,24 +130,70 @@ func setupScenarioContext(url string) *scenarioContext {
|
|||||||
return sc
|
return sc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
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 {
|
func newFakeUserAuthTokenService() *fakeUserAuthTokenService {
|
||||||
return &fakeUserAuthTokenService{
|
return &fakeUserAuthTokenService{
|
||||||
initContextWithTokenProvider: func(ctx *m.ReqContext, orgID int64) bool {
|
createTokenProvider: func(userId int64, clientIP, userAgent string) (auth.UserToken, error) {
|
||||||
return false
|
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 {
|
func (s *fakeUserAuthTokenService) CreateToken(userId int64, clientIP, userAgent string) (auth.UserToken, error) {
|
||||||
return s.initContextWithTokenProvider(ctx, orgID)
|
return s.createTokenProvider(userId, clientIP, userAgent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fakeUserAuthTokenService) UserAuthenticatedHook(user *m.User, c *m.ReqContext) error {
|
func (s *fakeUserAuthTokenService) LookupToken(unhashedToken string) (auth.UserToken, error) {
|
||||||
return nil
|
return s.lookupTokenProvider(unhashedToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fakeUserAuthTokenService) SignOutUser(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) RevokeToken(token auth.UserToken) error {
|
||||||
|
return s.revokeTokenProvider(token)
|
||||||
|
}
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/auth"
|
"github.com/grafana/grafana/pkg/services/auth/authtoken"
|
||||||
"github.com/grafana/grafana/pkg/services/cache"
|
"github.com/grafana/grafana/pkg/services/cache"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
"github.com/grafana/grafana/pkg/services/hooks"
|
"github.com/grafana/grafana/pkg/services/hooks"
|
||||||
@ -55,7 +55,7 @@ type HTTPServer struct {
|
|||||||
HooksService *hooks.HooksService `inject:""`
|
HooksService *hooks.HooksService `inject:""`
|
||||||
CacheService *cache.CacheService `inject:""`
|
CacheService *cache.CacheService `inject:""`
|
||||||
DatasourceCache datasources.CacheService `inject:""`
|
DatasourceCache datasources.CacheService `inject:""`
|
||||||
AuthTokenService auth.UserAuthTokenService `inject:""`
|
AuthTokenService authtoken.UserAuthTokenService `inject:""`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hs *HTTPServer) Init() error {
|
func (hs *HTTPServer) Init() error {
|
||||||
|
@ -5,11 +5,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/authtoken"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/log"
|
"github.com/grafana/grafana/pkg/log"
|
||||||
"github.com/grafana/grafana/pkg/login"
|
"github.com/grafana/grafana/pkg/login"
|
||||||
"github.com/grafana/grafana/pkg/metrics"
|
"github.com/grafana/grafana/pkg/metrics"
|
||||||
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@ -126,17 +129,23 @@ func (hs *HTTPServer) LoginPost(c *m.ReqContext, cmd dtos.LoginCommand) Response
|
|||||||
|
|
||||||
func (hs *HTTPServer) loginUserWithUser(user *m.User, c *m.ReqContext) {
|
func (hs *HTTPServer) loginUserWithUser(user *m.User, c *m.ReqContext) {
|
||||||
if user == nil {
|
if user == nil {
|
||||||
hs.log.Error("User login with nil user")
|
hs.log.Error("user login with nil user")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := hs.AuthTokenService.UserAuthenticatedHook(user, c)
|
userToken, err := hs.AuthTokenService.CreateToken(user.Id, c.RemoteAddr(), c.Req.UserAgent())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hs.log.Error("User auth hook failed", "error", err)
|
hs.log.Error("failed to create auth token", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
middleware.WriteSessionCookie(c, userToken.GetToken(), middleware.OneYearInSeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hs *HTTPServer) Logout(c *m.ReqContext) {
|
func (hs *HTTPServer) Logout(c *m.ReqContext) {
|
||||||
hs.AuthTokenService.SignOutUser(c)
|
if err := hs.AuthTokenService.RevokeToken(c.UserToken); err != nil && err != authtoken.ErrAuthTokenNotFound {
|
||||||
|
hs.log.Error("failed to revoke auth token", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware.WriteSessionCookie(c, "", -1)
|
||||||
|
|
||||||
if setting.SignoutRedirectUrl != "" {
|
if setting.SignoutRedirectUrl != "" {
|
||||||
c.Redirect(setting.SignoutRedirectUrl)
|
c.Redirect(setting.SignoutRedirectUrl)
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/components/apikeygen"
|
"github.com/grafana/grafana/pkg/components/apikeygen"
|
||||||
"github.com/grafana/grafana/pkg/log"
|
"github.com/grafana/grafana/pkg/log"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
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/services/session"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@ -21,7 +23,7 @@ var (
|
|||||||
ReqOrgAdmin = RoleAuth(m.ROLE_ADMIN)
|
ReqOrgAdmin = RoleAuth(m.ROLE_ADMIN)
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetContextHandler(ats auth.UserAuthTokenService) macaron.Handler {
|
func GetContextHandler(ats authtoken.UserAuthTokenService) macaron.Handler {
|
||||||
return func(c *macaron.Context) {
|
return func(c *macaron.Context) {
|
||||||
ctx := &m.ReqContext{
|
ctx := &m.ReqContext{
|
||||||
Context: c,
|
Context: c,
|
||||||
@ -49,7 +51,7 @@ func GetContextHandler(ats auth.UserAuthTokenService) macaron.Handler {
|
|||||||
case initContextWithApiKey(ctx):
|
case initContextWithApiKey(ctx):
|
||||||
case initContextWithBasicAuth(ctx, orgId):
|
case initContextWithBasicAuth(ctx, orgId):
|
||||||
case initContextWithAuthProxy(ctx, orgId):
|
case initContextWithAuthProxy(ctx, orgId):
|
||||||
case ats.InitContextWithToken(ctx, orgId):
|
case initContextWithToken(ats, ctx, orgId):
|
||||||
case initContextWithAnonymousUser(ctx):
|
case initContextWithAnonymousUser(ctx):
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +168,63 @@ func initContextWithBasicAuth(ctx *m.ReqContext, orgId int64) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cookieName = "grafana_session"
|
||||||
|
const OneYearInSeconds = 31557600 //used as default maxage for session cookies. We validate/rotate them more often.
|
||||||
|
|
||||||
|
func initContextWithToken(authTokenService authtoken.UserAuthTokenService, ctx *m.ReqContext, orgID int64) bool {
|
||||||
|
rawToken := ctx.GetCookie(cookieName)
|
||||||
|
if rawToken == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := authTokenService.LookupToken(rawToken)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Logger.Error("failed to look up user based on cookie", "error", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
query := m.GetSignedInUserQuery{UserId: token.GetUserId(), OrgId: orgID}
|
||||||
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
|
ctx.Logger.Error("failed to get user with id", "userId", token.GetUserId(), "error", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.SignedInUser = query.Result
|
||||||
|
ctx.IsSignedIn = true
|
||||||
|
ctx.UserToken = token
|
||||||
|
|
||||||
|
rotated, err := authTokenService.TryRotateToken(token, ctx.RemoteAddr(), ctx.Req.UserAgent())
|
||||||
|
if err != nil {
|
||||||
|
ctx.Logger.Error("failed to rotate token", "error", err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if rotated {
|
||||||
|
WriteSessionCookie(ctx, token.GetToken(), OneYearInSeconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteSessionCookie(ctx *m.ReqContext, value string, maxAge int) {
|
||||||
|
if setting.Env == setting.DEV {
|
||||||
|
ctx.Logger.Info("new token", "unhashed token", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Resp.Header().Del("Set-Cookie")
|
||||||
|
cookie := http.Cookie{
|
||||||
|
Name: cookieName,
|
||||||
|
Value: url.QueryEscape(value),
|
||||||
|
HttpOnly: true,
|
||||||
|
Path: setting.AppSubUrl + "/",
|
||||||
|
Secure: false, // TODO: use setting SecurityHTTPSCookies
|
||||||
|
MaxAge: maxAge,
|
||||||
|
SameSite: http.SameSiteLaxMode, // TODO: use setting LoginCookieSameSite
|
||||||
|
}
|
||||||
|
|
||||||
|
http.SetCookie(ctx.Resp, &cookie)
|
||||||
|
}
|
||||||
|
|
||||||
func AddDefaultResponseHeaders() macaron.Handler {
|
func AddDefaultResponseHeaders() macaron.Handler {
|
||||||
return func(ctx *m.ReqContext) {
|
return func(ctx *m.ReqContext) {
|
||||||
if ctx.IsApiRequest() && ctx.Req.Method == "GET" {
|
if ctx.IsApiRequest() && ctx.Req.Method == "GET" {
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
msession "github.com/go-macaron/session"
|
msession "github.com/go-macaron/session"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
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/services/session"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@ -146,17 +148,91 @@ func TestMiddlewareContext(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
middlewareScenario("Auth token service", func(sc *scenarioContext) {
|
middlewareScenario("Non-expired auth token in cookie which not are being rotated", func(sc *scenarioContext) {
|
||||||
var wasCalled bool
|
sc.withTokenSessionCookie("token")
|
||||||
sc.userAuthTokenService.initContextWithTokenProvider = func(ctx *m.ReqContext, orgId int64) bool {
|
|
||||||
wasCalled = true
|
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
||||||
return false
|
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()
|
sc.fakeReq("GET", "/").exec()
|
||||||
|
|
||||||
Convey("should call middleware", func() {
|
Convey("should init context with user info", func() {
|
||||||
So(wasCalled, ShouldBeTrue)
|
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
|
resp *httptest.ResponseRecorder
|
||||||
apiKey string
|
apiKey string
|
||||||
authHeader string
|
authHeader string
|
||||||
|
tokenSessionCookie string
|
||||||
respJson map[string]interface{}
|
respJson map[string]interface{}
|
||||||
handlerFunc handlerFunc
|
handlerFunc handlerFunc
|
||||||
defaultHandler macaron.Handler
|
defaultHandler macaron.Handler
|
||||||
@ -522,6 +599,11 @@ func (sc *scenarioContext) withValidApiKey() *scenarioContext {
|
|||||||
return sc
|
return sc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *scenarioContext) withTokenSessionCookie(unhashedToken string) *scenarioContext {
|
||||||
|
sc.tokenSessionCookie = unhashedToken
|
||||||
|
return sc
|
||||||
|
}
|
||||||
|
|
||||||
func (sc *scenarioContext) withAuthorizationHeader(authHeader string) *scenarioContext {
|
func (sc *scenarioContext) withAuthorizationHeader(authHeader string) *scenarioContext {
|
||||||
sc.authHeader = authHeader
|
sc.authHeader = authHeader
|
||||||
return sc
|
return sc
|
||||||
@ -571,6 +653,13 @@ func (sc *scenarioContext) exec() {
|
|||||||
sc.req.Header.Add("Authorization", sc.authHeader)
|
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)
|
sc.m.ServeHTTP(sc.resp, sc.req)
|
||||||
|
|
||||||
if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" {
|
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 scenarioFunc func(c *scenarioContext)
|
||||||
type handlerFunc func(c *m.ReqContext)
|
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 {
|
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 {
|
func newFakeUserAuthTokenService() *fakeUserAuthTokenService {
|
||||||
return &fakeUserAuthTokenService{
|
return &fakeUserAuthTokenService{
|
||||||
initContextWithTokenProvider: func(ctx *m.ReqContext, orgID int64) bool {
|
createTokenProvider: func(userId int64, clientIP, userAgent string) (auth.UserToken, error) {
|
||||||
return false
|
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 {
|
func (s *fakeUserAuthTokenService) CreateToken(userId int64, clientIP, userAgent string) (auth.UserToken, error) {
|
||||||
return s.initContextWithTokenProvider(ctx, orgID)
|
return s.createTokenProvider(userId, clientIP, userAgent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fakeUserAuthTokenService) UserAuthenticatedHook(user *m.User, c *m.ReqContext) error {
|
func (s *fakeUserAuthTokenService) LookupToken(unhashedToken string) (auth.UserToken, error) {
|
||||||
return nil
|
return s.lookupTokenProvider(unhashedToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fakeUserAuthTokenService) SignOutUser(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) RevokeToken(token auth.UserToken) error {
|
||||||
|
return s.revokeTokenProvider(token)
|
||||||
|
}
|
||||||
|
@ -3,6 +3,8 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
@ -14,14 +16,21 @@ func TestOrgRedirectMiddleware(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Can redirect to correct org", t, func() {
|
Convey("Can redirect to correct org", t, func() {
|
||||||
middlewareScenario("when setting a correct org for the user", func(sc *scenarioContext) {
|
middlewareScenario("when setting a correct org for the user", func(sc *scenarioContext) {
|
||||||
|
sc.withTokenSessionCookie("token")
|
||||||
bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error {
|
bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
sc.userAuthTokenService.initContextWithTokenProvider = func(ctx *m.ReqContext, orgId int64) bool {
|
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
||||||
ctx.SignedInUser = &m.SignedInUser{OrgId: 1, UserId: 12}
|
query.Result = &m.SignedInUser{OrgId: 1, UserId: 12}
|
||||||
ctx.IsSignedIn = true
|
return nil
|
||||||
return true
|
})
|
||||||
|
|
||||||
|
sc.userAuthTokenService.lookupTokenProvider = func(unhashedToken string) (auth.UserToken, error) {
|
||||||
|
return &userTokenImpl{
|
||||||
|
userId: 12,
|
||||||
|
token: "",
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sc.m.Get("/", sc.defaultHandler)
|
sc.m.Get("/", sc.defaultHandler)
|
||||||
@ -33,21 +42,23 @@ func TestOrgRedirectMiddleware(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
middlewareScenario("when setting an invalid org for user", func(sc *scenarioContext) {
|
middlewareScenario("when setting an invalid org for user", func(sc *scenarioContext) {
|
||||||
|
sc.withTokenSessionCookie("token")
|
||||||
bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error {
|
bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error {
|
||||||
return fmt.Errorf("")
|
return fmt.Errorf("")
|
||||||
})
|
})
|
||||||
|
|
||||||
sc.userAuthTokenService.initContextWithTokenProvider = func(ctx *m.ReqContext, orgId int64) bool {
|
|
||||||
ctx.SignedInUser = &m.SignedInUser{OrgId: 1, UserId: 12}
|
|
||||||
ctx.IsSignedIn = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
||||||
query.Result = &m.SignedInUser{OrgId: 1, UserId: 12}
|
query.Result = &m.SignedInUser{OrgId: 1, UserId: 12}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
sc.userAuthTokenService.lookupTokenProvider = func(unhashedToken string) (auth.UserToken, error) {
|
||||||
|
return &userTokenImpl{
|
||||||
|
userId: 12,
|
||||||
|
token: "",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
sc.m.Get("/", sc.defaultHandler)
|
sc.m.Get("/", sc.defaultHandler)
|
||||||
sc.fakeReq("GET", "/?orgId=3").exec()
|
sc.fakeReq("GET", "/?orgId=3").exec()
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/session"
|
"github.com/grafana/grafana/pkg/services/session"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
@ -74,10 +75,17 @@ func TestMiddlewareQuota(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
middlewareScenario("with user logged in", func(sc *scenarioContext) {
|
middlewareScenario("with user logged in", func(sc *scenarioContext) {
|
||||||
sc.userAuthTokenService.initContextWithTokenProvider = func(ctx *m.ReqContext, orgId int64) bool {
|
sc.withTokenSessionCookie("token")
|
||||||
ctx.SignedInUser = &m.SignedInUser{OrgId: 2, UserId: 12}
|
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
||||||
ctx.IsSignedIn = true
|
query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
|
||||||
return true
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
sc.userAuthTokenService.lookupTokenProvider = func(unhashedToken string) (auth.UserToken, error) {
|
||||||
|
return &userTokenImpl{
|
||||||
|
userId: 12,
|
||||||
|
token: "",
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
bus.AddHandler("globalQuota", func(query *m.GetGlobalQuotaByTargetQuery) error {
|
bus.AddHandler("globalQuota", func(query *m.GetGlobalQuotaByTargetQuery) error {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/log"
|
"github.com/grafana/grafana/pkg/log"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/session"
|
"github.com/grafana/grafana/pkg/services/session"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
type ReqContext struct {
|
type ReqContext struct {
|
||||||
*macaron.Context
|
*macaron.Context
|
||||||
*SignedInUser
|
*SignedInUser
|
||||||
|
UserToken auth.UserToken
|
||||||
|
|
||||||
// This should only be used by the auth_proxy
|
// This should only be used by the auth_proxy
|
||||||
Session session.SessionStore
|
Session session.SessionStore
|
||||||
|
Reference in New Issue
Block a user