mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: use remote cache instead of session storage (#16114)
Replaces session storage in auth_proxy middleware with remote cache Fixes #15161
This commit is contained in:
parent
645a6e5856
commit
67cbc7d4cf
@ -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.
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 (
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bmizerany/assert"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
34
pkg/infra/remotecache/testing.go
Normal file
34
pkg/infra/remotecache/testing.go
Normal file
@ -0,0 +1,34 @@
|
||||
package remotecache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// NewFakeStore creates store for testing
|
||||
func NewFakeStore(t *testing.T) *RemoteCache {
|
||||
t.Helper()
|
||||
|
||||
opts := &setting.RemoteCacheOptions{
|
||||
Name: "database",
|
||||
ConnStr: "",
|
||||
}
|
||||
|
||||
SQLStore := sqlstore.InitTestDB(t)
|
||||
|
||||
dc := &RemoteCache{
|
||||
SQLStore: SQLStore,
|
||||
Cfg: &setting.Cfg{
|
||||
RemoteCacheOptions: opts,
|
||||
},
|
||||
}
|
||||
|
||||
err := dc.Init()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to init remote cache for test. error: %v", err)
|
||||
}
|
||||
|
||||
return dc
|
||||
}
|
@ -9,18 +9,19 @@ import (
|
||||
"time"
|
||||
|
||||
"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,
|
||||
}
|
||||
// load the user if we have them
|
||||
if inCache {
|
||||
query.UserId = userID.(int64)
|
||||
|
||||
if err := syncGrafanaUserWithLdapUser(syncQuery); err != nil {
|
||||
if err == login.ErrInvalidCredentials {
|
||||
ctx.Handle(500, "Unable to authenticate user", err)
|
||||
return false
|
||||
}
|
||||
|
||||
ctx.Handle(500, "Failed to sync user", err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
query.UserId = getRequestUserId(ctx)
|
||||
// if we're using ldap, pass authproxy login name to ldap user sync
|
||||
} 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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// initialize a new session
|
||||
if err := ctx.Session.Start(ctx.Context); err != nil {
|
||||
log.Error(3, "Failed to start session. error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Session.Set(AUTH_PROXY_SESSION_VAR, proxyHeaderValue)
|
||||
|
||||
ctx.SignedInUser = query.Result
|
||||
ctx.IsSignedIn = true
|
||||
ctx.Session.Set(session.SESS_KEY_USERID, ctx.UserId)
|
||||
|
||||
expiration := time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute
|
||||
value := query.UserId
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -1,145 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/login"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/session"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func TestAuthProxyWithLdapEnabled(t *testing.T) {
|
||||
Convey("When calling sync grafana user with ldap user", t, func() {
|
||||
|
||||
setting.LdapEnabled = true
|
||||
setting.AuthProxyLdapSyncTtl = 60
|
||||
|
||||
servers := []*login.LdapServerConf{{Host: "127.0.0.1"}}
|
||||
login.LdapCfg = login.LdapConfig{Servers: servers}
|
||||
mockLdapAuther := mockLdapAuthenticator{}
|
||||
|
||||
login.NewLdapAuthenticator = func(server *login.LdapServerConf) login.ILdapAuther {
|
||||
return &mockLdapAuther
|
||||
}
|
||||
|
||||
Convey("When user logs in, call SyncUser", func() {
|
||||
// arrange
|
||||
sess := newMockSession()
|
||||
ctx := m.ReqContext{Session: &sess}
|
||||
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeNil)
|
||||
|
||||
// act
|
||||
syncGrafanaUserWithLdapUser(&m.LoginUserQuery{
|
||||
ReqContext: &ctx,
|
||||
Username: "test",
|
||||
})
|
||||
|
||||
// assert
|
||||
So(mockLdapAuther.syncUserCalled, ShouldBeTrue)
|
||||
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, 0)
|
||||
})
|
||||
|
||||
Convey("When session variable not expired, don't sync and don't change session var", func() {
|
||||
// arrange
|
||||
sess := newMockSession()
|
||||
ctx := m.ReqContext{Session: &sess}
|
||||
now := time.Now().Unix()
|
||||
sess.Set(session.SESS_KEY_LASTLDAPSYNC, now)
|
||||
sess.Set(AUTH_PROXY_SESSION_VAR, "test")
|
||||
|
||||
// act
|
||||
syncGrafanaUserWithLdapUser(&m.LoginUserQuery{
|
||||
ReqContext: &ctx,
|
||||
Username: "test",
|
||||
})
|
||||
|
||||
// assert
|
||||
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldEqual, now)
|
||||
So(mockLdapAuther.syncUserCalled, ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("When lastldapsync is expired, session variable should be updated", func() {
|
||||
// arrange
|
||||
sess := newMockSession()
|
||||
ctx := m.ReqContext{Session: &sess}
|
||||
expiredTime := time.Now().Add(time.Duration(-120) * time.Minute).Unix()
|
||||
sess.Set(session.SESS_KEY_LASTLDAPSYNC, expiredTime)
|
||||
sess.Set(AUTH_PROXY_SESSION_VAR, "test")
|
||||
|
||||
// act
|
||||
syncGrafanaUserWithLdapUser(&m.LoginUserQuery{
|
||||
ReqContext: &ctx,
|
||||
Username: "test",
|
||||
})
|
||||
|
||||
// assert
|
||||
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, expiredTime)
|
||||
So(mockLdapAuther.syncUserCalled, ShouldBeTrue)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type mockSession struct {
|
||||
value map[interface{}]interface{}
|
||||
}
|
||||
|
||||
func newMockSession() mockSession {
|
||||
session := mockSession{}
|
||||
session.value = make(map[interface{}]interface{})
|
||||
return session
|
||||
}
|
||||
|
||||
func (s *mockSession) Start(c *macaron.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *mockSession) Set(k interface{}, v interface{}) error {
|
||||
s.value[k] = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *mockSession) Get(k interface{}) interface{} {
|
||||
return s.value[k]
|
||||
}
|
||||
|
||||
func (s *mockSession) Delete(k interface{}) interface{} {
|
||||
delete(s.value, k)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *mockSession) ID() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *mockSession) Release() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *mockSession) Destory(c *macaron.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *mockSession) RegenerateId(c *macaron.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockLdapAuthenticator struct {
|
||||
syncUserCalled bool
|
||||
}
|
||||
|
||||
func (a *mockLdapAuthenticator) Login(query *m.LoginUserQuery) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *mockLdapAuthenticator) SyncUser(query *m.LoginUserQuery) error {
|
||||
a.syncUserCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *mockLdapAuthenticator) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *login.LdapUserInfo) (*m.User, error) {
|
||||
return nil, nil
|
||||
}
|
@ -11,7 +11,7 @@ func TestMiddlewareAuth(t *testing.T) {
|
||||
Convey("Given the grafana middleware", t, func() {
|
||||
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()
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
}
|
||||
|
@ -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) {
|
||||
Convey("auth_proxy", func() {
|
||||
setting.AuthProxyEnabled = true
|
||||
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
|
||||
setting.AuthProxyHeaderProperty = "username"
|
||||
setting.LdapEnabled = false
|
||||
|
||||
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
||||
query.Result = &m.SignedInUser{OrgId: 2, UserId: 12}
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
|
||||
cmd.Result = &m.User{Id: 12}
|
||||
return nil
|
||||
})
|
||||
|
||||
setting.SessionOptions = msession.Options{}
|
||||
sc.fakeReq("GET", "/")
|
||||
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
|
||||
sc.exec()
|
||||
|
||||
Convey("should init context with user info", func() {
|
||||
So(sc.context.IsSignedIn, ShouldBeTrue)
|
||||
So(sc.context.UserId, ShouldEqual, 12)
|
||||
So(sc.context.OrgId, ShouldEqual, 2)
|
||||
})
|
||||
})
|
||||
|
||||
middlewareScenario("When auth_proxy is enabled enabled and user does not exists", func(sc *scenarioContext) {
|
||||
setting.AuthProxyEnabled = true
|
||||
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
|
||||
setting.AuthProxyHeaderProperty = "username"
|
||||
setting.AuthProxyWhitelist = ""
|
||||
setting.AuthProxyAutoSignUp = true
|
||||
setting.LdapEnabled = false
|
||||
setting.LdapEnabled = true
|
||||
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
|
||||
setting.AuthProxyHeaderProperty = "username"
|
||||
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(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: 32}
|
||||
return nil
|
||||
})
|
||||
|
||||
sc.fakeReq("GET", "/")
|
||||
|
||||
sc.req.Header.Add(setting.AuthProxyHeaderName, name)
|
||||
sc.exec()
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
|
||||
cmd.Result = &m.User{Id: 33}
|
||||
return nil
|
||||
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
|
||||
}
|
||||
|
||||
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: 32}
|
||||
return nil
|
||||
})
|
||||
|
||||
key := fmt.Sprintf(cachePrefix, name)
|
||||
sc.remoteCacheService.Set(key, int64(33), 0)
|
||||
sc.fakeReq("GET", "/")
|
||||
|
||||
sc.req.Header.Add(setting.AuthProxyHeaderName, name)
|
||||
sc.exec()
|
||||
|
||||
cacheValue, cacheErr := sc.remoteCacheService.Get(key)
|
||||
|
||||
Convey("Should init user via cache", func() {
|
||||
So(called, ShouldBeFalse)
|
||||
|
||||
So(sc.context.IsSignedIn, ShouldBeTrue)
|
||||
So(sc.context.UserId, ShouldEqual, 32)
|
||||
So(sc.context.OrgId, ShouldEqual, 4)
|
||||
|
||||
So(cacheValue, ShouldEqual, 33)
|
||||
So(cacheErr, ShouldBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
sc.fakeReq("GET", "/")
|
||||
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
|
||||
sc.exec()
|
||||
middlewareScenario(t, "should create an user from a header", func(sc *scenarioContext) {
|
||||
setting.LdapEnabled = false
|
||||
setting.AuthProxyAutoSignUp = true
|
||||
|
||||
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)
|
||||
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
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
|
||||
cmd.Result = &m.User{Id: 33}
|
||||
return nil
|
||||
})
|
||||
|
||||
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(setting.AuthProxyHeaderName, name)
|
||||
sc.exec()
|
||||
|
||||
sc.fakeReq("GET", "/")
|
||||
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
|
||||
sc.req.RemoteAddr = "192.168.3.1:12345"
|
||||
sc.exec()
|
||||
|
||||
Convey("should return 407 status code", func() {
|
||||
So(sc.resp.Code, ShouldEqual, 407)
|
||||
So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 192.168.3.1 is not from the authentication proxy")
|
||||
})
|
||||
})
|
||||
|
||||
middlewareScenario("When auth_proxy is enabled and IPv4 request RemoteAddr is not within trusted CIDR block", func(sc *scenarioContext) {
|
||||
setting.AuthProxyEnabled = true
|
||||
setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
|
||||
setting.AuthProxyHeaderProperty = "username"
|
||||
setting.AuthProxyWhitelist = "192.168.1.0/24, 2001::0/120"
|
||||
|
||||
sc.fakeReq("GET", "/")
|
||||
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
|
||||
sc.req.RemoteAddr = "192.168.3.1:12345"
|
||||
sc.exec()
|
||||
|
||||
Convey("should return 407 status code", func() {
|
||||
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 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.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("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("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.GetSignedInUserQuery) error {
|
||||
query.Result = &m.SignedInUser{OrgId: 4, UserId: 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)
|
||||
})
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
|
||||
cmd.Result = &m.User{Id: 33}
|
||||
return nil
|
||||
middlewareScenario(t, "should get an existing user from header", func(sc *scenarioContext) {
|
||||
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
|
||||
})
|
||||
|
||||
sc.fakeReq("GET", "/")
|
||||
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, 12)
|
||||
So(sc.context.OrgId, ShouldEqual, 2)
|
||||
})
|
||||
})
|
||||
|
||||
sc.fakeReq("GET", "/")
|
||||
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
|
||||
sc.req.RemoteAddr = "[2001::23]:12345"
|
||||
sc.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
|
||||
|
||||
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: 4, UserId: 33}
|
||||
return nil
|
||||
})
|
||||
|
||||
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"
|
||||
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.req.RemoteAddr = "[2001::23]:12345"
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
|
||||
cmd.Result = &m.User{Id: 33}
|
||||
return nil
|
||||
})
|
||||
middlewareScenario(t, "should not allow the request from whitelist IP", func(sc *scenarioContext) {
|
||||
setting.AuthProxyWhitelist = "8.8.8.8"
|
||||
setting.LdapEnabled = false
|
||||
|
||||
sc.fakeReq("GET", "/")
|
||||
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
|
||||
sc.req.RemoteAddr = "192.168.1.10:12345"
|
||||
sc.exec()
|
||||
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
||||
query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
|
||||
return nil
|
||||
})
|
||||
|
||||
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(cmd *m.UpsertUserCommand) error {
|
||||
cmd.Result = &m.User{Id: 33}
|
||||
return nil
|
||||
})
|
||||
|
||||
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"
|
||||
sc.fakeReq("GET", "/")
|
||||
sc.req.Header.Add(setting.AuthProxyHeaderName, name)
|
||||
sc.req.RemoteAddr = "[2001::23]:12345"
|
||||
sc.exec()
|
||||
|
||||
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
|
||||
query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
|
||||
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.req.RemoteAddr = "[2001::23]:12345"
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
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(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: 32}
|
||||
return nil
|
||||
})
|
||||
|
||||
// create session
|
||||
sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) {
|
||||
c.Session.Set(session.SESS_KEY_USERID, int64(33))
|
||||
}).exec()
|
||||
|
||||
oldSessionID := sc.context.Session.ID()
|
||||
|
||||
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
|
||||
sc.exec()
|
||||
|
||||
newSessionID := sc.context.Session.ID()
|
||||
|
||||
Convey("Should not share session with other user", func() {
|
||||
So(oldSessionID, ShouldNotEqual, newSessionID)
|
||||
})
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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: 32}
|
||||
return nil
|
||||
})
|
||||
|
||||
sc.fakeReq("GET", "/")
|
||||
sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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("")
|
||||
|
@ -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}
|
||||
|
@ -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())
|
||||
|
@ -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"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user