Bläddra i källkod

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

Replaces session storage in auth_proxy middleware with remote cache

Fixes #15161
Oleg Gaidarenko 6 år sedan
förälder
incheckning
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.
 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.
 
 

+ 2 - 3
pkg/api/common_test.go

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

+ 15 - 13
pkg/api/http_server.go

@@ -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:""`
-	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 {
 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

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

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

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

@@ -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"
 )
 )

+ 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"
 	"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 (
-	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 {
 	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
-	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
 		// 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!
-	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
 	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()
-
-	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
 	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

+ 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() {
 	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()

+ 2 - 2
pkg/middleware/dashboard_redirect_test.go

@@ -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()

+ 3 - 4
pkg/middleware/middleware.go

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

+ 155 - 253
pkg/middleware/middleware_test.go

@@ -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) {
-			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.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
-			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 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() {
 	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
 }
 }
 
 

+ 3 - 4
pkg/middleware/org_redirect_test.go

@@ -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("")

+ 4 - 5
pkg/middleware/quota_test.go

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

+ 7 - 4
pkg/middleware/recovery_test.go

@@ -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())

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

@@ -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"
 )
 )