Chore: use remote cache instead of session storage (#16114)

Replaces session storage in auth_proxy middleware with remote cache

Fixes #15161
pull/16450/head
Oleg Gaidarenko 7 years ago committed by GitHub
parent 645a6e5856
commit 67cbc7d4cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      devenv/docker/blocks/openldap/notes.md
  2. 5
      pkg/api/common_test.go
  3. 28
      pkg/api/http_server.go
  4. 6
      pkg/infra/remotecache/remotecache.go
  5. 1
      pkg/infra/remotecache/remotecache_test.go
  6. 34
      pkg/infra/remotecache/testing.go
  7. 116
      pkg/middleware/auth_proxy.go
  8. 145
      pkg/middleware/auth_proxy_test.go
  9. 4
      pkg/middleware/auth_test.go
  10. 4
      pkg/middleware/dashboard_redirect_test.go
  11. 7
      pkg/middleware/middleware.go
  12. 408
      pkg/middleware/middleware_test.go
  13. 7
      pkg/middleware/org_redirect_test.go
  14. 9
      pkg/middleware/quota_test.go
  15. 11
      pkg/middleware/recovery_test.go
  16. 8
      pkg/services/sqlstore/sqlstore.go

@ -2,7 +2,7 @@
Any ldif files added to the prepopulate subdirectory will be automatically imported into the OpenLdap database. Any ldif files added to the prepopulate subdirectory will be automatically imported into the OpenLdap database.
The ldif files add three users, `ldapviewer`, `ldapeditor` and `ldapadmin`. Two groups, `admins` and `users`, are added that correspond with the group mappings in the default conf/ldap.toml. `ldapadmin` is a member of `admins` and `ldapeditor` is a member of `users`. The ldif files add eight users, `ldap-admin`, `ldap-editor`, `ldap-viewer`, `ldap-carl`, `ldap-daniel`, `ldap-leo`, `ldap-tobias` and `ldap-torkel`. Two groups, `admins` and `users`, are added that correspond with the group mappings in the default conf/ldap.toml. `ldap-admin` is a member of `admins` and `ldap-editor` is a member of `users`.
Note that users that are added here need to specify a `memberOf` attribute manually as well as the `member` attribute for the group. The `memberOf` module usually does this automatically (if you add a group in Apache Directory Studio for example) but this does not work in the entrypoint script as it uses the `slapadd` command to add entries before the server has started and before the `memberOf` module is loaded. Note that users that are added here need to specify a `memberOf` attribute manually as well as the `member` attribute for the group. The `memberOf` module usually does this automatically (if you add a group in Apache Directory Studio for example) but this does not work in the entrypoint script as it uses the `slapadd` command to add entries before the server has started and before the `memberOf` module is loaded.

@ -9,9 +9,8 @@ import (
"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" "github.com/grafana/grafana/pkg/services/auth"
"gopkg.in/macaron.v1"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"gopkg.in/macaron.v1"
) )
func loggedInUserScenario(desc string, url string, fn scenarioFunc) { func loggedInUserScenario(desc string, url string, fn scenarioFunc) {
@ -124,7 +123,7 @@ func setupScenarioContext(url string) *scenarioContext {
Delims: macaron.Delims{Left: "[[", Right: "]]"}, Delims: macaron.Delims{Left: "[[", Right: "]]"},
})) }))
sc.m.Use(middleware.GetContextHandler(nil)) sc.m.Use(middleware.GetContextHandler(nil, nil))
return sc return sc
} }

@ -16,6 +16,7 @@ import (
httpstatic "github.com/grafana/grafana/pkg/api/static" httpstatic "github.com/grafana/grafana/pkg/api/static"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
@ -26,7 +27,6 @@ import (
"github.com/grafana/grafana/pkg/services/hooks" "github.com/grafana/grafana/pkg/services/hooks"
"github.com/grafana/grafana/pkg/services/quota" "github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/services/rendering"
"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"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
@ -48,15 +48,16 @@ type HTTPServer struct {
streamManager *live.StreamManager streamManager *live.StreamManager
httpSrv *http.Server httpSrv *http.Server
RouteRegister routing.RouteRegister `inject:""` RouteRegister routing.RouteRegister `inject:""`
Bus bus.Bus `inject:""` Bus bus.Bus `inject:""`
RenderService rendering.Service `inject:""` RenderService rendering.Service `inject:""`
Cfg *setting.Cfg `inject:""` Cfg *setting.Cfg `inject:""`
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 models.UserTokenService `inject:""` AuthTokenService models.UserTokenService `inject:""`
QuotaService *quota.QuotaService `inject:""` QuotaService *quota.QuotaService `inject:""`
RemoteCacheService *remotecache.RemoteCache `inject:""`
} }
func (hs *HTTPServer) Init() error { func (hs *HTTPServer) Init() error {
@ -66,8 +67,6 @@ func (hs *HTTPServer) Init() error {
hs.macaron = hs.newMacaron() hs.macaron = hs.newMacaron()
hs.registerRoutes() hs.registerRoutes()
session.Init(&setting.SessionOptions, setting.SessionConnMaxLifetime)
return nil return nil
} }
@ -226,7 +225,10 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
m.Use(hs.healthHandler) m.Use(hs.healthHandler)
m.Use(hs.metricsEndpoint) m.Use(hs.metricsEndpoint)
m.Use(middleware.GetContextHandler(hs.AuthTokenService)) m.Use(middleware.GetContextHandler(
hs.AuthTokenService,
hs.RemoteCacheService,
))
m.Use(middleware.OrgRedirect()) m.Use(middleware.OrgRedirect())
// needs to be after context handler // needs to be after context handler

@ -7,12 +7,10 @@ import (
"errors" "errors"
"time" "time"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
) )
var ( var (

@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/bmizerany/assert" "github.com/bmizerany/assert"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )

@ -0,0 +1,34 @@
package remotecache
import (
"testing"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
// NewFakeStore creates store for testing
func NewFakeStore(t *testing.T) *RemoteCache {
t.Helper()
opts := &setting.RemoteCacheOptions{
Name: "database",
ConnStr: "",
}
SQLStore := sqlstore.InitTestDB(t)
dc := &RemoteCache{
SQLStore: SQLStore,
Cfg: &setting.Cfg{
RemoteCacheOptions: opts,
},
}
err := dc.Init()
if err != nil {
t.Fatalf("failed to init remote cache for test. error: %v", err)
}
return dc
}

@ -9,18 +9,19 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/login"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
var ( const (
AUTH_PROXY_SESSION_VAR = "authProxyHeaderValue"
// cachePrefix is a prefix for the cache key
cachePrefix = "auth-proxy-sync-ttl:%s"
) )
func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool { func initContextWithAuthProxy(store *remotecache.RemoteCache, ctx *m.ReqContext, orgID int64) bool {
if !setting.AuthProxyEnabled { if !setting.AuthProxyEnabled {
return false return false
} }
@ -36,46 +37,17 @@ func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
return true return true
} }
// initialize session
if err := ctx.Session.Start(ctx.Context); err != nil {
log.Error(3, "Failed to start session. error %v", err)
return false
}
defer func() {
if err := ctx.Session.Release(); err != nil {
ctx.Logger.Error("failed to save session data", "error", err)
}
}()
query := &m.GetSignedInUserQuery{OrgId: orgID} query := &m.GetSignedInUserQuery{OrgId: orgID}
cacheKey := fmt.Sprintf(cachePrefix, proxyHeaderValue)
userID, err := store.Get(cacheKey)
inCache := err == nil
// if this session has already been authenticated by authProxy just load the user // load the user if we have them
sessProxyValue := ctx.Session.Get(AUTH_PROXY_SESSION_VAR) if inCache {
if sessProxyValue != nil && sessProxyValue.(string) == proxyHeaderValue && getRequestUserId(ctx) > 0 { query.UserId = userID.(int64)
// if we're using ldap, sync user periodically
if setting.LdapEnabled {
syncQuery := &m.LoginUserQuery{
ReqContext: ctx,
Username: proxyHeaderValue,
}
if err := syncGrafanaUserWithLdapUser(syncQuery); err != nil {
if err == login.ErrInvalidCredentials {
ctx.Handle(500, "Unable to authenticate user", err)
return false
}
ctx.Handle(500, "Failed to sync user", err)
return false
}
}
query.UserId = getRequestUserId(ctx)
// if we're using ldap, pass authproxy login name to ldap user sync // if we're using ldap, pass authproxy login name to ldap user sync
} else if setting.LdapEnabled { } else if setting.LdapEnabled {
ctx.Session.Delete(session.SESS_KEY_LASTLDAPSYNC)
syncQuery := &m.LoginUserQuery{ syncQuery := &m.LoginUserQuery{
ReqContext: ctx, ReqContext: ctx,
Username: proxyHeaderValue, Username: proxyHeaderValue,
@ -86,9 +58,6 @@ func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
ctx.Handle(500, "Unable to authenticate user", err) ctx.Handle(500, "Unable to authenticate user", err)
return false return false
} }
ctx.Handle(500, "Failed to sync user", err)
return false
} }
if syncQuery.User == nil { if syncQuery.User == nil {
@ -149,67 +118,40 @@ func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
ctx.Handle(500, "Failed to find user", err) ctx.Handle(500, "Failed to find user", err)
return true return true
} }
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
// Make sure that we cannot share a session between different users! expiration := time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute
if getRequestUserId(ctx) > 0 && getRequestUserId(ctx) != query.Result.UserId { value := query.UserId
// remove session
if err := ctx.Session.Destory(ctx.Context); err != nil {
log.Error(3, "Failed to destroy session. error: %v", err)
}
// initialize a new session // This <if> is here to make sure we do not
if err := ctx.Session.Start(ctx.Context); err != nil { // rewrite the expiration all the time
log.Error(3, "Failed to start session. error: %v", err) if inCache == false {
if err = store.Set(cacheKey, value, expiration); err != nil {
ctx.Handle(500, "Couldn't write a user in cache key", err)
return true
} }
} }
ctx.Session.Set(AUTH_PROXY_SESSION_VAR, proxyHeaderValue)
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
ctx.Session.Set(session.SESS_KEY_USERID, ctx.UserId)
return true return true
} }
var syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error { var syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
expireEpoch := time.Now().Add(time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute).Unix() ldapCfg := login.LdapCfg
if len(ldapCfg.Servers) < 1 {
var lastLdapSync int64 return fmt.Errorf("No LDAP servers available")
if lastLdapSyncInSession := query.ReqContext.Session.Get(session.SESS_KEY_LASTLDAPSYNC); lastLdapSyncInSession != nil {
lastLdapSync = lastLdapSyncInSession.(int64)
} }
if lastLdapSync < expireEpoch { for _, server := range ldapCfg.Servers {
ldapCfg := login.LdapCfg author := login.NewLdapAuthenticator(server)
if err := author.SyncUser(query); err != nil {
if len(ldapCfg.Servers) < 1 { return err
return fmt.Errorf("No LDAP servers available")
} }
for _, server := range ldapCfg.Servers {
author := login.NewLdapAuthenticator(server)
if err := author.SyncUser(query); err != nil {
return err
}
}
query.ReqContext.Session.Set(session.SESS_KEY_LASTLDAPSYNC, time.Now().Unix())
} }
return nil return nil
} }
func getRequestUserId(c *m.ReqContext) int64 {
userID := c.Session.Get(session.SESS_KEY_USERID)
if userID != nil {
return userID.(int64)
}
return 0
}
func checkAuthenticationProxy(remoteAddr string, proxyHeaderValue string) error { func checkAuthenticationProxy(remoteAddr string, proxyHeaderValue string) error {
if len(strings.TrimSpace(setting.AuthProxyWhitelist)) == 0 { if len(strings.TrimSpace(setting.AuthProxyWhitelist)) == 0 {
return nil return nil

@ -1,145 +0,0 @@
package middleware
import (
"testing"
"time"
"github.com/grafana/grafana/pkg/login"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/macaron.v1"
)
func TestAuthProxyWithLdapEnabled(t *testing.T) {
Convey("When calling sync grafana user with ldap user", t, func() {
setting.LdapEnabled = true
setting.AuthProxyLdapSyncTtl = 60
servers := []*login.LdapServerConf{{Host: "127.0.0.1"}}
login.LdapCfg = login.LdapConfig{Servers: servers}
mockLdapAuther := mockLdapAuthenticator{}
login.NewLdapAuthenticator = func(server *login.LdapServerConf) login.ILdapAuther {
return &mockLdapAuther
}
Convey("When user logs in, call SyncUser", func() {
// arrange
sess := newMockSession()
ctx := m.ReqContext{Session: &sess}
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeNil)
// act
syncGrafanaUserWithLdapUser(&m.LoginUserQuery{
ReqContext: &ctx,
Username: "test",
})
// assert
So(mockLdapAuther.syncUserCalled, ShouldBeTrue)
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, 0)
})
Convey("When session variable not expired, don't sync and don't change session var", func() {
// arrange
sess := newMockSession()
ctx := m.ReqContext{Session: &sess}
now := time.Now().Unix()
sess.Set(session.SESS_KEY_LASTLDAPSYNC, now)
sess.Set(AUTH_PROXY_SESSION_VAR, "test")
// act
syncGrafanaUserWithLdapUser(&m.LoginUserQuery{
ReqContext: &ctx,
Username: "test",
})
// assert
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldEqual, now)
So(mockLdapAuther.syncUserCalled, ShouldBeFalse)
})
Convey("When lastldapsync is expired, session variable should be updated", func() {
// arrange
sess := newMockSession()
ctx := m.ReqContext{Session: &sess}
expiredTime := time.Now().Add(time.Duration(-120) * time.Minute).Unix()
sess.Set(session.SESS_KEY_LASTLDAPSYNC, expiredTime)
sess.Set(AUTH_PROXY_SESSION_VAR, "test")
// act
syncGrafanaUserWithLdapUser(&m.LoginUserQuery{
ReqContext: &ctx,
Username: "test",
})
// assert
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, expiredTime)
So(mockLdapAuther.syncUserCalled, ShouldBeTrue)
})
})
}
type mockSession struct {
value map[interface{}]interface{}
}
func newMockSession() mockSession {
session := mockSession{}
session.value = make(map[interface{}]interface{})
return session
}
func (s *mockSession) Start(c *macaron.Context) error {
return nil
}
func (s *mockSession) Set(k interface{}, v interface{}) error {
s.value[k] = v
return nil
}
func (s *mockSession) Get(k interface{}) interface{} {
return s.value[k]
}
func (s *mockSession) Delete(k interface{}) interface{} {
delete(s.value, k)
return nil
}
func (s *mockSession) ID() string {
return ""
}
func (s *mockSession) Release() error {
return nil
}
func (s *mockSession) Destory(c *macaron.Context) error {
return nil
}
func (s *mockSession) RegenerateId(c *macaron.Context) error {
return nil
}
type mockLdapAuthenticator struct {
syncUserCalled bool
}
func (a *mockLdapAuthenticator) Login(query *m.LoginUserQuery) error {
return nil
}
func (a *mockLdapAuthenticator) SyncUser(query *m.LoginUserQuery) error {
a.syncUserCalled = true
return nil
}
func (a *mockLdapAuthenticator) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *login.LdapUserInfo) (*m.User, error) {
return nil, nil
}

@ -11,7 +11,7 @@ func TestMiddlewareAuth(t *testing.T) {
Convey("Given the grafana middleware", t, func() { Convey("Given the grafana middleware", t, func() {
reqSignIn := Auth(&AuthOptions{ReqSignedIn: true}) reqSignIn := Auth(&AuthOptions{ReqSignedIn: true})
middlewareScenario("ReqSignIn true and unauthenticated request", func(sc *scenarioContext) { middlewareScenario(t, "ReqSignIn true and unauthenticated request", func(sc *scenarioContext) {
sc.m.Get("/secure", reqSignIn, sc.defaultHandler) sc.m.Get("/secure", reqSignIn, sc.defaultHandler)
sc.fakeReq("GET", "/secure").exec() sc.fakeReq("GET", "/secure").exec()
@ -21,7 +21,7 @@ func TestMiddlewareAuth(t *testing.T) {
}) })
}) })
middlewareScenario("ReqSignIn true and unauthenticated API request", func(sc *scenarioContext) { middlewareScenario(t, "ReqSignIn true and unauthenticated API request", func(sc *scenarioContext) {
sc.m.Get("/api/secure", reqSignIn, sc.defaultHandler) sc.m.Get("/api/secure", reqSignIn, sc.defaultHandler)
sc.fakeReq("GET", "/api/secure").exec() sc.fakeReq("GET", "/api/secure").exec()

@ -27,7 +27,7 @@ func TestMiddlewareDashboardRedirect(t *testing.T) {
return nil return nil
}) })
middlewareScenario("GET dashboard by legacy url", func(sc *scenarioContext) { middlewareScenario(t, "GET dashboard by legacy url", func(sc *scenarioContext) {
sc.m.Get("/dashboard/db/:slug", redirectFromLegacyDashboardUrl, sc.defaultHandler) sc.m.Get("/dashboard/db/:slug", redirectFromLegacyDashboardUrl, sc.defaultHandler)
sc.fakeReqWithParams("GET", "/dashboard/db/dash?orgId=1&panelId=2", map[string]string{}).exec() sc.fakeReqWithParams("GET", "/dashboard/db/dash?orgId=1&panelId=2", map[string]string{}).exec()
@ -40,7 +40,7 @@ func TestMiddlewareDashboardRedirect(t *testing.T) {
}) })
}) })
middlewareScenario("GET dashboard solo by legacy url", func(sc *scenarioContext) { middlewareScenario(t, "GET dashboard solo by legacy url", func(sc *scenarioContext) {
sc.m.Get("/dashboard-solo/db/:slug", redirectFromLegacyDashboardSoloUrl, sc.defaultHandler) sc.m.Get("/dashboard-solo/db/:slug", redirectFromLegacyDashboardSoloUrl, sc.defaultHandler)
sc.fakeReqWithParams("GET", "/dashboard-solo/db/dash?orgId=1&panelId=2", map[string]string{}).exec() sc.fakeReqWithParams("GET", "/dashboard-solo/db/dash?orgId=1&panelId=2", map[string]string{}).exec()

@ -8,9 +8,9 @@ import (
"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/infra/remotecache"
"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/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"
macaron "gopkg.in/macaron.v1" macaron "gopkg.in/macaron.v1"
@ -23,12 +23,11 @@ var (
ReqOrgAdmin = RoleAuth(m.ROLE_ADMIN) ReqOrgAdmin = RoleAuth(m.ROLE_ADMIN)
) )
func GetContextHandler(ats m.UserTokenService) macaron.Handler { func GetContextHandler(ats m.UserTokenService, remoteCache *remotecache.RemoteCache) macaron.Handler {
return func(c *macaron.Context) { return func(c *macaron.Context) {
ctx := &m.ReqContext{ ctx := &m.ReqContext{
Context: c, Context: c,
SignedInUser: &m.SignedInUser{}, SignedInUser: &m.SignedInUser{},
Session: session.GetSession(), // should only be used by auth_proxy
IsSignedIn: false, IsSignedIn: false,
AllowAnonymous: false, AllowAnonymous: false,
SkipCache: false, SkipCache: false,
@ -50,7 +49,7 @@ func GetContextHandler(ats m.UserTokenService) macaron.Handler {
case initContextWithRenderAuth(ctx): case initContextWithRenderAuth(ctx):
case initContextWithApiKey(ctx): case initContextWithApiKey(ctx):
case initContextWithBasicAuth(ctx, orgId): case initContextWithBasicAuth(ctx, orgId):
case initContextWithAuthProxy(ctx, orgId): case initContextWithAuthProxy(remoteCache, ctx, orgId):
case initContextWithToken(ats, ctx, orgId): case initContextWithToken(ats, ctx, orgId):
case initContextWithAnonymousUser(ctx): case initContextWithAnonymousUser(ctx):
} }

@ -2,6 +2,7 @@ package middleware
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"path/filepath" "path/filepath"
@ -10,6 +11,7 @@ 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"
"github.com/grafana/grafana/pkg/infra/remotecache"
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"
"github.com/grafana/grafana/pkg/services/session" "github.com/grafana/grafana/pkg/services/session"
@ -23,29 +25,29 @@ func TestMiddlewareContext(t *testing.T) {
setting.ERR_TEMPLATE_NAME = "error-template" setting.ERR_TEMPLATE_NAME = "error-template"
Convey("Given the grafana middleware", t, func() { Convey("Given the grafana middleware", t, func() {
middlewareScenario("middleware should add context to injector", func(sc *scenarioContext) { middlewareScenario(t, "middleware should add context to injector", func(sc *scenarioContext) {
sc.fakeReq("GET", "/").exec() sc.fakeReq("GET", "/").exec()
So(sc.context, ShouldNotBeNil) So(sc.context, ShouldNotBeNil)
}) })
middlewareScenario("Default middleware should allow get request", func(sc *scenarioContext) { middlewareScenario(t, "Default middleware should allow get request", func(sc *scenarioContext) {
sc.fakeReq("GET", "/").exec() sc.fakeReq("GET", "/").exec()
So(sc.resp.Code, ShouldEqual, 200) So(sc.resp.Code, ShouldEqual, 200)
}) })
middlewareScenario("middleware should add Cache-Control header for GET requests to API", func(sc *scenarioContext) { middlewareScenario(t, "middleware should add Cache-Control header for GET requests to API", func(sc *scenarioContext) {
sc.fakeReq("GET", "/api/search").exec() sc.fakeReq("GET", "/api/search").exec()
So(sc.resp.Header().Get("Cache-Control"), ShouldEqual, "no-cache") So(sc.resp.Header().Get("Cache-Control"), ShouldEqual, "no-cache")
So(sc.resp.Header().Get("Pragma"), ShouldEqual, "no-cache") So(sc.resp.Header().Get("Pragma"), ShouldEqual, "no-cache")
So(sc.resp.Header().Get("Expires"), ShouldEqual, "-1") So(sc.resp.Header().Get("Expires"), ShouldEqual, "-1")
}) })
middlewareScenario("middleware should not add Cache-Control header to for non-API GET requests", func(sc *scenarioContext) { middlewareScenario(t, "middleware should not add Cache-Control header to for non-API GET requests", func(sc *scenarioContext) {
sc.fakeReq("GET", "/").exec() sc.fakeReq("GET", "/").exec()
So(sc.resp.Header().Get("Cache-Control"), ShouldBeEmpty) So(sc.resp.Header().Get("Cache-Control"), ShouldBeEmpty)
}) })
middlewareScenario("Invalid api key", func(sc *scenarioContext) { middlewareScenario(t, "Invalid api key", func(sc *scenarioContext) {
sc.apiKey = "invalid_key_test" sc.apiKey = "invalid_key_test"
sc.fakeReq("GET", "/").exec() sc.fakeReq("GET", "/").exec()
@ -59,7 +61,7 @@ func TestMiddlewareContext(t *testing.T) {
}) })
}) })
middlewareScenario("Using basic auth", func(sc *scenarioContext) { middlewareScenario(t, "Using basic auth", func(sc *scenarioContext) {
bus.AddHandler("test", func(query *m.GetUserByLoginQuery) error { bus.AddHandler("test", func(query *m.GetUserByLoginQuery) error {
query.Result = &m.User{ query.Result = &m.User{
@ -89,7 +91,7 @@ func TestMiddlewareContext(t *testing.T) {
}) })
}) })
middlewareScenario("Valid api key", func(sc *scenarioContext) { middlewareScenario(t, "Valid api key", func(sc *scenarioContext) {
keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd") keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error { bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
@ -110,7 +112,7 @@ func TestMiddlewareContext(t *testing.T) {
}) })
}) })
middlewareScenario("Valid api key, but does not match db hash", func(sc *scenarioContext) { middlewareScenario(t, "Valid api key, but does not match db hash", func(sc *scenarioContext) {
keyhash := "something_not_matching" keyhash := "something_not_matching"
bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error { bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
@ -126,7 +128,7 @@ func TestMiddlewareContext(t *testing.T) {
}) })
}) })
middlewareScenario("Valid api key via Basic auth", func(sc *scenarioContext) { middlewareScenario(t, "Valid api key via Basic auth", func(sc *scenarioContext) {
keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd") keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error { bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error {
@ -148,7 +150,7 @@ func TestMiddlewareContext(t *testing.T) {
}) })
}) })
middlewareScenario("Non-expired auth token in cookie which not are being rotated", func(sc *scenarioContext) { middlewareScenario(t, "Non-expired auth token in cookie which not are being rotated", func(sc *scenarioContext) {
sc.withTokenSessionCookie("token") sc.withTokenSessionCookie("token")
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
@ -177,7 +179,7 @@ func TestMiddlewareContext(t *testing.T) {
}) })
}) })
middlewareScenario("Non-expired auth token in cookie which are being rotated", func(sc *scenarioContext) { middlewareScenario(t, "Non-expired auth token in cookie which are being rotated", func(sc *scenarioContext) {
sc.withTokenSessionCookie("token") sc.withTokenSessionCookie("token")
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
@ -224,7 +226,7 @@ func TestMiddlewareContext(t *testing.T) {
}) })
}) })
middlewareScenario("Invalid/expired auth token in cookie", func(sc *scenarioContext) { middlewareScenario(t, "Invalid/expired auth token in cookie", func(sc *scenarioContext) {
sc.withTokenSessionCookie("token") sc.withTokenSessionCookie("token")
sc.userAuthTokenService.LookupTokenProvider = func(unhashedToken string) (*m.UserToken, error) { sc.userAuthTokenService.LookupTokenProvider = func(unhashedToken string) (*m.UserToken, error) {
@ -240,7 +242,7 @@ func TestMiddlewareContext(t *testing.T) {
}) })
}) })
middlewareScenario("When anonymous access is enabled", func(sc *scenarioContext) { middlewareScenario(t, "When anonymous access is enabled", func(sc *scenarioContext) {
setting.AnonymousEnabled = true setting.AnonymousEnabled = true
setting.AnonymousOrgName = "test" setting.AnonymousOrgName = "test"
setting.AnonymousOrgRole = string(m.ROLE_EDITOR) setting.AnonymousOrgRole = string(m.ROLE_EDITOR)
@ -265,287 +267,192 @@ func TestMiddlewareContext(t *testing.T) {
}) })
}) })
middlewareScenario("When auth_proxy is enabled enabled and user exists", func(sc *scenarioContext) { Convey("auth_proxy", func() {
setting.AuthProxyEnabled = true
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
setting.AuthProxyHeaderProperty = "username"
setting.LdapEnabled = false
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
return nil
})
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
cmd.Result = &m.User{Id: 12}
return nil
})
setting.SessionOptions = msession.Options{}
sc.fakeReq("GET", "/")
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
sc.exec()
Convey("should init context with user info", func() {
So(sc.context.IsSignedIn, ShouldBeTrue)
So(sc.context.UserId, ShouldEqual, 12)
So(sc.context.OrgId, ShouldEqual, 2)
})
})
middlewareScenario("When auth_proxy is enabled enabled and user does not exists", func(sc *scenarioContext) {
setting.AuthProxyEnabled = true setting.AuthProxyEnabled = true
setting.AuthProxyWhitelist = ""
setting.AuthProxyAutoSignUp = true
setting.LdapEnabled = true
setting.AuthProxyHeaderName = "X-WEBAUTH-USER" setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
setting.AuthProxyHeaderProperty = "username" setting.AuthProxyHeaderProperty = "username"
setting.AuthProxyAutoSignUp = true name := "markelog"
setting.LdapEnabled = false
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { middlewareScenario(t, "should sync the user if it's not in the cache", func(sc *scenarioContext) {
if query.UserId > 0 { called := false
query.Result = &m.SignedInUser{OrgId: 4, UserId: 33} syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
called = true
query.User = &m.User{Id: 32}
return nil return nil
} }
return m.ErrUserNotFound
})
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
cmd.Result = &m.User{Id: 33}
return nil
})
sc.fakeReq("GET", "/") bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") query.Result = &m.User{Id: 32}
sc.exec() return nil
})
Convey("Should create user if auto sign up is enabled", func() {
So(sc.context.IsSignedIn, ShouldBeTrue)
So(sc.context.UserId, ShouldEqual, 33)
So(sc.context.OrgId, ShouldEqual, 4)
})
})
middlewareScenario("When auth_proxy is enabled and IPv4 request RemoteAddr is not trusted", func(sc *scenarioContext) {
setting.AuthProxyEnabled = true
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
setting.AuthProxyHeaderProperty = "username"
setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
sc.fakeReq("GET", "/")
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
sc.req.RemoteAddr = "192.168.3.1:12345"
sc.exec()
Convey("should return 407 status code", func() {
So(sc.resp.Code, ShouldEqual, 407)
So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 192.168.3.1 is not from the authentication proxy")
})
})
middlewareScenario("When auth_proxy is enabled and IPv4 request RemoteAddr is not within trusted CIDR block", func(sc *scenarioContext) {
setting.AuthProxyEnabled = true
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
setting.AuthProxyHeaderProperty = "username"
setting.AuthProxyWhitelist = "192.168.1.0/24, 2001::0/120"
sc.fakeReq("GET", "/")
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
sc.req.RemoteAddr = "192.168.3.1:12345"
sc.exec()
Convey("should return 407 status code", func() { bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
So(sc.resp.Code, ShouldEqual, 407) query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 192.168.3.1 is not from the authentication proxy") return nil
}) })
})
middlewareScenario("When auth_proxy is enabled and IPv6 request RemoteAddr is not trusted", func(sc *scenarioContext) { sc.fakeReq("GET", "/")
setting.AuthProxyEnabled = true
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
setting.AuthProxyHeaderProperty = "username"
setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
sc.fakeReq("GET", "/") sc.req.Header.Add(setting.AuthProxyHeaderName, name)
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") sc.exec()
sc.req.RemoteAddr = "[2001:23]:12345"
sc.exec()
Convey("should return 407 status code", func() { Convey("Should init user via ldap", func() {
So(sc.resp.Code, ShouldEqual, 407) So(called, ShouldBeTrue)
So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 2001:23 is not from the authentication proxy") So(sc.context.IsSignedIn, ShouldBeTrue)
So(sc.context.UserId, ShouldEqual, 32)
So(sc.context.OrgId, ShouldEqual, 4)
})
}) })
})
middlewareScenario("When auth_proxy is enabled and IPv6 request RemoteAddr is not within trusted CIDR block", func(sc *scenarioContext) {
setting.AuthProxyEnabled = true
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
setting.AuthProxyHeaderProperty = "username"
setting.AuthProxyWhitelist = "192.168.1.0/24, 2001::0/120"
sc.fakeReq("GET", "/")
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
sc.req.RemoteAddr = "[2001:23]:12345"
sc.exec()
Convey("should return 407 status code", func() { middlewareScenario(t, "should not sync the user if it's in the cache", func(sc *scenarioContext) {
So(sc.resp.Code, ShouldEqual, 407) called := false
So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 2001:23 is not from the authentication proxy") syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
}) called = true
}) query.User = &m.User{Id: 32}
return nil
}
middlewareScenario("When auth_proxy is enabled and request RemoteAddr is trusted", func(sc *scenarioContext) { bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
setting.AuthProxyEnabled = true query.Result = &m.User{Id: 32}
setting.AuthProxyHeaderName = "X-WEBAUTH-USER" return nil
setting.AuthProxyHeaderProperty = "username" })
setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
query.Result = &m.SignedInUser{OrgId: 4, UserId: 33} query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
return nil return nil
}) })
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error { key := fmt.Sprintf(cachePrefix, name)
cmd.Result = &m.User{Id: 33} sc.remoteCacheService.Set(key, int64(33), 0)
return nil sc.fakeReq("GET", "/")
})
sc.fakeReq("GET", "/") sc.req.Header.Add(setting.AuthProxyHeaderName, name)
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") sc.exec()
sc.req.RemoteAddr = "[2001::23]:12345"
sc.exec()
Convey("Should init context with user info", func() { cacheValue, cacheErr := sc.remoteCacheService.Get(key)
So(sc.context.IsSignedIn, ShouldBeTrue)
So(sc.context.UserId, ShouldEqual, 33)
So(sc.context.OrgId, ShouldEqual, 4)
})
})
middlewareScenario("When auth_proxy is enabled and IPv4 request RemoteAddr is within trusted CIDR block", func(sc *scenarioContext) { Convey("Should init user via cache", func() {
setting.AuthProxyEnabled = true So(called, ShouldBeFalse)
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
setting.AuthProxyHeaderProperty = "username"
setting.AuthProxyWhitelist = "192.168.1.0/24, 2001::0/120"
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { So(sc.context.IsSignedIn, ShouldBeTrue)
query.Result = &m.SignedInUser{OrgId: 4, UserId: 33} So(sc.context.UserId, ShouldEqual, 32)
return nil So(sc.context.OrgId, ShouldEqual, 4)
})
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error { So(cacheValue, ShouldEqual, 33)
cmd.Result = &m.User{Id: 33} So(cacheErr, ShouldBeNil)
return nil })
}) })
sc.fakeReq("GET", "/") middlewareScenario(t, "should create an user from a header", func(sc *scenarioContext) {
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") setting.LdapEnabled = false
sc.req.RemoteAddr = "192.168.1.10:12345" setting.AuthProxyAutoSignUp = true
sc.exec()
Convey("Should init context with user info", func() { bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
So(sc.context.IsSignedIn, ShouldBeTrue) if query.UserId > 0 {
So(sc.context.UserId, ShouldEqual, 33) query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
So(sc.context.OrgId, ShouldEqual, 4) return nil
}) }
}) return m.ErrUserNotFound
})
middlewareScenario("When auth_proxy is enabled and IPv6 request RemoteAddr is within trusted CIDR block", func(sc *scenarioContext) { bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
setting.AuthProxyEnabled = true cmd.Result = &m.User{Id: 33}
setting.AuthProxyHeaderName = "X-WEBAUTH-USER" return nil
setting.AuthProxyHeaderProperty = "username" })
setting.AuthProxyWhitelist = "192.168.1.0/24, 2001::0/120"
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { sc.fakeReq("GET", "/")
query.Result = &m.SignedInUser{OrgId: 4, UserId: 33} sc.req.Header.Add(setting.AuthProxyHeaderName, name)
return nil sc.exec()
})
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error { Convey("Should create user from header info", func() {
cmd.Result = &m.User{Id: 33} So(sc.context.IsSignedIn, ShouldBeTrue)
return nil So(sc.context.UserId, ShouldEqual, 33)
So(sc.context.OrgId, ShouldEqual, 4)
})
}) })
sc.fakeReq("GET", "/") middlewareScenario(t, "should get an existing user from header", func(sc *scenarioContext) {
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") setting.LdapEnabled = false
sc.req.RemoteAddr = "[2001::23]:12345"
sc.exec()
Convey("Should init context with user info", func() { bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
So(sc.context.IsSignedIn, ShouldBeTrue) query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
So(sc.context.UserId, ShouldEqual, 33) return nil
So(sc.context.OrgId, ShouldEqual, 4) })
})
})
middlewareScenario("When session exists for previous user, create a new session", func(sc *scenarioContext) { bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
setting.AuthProxyEnabled = true cmd.Result = &m.User{Id: 12}
setting.AuthProxyHeaderName = "X-WEBAUTH-USER" return nil
setting.AuthProxyHeaderProperty = "username" })
setting.AuthProxyWhitelist = ""
bus.AddHandler("test", func(query *m.UpsertUserCommand) error { sc.fakeReq("GET", "/")
query.Result = &m.User{Id: 32} sc.req.Header.Add(setting.AuthProxyHeaderName, name)
return nil sc.exec()
})
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { Convey("should init context with user info", func() {
query.Result = &m.SignedInUser{OrgId: 4, UserId: 32} So(sc.context.IsSignedIn, ShouldBeTrue)
return nil So(sc.context.UserId, ShouldEqual, 12)
So(sc.context.OrgId, ShouldEqual, 2)
})
}) })
// create session middlewareScenario(t, "should allow the request from whitelist IP", func(sc *scenarioContext) {
sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) { setting.AuthProxyWhitelist = "192.168.1.0/24, 2001::0/120"
c.Session.Set(session.SESS_KEY_USERID, int64(33)) setting.LdapEnabled = false
}).exec()
oldSessionID := sc.context.Session.ID() bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
return nil
})
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
sc.exec() cmd.Result = &m.User{Id: 33}
return nil
})
newSessionID := sc.context.Session.ID() sc.fakeReq("GET", "/")
sc.req.Header.Add(setting.AuthProxyHeaderName, name)
sc.req.RemoteAddr = "[2001::23]:12345"
sc.exec()
Convey("Should not share session with other user", func() { Convey("Should init context with user info", func() {
So(oldSessionID, ShouldNotEqual, newSessionID) So(sc.context.IsSignedIn, ShouldBeTrue)
So(sc.context.UserId, ShouldEqual, 33)
So(sc.context.OrgId, ShouldEqual, 4)
})
}) })
})
middlewareScenario("When auth_proxy and ldap enabled call sync with ldap user", func(sc *scenarioContext) { middlewareScenario(t, "should not allow the request from whitelist IP", func(sc *scenarioContext) {
setting.AuthProxyEnabled = true setting.AuthProxyWhitelist = "8.8.8.8"
setting.AuthProxyHeaderName = "X-WEBAUTH-USER" setting.LdapEnabled = false
setting.AuthProxyHeaderProperty = "username"
setting.AuthProxyWhitelist = ""
setting.LdapEnabled = true
called := false
syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
called = true
query.User = &m.User{Id: 32}
return nil
}
bus.AddHandler("test", func(query *m.UpsertUserCommand) error { bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
query.Result = &m.User{Id: 32} query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
return nil return nil
}) })
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
query.Result = &m.SignedInUser{OrgId: 4, UserId: 32} cmd.Result = &m.User{Id: 33}
return nil return nil
}) })
sc.fakeReq("GET", "/") sc.fakeReq("GET", "/")
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") sc.req.Header.Add(setting.AuthProxyHeaderName, name)
sc.exec() sc.req.RemoteAddr = "[2001::23]:12345"
sc.exec()
Convey("Should call syncGrafanaUserWithLdapUser", func() { Convey("should return 407 status code", func() {
So(called, ShouldBeTrue) So(sc.resp.Code, ShouldEqual, 407)
So(sc.context, ShouldBeNil)
})
}) })
}) })
}) })
} }
func middlewareScenario(desc string, fn scenarioFunc) { func middlewareScenario(t *testing.T, desc string, fn scenarioFunc) {
Convey(desc, func() { Convey(desc, func() {
defer bus.ClearBusHandlers() defer bus.ClearBusHandlers()
@ -562,9 +469,10 @@ func middlewareScenario(desc string, fn scenarioFunc) {
Delims: macaron.Delims{Left: "[[", Right: "]]"}, Delims: macaron.Delims{Left: "[[", Right: "]]"},
})) }))
session.Init(&msession.Options{}, 0)
sc.userAuthTokenService = auth.NewFakeUserAuthTokenService() sc.userAuthTokenService = auth.NewFakeUserAuthTokenService()
sc.m.Use(GetContextHandler(sc.userAuthTokenService)) sc.remoteCacheService = remotecache.NewFakeStore(t)
sc.m.Use(GetContextHandler(sc.userAuthTokenService, sc.remoteCacheService))
// mock out gc goroutine // mock out gc goroutine
session.StartSessionGC = func() {} session.StartSessionGC = func() {}
setting.SessionOptions = msession.Options{} setting.SessionOptions = msession.Options{}
@ -597,6 +505,7 @@ type scenarioContext struct {
defaultHandler macaron.Handler defaultHandler macaron.Handler
url string url string
userAuthTokenService *auth.FakeUserAuthTokenService userAuthTokenService *auth.FakeUserAuthTokenService
remoteCacheService *remotecache.RemoteCache
req *http.Request req *http.Request
} }
@ -622,13 +531,6 @@ func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
So(err, ShouldBeNil) So(err, ShouldBeNil)
sc.req = req sc.req = req
// add session cookie from last request
if sc.context != nil {
if sc.context.Session.ID() != "" {
req.Header.Add("Cookie", "grafana_sess="+sc.context.Session.ID()+";")
}
}
return sc return sc
} }

@ -1,9 +1,8 @@
package middleware package middleware
import ( import (
"testing"
"fmt" "fmt"
"testing"
"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"
@ -13,7 +12,7 @@ import (
func TestOrgRedirectMiddleware(t *testing.T) { 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(t, "when setting a correct org for the user", func(sc *scenarioContext) {
sc.withTokenSessionCookie("token") sc.withTokenSessionCookie("token")
bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error { bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error {
return nil return nil
@ -39,7 +38,7 @@ func TestOrgRedirectMiddleware(t *testing.T) {
}) })
}) })
middlewareScenario("when setting an invalid org for user", func(sc *scenarioContext) { middlewareScenario(t, "when setting an invalid org for user", func(sc *scenarioContext) {
sc.withTokenSessionCookie("token") 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("")

@ -3,11 +3,10 @@ package middleware
import ( import (
"testing" "testing"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/quota"
"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/quota"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
) )
@ -43,7 +42,7 @@ func TestMiddlewareQuota(t *testing.T) {
} }
QuotaFn := Quota(qs) QuotaFn := Quota(qs)
middlewareScenario("with user not logged in", func(sc *scenarioContext) { middlewareScenario(t, "with user not logged in", func(sc *scenarioContext) {
bus.AddHandler("globalQuota", func(query *m.GetGlobalQuotaByTargetQuery) error { bus.AddHandler("globalQuota", func(query *m.GetGlobalQuotaByTargetQuery) error {
query.Result = &m.GlobalQuotaDTO{ query.Result = &m.GlobalQuotaDTO{
Target: query.Target, Target: query.Target,
@ -81,7 +80,7 @@ func TestMiddlewareQuota(t *testing.T) {
}) })
}) })
middlewareScenario("with user logged in", func(sc *scenarioContext) { middlewareScenario(t, "with user logged in", func(sc *scenarioContext) {
sc.withTokenSessionCookie("token") sc.withTokenSessionCookie("token")
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
query.Result = &m.SignedInUser{OrgId: 2, UserId: 12} query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}

@ -5,6 +5,7 @@ import (
"testing" "testing"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/remotecache"
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"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -17,7 +18,7 @@ func TestRecoveryMiddleware(t *testing.T) {
Convey("Given an api route that panics", t, func() { Convey("Given an api route that panics", t, func() {
apiURL := "/api/whatever" apiURL := "/api/whatever"
recoveryScenario("recovery middleware should return json", apiURL, func(sc *scenarioContext) { recoveryScenario(t, "recovery middleware should return json", apiURL, func(sc *scenarioContext) {
sc.handlerFunc = PanicHandler sc.handlerFunc = PanicHandler
sc.fakeReq("GET", apiURL).exec() sc.fakeReq("GET", apiURL).exec()
sc.req.Header.Add("content-type", "application/json") sc.req.Header.Add("content-type", "application/json")
@ -30,7 +31,7 @@ func TestRecoveryMiddleware(t *testing.T) {
Convey("Given a non-api route that panics", t, func() { Convey("Given a non-api route that panics", t, func() {
apiURL := "/whatever" apiURL := "/whatever"
recoveryScenario("recovery middleware should return html", apiURL, func(sc *scenarioContext) { recoveryScenario(t, "recovery middleware should return html", apiURL, func(sc *scenarioContext) {
sc.handlerFunc = PanicHandler sc.handlerFunc = PanicHandler
sc.fakeReq("GET", apiURL).exec() sc.fakeReq("GET", apiURL).exec()
@ -45,7 +46,7 @@ func PanicHandler(c *m.ReqContext) {
panic("Handler has panicked") panic("Handler has panicked")
} }
func recoveryScenario(desc string, url string, fn scenarioFunc) { func recoveryScenario(t *testing.T, desc string, url string, fn scenarioFunc) {
Convey(desc, func() { Convey(desc, func() {
defer bus.ClearBusHandlers() defer bus.ClearBusHandlers()
@ -64,7 +65,9 @@ func recoveryScenario(desc string, url string, fn scenarioFunc) {
})) }))
sc.userAuthTokenService = auth.NewFakeUserAuthTokenService() sc.userAuthTokenService = auth.NewFakeUserAuthTokenService()
sc.m.Use(GetContextHandler(sc.userAuthTokenService)) sc.remoteCacheService = remotecache.NewFakeStore(t)
sc.m.Use(GetContextHandler(sc.userAuthTokenService, sc.remoteCacheService))
// mock out gc goroutine // mock out gc goroutine
sc.m.Use(OrgRedirect()) sc.m.Use(OrgRedirect())
sc.m.Use(AddDefaultResponseHeaders()) sc.m.Use(AddDefaultResponseHeaders())

@ -11,6 +11,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"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"
@ -21,12 +23,8 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil" "github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
_ "github.com/grafana/grafana/pkg/tsdb/mssql" _ "github.com/grafana/grafana/pkg/tsdb/mssql"
"github.com/grafana/grafana/pkg/util"
_ "github.com/lib/pq" _ "github.com/lib/pq"
sqlite3 "github.com/mattn/go-sqlite3" sqlite3 "github.com/mattn/go-sqlite3"
) )

Loading…
Cancel
Save