Browse Source

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

Replaces session storage in auth_proxy middleware with remote cache

Fixes #15161
Oleg Gaidarenko 6 năm trước cách đây
mục cha
commit
67cbc7d4cf

+ 1 - 1
devenv/docker/blocks/openldap/notes.md

@@ -2,7 +2,7 @@
 
 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.
 

+ 2 - 3
pkg/api/common_test.go

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

+ 15 - 13
pkg/api/http_server.go

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

+ 2 - 4
pkg/infra/remotecache/remotecache.go

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

+ 0 - 1
pkg/infra/remotecache/remotecache_test.go

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

+ 34 - 0
pkg/infra/remotecache/testing.go

@@ -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
+}

+ 29 - 87
pkg/middleware/auth_proxy.go

@@ -9,18 +9,19 @@ import (
 	"time"
 
 	"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"
 	m "github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/services/session"
 	"github.com/grafana/grafana/pkg/setting"
 )
 
-var (
-	AUTH_PROXY_SESSION_VAR = "authProxyHeaderValue"
+const (
+
+	// 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 {
 		return false
 	}
@@ -36,46 +37,17 @@ func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
 		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}
+	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
-	sessProxyValue := ctx.Session.Get(AUTH_PROXY_SESSION_VAR)
-	if sessProxyValue != nil && sessProxyValue.(string) == proxyHeaderValue && getRequestUserId(ctx) > 0 {
-		// 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
-				}
+	// load the user if we have them
+	if inCache {
+		query.UserId = userID.(int64)
 
-				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
 	} else if setting.LdapEnabled {
-		ctx.Session.Delete(session.SESS_KEY_LASTLDAPSYNC)
-
 		syncQuery := &m.LoginUserQuery{
 			ReqContext: ctx,
 			Username:   proxyHeaderValue,
@@ -86,9 +58,6 @@ func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
 				ctx.Handle(500, "Unable to authenticate user", err)
 				return false
 			}
-
-			ctx.Handle(500, "Failed to sync user", err)
-			return false
 		}
 
 		if syncQuery.User == nil {
@@ -149,67 +118,40 @@ func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
 		ctx.Handle(500, "Failed to find user", err)
 		return true
 	}
+	ctx.SignedInUser = query.Result
+	ctx.IsSignedIn = true
 
-	// Make sure that we cannot share a session between different users!
-	if getRequestUserId(ctx) > 0 && getRequestUserId(ctx) != query.Result.UserId {
-		// remove session
-		if err := ctx.Session.Destory(ctx.Context); err != nil {
-			log.Error(3, "Failed to destroy session. error: %v", err)
-		}
+	expiration := time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute
+	value := query.UserId
 
-		// initialize a new session
-		if err := ctx.Session.Start(ctx.Context); err != nil {
-			log.Error(3, "Failed to start session. error: %v", err)
+	// This <if> is here to make sure we do not
+	// rewrite the expiration all the time
+	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
 }
 
 var syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
-	expireEpoch := time.Now().Add(time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute).Unix()
-
-	var lastLdapSync int64
-	if lastLdapSyncInSession := query.ReqContext.Session.Get(session.SESS_KEY_LASTLDAPSYNC); lastLdapSyncInSession != nil {
-		lastLdapSync = lastLdapSyncInSession.(int64)
+	ldapCfg := login.LdapCfg
+	if len(ldapCfg.Servers) < 1 {
+		return fmt.Errorf("No LDAP servers available")
 	}
 
-	if lastLdapSync < expireEpoch {
-		ldapCfg := login.LdapCfg
-
-		if len(ldapCfg.Servers) < 1 {
-			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
 		}
-
-		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
 }
 
-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 {
 	if len(strings.TrimSpace(setting.AuthProxyWhitelist)) == 0 {
 		return nil

+ 0 - 145
pkg/middleware/auth_proxy_test.go

@@ -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
-}

+ 2 - 2
pkg/middleware/auth_test.go

@@ -11,7 +11,7 @@ func TestMiddlewareAuth(t *testing.T) {
 	Convey("Given the grafana middleware", t, func() {
 		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.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.fakeReq("GET", "/api/secure").exec()

+ 2 - 2
pkg/middleware/dashboard_redirect_test.go

@@ -27,7 +27,7 @@ func TestMiddlewareDashboardRedirect(t *testing.T) {
 			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.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.fakeReqWithParams("GET", "/dashboard-solo/db/dash?orgId=1&panelId=2", map[string]string{}).exec()

+ 3 - 4
pkg/middleware/middleware.go

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

+ 155 - 253
pkg/middleware/middleware_test.go

@@ -2,6 +2,7 @@ package middleware
 
 import (
 	"encoding/json"
+	"fmt"
 	"net/http"
 	"net/http/httptest"
 	"path/filepath"
@@ -10,6 +11,7 @@ import (
 
 	msession "github.com/go-macaron/session"
 	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/infra/remotecache"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/auth"
 	"github.com/grafana/grafana/pkg/services/session"
@@ -23,29 +25,29 @@ func TestMiddlewareContext(t *testing.T) {
 	setting.ERR_TEMPLATE_NAME = "error-template"
 
 	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()
 			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()
 			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()
 			So(sc.resp.Header().Get("Cache-Control"), ShouldEqual, "no-cache")
 			So(sc.resp.Header().Get("Pragma"), ShouldEqual, "no-cache")
 			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()
 			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.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 {
 				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")
 
 			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"
 
 			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")
 
 			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")
 
 			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")
 
 			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.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.AnonymousOrgName = "test"
 			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) {
-			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) {
+		Convey("auth_proxy", func() {
 			setting.AuthProxyEnabled = true
+			setting.AuthProxyWhitelist = ""
+			setting.AuthProxyAutoSignUp = true
+			setting.LdapEnabled = true
 			setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
 			setting.AuthProxyHeaderProperty = "username"
-			setting.AuthProxyAutoSignUp = true
-			setting.LdapEnabled = false
+			name := "markelog"
 
-			bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
-				if query.UserId > 0 {
-					query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
+			middlewareScenario(t, "should sync the user if it's not in the cache", func(sc *scenarioContext) {
+				called := false
+				syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
+					called = true
+					query.User = &m.User{Id: 32}
 					return nil
 				}
-				return m.ErrUserNotFound
-			})
-
-			bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
-				cmd.Result = &m.User{Id: 33}
-				return nil
-			})
 
-			sc.fakeReq("GET", "/")
-			sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
-			sc.exec()
-
-			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()
+				bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
+					query.Result = &m.User{Id: 32}
+					return nil
+				})
 
-			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")
-			})
-		})
+				bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
+					query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
+					return nil
+				})
 
-		middlewareScenario("When auth_proxy is enabled and IPv6 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.fakeReq("GET", "/")
-			sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
-			sc.req.RemoteAddr = "[2001:23]:12345"
-			sc.exec()
+				sc.req.Header.Add(setting.AuthProxyHeaderName, name)
+				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 2001:23 is not from the authentication proxy")
+				Convey("Should init user via ldap", func() {
+					So(called, ShouldBeTrue)
+					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() {
-				So(sc.resp.Code, ShouldEqual, 407)
-				So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 2001:23 is not from the authentication proxy")
-			})
-		})
+			middlewareScenario(t, "should not sync the user if it's in the cache", func(sc *scenarioContext) {
+				called := false
+				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) {
-			setting.AuthProxyEnabled = true
-			setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
-			setting.AuthProxyHeaderProperty = "username"
-			setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
+				bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
+					query.Result = &m.User{Id: 32}
+					return nil
+				})
 
-			bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
-				query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
-				return nil
-			})
+				bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
+					query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
+					return nil
+				})
 
-			bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
-				cmd.Result = &m.User{Id: 33}
-				return nil
-			})
+				key := fmt.Sprintf(cachePrefix, name)
+				sc.remoteCacheService.Set(key, int64(33), 0)
+				sc.fakeReq("GET", "/")
 
-			sc.fakeReq("GET", "/")
-			sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
-			sc.req.RemoteAddr = "[2001::23]:12345"
-			sc.exec()
+				sc.req.Header.Add(setting.AuthProxyHeaderName, name)
+				sc.exec()
 
-			Convey("Should init context with user info", func() {
-				So(sc.context.IsSignedIn, ShouldBeTrue)
-				So(sc.context.UserId, ShouldEqual, 33)
-				So(sc.context.OrgId, ShouldEqual, 4)
-			})
-		})
+				cacheValue, cacheErr := sc.remoteCacheService.Get(key)
 
-		middlewareScenario("When auth_proxy is enabled and IPv4 request RemoteAddr is 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"
+				Convey("Should init user via cache", func() {
+					So(called, ShouldBeFalse)
 
-			bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
-				query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
-				return nil
-			})
+					So(sc.context.IsSignedIn, ShouldBeTrue)
+					So(sc.context.UserId, ShouldEqual, 32)
+					So(sc.context.OrgId, ShouldEqual, 4)
 
-			bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
-				cmd.Result = &m.User{Id: 33}
-				return nil
+					So(cacheValue, ShouldEqual, 33)
+					So(cacheErr, ShouldBeNil)
+				})
 			})
 
-			sc.fakeReq("GET", "/")
-			sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
-			sc.req.RemoteAddr = "192.168.1.10:12345"
-			sc.exec()
+			middlewareScenario(t, "should create an user from a header", func(sc *scenarioContext) {
+				setting.LdapEnabled = false
+				setting.AuthProxyAutoSignUp = true
 
-			Convey("Should init context with user info", func() {
-				So(sc.context.IsSignedIn, ShouldBeTrue)
-				So(sc.context.UserId, ShouldEqual, 33)
-				So(sc.context.OrgId, ShouldEqual, 4)
-			})
-		})
+				bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
+					if query.UserId > 0 {
+						query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
+						return nil
+					}
+					return m.ErrUserNotFound
+				})
 
-		middlewareScenario("When auth_proxy is enabled and IPv6 request RemoteAddr is 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"
+				bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
+					cmd.Result = &m.User{Id: 33}
+					return nil
+				})
 
-			bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
-				query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
-				return nil
-			})
+				sc.fakeReq("GET", "/")
+				sc.req.Header.Add(setting.AuthProxyHeaderName, name)
+				sc.exec()
 
-			bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
-				cmd.Result = &m.User{Id: 33}
-				return nil
+				Convey("Should create user from header info", func() {
+					So(sc.context.IsSignedIn, ShouldBeTrue)
+					So(sc.context.UserId, ShouldEqual, 33)
+					So(sc.context.OrgId, ShouldEqual, 4)
+				})
 			})
 
-			sc.fakeReq("GET", "/")
-			sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
-			sc.req.RemoteAddr = "[2001::23]:12345"
-			sc.exec()
+			middlewareScenario(t, "should get an existing user from header", func(sc *scenarioContext) {
+				setting.LdapEnabled = false
 
-			Convey("Should init context with user info", func() {
-				So(sc.context.IsSignedIn, ShouldBeTrue)
-				So(sc.context.UserId, ShouldEqual, 33)
-				So(sc.context.OrgId, ShouldEqual, 4)
-			})
-		})
+				bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
+					query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
+					return nil
+				})
 
-		middlewareScenario("When session exists for previous user, create a new session", func(sc *scenarioContext) {
-			setting.AuthProxyEnabled = true
-			setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
-			setting.AuthProxyHeaderProperty = "username"
-			setting.AuthProxyWhitelist = ""
+				bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
+					cmd.Result = &m.User{Id: 12}
+					return nil
+				})
 
-			bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
-				query.Result = &m.User{Id: 32}
-				return nil
-			})
+				sc.fakeReq("GET", "/")
+				sc.req.Header.Add(setting.AuthProxyHeaderName, name)
+				sc.exec()
 
-			bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
-				query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
-				return nil
+				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)
+				})
 			})
 
-			// create session
-			sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) {
-				c.Session.Set(session.SESS_KEY_USERID, int64(33))
-			}).exec()
+			middlewareScenario(t, "should allow the request from whitelist IP", func(sc *scenarioContext) {
+				setting.AuthProxyWhitelist = "192.168.1.0/24, 2001::0/120"
+				setting.LdapEnabled = false
 
-			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")
-			sc.exec()
+				bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
+					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() {
-				So(oldSessionID, ShouldNotEqual, newSessionID)
+				Convey("Should init context with user info", func() {
+					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) {
-			setting.AuthProxyEnabled = true
-			setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
-			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
-			}
+			middlewareScenario(t, "should not allow the request from whitelist IP", func(sc *scenarioContext) {
+				setting.AuthProxyWhitelist = "8.8.8.8"
+				setting.LdapEnabled = false
 
-			bus.AddHandler("test", func(query *m.UpsertUserCommand) error {
-				query.Result = &m.User{Id: 32}
-				return nil
-			})
+				bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
+					query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
+					return nil
+				})
 
-			bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
-				query.Result = &m.SignedInUser{OrgId: 4, UserId: 32}
-				return nil
-			})
+				bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
+					cmd.Result = &m.User{Id: 33}
+					return nil
+				})
 
-			sc.fakeReq("GET", "/")
-			sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
-			sc.exec()
+				sc.fakeReq("GET", "/")
+				sc.req.Header.Add(setting.AuthProxyHeaderName, name)
+				sc.req.RemoteAddr = "[2001::23]:12345"
+				sc.exec()
 
-			Convey("Should call syncGrafanaUserWithLdapUser", func() {
-				So(called, ShouldBeTrue)
+				Convey("should return 407 status code", func() {
+					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() {
 		defer bus.ClearBusHandlers()
 
@@ -562,9 +469,10 @@ func middlewareScenario(desc string, fn scenarioFunc) {
 			Delims:    macaron.Delims{Left: "[[", Right: "]]"},
 		}))
 
-		session.Init(&msession.Options{}, 0)
 		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
 		session.StartSessionGC = func() {}
 		setting.SessionOptions = msession.Options{}
@@ -597,6 +505,7 @@ type scenarioContext struct {
 	defaultHandler       macaron.Handler
 	url                  string
 	userAuthTokenService *auth.FakeUserAuthTokenService
+	remoteCacheService   *remotecache.RemoteCache
 
 	req *http.Request
 }
@@ -622,13 +531,6 @@ func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
 	So(err, ShouldBeNil)
 	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
 }
 

+ 3 - 4
pkg/middleware/org_redirect_test.go

@@ -1,9 +1,8 @@
 package middleware
 
 import (
-	"testing"
-
 	"fmt"
+	"testing"
 
 	"github.com/grafana/grafana/pkg/bus"
 	m "github.com/grafana/grafana/pkg/models"
@@ -13,7 +12,7 @@ import (
 func TestOrgRedirectMiddleware(t *testing.T) {
 
 	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")
 			bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error {
 				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")
 			bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error {
 				return fmt.Errorf("")

+ 4 - 5
pkg/middleware/quota_test.go

@@ -3,11 +3,10 @@ package middleware
 import (
 	"testing"
 
-	"github.com/grafana/grafana/pkg/services/auth"
-	"github.com/grafana/grafana/pkg/services/quota"
-
 	"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/quota"
 	"github.com/grafana/grafana/pkg/setting"
 	. "github.com/smartystreets/goconvey/convey"
 )
@@ -43,7 +42,7 @@ func TestMiddlewareQuota(t *testing.T) {
 		}
 		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 {
 				query.Result = &m.GlobalQuotaDTO{
 					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")
 			bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
 				query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}

+ 7 - 4
pkg/middleware/recovery_test.go

@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/infra/remotecache"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/auth"
 	"github.com/grafana/grafana/pkg/setting"
@@ -17,7 +18,7 @@ func TestRecoveryMiddleware(t *testing.T) {
 
 	Convey("Given an api route that panics", t, func() {
 		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.fakeReq("GET", apiURL).exec()
 			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() {
 		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.fakeReq("GET", apiURL).exec()
 
@@ -45,7 +46,7 @@ func PanicHandler(c *m.ReqContext) {
 	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() {
 		defer bus.ClearBusHandlers()
 
@@ -64,7 +65,9 @@ func recoveryScenario(desc string, url string, fn scenarioFunc) {
 		}))
 
 		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
 		sc.m.Use(OrgRedirect())
 		sc.m.Use(AddDefaultResponseHeaders())

+ 3 - 5
pkg/services/sqlstore/sqlstore.go

@@ -11,6 +11,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/go-sql-driver/mysql"
+	"github.com/go-xorm/xorm"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/log"
 	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/sqlutil"
 	"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/util"
 	_ "github.com/lib/pq"
 	sqlite3 "github.com/mattn/go-sqlite3"
 )